From 896fde13af078b1df7a03ddf9e942e8b096cba36 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sun, 7 Apr 2024 12:28:09 +0330 Subject: [PATCH 01/53] Update data structre - Remove TodoList - Replace TodoArray with TodoList - Add restriction function to App struct --- src/cli_app.rs | 9 +- src/todo_app.rs | 159 +++------- src/todo_app/todo.rs | 13 +- src/todo_app/todo/dependency.rs | 14 +- src/todo_app/todo_list.rs | 497 ++++++++------------------------ 5 files changed, 180 insertions(+), 512 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index b245761..80e3756 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -1,7 +1,7 @@ use std::io; use crate::DisplayArgs; -use super::todo_app::{App ,TodoList, Todo}; +use super::todo_app::{App ,TodoArray, Todo}; #[inline] pub fn run(app: &mut App) -> io::Result<()>{ @@ -91,11 +91,8 @@ impl PrintTodoTree { } #[inline] - pub fn print_list(&mut self, todo_list: &TodoList, display_args: &DisplayArgs) { - let mut todos = todo_list.undone.todos.clone(); - if display_args.show_done { - todos.extend(todo_list.done.todos.clone()) - } + pub fn print_list(&mut self, todo_list: &TodoArray, display_args: &DisplayArgs) { + let todos = todo_list.todos.clone(); for (index, todo) in todos.iter().enumerate() { self.is_last = index == todos.len() - 1; diff --git a/src/todo_app.rs b/src/todo_app.rs index d6bf35d..75a388b 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -5,11 +5,11 @@ mod todo_list; mod todo; mod search; use search::Search; -pub use todo_list::TodoList; pub use todo::Todo; use crate::Args; pub use self::todo::PriorityType; +pub use self::todo_list::TodoArray; #[derive(Clone)] struct SearchPosition { @@ -20,7 +20,7 @@ struct SearchPosition { pub struct App { selected: Vec, clipboard: Clipboard, - pub(super) todo_list: TodoList, + pub(super) todo_list: TodoArray, index: usize, prior_indexes: Vec, changed:bool, @@ -30,13 +30,14 @@ pub struct App { tree_search_positions: Vec, last_query: String, x_index: usize, - y_index: usize + y_index: usize, + restriction: Option bool>, } impl App { #[inline] pub fn new(args: Args) -> Self { - let todo_list = TodoList::read(&args.todo_path, !args.no_tree, true); + let todo_list = TodoArray::read(&args.todo_path, !args.no_tree, true); let mut app = App { x_index: 0, y_index: 0, @@ -51,6 +52,7 @@ impl App { changed: false, args, search: Search::new(), + restriction: Some(|todo: &Todo| !todo.done()), }; for str in app.args.search_and_select.clone() { app.search(Some(str)); @@ -64,12 +66,12 @@ impl App { #[inline] pub fn append_list_from_path(&mut self, path: PathBuf) { - let todo_list = TodoList::read(&path, !self.args.no_tree, true); + let todo_list = TodoArray::read(&path, !self.args.no_tree, true); self.append_list(todo_list) } #[inline] - pub fn append_list(&mut self, todo_list: TodoList) { + pub fn append_list(&mut self, todo_list: TodoArray) { self.mut_current_list().append_list(todo_list) } @@ -82,10 +84,10 @@ impl App { let sel_index = *sel_index - index_shift; let iter_index = iter_index - iter_index; if let Some(priority) = self.args.set_selected_priority { - self.todo_list[sel_index].set_priority(priority as PriorityType); + self.todo_list.index_mut(sel_index).set_priority(priority as PriorityType); } if let Some(message) = self.args.set_selected_message.clone() { - self.todo_list[sel_index].set_message(message); + self.todo_list.index_mut(sel_index).set_message(message); } if self.args.delete_selected { self.todo_list.remove(sel_index); @@ -94,7 +96,7 @@ impl App { self.changed = true; } if self.args.done_selected { - self.todo_list[sel_index].toggle_done(); + self.todo_list.index_mut(sel_index).toggle_done(); if !self.show_done() { self.selected.remove(iter_index); } @@ -103,27 +105,15 @@ impl App { } } - fn traverse_parents_from_root(&mut self, callback: fn(&mut App, &TodoList, &[usize])) { - if self.show_done() { - self.todo_list.clone().traverse_tree(callback, None, self) - } else { - self.todo_list.clone().traverse_tree_undone(callback, None, self) - } + fn traverse_parents_from_root(&mut self, callback: fn(&mut App, &TodoArray, &[usize])) { + self.todo_list.clone().traverse_tree(callback, None, self) } - fn add_to_tree_positions(&mut self, list: &TodoList, prior_indices: &[usize]) { + fn add_to_tree_positions(&mut self, list: &TodoArray, prior_indices: &[usize]) { let mut matching_indices : Vec = vec![]; - if self.show_done() { - for (i, todo) in todo_list::all_todos!(list).enumerate() { - if todo.matches(self.last_query.as_str()) { - matching_indices.push(i) - } - } - } else { - for (i, todo) in todo_list::undone_todos!(list).enumerate() { - if todo.matches(self.last_query.as_str()) { - matching_indices.push(i) - } + for (i, todo) in list.todos(self.restriction).iter().enumerate() { + if todo.matches(self.last_query.as_str()) { + matching_indices.push(i) } } if !matching_indices.is_empty() { @@ -158,7 +148,7 @@ impl App { self.prior_indexes = position.prior_indices.clone(); let list = self.current_list(); for index in position.matching_indices.clone() { - println!("{}",list[index].display(&self.args.display_args)); + println!("{}",list.index(index,self.restriction).display(&self.args.display_args)); } } } @@ -196,68 +186,16 @@ impl App { #[inline] pub fn append(&mut self, message:String) { self.mut_current_list().push(Todo::default(message, 0)); - self.index = self.current_list().undone.len()-1; + self.index = self.current_list().len(self.restriction)-1; } pub fn index(&self) -> usize { self.index } - #[inline] - /// fix done and undone lists of current list - pub fn fix_done_undone(&mut self) { - self.fix_dependency_done_undone(); - let show_done = self.show_done(); - let current_list = self.mut_current_list(); - current_list.fix_undone(); - if show_done { - current_list.fix_done(); - } - - self.traverse_up_and_fix(); - } - - #[inline] - /// fix done and undone lists of current todo's dependency - fn fix_dependency_done_undone(&mut self) { - let show_done = self.show_done(); - if let Some(todo) = self.mut_todo() { - - let dep_list = &mut todo.dependency.todo_list; - - dep_list.fix_undone(); - if show_done { - dep_list.fix_done(); - } - - } - - } - - #[inline] - /// traverses up and fixes the undone todo, if the dependencies are done, recursively - fn traverse_up_and_fix(&mut self) { - while self.only_undone_empty() && !self.is_root() { - self.traverse_up(); - match self.mut_todo() { - Some(todo) => { - todo.set_done(true) - } - _ => {} - } - self.mut_current_list().fix_undone(); - if self.show_done() { - self.mut_current_list().fix_done(); - } - } - } - #[inline] pub fn search(&mut self, query:Option) { - let mut todo_messages = self.current_list().undone.messages(); - if self.show_done() { - todo_messages.extend(self.current_list().done.messages()); - } + let todo_messages = self.current_list().messages(self.restriction); self.search.search(query, todo_messages); } @@ -276,10 +214,6 @@ impl App { #[inline] pub fn toggle_show_done(&mut self) { self.args.display_args.show_done = !self.show_done(); - self.search(None); - if !self.tree_search_positions.is_empty() { - self.tree_search(None) - } } #[inline] @@ -319,23 +253,14 @@ impl App { #[inline] pub fn toggle_current_done(&mut self) { - let was_done = self.todo().unwrap().done(); self.mut_todo().unwrap().toggle_done(); - self.fix_done_undone(); - if self.show_done() { - let index = if was_done { - self.current_list().undone.len()-1 - } else { - self.current_list().len()-1 - }; - self.index = self.mut_current_list().reorder(index); - } + // self.traverse_up(); } #[inline] pub fn read(&mut self) { self.changed = false; - self.todo_list = TodoList::read(&self.args.todo_path, true, true); + self.todo_list = TodoArray::read(&self.args.todo_path, true, true); } #[inline] @@ -352,8 +277,8 @@ impl App { let mut list = &self.todo_list; let mut parent = None; for index in self.prior_indexes.iter() { - parent = Some(&list[*index]); - if let Some(todo_list) = &list[*index].dependency.todo_list() { + parent = Some(list.index(*index, self.restriction)); + if let Some(todo_list) = list.index(*index, self.restriction).dependency.todo_list() { list = todo_list } else { break @@ -428,7 +353,7 @@ impl App { #[inline] pub fn is_todos_empty(&self) -> bool{ if self.show_done() { - self.current_list().is_empty() + self.current_list().is_empty(self.restriction) } else { self.is_undone_empty() } @@ -443,10 +368,10 @@ impl App { let size = self.len(); if size <= index { - return Some(&mut self.mut_current_list()[size - 1]); + return Some(self.mut_current_list().index_mut(size - 1)); } - Some(&mut self.mut_current_list()[index]) + Some(self.mut_current_list().index_mut(index)) } #[inline] @@ -461,15 +386,11 @@ impl App { #[inline] pub fn len(&self) -> usize { - if self.show_done() { - self.current_list().len() - } else { - self.current_list().undone.len() - } + self.current_list().len(self.restriction) } #[inline] - pub fn mut_current_list(&mut self) -> &mut TodoList { + pub fn mut_current_list(&mut self) -> &mut TodoArray { self.changed = true; let is_root = self.is_root(); let mut list = &mut self.todo_list; @@ -477,19 +398,19 @@ impl App { return list; } for index in self.prior_indexes.iter() { - list = &mut list[*index].dependency.todo_list + list = &mut list.index_mut(*index).dependency.todo_list }; list } #[inline] - pub fn current_list(&self) -> &TodoList { + pub fn current_list(&self) -> &TodoArray { let mut list = &self.todo_list; if self.is_root() { return list; } for index in self.prior_indexes.iter() { - if let Some(todo_list) = &list[*index].dependency.todo_list() { + if let Some(todo_list) = &list.index(*index, self.restriction).dependency.todo_list() { list = todo_list } else { break @@ -548,12 +469,12 @@ impl App { #[inline] pub fn is_undone_empty(&self) -> bool{ - self.current_list().undone.is_empty() + self.current_list().is_empty(self.restriction) } #[inline] pub fn is_done_empty(&self) -> bool{ - self.current_list().done.is_empty() + self.current_list().is_empty(self.restriction) } #[inline] @@ -583,10 +504,10 @@ impl App { let size = self.len(); if size <= index { - return Some(¤t_list[size - 1]); + return Some(¤t_list.index(size - 1, self.restriction)); } - Some(&self.current_list()[index]) + Some(&self.current_list().index(index, self.restriction)) } #[inline] @@ -610,8 +531,8 @@ impl App { } #[inline] - pub fn display_list(&self, todo_list: &TodoList) -> Vec { - todo_list.display(&self.args.display_args) + pub fn display_list(&self, todo_list: &TodoArray) -> Vec { + todo_list.display(&self.args.display_args, self.restriction) } #[inline] @@ -664,7 +585,7 @@ impl App { let todos_count = self.len(); match Todo::try_from(self.clipboard.get_text()) { Ok(mut todo) => { - let todo_parent = TodoList::dependency_parent(&self.args.todo_path, true); + let todo_parent = TodoArray::dependency_parent(&self.args.todo_path, true); let _ = todo.dependency.read(&todo_parent); let bottom = self.bottom()+1; let list = &mut self.mut_current_list(); @@ -697,7 +618,7 @@ impl App { #[inline] pub fn print_selected(&self) { for index in self.selected.clone() { - println!("{}", self.todo_list[index].display(&self.args.display_args)); + println!("{}", self.todo_list.index(index, self.restriction).display(&self.args.display_args)); } } } diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 7af402e..383639a 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -10,7 +10,7 @@ mod dependency; use dependency::Dependency; use schedule::Schedule; use note::{sha1, open_temp_editor}; -use super::TodoList; +use super::TodoArray; use crate::DisplayArgs; //}}} @@ -169,7 +169,7 @@ impl Todo { } #[inline] - pub fn dependencies(&self) -> Option<&TodoList> { + pub fn dependencies(&self) -> Option<&TodoArray> { self.dependency.todo_list() } @@ -346,10 +346,11 @@ impl Todo { } else { self.priority }; - if self.schedule.is_reminder() { - priority*10-5 - } else { - priority*10 + match (self.schedule.is_reminder(), self.done()) { + (true, true) => priority*20-10, + (false, true) => priority*20, + (false, false) => priority*2, + (true, false) => priority*2-1, } } diff --git a/src/todo_app/todo/dependency.rs b/src/todo_app/todo/dependency.rs index d2c24e3..5b03b99 100644 --- a/src/todo_app/todo/dependency.rs +++ b/src/todo_app/todo/dependency.rs @@ -1,6 +1,6 @@ use scanf::sscanf; use std::{io::{self, Write}, path::PathBuf, fs::File}; -use super::TodoList; +use super::TodoArray; use super::Todo; #[derive(Debug, PartialEq, Clone, Default)] @@ -17,7 +17,7 @@ pub struct Dependency { mode: DependencyMode, note: String, written: bool, - pub(crate) todo_list: TodoList, + pub(crate) todo_list: TodoArray, } impl Dependency { @@ -37,7 +37,7 @@ impl Dependency { mode, name, note: String::new(), - todo_list: TodoList::new(), + todo_list: TodoArray::new(), } } @@ -95,7 +95,7 @@ impl Dependency { self.name = name_todo; self.mode = DependencyMode::TodoList; } - self.todo_list = TodoList::read(&path.join(&self.name), true, false); + self.todo_list = TodoArray::read(&path.join(&self.name), true, false); } _ => {} }; @@ -105,7 +105,7 @@ impl Dependency { } #[inline] - pub fn todo_list(&self) -> Option<&TodoList> { + pub fn todo_list(&self) -> Option<&TodoArray> { if self.mode == DependencyMode::TodoList { Some(&self.todo_list) } else { @@ -114,7 +114,7 @@ impl Dependency { } #[inline] - pub fn todo_list_mut(&mut self) -> Option<&mut TodoList> { + pub fn todo_list_mut(&mut self) -> Option<&mut TodoArray> { if self.mode == DependencyMode::TodoList { Some(&mut self.todo_list) } else { @@ -142,7 +142,7 @@ impl Dependency { #[inline] pub fn path(&self ,path: &PathBuf) -> Option{ match path.parent() { - Some(path) => Some(TodoList::dependency_parent(&path.to_path_buf(), false)), + Some(path) => Some(TodoArray::dependency_parent(&path.to_path_buf(), false)), None => None, } } diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 134c414..47bfead 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -7,232 +7,67 @@ use std::io; use super::{App, Todo}; use crate::DisplayArgs; -macro_rules! all_todos { - ($self:ident) => { - [&$self.undone.todos, &$self.done.todos].iter().flat_map(|v| v.iter()) - }; -} - -macro_rules! undone_todos { - ($self:ident) => { - [&$self.undone.todos].iter().flat_map(|v| v.iter()) - }; -} - -pub(crate) use all_todos; -pub(crate) use undone_todos; - -macro_rules! all_todos_mut { - ($self:ident) => { - [&mut $self.undone.todos, &mut $self.done.todos].iter_mut().flat_map(|v| v.iter_mut()) - }; -} - #[derive(Debug,PartialEq, Clone, Default)] pub struct TodoArray { - pub todos: Vec -} - -impl Index for TodoArray { - type Output = Todo; - fn index(&self, index:usize) -> &Self::Output { - &self.todos[index] - } -} - -impl IndexMut for TodoArray { - fn index_mut(&mut self, index:usize) -> &mut Todo { - &mut self.todos[index] - } -} - -impl Index for TodoList { - type Output = Todo; - fn index(&self, index:usize) -> &Self::Output { - let size = self.undone.len(); - if index < size { - &self.undone[index] - } else { - &self.done[index-size] - } - } -} - -impl IndexMut for TodoList { - fn index_mut(&mut self, index:usize) -> &mut Todo { - let size = self.undone.len(); - if index < size { - &mut self.undone[index] - } else { - &mut self.done[index-size] - } - } + todos: Vec, } +type Output = Todo; impl TodoArray { pub fn new() -> Self{ TodoArray { - todos: Vec::new() - } - } - - pub fn with_capacity(capacity: usize) -> Self{ - TodoArray { - todos: Vec::with_capacity(capacity), + todos: Vec::new(), } } - pub fn iter(&self) -> std::slice::Iter { - self.todos.iter() + pub fn index(&self, index:usize, restriction: Option bool>) -> &Output { + self.todos(restriction)[index] } - pub fn iter_mut(&mut self) -> std::slice::IterMut { - self.todos.iter_mut() - } - - pub fn messages(&self) -> Vec { - self.todos.iter().map(|todo| todo.message.clone()).collect() - } - - pub fn display(&self, args: &DisplayArgs) -> Vec { - self.todos.iter().map(|todo| todo.display(&args)).collect() - } - - pub fn len(&self) -> usize { - self.todos.len() - } - - pub fn is_empty(&self) -> bool { - self.todos.is_empty() - } - - pub fn remove(&mut self, index:usize) -> Todo{ - self.todos.remove(index) - } - - pub fn push(&mut self,item:Todo) { - self.todos.push(item) - } - - fn insert(&mut self,index:usize, item:Todo) { - self.todos.insert(index,item) + pub fn index_mut(&mut self, index:usize) -> &mut Output { + &mut self.todos[index] } - #[inline(always)] - fn reorder_low_high(&self, index:usize) -> (usize, usize){ - let priority = self.todos[index].comparison_priority(); - if index+1 < self.todos.len() && priority > self.todos[index+1].comparison_priority() { - (index+1, self.todos.len()-1) + pub fn todos(&self, restriction: Option bool>) -> Vec<&Todo> { + if let Some(restriction) = restriction { + self.todos.iter().filter(|todo| restriction(todo)).collect() } else { - (0, index) - } - } - - pub fn reorder(&mut self, index:usize) -> usize { - let priority = self.todos[index].comparison_priority(); - - if priority < self.todos[0].comparison_priority() { - return self.move_index(index, 0, 1) + self.todos.iter().collect() } - - let (low, high) = self.reorder_low_high(index); - for middle in low..high { - if priority < self.todos[middle+1].comparison_priority() && - priority >= self.todos[middle].comparison_priority() { - return self.move_index(index, middle, 0); - } - } - self.move_index(index, high, 0) } - #[inline(always)] - fn move_index(&mut self, from: usize, to: usize, shift:usize) -> usize{ - - let mut i = from; - if from < to - { - for j in from..to { - self.todos.swap(j, j+1); - i = j+1; - } + pub fn mut_todos(&mut self, restriction: Option bool>) -> Vec<&mut Todo> { + if let Some(restriction) = restriction { + self.todos.iter_mut().filter(|todo| restriction(todo)).collect() } else { - for j in (to+1-shift..from).rev() { - self.todos.swap(j, j+1); - i = j; - } - - } - i - } - - pub fn print (&self) { - let mut i = 1; - for todo in &self.todos { - println!("{} - {}", i,todo.as_string()); - i+=1; + self.todos.iter_mut().collect() } } - #[inline(always)] - pub fn sort (&mut self) { - // , ascending:Option - // let ascending = ascending.unwrap_or(false); - self.todos.sort_by(|a, b| a.comparison_priority().cmp(&b.comparison_priority())); - } -} - -#[derive(Debug, PartialEq, Clone, Default)] -pub struct TodoList { - pub undone: TodoArray, - pub done: TodoArray, -} - -impl TodoList { - pub fn new () -> Self { - let undone = TodoArray::new(); - let done = TodoArray::new(); - - TodoList { - done, - undone + #[inline] + pub(super) fn delete_removed_dependent_files(&mut self, filename: &PathBuf) -> io::Result<()> { + for todo in &mut self.todos { + todo.delete_removed_dependent_files(filename)?; } + Ok(()) } - pub fn append_list(&mut self, mut todo_list: TodoList) { - self.undone.todos.append(&mut todo_list.undone.todos); - self.done.todos.append(&mut todo_list.done.todos); - self.done.sort(); - self.undone.sort(); - } - - pub fn remove(&mut self, index: usize) -> Todo { - let size = self.undone.len(); - - if index < size { - self.undone.remove(index) - } else { - let index = index - size; - self.done.remove(index) - } - + #[inline] + pub fn prepend(&mut self, todo:Todo) { + self.insert(0,todo); } - pub fn traverse_tree(&self,callback: fn(&mut App, &TodoList, &[usize]), prior_indices: Option>, app:&mut App) { - let prior_indices = prior_indices.unwrap_or(vec![]); - callback(app, self, prior_indices.as_slice()); - for (i, todo) in all_todos!(self).enumerate() { - if let Some(todo_list) = todo.dependency.todo_list() { - let mut prior_indices = prior_indices.clone(); - prior_indices.push(i); - todo_list.traverse_tree(callback, Some(prior_indices), app); - } - } + #[inline] + pub fn print(&self) -> io::Result<()> { + let mut stdout_writer = BufWriter::new(stdout()); + self.write_to_buf(&mut stdout_writer)?; + Ok(()) } - pub fn traverse_tree_undone(&self,callback: fn(&mut App, &TodoList, &[usize]), prior_indices: Option>, app:&mut App) { + pub fn traverse_tree(&self,callback: fn(&mut App, &TodoArray, &[usize]), prior_indices: Option>, app:&mut App) { let prior_indices = prior_indices.unwrap_or(vec![]); callback(app, self, prior_indices.as_slice()); - for (i, todo) in undone_todos!(self).enumerate() { + for (i, todo) in self.todos.iter().enumerate() { if let Some(todo_list) = todo.dependency.todo_list() { let mut prior_indices = prior_indices.clone(); prior_indices.push(i); @@ -241,45 +76,17 @@ impl TodoList { } } - pub fn reorder(&mut self, index: usize) -> usize { - let size = self.undone.len(); - - if index < size { - self.undone.reorder(index) - } else { - self.done.reorder(index - size) + size - } - } - - pub fn is_empty(&self) -> bool { - self.undone.is_empty() && self.done.is_empty() - } - - pub fn len(&self) -> usize { - self.undone.len() + self.done.len() - } - - pub fn display(&self, display_args:&DisplayArgs) -> Vec { - let mut display_list = self.undone.display(display_args); - - if display_args.show_done { - display_list.extend(self.done.display(display_args)); - } - display_list - } - - pub fn dependency_parent(filename: &PathBuf, is_root: bool) -> PathBuf { - if is_root { - filename.parent().unwrap().to_path_buf().join("notes") - } else { - filename.parent().unwrap().to_path_buf() + pub(super) fn remove_dependency_files(&mut self, filename: &PathBuf) -> io::Result<()> { + for todo in &mut self.todos { + todo.delete_dependency_file(filename)?; } + Ok(()) } pub fn read(filename: &PathBuf, read_dependencies: bool, is_root: bool) -> Self{ - let mut todo_list = Self::new(); + let mut todo_array = Self::new(); if !filename.is_file() { - return todo_list + return todo_array } let file_data = read(filename).unwrap(); @@ -288,67 +95,40 @@ impl TodoList { Ok(value) => value, Err(..) => continue, }; - todo_list.push(todo); + todo_array.push(todo); } - todo_list.undone.sort(); - todo_list.done.sort(); + todo_array.sort(); if read_dependencies { let dependency_path = Self::dependency_parent(filename, is_root); - let _ = todo_list.read_dependencies(&dependency_path); + let _ = todo_array.read_dependencies(&dependency_path); } - todo_list + todo_array } fn read_dependencies(&mut self, path: &PathBuf) -> io::Result<()>{ - for todo in all_todos_mut!(self) { + for todo in &mut self.todos { todo.dependency.read(&path)?; } Ok(()) } - #[inline] - pub fn push(&mut self, todo:Todo) { - if todo.done() { - self.done.push(todo); + pub fn dependency_parent(filename: &PathBuf, is_root: bool) -> PathBuf { + if is_root { + filename.parent().unwrap().to_path_buf().join("notes") } else { - self.undone.push(todo); + filename.parent().unwrap().to_path_buf() } } - #[inline] - pub fn prepend(&mut self, todo:Todo) { - self.undone.insert(0,todo); - } - - #[inline] - pub fn fix_undone(&mut self) { - for index in 0..self.undone.todos.len() { - if self.undone.todos[index].done() { - self.done.push(self.undone.todos.remove(index)); - } - if index+1 >= self.undone.todos.len() { - break; - } - } - } - - pub fn fix_done(&mut self) { - for index in 0..self.done.todos.len() { - if !self.done.todos[index].done() { - self.undone.push(self.done.todos.remove(index)); - } - if index+1 >= self.done.todos.len() { - break; - } + pub fn with_capacity(capacity: usize) -> Self{ + TodoArray { + todos: Vec::with_capacity(capacity), } - } #[inline] fn write_to_buf (&self, writer: &mut BufWriter) -> io::Result<()> { - let todos = [&self.undone.todos, &self.done.todos]; - - for todo in todos.iter().flat_map(|v| v.iter()) { + for todo in &self.todos { writeln!(writer, "{}", todo.as_string())?; } writer.flush()?; @@ -357,30 +137,12 @@ impl TodoList { #[inline] pub(super) fn write_dependencies(&mut self, filename: &PathBuf) -> io::Result<()> { - for todo in all_todos_mut!(self) { + for todo in &mut self.todos { todo.dependency.todo_list.write_dependencies(filename)?; todo.dependency.write(filename)?; } Ok(()) } - - #[inline] - pub(super) fn remove_dependency_files(&mut self, filename: &PathBuf) -> io::Result<()> { - for todo in all_todos_mut!(self) { - todo.delete_dependency_file(filename)?; - } - Ok(()) - } - - #[inline] - pub(super) fn delete_removed_dependent_files(&mut self, filename: &PathBuf) -> io::Result<()> { - for todo in all_todos_mut!(self) { - todo.delete_removed_dependent_files(filename)?; - } - Ok(()) - } - - #[inline] pub fn write(&mut self, filename: &PathBuf, is_root: bool) -> io::Result { let dependency_path = Self::dependency_parent(filename, is_root); @@ -391,112 +153,99 @@ impl TodoList { Ok(dependency_path) } - #[inline] - pub fn print(&self) -> io::Result<()> { - let mut stdout_writer = BufWriter::new(stdout()); - self.write_to_buf(&mut stdout_writer)?; - Ok(()) - } -} -#[cfg(test)] -mod tests { - use std::fs::{self, remove_dir_all, remove_file}; - use super::*; + pub fn iter(&self) -> std::slice::Iter { + self.todos.iter() + } - fn get_todo_list() -> TodoList { - let path = PathBuf::from("tests/TODO_LIST"); - TodoList::read(&path, true, true) + pub fn iter_mut(&mut self) -> std::slice::IterMut { + self.todos.iter_mut() } - #[test] - fn test_todolist_read_undone() { - let todo_list = get_todo_list(); - let expected_undone = vec![Todo::written("this todo has prio 1".to_string(), 1, false) - ,Todo::written("this one has prio 2".to_string(), 2, false)]; + pub fn messages(&self, restriction: Option bool>) -> Vec { + self.todos(restriction).iter().map(|todo| todo.message.clone()).collect() + } - assert_eq!(expected_undone, todo_list.undone.todos); + pub fn display(&self, args: &DisplayArgs, restriction: Option bool>) -> Vec { + self.todos(restriction).iter().map(|todo| todo.display(&args)).collect() } - #[test] - fn test_todolist_read_done() { - let todo_list = get_todo_list(); - let expected_done = vec![Todo::written("this one is 2 and done".to_string(), 2, true),Todo::written("this one is 0 and done".to_string(), 0, true)]; - assert_eq!(expected_done, todo_list.done.todos); + pub fn len(&self, restriction: Option bool>) -> usize { + self.todos(restriction).len() } - #[test] - fn test_len() { - let todo_list = get_todo_list(); - assert_eq!(todo_list.len(), 4); + pub fn is_empty(&self, restriction: Option bool>) -> bool { + self.todos(restriction).is_empty() } - #[test] - fn test_write() { - let mut todo_list = get_todo_list(); - let path = PathBuf::from("todo-list-test-write/tmplist"); - let _ = todo_list.write(&path, true); + pub fn remove(&mut self, index:usize) -> Todo{ + self.todos.remove(index).clone() + } + pub fn push(&mut self,item:Todo) { + self.todos.push(item) + } - let contents = fs::read_to_string(&path).expect("Reading file failed :("); - let expected = "[1] this todo has prio 1 -[2] this one has prio 2 -[-2] this one is 2 and done -[-0] this one is 0 and done -"; + fn insert(&mut self,index:usize, item:Todo) { + self.todos.insert(index,item) + } - remove_dir_all(&path.parent().unwrap()).expect("Remove test failed"); - let _ = remove_file(path); - assert_eq!(contents, expected) + #[inline(always)] + fn reorder_low_high(&self, index:usize) -> (usize, usize){ + let priority = self.todos[index].comparison_priority(); + if index+1 < self.todos.len() && priority > self.todos[index+1].comparison_priority() { + (index+1, self.todos.len()-1) + } else { + (0, index) + } } - #[test] - fn test_push() { - let mut todo_list = get_todo_list(); - let path = PathBuf::from("todo-list-test-push/tmplist"); - todo_list.push(Todo::default("Show me your warface".to_string(), 0)); - let _ = todo_list.write(&path, true); - - let contents = fs::read_to_string(&path).expect("Reading file failed :("); - let expected = "[1] this todo has prio 1 -[2] this one has prio 2 -[0] Show me your warface -[-2] this one is 2 and done -[-0] this one is 0 and done -"; - - remove_dir_all(&path.parent().unwrap()).expect("Remove test failed"); - let _ = remove_file(path); - assert_eq!(contents, expected); + pub fn reorder(&mut self, index:usize) -> usize { + let priority = self.todos[index].comparison_priority(); + + if priority < self.todos[0].comparison_priority() { + return self.move_index(index, 0, 1) + } + + let (low, high) = self.reorder_low_high(index); + for middle in low..high { + if priority < self.todos[middle+1].comparison_priority() && + priority >= self.todos[middle].comparison_priority() { + return self.move_index(index, middle, 0); + } + } + self.move_index(index, high, 0) } - #[test] - fn test_initially_sorted() { - let todo_list = get_todo_list(); - let mut sorted_list = todo_list.clone(); - sorted_list.undone.sort(); - sorted_list.done.sort(); + #[inline(always)] + fn move_index(&mut self, from: usize, to: usize, shift:usize) -> usize{ + + let mut i = from; + if from < to + { + for j in from..to { + self.todos.swap(j, j+1); + i = j+1; + } + } else { + for j in (to+1-shift..from).rev() { + self.todos.swap(j, j+1); + i = j; + } + + } + i + } - assert_eq!(todo_list, sorted_list) + pub fn append_list(&mut self, mut todo_list: TodoArray) { + self.todos.append(&mut todo_list.todos); + self.sort(); } - #[test] - fn test_write_dependencies() -> io::Result<()>{ - let mut todo_list = get_todo_list(); - let _ = todo_list[0].add_todo_dependency(); - let path = PathBuf::from("test-write-dependency/tmplist"); - todo_list[0].dependency.push(Todo::try_from("[0] Some dependency").unwrap()); - let dependency_path = todo_list.write(&path, true)?; - todo_list.write_dependencies(&dependency_path)?; - - let todo_dependency_path = PathBuf::from(format!("test-write-dependency/notes/{}.todo", todo_list[0].hash())); - let contents = fs::read_to_string(&todo_dependency_path).expect("Reading file failed :("); - let expected = "[0] Some dependency\n"; - assert_eq!(contents, expected); - - todo_list[0].remove_dependency(); - todo_list.write(&path, true)?; - remove_dir_all(&path.parent().unwrap())?; - Ok(()) + #[inline(always)] + pub fn sort (&mut self) { + // , ascending:Option + // let ascending = ascending.unwrap_or(false); + self.todos.sort_by(|a, b| a.comparison_priority().cmp(&b.comparison_priority())); } } From 9d949ff7a163c8e5abc78dbd60826dac12c1c95b Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sun, 7 Apr 2024 16:26:44 +0330 Subject: [PATCH 02/53] Add toggle_show_done for new structure --- src/cli_app.rs | 112 +------------------------------------------ src/todo_app.rs | 18 ++++++- src/todo_app/todo.rs | 4 ++ 3 files changed, 22 insertions(+), 112 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index 80e3756..4d34105 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -53,119 +53,11 @@ impl <'a>CliApp <'a>{ return Ok(()) } if self.todo_app.is_tree() { - let mut print_todo = PrintTodoTree::new(self.todo_app.args.minimal_tree); - print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args); + // let mut print_todo = PrintTodoTree::new(self.todo_app.args.minimal_tree); + // print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args); } else { self.print_list() } Ok(()) } } - -#[derive(Clone)] -struct PrintTodoTree { - was_last: Vec, - should_print_indention: bool, - is_last: bool, - depth: usize, -} - -impl PrintTodoTree { - #[inline] - pub fn new(should_print_indention:bool) -> Self { - PrintTodoTree { - was_last: vec![], - is_last: false, - depth: 0, - should_print_indention, - } - } - - #[inline] - pub fn tree_child(&self) -> Self { - let mut new_print = self.clone(); - new_print.depth+=1; - new_print.was_last.push(self.is_last); - - new_print - } - - #[inline] - pub fn print_list(&mut self, todo_list: &TodoArray, display_args: &DisplayArgs) { - let todos = todo_list.todos.clone(); - - for (index, todo) in todos.iter().enumerate() { - self.is_last = index == todos.len() - 1; - if self.depth > 0 { - self.print_indention(); - } - self.print_todo(todo, display_args); - - if let Some(todo_list) = todo.dependency.todo_list() { - let mut tree_child = self.tree_child(); - tree_child.print_list(todo_list, display_args); - } - - if let Some(note) = todo.dependency.note() { - self.print_note(note) - } - } - } - - #[inline] - fn print_todo(&self, todo: &Todo, display_args: &DisplayArgs) { - println!("{}", todo.display(&display_args)); - } - - #[inline] - fn print_note(&mut self, note: &String) { - self.was_last.push(self.is_last); - self.is_last = true; - - let mut lines = note.lines(); - self.print_indention_with_depth(self.depth+1); - if let Some(line) = lines.next() { - println!("{}", line); - } - for line in lines { - self.print_prenote(); - println!("{}", line); - } - } - - #[inline] - fn print_prenote(&self) { - for i in 0..self.depth { - if self.was_last[i+1] { - print!(" ") - } else { - print!("│ ") - } - } - print!(" ") - } - - #[inline] - fn print_indention_with_depth(&self, depth: usize) { - if self.should_print_indention { - return - } - for i in 1..depth { - if self.was_last[i] { - print!(" ") - } else { - print!("│ ") - } - } - if self.is_last { - print!("└── "); - } else { - print!("├── "); - } - } - - #[inline] - fn print_indention(&self) { - self.print_indention_with_depth(self.depth); - } -} diff --git a/src/todo_app.rs b/src/todo_app.rs index 75a388b..42c51b6 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -52,8 +52,9 @@ impl App { changed: false, args, search: Search::new(), - restriction: Some(|todo: &Todo| !todo.done()), + restriction: None, }; + app.update_show_done_restriction(); for str in app.args.search_and_select.clone() { app.search(Some(str)); for index in app.search.indices() { @@ -70,6 +71,11 @@ impl App { self.append_list(todo_list) } + #[inline] + pub fn set_priority_restriction(&mut self, priority:PriorityType) { + // self.restriction = Some(|todo| todo.prio) + } + #[inline] pub fn append_list(&mut self, todo_list: TodoArray) { self.mut_current_list().append_list(todo_list) @@ -214,6 +220,15 @@ impl App { #[inline] pub fn toggle_show_done(&mut self) { self.args.display_args.show_done = !self.show_done(); + self.update_show_done_restriction(); + } + + pub fn update_show_done_restriction(&mut self) { + if self.show_done() { + self.restriction = None + } else { + self.restriction = Some(|todo| !todo.done()) + } } #[inline] @@ -254,7 +269,6 @@ impl App { #[inline] pub fn toggle_current_done(&mut self) { self.mut_todo().unwrap().toggle_done(); - // self.traverse_up(); } #[inline] diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 383639a..28d7e11 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -151,6 +151,10 @@ impl Todo { self.message.contains(query) || self.message.to_lowercase().contains(query) } + #[inline] + pub fn priority(&self) -> PriorityType{ + self.priority + } #[inline] pub fn new(message:String, priority:PriorityType, done: bool, dependency: Dependency) -> Self { Todo { From bb3e50d55cf28768d09b7a33d5e3fc9346122286 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sun, 7 Apr 2024 17:04:06 +0330 Subject: [PATCH 03/53] Add limit priority --- src/todo_app.rs | 50 ++++++++++++++++++++++----------------- src/todo_app/todo_list.rs | 16 ++++++------- src/tui_app.rs | 32 +++++++++++++++++++------ 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 42c51b6..e4b3d99 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -17,6 +17,7 @@ struct SearchPosition { matching_indices: Vec, } +pub type RestrictionFunction = Option bool>>; pub struct App { selected: Vec, clipboard: Clipboard, @@ -31,7 +32,7 @@ pub struct App { last_query: String, x_index: usize, y_index: usize, - restriction: Option bool>, + restriction: RestrictionFunction, } impl App { @@ -71,11 +72,6 @@ impl App { self.append_list(todo_list) } - #[inline] - pub fn set_priority_restriction(&mut self, priority:PriorityType) { - // self.restriction = Some(|todo| todo.prio) - } - #[inline] pub fn append_list(&mut self, todo_list: TodoArray) { self.mut_current_list().append_list(todo_list) @@ -117,7 +113,7 @@ impl App { fn add_to_tree_positions(&mut self, list: &TodoArray, prior_indices: &[usize]) { let mut matching_indices : Vec = vec![]; - for (i, todo) in list.todos(self.restriction).iter().enumerate() { + for (i, todo) in list.todos(&self.restriction).iter().enumerate() { if todo.matches(self.last_query.as_str()) { matching_indices.push(i) } @@ -154,7 +150,7 @@ impl App { self.prior_indexes = position.prior_indices.clone(); let list = self.current_list(); for index in position.matching_indices.clone() { - println!("{}",list.index(index,self.restriction).display(&self.args.display_args)); + println!("{}",list.index(index,&self.restriction).display(&self.args.display_args)); } } } @@ -192,7 +188,7 @@ impl App { #[inline] pub fn append(&mut self, message:String) { self.mut_current_list().push(Todo::default(message, 0)); - self.index = self.current_list().len(self.restriction)-1; + self.index = self.current_list().len(&self.restriction)-1; } pub fn index(&self) -> usize { @@ -201,7 +197,7 @@ impl App { #[inline] pub fn search(&mut self, query:Option) { - let todo_messages = self.current_list().messages(self.restriction); + let todo_messages = self.current_list().messages(&self.restriction); self.search.search(query, todo_messages); } @@ -227,7 +223,7 @@ impl App { if self.show_done() { self.restriction = None } else { - self.restriction = Some(|todo| !todo.done()) + self.restriction = Some(Box::new(|todo| !todo.done())) } } @@ -291,8 +287,8 @@ impl App { let mut list = &self.todo_list; let mut parent = None; for index in self.prior_indexes.iter() { - parent = Some(list.index(*index, self.restriction)); - if let Some(todo_list) = list.index(*index, self.restriction).dependency.todo_list() { + parent = Some(list.index(*index, &self.restriction)); + if let Some(todo_list) = list.index(*index, &self.restriction).dependency.todo_list() { list = todo_list } else { break @@ -367,7 +363,7 @@ impl App { #[inline] pub fn is_todos_empty(&self) -> bool{ if self.show_done() { - self.current_list().is_empty(self.restriction) + self.current_list().is_empty(&self.restriction) } else { self.is_undone_empty() } @@ -400,7 +396,7 @@ impl App { #[inline] pub fn len(&self) -> usize { - self.current_list().len(self.restriction) + self.current_list().len(&self.restriction) } #[inline] @@ -424,7 +420,7 @@ impl App { return list; } for index in self.prior_indexes.iter() { - if let Some(todo_list) = &list.index(*index, self.restriction).dependency.todo_list() { + if let Some(todo_list) = &list.index(*index, &self.restriction).dependency.todo_list() { list = todo_list } else { break @@ -483,12 +479,22 @@ impl App { #[inline] pub fn is_undone_empty(&self) -> bool{ - self.current_list().is_empty(self.restriction) + self.current_list().is_empty(&self.restriction) } #[inline] pub fn is_done_empty(&self) -> bool{ - self.current_list().is_empty(self.restriction) + self.current_list().is_empty(&self.restriction) + } + + #[inline] + pub fn unset_restriction(&mut self) { + self.restriction = None + } + + #[inline] + pub fn set_priority_limit(&mut self, priority:PriorityType) { + self.restriction = Some(Box::new(move |todo| todo.priority() == priority)) } #[inline] @@ -518,10 +524,10 @@ impl App { let size = self.len(); if size <= index { - return Some(¤t_list.index(size - 1, self.restriction)); + return Some(¤t_list.index(size - 1, &self.restriction)); } - Some(&self.current_list().index(index, self.restriction)) + Some(&self.current_list().index(index, &self.restriction)) } #[inline] @@ -546,7 +552,7 @@ impl App { #[inline] pub fn display_list(&self, todo_list: &TodoArray) -> Vec { - todo_list.display(&self.args.display_args, self.restriction) + todo_list.display(&self.args.display_args, &self.restriction) } #[inline] @@ -632,7 +638,7 @@ impl App { #[inline] pub fn print_selected(&self) { for index in self.selected.clone() { - println!("{}", self.todo_list.index(index, self.restriction).display(&self.args.display_args)); + println!("{}", self.todo_list.index(index, &self.restriction).display(&self.args.display_args)); } } } diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 47bfead..25db319 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -4,7 +4,7 @@ use std::ops::{Index, IndexMut}; use std::io::{stdout, BufRead, BufWriter, Write}; use std::io; -use super::{App, Todo}; +use super::{App, Todo, RestrictionFunction}; use crate::DisplayArgs; #[derive(Debug,PartialEq, Clone, Default)] @@ -20,7 +20,7 @@ impl TodoArray { } } - pub fn index(&self, index:usize, restriction: Option bool>) -> &Output { + pub fn index(&self, index:usize, restriction: &RestrictionFunction) -> &Output { self.todos(restriction)[index] } @@ -28,7 +28,7 @@ impl TodoArray { &mut self.todos[index] } - pub fn todos(&self, restriction: Option bool>) -> Vec<&Todo> { + pub fn todos(&self, restriction: &RestrictionFunction) -> Vec<&Todo> { if let Some(restriction) = restriction { self.todos.iter().filter(|todo| restriction(todo)).collect() } else { @@ -36,7 +36,7 @@ impl TodoArray { } } - pub fn mut_todos(&mut self, restriction: Option bool>) -> Vec<&mut Todo> { + pub fn mut_todos(&mut self, restriction: &RestrictionFunction) -> Vec<&mut Todo> { if let Some(restriction) = restriction { self.todos.iter_mut().filter(|todo| restriction(todo)).collect() } else { @@ -162,19 +162,19 @@ impl TodoArray { self.todos.iter_mut() } - pub fn messages(&self, restriction: Option bool>) -> Vec { + pub fn messages(&self, restriction: &RestrictionFunction) -> Vec { self.todos(restriction).iter().map(|todo| todo.message.clone()).collect() } - pub fn display(&self, args: &DisplayArgs, restriction: Option bool>) -> Vec { + pub fn display(&self, args: &DisplayArgs, restriction: &RestrictionFunction) -> Vec { self.todos(restriction).iter().map(|todo| todo.display(&args)).collect() } - pub fn len(&self, restriction: Option bool>) -> usize { + pub fn len(&self, restriction: &RestrictionFunction) -> usize { self.todos(restriction).len() } - pub fn is_empty(&self, restriction: Option bool>) -> bool { + pub fn is_empty(&self, restriction: &RestrictionFunction) -> bool { self.todos(restriction).is_empty() } diff --git a/src/tui_app.rs b/src/tui_app.rs index 7ed4441..b6dac5d 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -241,6 +241,11 @@ impl<'a>TuiApp<'a>{ self.set_text_mode(Self::on_append_todo, "Add todo", "Enter the todo message"); } + #[inline] + pub fn priority_prompt(&mut self) { + self.set_text_mode(Self::on_priority_prompt, "Limit priority", "Enter priority to show"); + } + #[inline] pub fn append_prompt(&mut self) { self.set_text_mode(Self::on_prepend_todo, "Add todo at first", "Enter the todo message"); @@ -256,24 +261,36 @@ impl<'a>TuiApp<'a>{ } #[inline] - fn on_save_prompt(app:&mut TuiApp, str:String) { + fn on_priority_prompt(&mut self, str: String) { + if str.is_empty() { + self.todo_app.unset_restriction(); + return + } + let priority = str.parse::().ok(); + if let Some(priority) = priority { + self.todo_app.set_priority_limit(priority) + } + } + + #[inline] + fn on_save_prompt(&mut self, str:String) { let lower = str.to_lowercase(); if lower.starts_with("y") { - let _ = app.todo_app.write(); + let _ = self.todo_app.write(); } else if lower.starts_with("c") { return; } - let _ = app.quit(); + let _ = self.quit(); } #[inline] - fn on_append_todo(app: &mut Self, str:String) { - app.todo_app.append(str); + fn on_append_todo(&mut self, str:String) { + self.todo_app.append(str); } #[inline] - fn on_prepend_todo(app:&mut TuiApp,str:String) { - app.todo_app.prepend(str); + fn on_prepend_todo(&mut self,str:String) { + self.todo_app.prepend(str); } #[inline] @@ -381,6 +398,7 @@ impl<'a>TuiApp<'a>{ Char('S') => self.schedule_prompt(), Char('m') => self.reminder_prompt(), Char('!') => self.todo_app.toggle_show_done(), + Char('@') => self.priority_prompt(), Char('y') => self.todo_app.yank_todo(), Char('p') => self.todo_app.paste_todo(), Char('i') => self.todo_app.increase_day_done(), From d7f2cca7d66200f667908c435f554385a9ba2dbd Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 8 Apr 2024 12:10:28 +0330 Subject: [PATCH 04/53] Update to single todo vector - Fix index_mut method of TodoArray --- src/todo_app.rs | 21 ++++++++++++--------- src/todo_app/todo.rs | 21 ++++++++++++--------- src/todo_app/todo_list.rs | 11 +++++++---- src/tui_app/app.rs | 0 4 files changed, 31 insertions(+), 22 deletions(-) delete mode 100644 src/tui_app/app.rs diff --git a/src/todo_app.rs b/src/todo_app.rs index e4b3d99..0fac857 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -5,6 +5,7 @@ mod todo_list; mod todo; mod search; use search::Search; +use std::rc::Rc; pub use todo::Todo; use crate::Args; @@ -17,7 +18,7 @@ struct SearchPosition { matching_indices: Vec, } -pub type RestrictionFunction = Option bool>>; +pub type RestrictionFunction = Option bool>>; pub struct App { selected: Vec, clipboard: Clipboard, @@ -86,10 +87,10 @@ impl App { let sel_index = *sel_index - index_shift; let iter_index = iter_index - iter_index; if let Some(priority) = self.args.set_selected_priority { - self.todo_list.index_mut(sel_index).set_priority(priority as PriorityType); + self.todo_list.index_mut(sel_index, self.restriction.clone()).set_priority(priority as PriorityType); } if let Some(message) = self.args.set_selected_message.clone() { - self.todo_list.index_mut(sel_index).set_message(message); + self.todo_list.index_mut(sel_index, self.restriction.clone()).set_message(message); } if self.args.delete_selected { self.todo_list.remove(sel_index); @@ -98,7 +99,7 @@ impl App { self.changed = true; } if self.args.done_selected { - self.todo_list.index_mut(sel_index).toggle_done(); + self.todo_list.index_mut(sel_index, self.restriction.clone()).toggle_done(); if !self.show_done() { self.selected.remove(iter_index); } @@ -223,7 +224,7 @@ impl App { if self.show_done() { self.restriction = None } else { - self.restriction = Some(Box::new(|todo| !todo.done())) + self.restriction = Some(Rc::new(|todo| !todo.done())) } } @@ -376,12 +377,13 @@ impl App { } let index = self.index.min(self.len() - 1); let size = self.len(); + let res_cloned = self.restriction.clone(); if size <= index { - return Some(self.mut_current_list().index_mut(size - 1)); + return Some(self.mut_current_list().index_mut(size - 1, res_cloned)); } - Some(self.mut_current_list().index_mut(index)) + Some(self.mut_current_list().index_mut(index, res_cloned)) } #[inline] @@ -408,7 +410,7 @@ impl App { return list; } for index in self.prior_indexes.iter() { - list = &mut list.index_mut(*index).dependency.todo_list + list = &mut list.index_mut(*index, self.restriction.clone()).dependency.todo_list }; list } @@ -494,7 +496,8 @@ impl App { #[inline] pub fn set_priority_limit(&mut self, priority:PriorityType) { - self.restriction = Some(Box::new(move |todo| todo.priority() == priority)) + self.args.display_args.show_done = true; + self.restriction = Some(Rc::new(move |todo| todo.priority() == priority)) } #[inline] diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 28d7e11..9fffa98 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -344,18 +344,21 @@ impl Todo { } #[inline(always)] - pub fn comparison_priority(&self) -> PriorityType{ - let priority = if self.priority == 0 { - 10 + pub fn comparison_priority(&self) -> PriorityType { + let mut priority = if self.priority == 0 { + 20 } else { - self.priority + self.priority*2 }; - match (self.schedule.is_reminder(), self.done()) { - (true, true) => priority*20-10, - (false, true) => priority*20, - (false, false) => priority*2, - (true, false) => priority*2-1, + + if self.schedule.is_reminder() { + priority-=1; + } + if self.done() { + priority*=12; } + + priority } #[inline] diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 25db319..89fdbc0 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -1,6 +1,5 @@ use std::fs::{create_dir_all, read, File}; use std::path::PathBuf; -use std::ops::{Index, IndexMut}; use std::io::{stdout, BufRead, BufWriter, Write}; use std::io; @@ -24,8 +23,12 @@ impl TodoArray { self.todos(restriction)[index] } - pub fn index_mut(&mut self, index:usize) -> &mut Output { - &mut self.todos[index] + pub fn index_mut(&mut self, index:usize, restriction: RestrictionFunction) -> &mut Todo { + if let Some(restriction) = restriction { + self.todos.iter_mut().filter(|todo| restriction(todo)).nth(index).unwrap() + } else { + self.todos.iter_mut().nth(index).unwrap() + } } pub fn todos(&self, restriction: &RestrictionFunction) -> Vec<&Todo> { @@ -36,7 +39,7 @@ impl TodoArray { } } - pub fn mut_todos(&mut self, restriction: &RestrictionFunction) -> Vec<&mut Todo> { + pub fn mut_todos(&mut self, restriction: RestrictionFunction) -> Vec<&mut Todo> { if let Some(restriction) = restriction { self.todos.iter_mut().filter(|todo| restriction(todo)).collect() } else { diff --git a/src/tui_app/app.rs b/src/tui_app/app.rs deleted file mode 100644 index e69de29..0000000 From 9dc95f252932c2988725facb7d5a4f9064a6749d Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 8 Apr 2024 12:21:35 +0330 Subject: [PATCH 05/53] Change name of TodoArray Change name of TodoArray to TodoList --- src/cli_app.rs | 4 +--- src/todo_app.rs | 24 ++++++++++++------------ src/todo_app/todo.rs | 4 ++-- src/todo_app/todo/dependency.rs | 14 +++++++------- src/todo_app/todo_list.rs | 12 ++++++------ 5 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index 4d34105..054471f 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -1,7 +1,5 @@ use std::io; -use crate::DisplayArgs; - -use super::todo_app::{App ,TodoArray, Todo}; +use super::todo_app::App; #[inline] pub fn run(app: &mut App) -> io::Result<()>{ diff --git a/src/todo_app.rs b/src/todo_app.rs index 0fac857..42f03f3 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -10,7 +10,7 @@ pub use todo::Todo; use crate::Args; pub use self::todo::PriorityType; -pub use self::todo_list::TodoArray; +pub use self::todo_list::TodoList; #[derive(Clone)] struct SearchPosition { @@ -22,7 +22,7 @@ pub type RestrictionFunction = Option bool>>; pub struct App { selected: Vec, clipboard: Clipboard, - pub(super) todo_list: TodoArray, + pub(super) todo_list: TodoList, index: usize, prior_indexes: Vec, changed:bool, @@ -39,7 +39,7 @@ pub struct App { impl App { #[inline] pub fn new(args: Args) -> Self { - let todo_list = TodoArray::read(&args.todo_path, !args.no_tree, true); + let todo_list = TodoList::read(&args.todo_path, !args.no_tree, true); let mut app = App { x_index: 0, y_index: 0, @@ -69,12 +69,12 @@ impl App { #[inline] pub fn append_list_from_path(&mut self, path: PathBuf) { - let todo_list = TodoArray::read(&path, !self.args.no_tree, true); + let todo_list = TodoList::read(&path, !self.args.no_tree, true); self.append_list(todo_list) } #[inline] - pub fn append_list(&mut self, todo_list: TodoArray) { + pub fn append_list(&mut self, todo_list: TodoList) { self.mut_current_list().append_list(todo_list) } @@ -108,11 +108,11 @@ impl App { } } - fn traverse_parents_from_root(&mut self, callback: fn(&mut App, &TodoArray, &[usize])) { + fn traverse_parents_from_root(&mut self, callback: fn(&mut App, &TodoList, &[usize])) { self.todo_list.clone().traverse_tree(callback, None, self) } - fn add_to_tree_positions(&mut self, list: &TodoArray, prior_indices: &[usize]) { + fn add_to_tree_positions(&mut self, list: &TodoList, prior_indices: &[usize]) { let mut matching_indices : Vec = vec![]; for (i, todo) in list.todos(&self.restriction).iter().enumerate() { if todo.matches(self.last_query.as_str()) { @@ -271,7 +271,7 @@ impl App { #[inline] pub fn read(&mut self) { self.changed = false; - self.todo_list = TodoArray::read(&self.args.todo_path, true, true); + self.todo_list = TodoList::read(&self.args.todo_path, true, true); } #[inline] @@ -402,7 +402,7 @@ impl App { } #[inline] - pub fn mut_current_list(&mut self) -> &mut TodoArray { + pub fn mut_current_list(&mut self) -> &mut TodoList { self.changed = true; let is_root = self.is_root(); let mut list = &mut self.todo_list; @@ -416,7 +416,7 @@ impl App { } #[inline] - pub fn current_list(&self) -> &TodoArray { + pub fn current_list(&self) -> &TodoList { let mut list = &self.todo_list; if self.is_root() { return list; @@ -554,7 +554,7 @@ impl App { } #[inline] - pub fn display_list(&self, todo_list: &TodoArray) -> Vec { + pub fn display_list(&self, todo_list: &TodoList) -> Vec { todo_list.display(&self.args.display_args, &self.restriction) } @@ -608,7 +608,7 @@ impl App { let todos_count = self.len(); match Todo::try_from(self.clipboard.get_text()) { Ok(mut todo) => { - let todo_parent = TodoArray::dependency_parent(&self.args.todo_path, true); + let todo_parent = TodoList::dependency_parent(&self.args.todo_path, true); let _ = todo.dependency.read(&todo_parent); let bottom = self.bottom()+1; let list = &mut self.mut_current_list(); diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 9fffa98..425d01f 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -10,7 +10,7 @@ mod dependency; use dependency::Dependency; use schedule::Schedule; use note::{sha1, open_temp_editor}; -use super::TodoArray; +use super::TodoList; use crate::DisplayArgs; //}}} @@ -173,7 +173,7 @@ impl Todo { } #[inline] - pub fn dependencies(&self) -> Option<&TodoArray> { + pub fn dependencies(&self) -> Option<&TodoList> { self.dependency.todo_list() } diff --git a/src/todo_app/todo/dependency.rs b/src/todo_app/todo/dependency.rs index 5b03b99..d2c24e3 100644 --- a/src/todo_app/todo/dependency.rs +++ b/src/todo_app/todo/dependency.rs @@ -1,6 +1,6 @@ use scanf::sscanf; use std::{io::{self, Write}, path::PathBuf, fs::File}; -use super::TodoArray; +use super::TodoList; use super::Todo; #[derive(Debug, PartialEq, Clone, Default)] @@ -17,7 +17,7 @@ pub struct Dependency { mode: DependencyMode, note: String, written: bool, - pub(crate) todo_list: TodoArray, + pub(crate) todo_list: TodoList, } impl Dependency { @@ -37,7 +37,7 @@ impl Dependency { mode, name, note: String::new(), - todo_list: TodoArray::new(), + todo_list: TodoList::new(), } } @@ -95,7 +95,7 @@ impl Dependency { self.name = name_todo; self.mode = DependencyMode::TodoList; } - self.todo_list = TodoArray::read(&path.join(&self.name), true, false); + self.todo_list = TodoList::read(&path.join(&self.name), true, false); } _ => {} }; @@ -105,7 +105,7 @@ impl Dependency { } #[inline] - pub fn todo_list(&self) -> Option<&TodoArray> { + pub fn todo_list(&self) -> Option<&TodoList> { if self.mode == DependencyMode::TodoList { Some(&self.todo_list) } else { @@ -114,7 +114,7 @@ impl Dependency { } #[inline] - pub fn todo_list_mut(&mut self) -> Option<&mut TodoArray> { + pub fn todo_list_mut(&mut self) -> Option<&mut TodoList> { if self.mode == DependencyMode::TodoList { Some(&mut self.todo_list) } else { @@ -142,7 +142,7 @@ impl Dependency { #[inline] pub fn path(&self ,path: &PathBuf) -> Option{ match path.parent() { - Some(path) => Some(TodoArray::dependency_parent(&path.to_path_buf(), false)), + Some(path) => Some(TodoList::dependency_parent(&path.to_path_buf(), false)), None => None, } } diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 89fdbc0..bcb8aaf 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -7,14 +7,14 @@ use super::{App, Todo, RestrictionFunction}; use crate::DisplayArgs; #[derive(Debug,PartialEq, Clone, Default)] -pub struct TodoArray { +pub struct TodoList { todos: Vec, } type Output = Todo; -impl TodoArray { +impl TodoList { pub fn new() -> Self{ - TodoArray { + TodoList { todos: Vec::new(), } } @@ -67,7 +67,7 @@ impl TodoArray { Ok(()) } - pub fn traverse_tree(&self,callback: fn(&mut App, &TodoArray, &[usize]), prior_indices: Option>, app:&mut App) { + pub fn traverse_tree(&self,callback: fn(&mut App, &TodoList, &[usize]), prior_indices: Option>, app:&mut App) { let prior_indices = prior_indices.unwrap_or(vec![]); callback(app, self, prior_indices.as_slice()); for (i, todo) in self.todos.iter().enumerate() { @@ -124,7 +124,7 @@ impl TodoArray { } pub fn with_capacity(capacity: usize) -> Self{ - TodoArray { + TodoList { todos: Vec::with_capacity(capacity), } } @@ -240,7 +240,7 @@ impl TodoArray { i } - pub fn append_list(&mut self, mut todo_list: TodoArray) { + pub fn append_list(&mut self, mut todo_list: TodoList) { self.todos.append(&mut todo_list.todos); self.sort(); } From 78b2b2a2ef379c79bec9af195d11a00de6df94f0 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 8 Apr 2024 13:25:44 +0330 Subject: [PATCH 06/53] Fix tree print --- src/cli_app.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++++-- src/todo_app.rs | 2 +- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index 054471f..1114d83 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -1,5 +1,6 @@ use std::io; -use super::todo_app::App; +use super::todo_app::{App, TodoList, Todo, RestrictionFunction}; +use crate::DisplayArgs; #[inline] pub fn run(app: &mut App) -> io::Result<()>{ @@ -51,11 +52,120 @@ impl <'a>CliApp <'a>{ return Ok(()) } if self.todo_app.is_tree() { - // let mut print_todo = PrintTodoTree::new(self.todo_app.args.minimal_tree); - // print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args); + let mut print_todo = PrintTodoTree::new(self.todo_app.args.minimal_tree); + print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args, &self.todo_app.restriction); } else { self.print_list() } Ok(()) } } + +// TODO: Use traverse_tree instead of this struct for printing todo tree. +#[derive(Clone)] +struct PrintTodoTree { + was_last: Vec, + should_print_indention: bool, + is_last: bool, + depth: usize, +} + +impl PrintTodoTree { + #[inline] + pub fn new(should_print_indention:bool) -> Self { + PrintTodoTree { + was_last: vec![], + is_last: false, + depth: 0, + should_print_indention, + } + } + + #[inline] + pub fn tree_child(&self) -> Self { + let mut new_print = self.clone(); + new_print.depth+=1; + new_print.was_last.push(self.is_last); + + new_print + } + + #[inline] + pub fn print_list(&mut self, todo_list: &TodoList, display_args: &DisplayArgs, restriction: &RestrictionFunction) { + let todos = todo_list.todos(restriction); + + for (index, todo) in todos.iter().enumerate() { + self.is_last = index == todos.len() - 1; + if self.depth > 0 { + self.print_indention(); + } + self.print_todo(todo, display_args); + + if let Some(todo_list) = todo.dependency.todo_list() { + let mut tree_child = self.tree_child(); + tree_child.print_list(todo_list, display_args, restriction); + } + + if let Some(note) = todo.dependency.note() { + self.print_note(note) + } + } + } + + #[inline] + fn print_todo(&self, todo: &Todo, display_args: &DisplayArgs) { + println!("{}", todo.display(&display_args)); + } + + #[inline] + fn print_note(&mut self, note: &String) { + self.was_last.push(self.is_last); + self.is_last = true; + + let mut lines = note.lines(); + self.print_indention_with_depth(self.depth+1); + if let Some(line) = lines.next() { + println!("{}", line); + } + for line in lines { + self.print_prenote(); + println!("{}", line); + } + } + + #[inline] + fn print_prenote(&self) { + for i in 0..self.depth { + if self.was_last[i+1] { + print!(" ") + } else { + print!("│ ") + } + } + print!(" ") + } + + #[inline] + fn print_indention_with_depth(&self, depth: usize) { + if self.should_print_indention { + return + } + for i in 1..depth { + if self.was_last[i] { + print!(" ") + } else { + print!("│ ") + } + } + if self.is_last { + print!("└── "); + } else { + print!("├── "); + } + } + + #[inline] + fn print_indention(&self) { + self.print_indention_with_depth(self.depth); + } +} diff --git a/src/todo_app.rs b/src/todo_app.rs index 42f03f3..78fb9f2 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -33,7 +33,7 @@ pub struct App { last_query: String, x_index: usize, y_index: usize, - restriction: RestrictionFunction, + pub(super) restriction: RestrictionFunction, } impl App { From 767437a2e63ee2c98f38a394b0733ea22879e46f Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 8 Apr 2024 13:58:06 +0330 Subject: [PATCH 07/53] Fix minor problems - Add sort after toggling done - Remove unnecessary state=State::Priority in todo.rs --- src/todo_app.rs | 1 + src/todo_app/todo.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 78fb9f2..b4362e0 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -266,6 +266,7 @@ impl App { #[inline] pub fn toggle_current_done(&mut self) { self.mut_todo().unwrap().toggle_done(); + self.mut_current_list().sort(); } #[inline] diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 425d01f..3c191d1 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -85,7 +85,6 @@ impl TryFrom<&str> for Todo { match state { State::Priority => { if c == '-' { - state = State::Priority; done = true; } else if c.is_digit(10) { priority = c.to_digit(10).unwrap() as u8; From ef48df33b284a2e61d50ff5bdfc2629c4ca9d110 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Tue, 9 Apr 2024 14:08:12 +0330 Subject: [PATCH 08/53] Fix sort comparison problem - +20 instead of *12 in comparison_priority method - Add reorder_last method, uses the actual array last index to reorder --- src/todo_app.rs | 2 +- src/todo_app/todo.rs | 2 +- src/todo_app/todo_list.rs | 14 ++++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index b4362e0..33c545e 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -189,7 +189,7 @@ impl App { #[inline] pub fn append(&mut self, message:String) { self.mut_current_list().push(Todo::default(message, 0)); - self.index = self.current_list().len(&self.restriction)-1; + self.index = self.mut_current_list().reorder_last(); } pub fn index(&self) -> usize { diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 3c191d1..e59c811 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -354,7 +354,7 @@ impl Todo { priority-=1; } if self.done() { - priority*=12; + priority+=20; } priority diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index bcb8aaf..c3f0322 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -39,14 +39,6 @@ impl TodoList { } } - pub fn mut_todos(&mut self, restriction: RestrictionFunction) -> Vec<&mut Todo> { - if let Some(restriction) = restriction { - self.todos.iter_mut().filter(|todo| restriction(todo)).collect() - } else { - self.todos.iter_mut().collect() - } - } - #[inline] pub(super) fn delete_removed_dependent_files(&mut self, filename: &PathBuf) -> io::Result<()> { for todo in &mut self.todos { @@ -203,6 +195,12 @@ impl TodoList { } } + #[inline(always)] + pub fn reorder_last(&mut self) -> usize { + self.reorder(self.todos.len()-1) + } + + pub fn reorder(&mut self, index:usize) -> usize { let priority = self.todos[index].comparison_priority(); From 1905a170a88f89cf75c6fcb6d9365a31d7b1eeba Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Tue, 9 Apr 2024 14:55:00 +0330 Subject: [PATCH 09/53] Update methods to use RestrictionFunction Use RestrictionFunction instead of &RestrictionFunction --- src/cli_app.rs | 8 ++++---- src/todo_app.rs | 36 ++++++++++++++++++------------------ src/todo_app/todo_list.rs | 12 ++++++------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index 1114d83..ec09bdc 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -53,7 +53,7 @@ impl <'a>CliApp <'a>{ } if self.todo_app.is_tree() { let mut print_todo = PrintTodoTree::new(self.todo_app.args.minimal_tree); - print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args, &self.todo_app.restriction); + print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args, self.todo_app.restriction.clone()); } else { self.print_list() } @@ -91,8 +91,8 @@ impl PrintTodoTree { } #[inline] - pub fn print_list(&mut self, todo_list: &TodoList, display_args: &DisplayArgs, restriction: &RestrictionFunction) { - let todos = todo_list.todos(restriction); + pub fn print_list(&mut self, todo_list: &TodoList, display_args: &DisplayArgs, restriction: RestrictionFunction) { + let todos = todo_list.todos(restriction.clone()); for (index, todo) in todos.iter().enumerate() { self.is_last = index == todos.len() - 1; @@ -103,7 +103,7 @@ impl PrintTodoTree { if let Some(todo_list) = todo.dependency.todo_list() { let mut tree_child = self.tree_child(); - tree_child.print_list(todo_list, display_args, restriction); + tree_child.print_list(todo_list, display_args, restriction.clone()); } if let Some(note) = todo.dependency.note() { diff --git a/src/todo_app.rs b/src/todo_app.rs index 33c545e..25b72d4 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -114,7 +114,7 @@ impl App { fn add_to_tree_positions(&mut self, list: &TodoList, prior_indices: &[usize]) { let mut matching_indices : Vec = vec![]; - for (i, todo) in list.todos(&self.restriction).iter().enumerate() { + for (i, todo) in list.todos(self.restriction.clone()).iter().enumerate() { if todo.matches(self.last_query.as_str()) { matching_indices.push(i) } @@ -151,7 +151,7 @@ impl App { self.prior_indexes = position.prior_indices.clone(); let list = self.current_list(); for index in position.matching_indices.clone() { - println!("{}",list.index(index,&self.restriction).display(&self.args.display_args)); + println!("{}",list.index(index,self.restriction.clone()).display(&self.args.display_args)); } } } @@ -198,7 +198,7 @@ impl App { #[inline] pub fn search(&mut self, query:Option) { - let todo_messages = self.current_list().messages(&self.restriction); + let todo_messages = self.current_list().messages(self.restriction.clone()); self.search.search(query, todo_messages); } @@ -265,7 +265,11 @@ impl App { #[inline] pub fn toggle_current_done(&mut self) { + let restriction = self.restriction.clone(); self.mut_todo().unwrap().toggle_done(); + if self.mut_current_list().is_empty(restriction) { + + } self.mut_current_list().sort(); } @@ -289,8 +293,8 @@ impl App { let mut list = &self.todo_list; let mut parent = None; for index in self.prior_indexes.iter() { - parent = Some(list.index(*index, &self.restriction)); - if let Some(todo_list) = list.index(*index, &self.restriction).dependency.todo_list() { + parent = Some(list.index(*index, self.restriction.clone())); + if let Some(todo_list) = list.index(*index, self.restriction.clone()).dependency.todo_list() { list = todo_list } else { break @@ -365,7 +369,7 @@ impl App { #[inline] pub fn is_todos_empty(&self) -> bool{ if self.show_done() { - self.current_list().is_empty(&self.restriction) + self.current_list().is_empty(self.restriction.clone()) } else { self.is_undone_empty() } @@ -399,7 +403,7 @@ impl App { #[inline] pub fn len(&self) -> usize { - self.current_list().len(&self.restriction) + self.current_list().len(self.restriction.clone()) } #[inline] @@ -423,7 +427,7 @@ impl App { return list; } for index in self.prior_indexes.iter() { - if let Some(todo_list) = &list.index(*index, &self.restriction).dependency.todo_list() { + if let Some(todo_list) = &list.index(*index, self.restriction.clone()).dependency.todo_list() { list = todo_list } else { break @@ -482,12 +486,12 @@ impl App { #[inline] pub fn is_undone_empty(&self) -> bool{ - self.current_list().is_empty(&self.restriction) + self.current_list().is_empty(self.restriction.clone()) } #[inline] pub fn is_done_empty(&self) -> bool{ - self.current_list().is_empty(&self.restriction) + self.current_list().is_empty(self.restriction.clone()) } #[inline] @@ -528,10 +532,10 @@ impl App { let size = self.len(); if size <= index { - return Some(¤t_list.index(size - 1, &self.restriction)); + return Some(¤t_list.index(size - 1, self.restriction.clone())); } - Some(&self.current_list().index(index, &self.restriction)) + Some(&self.current_list().index(index, self.restriction.clone())) } #[inline] @@ -556,7 +560,7 @@ impl App { #[inline] pub fn display_list(&self, todo_list: &TodoList) -> Vec { - todo_list.display(&self.args.display_args, &self.restriction) + todo_list.display(&self.args.display_args, self.restriction.clone()) } #[inline] @@ -642,7 +646,7 @@ impl App { #[inline] pub fn print_selected(&self) { for index in self.selected.clone() { - println!("{}", self.todo_list.index(index, &self.restriction).display(&self.args.display_args)); + println!("{}", self.todo_list.index(index, self.restriction.clone()).display(&self.args.display_args)); } } } @@ -680,10 +684,6 @@ mod tests { Ok(app) } - // fn remove_test() { - // // let _ = Command::new("rm").args(vec!["-f","-r", DIR_NAME]).spawn(); - // } - #[test] fn test_write() -> io::Result<()> { let dir = dir("test-write")?; diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index c3f0322..d93463e 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -19,7 +19,7 @@ impl TodoList { } } - pub fn index(&self, index:usize, restriction: &RestrictionFunction) -> &Output { + pub fn index(&self, index:usize, restriction: RestrictionFunction) -> &Output { self.todos(restriction)[index] } @@ -31,7 +31,7 @@ impl TodoList { } } - pub fn todos(&self, restriction: &RestrictionFunction) -> Vec<&Todo> { + pub fn todos(&self, restriction: RestrictionFunction) -> Vec<&Todo> { if let Some(restriction) = restriction { self.todos.iter().filter(|todo| restriction(todo)).collect() } else { @@ -157,19 +157,19 @@ impl TodoList { self.todos.iter_mut() } - pub fn messages(&self, restriction: &RestrictionFunction) -> Vec { + pub fn messages(&self, restriction: RestrictionFunction) -> Vec { self.todos(restriction).iter().map(|todo| todo.message.clone()).collect() } - pub fn display(&self, args: &DisplayArgs, restriction: &RestrictionFunction) -> Vec { + pub fn display(&self, args: &DisplayArgs, restriction: RestrictionFunction) -> Vec { self.todos(restriction).iter().map(|todo| todo.display(&args)).collect() } - pub fn len(&self, restriction: &RestrictionFunction) -> usize { + pub fn len(&self, restriction: RestrictionFunction) -> usize { self.todos(restriction).len() } - pub fn is_empty(&self, restriction: &RestrictionFunction) -> bool { + pub fn is_empty(&self, restriction: RestrictionFunction) -> bool { self.todos(restriction).is_empty() } From 870cfe9e6ab89eee237b9ad3c1f03762cef3aeff Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Tue, 9 Apr 2024 16:35:32 +0330 Subject: [PATCH 10/53] Update index method of todo_list.rs --- src/todo_app/todo_list.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index d93463e..3f8cfdf 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -20,10 +20,14 @@ impl TodoList { } pub fn index(&self, index:usize, restriction: RestrictionFunction) -> &Output { - self.todos(restriction)[index] + if let Some(restriction) = restriction { + self.todos.iter().filter(|todo| restriction(todo)).nth(index).unwrap() + } else { + self.todos.iter().nth(index).unwrap() + } } - pub fn index_mut(&mut self, index:usize, restriction: RestrictionFunction) -> &mut Todo { + pub fn index_mut(&mut self, index:usize, restriction: RestrictionFunction) -> &mut Output { if let Some(restriction) = restriction { self.todos.iter_mut().filter(|todo| restriction(todo)).nth(index).unwrap() } else { From 39a18798e09c76355560c478a49e23afc3ab2b0b Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Tue, 9 Apr 2024 20:59:00 +0330 Subject: [PATCH 11/53] Fix minor duplications --- src/todo_app.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 25b72d4..824e569 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -183,7 +183,7 @@ impl App { #[inline] pub fn prepend(&mut self, message:String) { self.mut_current_list().prepend(Todo::default(message, 1)); - self.index = 0; + self.go_top(); } #[inline] @@ -307,8 +307,7 @@ impl App { pub fn increment(&mut self) { let size = self.len(); if size == 0 { - self.index = 0; - return; + return self.go_top(); }; if self.index != size - 1 { self.index += 1 @@ -337,7 +336,7 @@ impl App { match self.todo() { Some(todo) if todo.dependency.is_list() => { self.prior_indexes.push(self.index); - self.index = 0; + self.go_top(); self.search(None); } _ => {}, From 78c87eb7d57691faca921f605f89110e4e4ab336 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Wed, 10 Apr 2024 13:51:56 +0330 Subject: [PATCH 12/53] Add restriction to todo_list::remove --- src/todo_app.rs | 8 +++++--- src/todo_app/todo_list.rs | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 824e569..af161f9 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -93,7 +93,7 @@ impl App { self.todo_list.index_mut(sel_index, self.restriction.clone()).set_message(message); } if self.args.delete_selected { - self.todo_list.remove(sel_index); + self.todo_list.remove(sel_index, self.restriction.clone()); self.selected.remove(iter_index); index_shift += 1; self.changed = true; @@ -392,9 +392,10 @@ impl App { #[inline] pub fn cut_todo(&mut self) { + let restriction = self.restriction.clone(); if !self.is_todos_empty() { let index = self.index; - let todo = self.mut_current_list().remove(index); + let todo = self.mut_current_list().remove(index, restriction); let todo_string:String = (&todo).into(); self.clipboard.set_text(todo_string); } @@ -545,9 +546,10 @@ impl App { #[inline] pub fn delete_todo(&mut self) { + let restriction = self.restriction.clone(); if !self.is_todos_empty() { let index = self.index; - let todo = self.mut_current_list().remove(index); + let todo = self.mut_current_list().remove(index, restriction); self.removed_todos.push(todo); } } diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 3f8cfdf..528bd78 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -177,8 +177,11 @@ impl TodoList { self.todos(restriction).is_empty() } - pub fn remove(&mut self, index:usize) -> Todo{ - self.todos.remove(index).clone() + pub fn remove(&mut self, index:usize, restriction: RestrictionFunction) -> Todo{ + let mut binding = self.todos(restriction); + let filtered:Vec<_> = binding.iter_mut().collect(); + let index_in_vec = self.todos.iter().position(|x| &x == filtered[index]).unwrap(); + self.todos.remove(index_in_vec) } pub fn push(&mut self,item:Todo) { From a3fddbbb7483bd6684d622d2cc5979bf95a841ca Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Wed, 10 Apr 2024 14:42:01 +0330 Subject: [PATCH 13/53] Refactor todo_app::do_commands_on_selected --- src/cli_app.rs | 4 +--- src/todo_app.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index ec09bdc..f68b209 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -26,9 +26,7 @@ impl <'a>CliApp <'a>{ app.append_list_from_path(path) } app.do_commands_on_selected(); - if !app.args.append_todo.is_empty() || !app.args.prepend_todo.is_empty() || app.is_changed(){ - let _ = app.write(); - } + let _ = app.write(); CliApp { todo_app: app, } diff --git a/src/todo_app.rs b/src/todo_app.rs index af161f9..323c3a4 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -86,24 +86,24 @@ impl App { } let sel_index = *sel_index - index_shift; let iter_index = iter_index - iter_index; + let todo = self.todo_list.index_mut(sel_index, self.restriction.clone()); + self.changed = true; if let Some(priority) = self.args.set_selected_priority { - self.todo_list.index_mut(sel_index, self.restriction.clone()).set_priority(priority as PriorityType); + todo.set_priority(priority as PriorityType); } if let Some(message) = self.args.set_selected_message.clone() { - self.todo_list.index_mut(sel_index, self.restriction.clone()).set_message(message); - } - if self.args.delete_selected { - self.todo_list.remove(sel_index, self.restriction.clone()); - self.selected.remove(iter_index); - index_shift += 1; - self.changed = true; + todo.set_message(message); } if self.args.done_selected { - self.todo_list.index_mut(sel_index, self.restriction.clone()).toggle_done(); + todo.toggle_done(); if !self.show_done() { self.selected.remove(iter_index); } - self.changed = true; + } + if self.args.delete_selected { + self.todo_list.remove(sel_index, self.restriction.clone()); + self.selected.remove(iter_index); + index_shift += 1; } } } From 23af8089bdfaf0cb48083059b09fcbf348da2d9b Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Wed, 10 Apr 2024 15:06:29 +0330 Subject: [PATCH 14/53] Add show only undone when priority limiting --- src/todo_app.rs | 6 ++++++ src/tui_app.rs | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 323c3a4..4402a56 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -505,6 +505,12 @@ impl App { self.restriction = Some(Rc::new(move |todo| todo.priority() == priority)) } + #[inline] + pub fn set_priority_limit_no_done(&mut self, priority:PriorityType) { + self.args.display_args.show_done = false; + self.restriction = Some(Rc::new(move |todo| todo.priority() == priority && !todo.done())) + } + #[inline] pub fn set_current_priority(&mut self, priority:PriorityType) { if let Some(todo) = self.mut_todo() { diff --git a/src/tui_app.rs b/src/tui_app.rs index b6dac5d..acca140 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -261,14 +261,23 @@ impl<'a>TuiApp<'a>{ } #[inline] - fn on_priority_prompt(&mut self, str: String) { + fn on_priority_prompt(&mut self, mut str: String) { if str.is_empty() { - self.todo_app.unset_restriction(); - return + return self.todo_app.unset_restriction(); } + let show_done = if str.chars().last().unwrap() == 'd' { + str.pop(); + true + } else { + false + }; let priority = str.parse::().ok(); if let Some(priority) = priority { - self.todo_app.set_priority_limit(priority) + if show_done { + self.todo_app.set_priority_limit(priority) + } else { + self.todo_app.set_priority_limit_no_done(priority) + } } } From fec6ce82f687bead1464074950a1bb30c311d99c Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Wed, 10 Apr 2024 15:18:28 +0330 Subject: [PATCH 15/53] Add standardize_priority method in todo --- src/todo_app/todo.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index e59c811..0f684b5 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -312,10 +312,22 @@ impl Todo { self.done = done; } + #[inline(always)] + fn standardize_priority(priority:PriorityType) -> PriorityType { + match priority { + 0 => 10, + any => any, + } + } + + #[inline(always)] + fn standardized_priority(&self) -> PriorityType { + Self::standardize_priority(self.priority) + } #[inline] pub fn decrease_priority(&mut self) { - if self.comparison_priority() < 9 { + if self.standardized_priority() < 9 { self.priority+=1 } else { self.priority=0 @@ -324,8 +336,8 @@ impl Todo { #[inline] pub fn increase_priority(&mut self) { - if self.comparison_priority() > 1 { - self.priority=self.comparison_priority()-1 + if self.standardized_priority() > 1 { + self.priority=self.standardized_priority()-1 } else { self.priority=1 } @@ -344,17 +356,12 @@ impl Todo { #[inline(always)] pub fn comparison_priority(&self) -> PriorityType { - let mut priority = if self.priority == 0 { - 20 - } else { - self.priority*2 - }; - + let mut priority = self.standardized_priority()*2; if self.schedule.is_reminder() { priority-=1; } if self.done() { - priority+=20; + priority+=Self::standardize_priority(0)*2; } priority From 8bde5d9f542f475e9862581a56a036e1d812a061 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 11 Apr 2024 01:26:06 +0330 Subject: [PATCH 16/53] Fix show_done state bug --- src/tui_app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui_app.rs b/src/tui_app.rs index acca140..85d4d55 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -263,7 +263,7 @@ impl<'a>TuiApp<'a>{ #[inline] fn on_priority_prompt(&mut self, mut str: String) { if str.is_empty() { - return self.todo_app.unset_restriction(); + return self.todo_app.update_show_done_restriction(); } let show_done = if str.chars().last().unwrap() == 'd' { str.pop(); From e6a5c2d2219e1fbe4b6271a6994516f70df04d63 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 11 Apr 2024 13:14:18 +0330 Subject: [PATCH 17/53] Add reordering when show_done is true --- src/todo_app.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 4402a56..4101b60 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -265,12 +265,13 @@ impl App { #[inline] pub fn toggle_current_done(&mut self) { - let restriction = self.restriction.clone(); + let index = self.index; self.mut_todo().unwrap().toggle_done(); - if self.mut_current_list().is_empty(restriction) { - + if self.show_done() { + self.index = self.mut_current_list().reorder(index); + } else { + self.mut_current_list().sort(); } - self.mut_current_list().sort(); } #[inline] From 938c534139fe8d75fb6d5dfa05d2c1ccde9c59a1 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 11 Apr 2024 14:15:30 +0330 Subject: [PATCH 18/53] Clean todo_app::do_commands_on_selected method --- src/todo_app.rs | 50 ++++++++++++++++----------------------- src/todo_app/search.rs | 5 ---- src/todo_app/todo_list.rs | 4 ++++ 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 4101b60..f6bf81a 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -57,13 +57,6 @@ impl App { restriction: None, }; app.update_show_done_restriction(); - for str in app.args.search_and_select.clone() { - app.search(Some(str)); - for index in app.search.indices() { - app.selected.push(index); - } - } - app } @@ -79,33 +72,30 @@ impl App { } pub fn do_commands_on_selected(&mut self) { - let mut index_shift = 0; - for (iter_index, sel_index) in self.selected.clone().iter().enumerate() { - if index_shift > *sel_index || index_shift > iter_index { - break - } - let sel_index = *sel_index - index_shift; - let iter_index = iter_index - iter_index; - let todo = self.todo_list.index_mut(sel_index, self.restriction.clone()); - self.changed = true; - if let Some(priority) = self.args.set_selected_priority { - todo.set_priority(priority as PriorityType); - } - if let Some(message) = self.args.set_selected_message.clone() { - todo.set_message(message); + for query in self.args.search_and_select.iter() { + if self.args.delete_selected { + self.changed = true; + self.todo_list.set_todos(self.todo_list + .iter() + .filter(|todo| !todo.matches(query)) + .cloned() + .collect()); + continue; } - if self.args.done_selected { - todo.toggle_done(); - if !self.show_done() { - self.selected.remove(iter_index); + for todo in self.todo_list.iter_mut().filter(|todo| todo.matches(query)) { + self.changed = true; + if let Some(priority) = self.args.set_selected_priority { + todo.set_priority(priority as PriorityType); + } + if let Some(message) = self.args.set_selected_message.clone() { + todo.set_message(message); + } + if self.args.done_selected { + todo.set_done(true); } - } - if self.args.delete_selected { - self.todo_list.remove(sel_index, self.restriction.clone()); - self.selected.remove(iter_index); - index_shift += 1; } } + self.args.search_and_select = vec![]; } fn traverse_parents_from_root(&mut self, callback: fn(&mut App, &TodoList, &[usize])) { diff --git a/src/todo_app/search.rs b/src/todo_app/search.rs index beb6a85..04dd59d 100644 --- a/src/todo_app/search.rs +++ b/src/todo_app/search.rs @@ -47,11 +47,6 @@ impl Search { } } - #[inline] - pub fn indices(&self) -> Vec{ - self.indices.clone() - } - #[inline] pub fn next(&mut self) -> Option { if self.indices.is_empty() { diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 528bd78..d6f4036 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -152,6 +152,10 @@ impl TodoList { Ok(dependency_path) } + #[inline(always)] + pub (super) fn set_todos(&mut self, todos:Vec) { + self.todos = todos + } pub fn iter(&self) -> std::slice::Iter { self.todos.iter() From adad674b638f722c473fe2f4532556e7c69e2f12 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 11 Apr 2024 14:34:22 +0330 Subject: [PATCH 19/53] Change name of prior_indexes property --- src/todo_app.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index f6bf81a..77fa796 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -14,7 +14,7 @@ pub use self::todo_list::TodoList; #[derive(Clone)] struct SearchPosition { - prior_indices: Vec, + tree_path: Vec, matching_indices: Vec, } @@ -24,7 +24,7 @@ pub struct App { clipboard: Clipboard, pub(super) todo_list: TodoList, index: usize, - prior_indexes: Vec, + tree_path: Vec, changed:bool, pub(super) args: Args, removed_todos: Vec, @@ -50,7 +50,7 @@ impl App { todo_list, clipboard: Clipboard::new(), index: 0, - prior_indexes: vec![], + tree_path: vec![], changed: false, args, search: Search::new(), @@ -111,7 +111,7 @@ impl App { } if !matching_indices.is_empty() { self.tree_search_positions.push(SearchPosition { - prior_indices: prior_indices.to_vec(), + tree_path: prior_indices.to_vec(), matching_indices, }) } @@ -128,7 +128,7 @@ impl App { return; } let before_position = SearchPosition { - prior_indices: self.prior_indexes.clone(), + tree_path: self.tree_path.clone(), matching_indices: vec![self.index], }; self.tree_search_positions.push(before_position); @@ -138,7 +138,7 @@ impl App { pub fn print_searched(&mut self) { for position in self.tree_search_positions.iter() { - self.prior_indexes = position.prior_indices.clone(); + self.tree_path = position.tree_path.clone(); let list = self.current_list(); for index in position.matching_indices.clone() { println!("{}",list.index(index,self.restriction.clone()).display(&self.args.display_args)); @@ -242,7 +242,7 @@ impl App { #[inline] fn set_tree_search_position(&mut self) { let item = self.tree_search_positions[self.x_index].clone(); - self.prior_indexes = item.prior_indices; + self.tree_path = item.tree_path; self.index = item.matching_indices[self.y_index]; } @@ -283,7 +283,7 @@ impl App { pub fn parent(&self) -> Option<&Todo>{ let mut list = &self.todo_list; let mut parent = None; - for index in self.prior_indexes.iter() { + for index in self.tree_path.iter() { parent = Some(list.index(*index, self.restriction.clone())); if let Some(todo_list) = list.index(*index, self.restriction.clone()).dependency.todo_list() { list = todo_list @@ -326,7 +326,7 @@ impl App { if self.is_tree() { match self.todo() { Some(todo) if todo.dependency.is_list() => { - self.prior_indexes.push(self.index); + self.tree_path.push(self.index); self.go_top(); self.search(None); } @@ -338,7 +338,7 @@ impl App { #[inline] pub fn traverse_up(&mut self) { if !self.is_root() { - self.index = self.prior_indexes.remove(self.prior_indexes.len()-1); + self.index = self.tree_path.remove(self.tree_path.len()-1); self.search(None); } } @@ -405,7 +405,7 @@ impl App { if is_root{ return list; } - for index in self.prior_indexes.iter() { + for index in self.tree_path.iter() { list = &mut list.index_mut(*index, self.restriction.clone()).dependency.todo_list }; list @@ -417,7 +417,7 @@ impl App { if self.is_root() { return list; } - for index in self.prior_indexes.iter() { + for index in self.tree_path.iter() { if let Some(todo_list) = &list.index(*index, self.restriction.clone()).dependency.todo_list() { list = todo_list } else { @@ -453,7 +453,7 @@ impl App { #[inline] pub fn is_root(&self) -> bool { - self.prior_indexes.is_empty() + self.tree_path.is_empty() } #[inline] From b703f49ae5438c1d9010954c3d0cdac91314b788 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 11 Apr 2024 15:41:28 +0330 Subject: [PATCH 20/53] Improve performance of schedule parsing Replace sscanf with a state machine --- src/cli_app.rs | 6 ++-- src/todo_app/todo.rs | 5 +-- src/todo_app/todo/schedule.rs | 62 +++++++++++++++++++++++++++-------- 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index f68b209..58e0537 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -34,9 +34,9 @@ impl <'a>CliApp <'a>{ #[inline] fn print_list(&self) { - for display in self.todo_app.display_current() { - println!("{}", display); - } + // for display in self.todo_app.display_current() { + // println!("{}", display); + // } } #[inline] diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 0f684b5..c2cb6ae 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -59,7 +59,6 @@ enum State { Priority, Dependency, Message, - End, } impl TryFrom<&str> for Todo { @@ -106,13 +105,11 @@ impl TryFrom<&str> for Todo { } State::Message => { if i == schedule_start_index.unwrap()-1 { - state = State::End; + break; } else { message.push(c); } } - State::End => { - } } } diff --git a/src/todo_app/todo/schedule.rs b/src/todo_app/todo/schedule.rs index 5fe6af7..ea3c5a9 100644 --- a/src/todo_app/todo/schedule.rs +++ b/src/todo_app/todo/schedule.rs @@ -1,5 +1,3 @@ -use scanf::sscanf; - use crate::date; #[derive(Debug, PartialEq, Clone, Default)] @@ -19,24 +17,62 @@ pub struct Schedule { pub last_type: Type, } +#[derive(Default)] +enum State { + #[default] + Type, + Days, + PreDate, + Date, +} + impl From for Schedule where T: ToString, { fn from(input:T) -> Schedule { - let mut day = 0; let mut date_string = String::new(); - let _type = match input { - _ if sscanf!(input.to_string().as_str(), "D{}({})", day, date_string).is_ok() => { - Type::Scheduled - } - _ if sscanf!(input.to_string().as_str(), "R({})", date_string).is_ok() => { - Type::Reminder + let mut state = State::default(); + let mut _type = Type::None; + let mut day_str = String::new(); + + for c in input.to_string().chars() { + match state { + State::Type => { + if c == 'D' { + _type = Type::Scheduled; + state = State::Days; + } else if c == 'R' { + _type = Type::Reminder; + state = State::PreDate; + } else { + break; + } + } + State::Days => { + if c.is_digit(10) { + day_str.push(c); + } else if c == '(' { + state = State::Date; + } + } + State::PreDate => { + if c == '(' { + state = State::Date; + } + } + State::Date => { + if c == ')' { + break; + } else { + date_string.push(c) + } + } } - _ => { - Type::None - }, - }; + } + + let day:i64 = day_str.parse().unwrap_or(0); + let date = match date::parse(&date_string) { Ok(value) => Some(value), Err(_) => None From d3a22fba6eaeabf3f6ae522ada138603eba07082 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 11 Apr 2024 15:55:19 +0330 Subject: [PATCH 21/53] Improve performance of dependency parsing Remove sscanf from dependency parsing method --- src/cli_app.rs | 6 +++--- src/todo_app/todo/dependency.rs | 19 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index 58e0537..f68b209 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -34,9 +34,9 @@ impl <'a>CliApp <'a>{ #[inline] fn print_list(&self) { - // for display in self.todo_app.display_current() { - // println!("{}", display); - // } + for display in self.todo_app.display_current() { + println!("{}", display); + } } #[inline] diff --git a/src/todo_app/todo/dependency.rs b/src/todo_app/todo/dependency.rs index d2c24e3..58d52d5 100644 --- a/src/todo_app/todo/dependency.rs +++ b/src/todo_app/todo/dependency.rs @@ -1,4 +1,3 @@ -use scanf::sscanf; use std::{io::{self, Write}, path::PathBuf, fs::File}; use super::TodoList; use super::Todo; @@ -193,19 +192,17 @@ impl From<&str> for Dependency { fn from (input: &str) -> Dependency { let mut name = String::new(); let mode: DependencyMode; - - match input { - _ if sscanf!(input, "{}.todo", name).is_ok() => { + if input.is_empty() { + mode = DependencyMode::None; + } else { + name = String::from(input); + if input.ends_with(".todo") { mode = DependencyMode::TodoList; - name = format!("{name}.todo"); - } - _ if sscanf!(input, "{}", name).is_ok() && !name.is_empty() => { + } else { mode = DependencyMode::Note; } - _ => { - mode = DependencyMode::None; - } - }; + } + Dependency::new(name, mode, false) } From cf06be1e16a7676a1fd4d7df9736118b3b7afb1b Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 11 Apr 2024 19:58:04 +0330 Subject: [PATCH 22/53] Remove scanf from dependencies and update jalali-date.patch --- Cargo.lock | 32 ------------------- Cargo.toml | 1 - patches/jalali-date.patch | 67 +++++++++++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index daca859..c40a47c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,7 +125,6 @@ dependencies = [ "crossterm", "home", "ratatui", - "scanf", "sha1", "tui-textarea", ] @@ -390,18 +389,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "mio" version = "0.8.10" @@ -414,16 +401,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "num-traits" version = "0.2.17" @@ -520,15 +497,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" -[[package]] -name = "scanf" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1af23b806e681b4f838a9887655219b755a418f60353968a88b0b87a35b3e8ce" -dependencies = [ - "nom", -] - [[package]] name = "scopeguard" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index 82185e6..2da7de8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ version = "0.9.0" edition = "2021" [dependencies] -scanf = "1.2.1" sha1 = "0.10.1" home = "0.5.9" ratatui = "0.25.0" diff --git a/patches/jalali-date.patch b/patches/jalali-date.patch index 8fda82b..fdde91e 100644 --- a/patches/jalali-date.patch +++ b/patches/jalali-date.patch @@ -1,16 +1,18 @@ diff --git a/Cargo.lock b/Cargo.lock -index 7102f72..a095dfe 100644 +index c40a47c..0988fcc 100644 --- a/Cargo.lock +++ b/Cargo.lock -@@ -124,6 +124,7 @@ dependencies = [ +@@ -124,7 +124,9 @@ dependencies = [ "clap", "crossterm", "home", + "jalali-date", "ratatui", - "scanf", ++ "scanf", "sha1", -@@ -350,6 +351,12 @@ dependencies = [ + "tui-textarea", + ] +@@ -349,6 +351,12 @@ dependencies = [ "either", ] @@ -23,15 +25,68 @@ index 7102f72..a095dfe 100644 [[package]] name = "js-sys" version = "0.3.67" +@@ -389,6 +397,18 @@ dependencies = [ + "hashbrown", + ] + ++[[package]] ++name = "memchr" ++version = "2.7.2" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" ++ ++[[package]] ++name = "minimal-lexical" ++version = "0.2.1" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" ++ + [[package]] + name = "mio" + version = "0.8.10" +@@ -401,6 +421,16 @@ dependencies = [ + "windows-sys 0.48.0", + ] + ++[[package]] ++name = "nom" ++version = "7.1.3" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" ++dependencies = [ ++ "memchr", ++ "minimal-lexical", ++] ++ + [[package]] + name = "num-traits" + version = "0.2.17" +@@ -497,6 +527,15 @@ version = "1.0.14" + source = "registry+https://github.com/rust-lang/crates.io-index" + checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + ++[[package]] ++name = "scanf" ++version = "1.2.1" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++checksum = "1af23b806e681b4f838a9887655219b755a418f60353968a88b0b87a35b3e8ce" ++dependencies = [ ++ "nom", ++] ++ + [[package]] + name = "scopeguard" + version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml -index dc3fbce..d4ddba8 100644 +index 2da7de8..266ecb1 100644 --- a/Cargo.toml +++ b/Cargo.toml -@@ -12,6 +12,7 @@ crossterm = "0.27.0" +@@ -11,6 +11,8 @@ crossterm = "0.27.0" tui-textarea = "0.4.0" chrono = "0.4.31" clap = { version = "4.4.18", features = ["derive", "string"] } +jalali-date = "0.2.0" ++scanf = "1.2.1" [profile.release] codegen-units = 1 From b1a40fabbbaaf0e287d522239ab647309e6020b0 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Fri, 12 Apr 2024 11:01:35 +0330 Subject: [PATCH 23/53] Clean TUI code --- src/todo_app.rs | 4 ++-- src/todo_app/todo_list.rs | 8 +++++++- src/tui_app.rs | 41 ++++++++++++++++++++++++--------------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 77fa796..de21d70 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -386,7 +386,7 @@ impl App { let restriction = self.restriction.clone(); if !self.is_todos_empty() { let index = self.index; - let todo = self.mut_current_list().remove(index, restriction); + let todo = self.mut_current_list().cut(index, restriction); let todo_string:String = (&todo).into(); self.clipboard.set_text(todo_string); } @@ -546,7 +546,7 @@ impl App { let restriction = self.restriction.clone(); if !self.is_todos_empty() { let index = self.index; - let todo = self.mut_current_list().remove(index, restriction); + let todo = self.mut_current_list().cut(index, restriction); self.removed_todos.push(todo); } } diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index d6f4036..7d37ea2 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -181,7 +181,13 @@ impl TodoList { self.todos(restriction).is_empty() } - pub fn remove(&mut self, index:usize, restriction: RestrictionFunction) -> Todo{ + pub fn remove(&mut self, index:usize, restriction: RestrictionFunction) { + let mut binding = self.todos(restriction); + let filtered:Vec<_> = binding.iter_mut().collect(); + self.todos = self.todos.iter().filter(|x| &x != &filtered[index]).cloned().collect(); + } + + pub fn cut(&mut self, index:usize, restriction: RestrictionFunction) -> Todo{ let mut binding = self.todos(restriction); let filtered:Vec<_> = binding.iter_mut().collect(); let index_in_vec = self.todos.iter().position(|x| &x == filtered[index]).unwrap(); diff --git a/src/tui_app.rs b/src/tui_app.rs index 85d4d55..2366efb 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -38,13 +38,14 @@ pub enum TodoWidget<'a> { pub fn create_todo_widget<'a>(display_list:&Vec, title:String, highlight_symbol: &'a str) -> TodoWidget<'a> { if display_list.is_empty() { - return TodoWidget::Paragraph(Paragraph::new("No todo.").block(default_block(title))) + TodoWidget::Paragraph(Paragraph::new("No todo.").block(default_block(title))) + } else { + TodoWidget::List(List::new((*display_list).clone()) + .block(default_block(title)) + .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) + .highlight_symbol(highlight_symbol) + .repeat_highlight_symbol(true)) } - TodoWidget::List(List::new((*display_list).clone()) - .block(default_block(title)) - .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) - .highlight_symbol(highlight_symbol) - .repeat_highlight_symbol(true)) } /// Shutdown TUI app (undo everything did in startup, and show cursor) @@ -507,19 +508,27 @@ impl<'a>TuiApp<'a>{ frame.render_widget(note_widget, dependency_layout); } if let Some(todo_list) = todo.dependency.todo_list() { - match create_todo_widget(&self.todo_app.display_list(todo_list), String::from("Todo dependencies"), self.highlight_string()) { - TodoWidget::List(widget) => frame.render_widget(widget, dependency_layout), - TodoWidget::Paragraph(widget) => frame.render_widget(widget, dependency_layout), - } + self.render_todos_widget(frame, None, dependency_layout, &self.todo_app.display_list(todo_list), String::from("Todo dependencies")) } } } - #[inline] - fn render_todos_widget(&self, frame: &mut Frame, list_state: &mut ListState, todo_layout: &Rc<[Rect]>) { - match create_todo_widget(&self.todo_app.display_current(), self.title(), self.highlight_string()) { - TodoWidget::Paragraph(widget) => frame.render_widget(widget, todo_layout[1]), - TodoWidget::List(widget) => frame.render_stateful_widget(widget, todo_layout[1], list_state), + #[inline(always)] + fn render_current_todos_widget(&self, frame: &mut Frame, list_state: &mut ListState, todo_layout: Rect) { + self.render_todos_widget(frame, Some(list_state), todo_layout, &self.todo_app.display_current(), self.title()) + } + + #[inline(always)] + fn render_todos_widget(&self, frame: &mut Frame, list_state: Option<&mut ListState>, todo_layout: Rect, display_list:&Vec, title: String) { + match create_todo_widget(display_list, title, self.highlight_string()) { + TodoWidget::Paragraph(widget) => frame.render_widget(widget, todo_layout), + TodoWidget::List(widget) => { + if let Some(list_state) = list_state { + frame.render_stateful_widget(widget, todo_layout, list_state) + } else { + frame.render_widget(widget, todo_layout) + } + } } } @@ -548,6 +557,6 @@ impl<'a>TuiApp<'a>{ self.render_dependency_widget(frame, todo, todo_app_layout[1]); frame.render_widget(self.textarea.widget(), todo_and_textarea_layout[0]); - self.render_todos_widget(frame, list_state, &todo_and_textarea_layout); + self.render_current_todos_widget(frame, list_state, todo_and_textarea_layout[1]); } } From 5497fadee8616b299037268f1f2881c9801a2c2f Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Fri, 12 Apr 2024 11:02:01 +0330 Subject: [PATCH 24/53] Remove comments --- src/tui_app.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/tui_app.rs b/src/tui_app.rs index 2366efb..c3357b4 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -48,7 +48,6 @@ pub fn create_todo_widget<'a>(display_list:&Vec, title:String, highlight } } -/// Shutdown TUI app (undo everything did in startup, and show cursor) pub fn shutdown() -> io::Result<()> { disable_raw_mode()?; stdout().execute(crossterm::cursor::Show)?; @@ -56,14 +55,12 @@ pub fn shutdown() -> io::Result<()> { Ok(()) } -/// Prepare terminal for TUI applicaton by enabling rowmode and entering alternate screen. pub fn startup() -> io::Result<()> { enable_raw_mode()?; stdout().execute(EnterAlternateScreen)?; Ok(()) } -/// Restart terminal #[inline] pub fn restart(terminal: &mut Terminal>) -> io::Result<()> { terminal.clear()?; @@ -71,7 +68,6 @@ pub fn restart(terminal: &mut Terminal>) -> io::Res Ok(()) } -/// Restart TUI app #[inline] pub fn run(app:&mut App) -> io::Result<()> { startup()?; From 37b99a99fe4b0f571be1cde0829fcd28e6f83768 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Fri, 12 Apr 2024 13:56:29 +0330 Subject: [PATCH 25/53] Fix restriction change path problem --- src/todo_app.rs | 40 ++++++++++++++++++++++++++++++---------- src/tui_app.rs | 2 +- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index de21d70..148b2b6 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -271,11 +271,10 @@ impl App { } #[inline] - pub fn fix_index(&mut self) { - let size = self.len(); - self.index = match size { - 0 => 0, - _ => self.index.min(size-1), + pub fn fixed_index(&mut self) { + self.index = match Self::fix_index(self.restriction.clone() ,self.current_list(), self.index) { + Some(index) => index, + None => 0, }; } @@ -284,13 +283,17 @@ impl App { let mut list = &self.todo_list; let mut parent = None; for index in self.tree_path.iter() { - parent = Some(list.index(*index, self.restriction.clone())); - if let Some(todo_list) = list.index(*index, self.restriction.clone()).dependency.todo_list() { + let index = Self::fix_index(self.restriction.clone(), list, *index); + if index.is_none() { + break + } + parent = Some(list.index(index.unwrap(), self.restriction.clone())); + if let Some(todo_list) = list.index(index.unwrap(), self.restriction.clone()).dependency.todo_list() { list = todo_list } else { break } - }; + } parent } @@ -397,6 +400,14 @@ impl App { self.current_list().len(self.restriction.clone()) } + pub fn fix_index(restriction: RestrictionFunction, list: &TodoList, index: usize) -> Option { + let size = list.len(restriction); + match size { + 0 => None, + _ => Some(index.min(size-1)), + } + } + #[inline] pub fn mut_current_list(&mut self) -> &mut TodoList { self.changed = true; @@ -406,7 +417,12 @@ impl App { return list; } for index in self.tree_path.iter() { - list = &mut list.index_mut(*index, self.restriction.clone()).dependency.todo_list + let index = Self::fix_index(self.restriction.clone() ,list, *index); + if let Some(index) = index { + list = &mut list.index_mut(index, self.restriction.clone()).dependency.todo_list + } else { + break; + } }; list } @@ -418,7 +434,11 @@ impl App { return list; } for index in self.tree_path.iter() { - if let Some(todo_list) = &list.index(*index, self.restriction.clone()).dependency.todo_list() { + let index = Self::fix_index(self.restriction.clone() ,list, *index); + if index.is_none() { + break + } + if let Some(todo_list) = &list.index(index.unwrap(), self.restriction.clone()).dependency.todo_list() { list = todo_list } else { break diff --git a/src/tui_app.rs b/src/tui_app.rs index c3357b4..1deb1f9 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -367,7 +367,7 @@ impl<'a>TuiApp<'a>{ output = self.update_editor()?; } else { output = self.update_no_editor()?; - self.todo_app.fix_index(); + self.todo_app.fixed_index(); } Ok(output) } From f83e1563b6e34d509aaf31c7c5342b0018430de7 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Fri, 12 Apr 2024 13:57:57 +0330 Subject: [PATCH 26/53] Complete fix for index problem --- src/todo_app.rs | 8 +++++++- src/tui_app.rs | 15 ++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 148b2b6..a4b6d8c 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -279,9 +279,10 @@ impl App { } #[inline] - pub fn parent(&self) -> Option<&Todo>{ + pub fn parent(&mut self) -> Option<&Todo>{ let mut list = &self.todo_list; let mut parent = None; + let mut count = 0; for index in self.tree_path.iter() { let index = Self::fix_index(self.restriction.clone(), list, *index); if index.is_none() { @@ -293,7 +294,9 @@ impl App { } else { break } + count+=1; } + self.tree_path.truncate(count); parent } @@ -416,6 +419,7 @@ impl App { if is_root{ return list; } + let mut count = 0; for index in self.tree_path.iter() { let index = Self::fix_index(self.restriction.clone() ,list, *index); if let Some(index) = index { @@ -423,7 +427,9 @@ impl App { } else { break; } + count+=1; }; + self.tree_path.truncate(count); list } diff --git a/src/tui_app.rs b/src/tui_app.rs index 1deb1f9..50b0756 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -123,7 +123,7 @@ impl<'a>TuiApp<'a>{ } #[inline] - pub fn title(&self) -> String { + pub fn title(&mut self) -> String { let changed_str = if self.todo_app.is_changed() { "*" } else { @@ -504,19 +504,20 @@ impl<'a>TuiApp<'a>{ frame.render_widget(note_widget, dependency_layout); } if let Some(todo_list) = todo.dependency.todo_list() { - self.render_todos_widget(frame, None, dependency_layout, &self.todo_app.display_list(todo_list), String::from("Todo dependencies")) + Self::render_todos_widget(self.highlight_string(), frame, None, dependency_layout, &self.todo_app.display_list(todo_list), String::from("Todo dependencies")) } } } #[inline(always)] - fn render_current_todos_widget(&self, frame: &mut Frame, list_state: &mut ListState, todo_layout: Rect) { - self.render_todos_widget(frame, Some(list_state), todo_layout, &self.todo_app.display_current(), self.title()) + fn render_current_todos_widget(&mut self, frame: &mut Frame, list_state: &mut ListState, todo_layout: Rect) { + let title = self.title(); + Self::render_todos_widget(self.highlight_string(),frame, Some(list_state), todo_layout, &self.todo_app.display_current(), title) } #[inline(always)] - fn render_todos_widget(&self, frame: &mut Frame, list_state: Option<&mut ListState>, todo_layout: Rect, display_list:&Vec, title: String) { - match create_todo_widget(display_list, title, self.highlight_string()) { + fn render_todos_widget(highlight_symbol: &str,frame: &mut Frame, list_state: Option<&mut ListState>, todo_layout: Rect, display_list:&Vec, title: String) { + match create_todo_widget(display_list, title, highlight_symbol) { TodoWidget::Paragraph(widget) => frame.render_widget(widget, todo_layout), TodoWidget::List(widget) => { if let Some(list_state) = list_state { @@ -529,7 +530,7 @@ impl<'a>TuiApp<'a>{ } #[inline] - pub fn ui(&self, frame:&mut Frame, list_state: &mut ListState) { + pub fn ui(&mut self, frame:&mut Frame, list_state: &mut ListState) { let todo = self.todo_app.todo(); list_state.select(Some(self.todo_app.index())); From 2d00ddb5e5d5e228512c0e307a93291abca1a32d Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sat, 13 Apr 2024 15:28:49 +0330 Subject: [PATCH 27/53] Fix tree path problems - Save real path as state instead of index - Index through the actual array instead of using restriction for path finding --- src/cli_app.rs | 6 +-- src/todo_app.rs | 90 ++++++++++++++++++--------------------- src/todo_app/todo_list.rs | 28 ++++++------ src/tui_app.rs | 2 +- 4 files changed, 61 insertions(+), 65 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index f68b209..477f36c 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -1,5 +1,5 @@ use std::io; -use super::todo_app::{App, TodoList, Todo, RestrictionFunction}; +use super::todo_app::{App, TodoList, Todo, Restriction}; use crate::DisplayArgs; #[inline] @@ -51,7 +51,7 @@ impl <'a>CliApp <'a>{ } if self.todo_app.is_tree() { let mut print_todo = PrintTodoTree::new(self.todo_app.args.minimal_tree); - print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args, self.todo_app.restriction.clone()); + print_todo.print_list(&self.todo_app.todo_list, &self.todo_app.args.display_args, self.todo_app.restriction()); } else { self.print_list() } @@ -89,7 +89,7 @@ impl PrintTodoTree { } #[inline] - pub fn print_list(&mut self, todo_list: &TodoList, display_args: &DisplayArgs, restriction: RestrictionFunction) { + pub fn print_list(&mut self, todo_list: &TodoList, display_args: &DisplayArgs, restriction: Restriction) { let todos = todo_list.todos(restriction.clone()); for (index, todo) in todos.iter().enumerate() { diff --git a/src/todo_app.rs b/src/todo_app.rs index a4b6d8c..599462d 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -18,7 +18,8 @@ struct SearchPosition { matching_indices: Vec, } -pub type RestrictionFunction = Option bool>>; +pub type RestrictionFunction = Rc bool>; +pub type Restriction = Option; pub struct App { selected: Vec, clipboard: Clipboard, @@ -33,7 +34,7 @@ pub struct App { last_query: String, x_index: usize, y_index: usize, - pub(super) restriction: RestrictionFunction, + restriction: Restriction, } impl App { @@ -60,6 +61,10 @@ impl App { app } + pub fn restriction(&self) -> Restriction{ + self.restriction.clone() + } + #[inline] pub fn append_list_from_path(&mut self, path: PathBuf) { let todo_list = TodoList::read(&path, !self.args.no_tree, true); @@ -212,9 +217,9 @@ impl App { pub fn update_show_done_restriction(&mut self) { if self.show_done() { - self.restriction = None + self.unset_restriction() } else { - self.restriction = Some(Rc::new(|todo| !todo.done())) + self.set_restriction(Rc::new(|todo| !todo.done())) } } @@ -271,32 +276,26 @@ impl App { } #[inline] - pub fn fixed_index(&mut self) { - self.index = match Self::fix_index(self.restriction.clone() ,self.current_list(), self.index) { - Some(index) => index, - None => 0, - }; + pub fn fix_index(&mut self) { + let size = self.current_list().len(self.restriction.clone()); + self.index = match size { + 0 => 0, + _ => self.index.min(size-1), + } } #[inline] pub fn parent(&mut self) -> Option<&Todo>{ let mut list = &self.todo_list; let mut parent = None; - let mut count = 0; - for index in self.tree_path.iter() { - let index = Self::fix_index(self.restriction.clone(), list, *index); - if index.is_none() { - break - } - parent = Some(list.index(index.unwrap(), self.restriction.clone())); - if let Some(todo_list) = list.index(index.unwrap(), self.restriction.clone()).dependency.todo_list() { + for index in self.tree_path.clone() { + parent = Some(&list.todos[index]); + if let Some(todo_list) = list.todos[index].dependency.todo_list() { list = todo_list } else { break } - count+=1; } - self.tree_path.truncate(count); parent } @@ -332,7 +331,7 @@ impl App { if self.is_tree() { match self.todo() { Some(todo) if todo.dependency.is_list() => { - self.tree_path.push(self.index); + self.tree_path.push(self.todo_list.true_position_in_list(self.index, self.restriction.clone())); self.go_top(); self.search(None); } @@ -343,8 +342,8 @@ impl App { #[inline] pub fn traverse_up(&mut self) { - if !self.is_root() { - self.index = self.tree_path.remove(self.tree_path.len()-1); + if let Some(index) = self.tree_path.pop() { + self.index = index; self.search(None); } } @@ -403,14 +402,6 @@ impl App { self.current_list().len(self.restriction.clone()) } - pub fn fix_index(restriction: RestrictionFunction, list: &TodoList, index: usize) -> Option { - let size = list.len(restriction); - match size { - 0 => None, - _ => Some(index.min(size-1)), - } - } - #[inline] pub fn mut_current_list(&mut self) -> &mut TodoList { self.changed = true; @@ -419,17 +410,17 @@ impl App { if is_root{ return list; } - let mut count = 0; - for index in self.tree_path.iter() { - let index = Self::fix_index(self.restriction.clone() ,list, *index); - if let Some(index) = index { - list = &mut list.index_mut(index, self.restriction.clone()).dependency.todo_list - } else { - break; - } - count+=1; + for index in self.tree_path.clone() { + list = &mut list.todos[index].dependency.todo_list }; - self.tree_path.truncate(count); + // for index in self.tree_path.iter() { + // let index = Self::fix_index(self.restriction.clone() ,list, *index); + // if let Some(index) = index { + // list = &mut list.index_mut(index, self.restriction.clone()).dependency.todo_list + // } else { + // break; + // } + // }; list } @@ -439,12 +430,8 @@ impl App { if self.is_root() { return list; } - for index in self.tree_path.iter() { - let index = Self::fix_index(self.restriction.clone() ,list, *index); - if index.is_none() { - break - } - if let Some(todo_list) = &list.index(index.unwrap(), self.restriction.clone()).dependency.todo_list() { + for index in self.tree_path.clone() { + if let Some(todo_list) = &list.todos[index].dependency.todo_list() { list = todo_list } else { break @@ -511,21 +498,26 @@ impl App { self.current_list().is_empty(self.restriction.clone()) } - #[inline] + #[inline(always)] pub fn unset_restriction(&mut self) { self.restriction = None } + #[inline(always)] + pub fn set_restriction(&mut self, restriction: RestrictionFunction) { + self.restriction = Some(restriction) + } + #[inline] pub fn set_priority_limit(&mut self, priority:PriorityType) { self.args.display_args.show_done = true; - self.restriction = Some(Rc::new(move |todo| todo.priority() == priority)) + self.set_restriction(Rc::new(move |todo| todo.priority() == priority)) } #[inline] pub fn set_priority_limit_no_done(&mut self, priority:PriorityType) { self.args.display_args.show_done = false; - self.restriction = Some(Rc::new(move |todo| todo.priority() == priority && !todo.done())) + self.set_restriction(Rc::new(move |todo| todo.priority() == priority && !todo.done())) } #[inline] diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 7d37ea2..a020b7b 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -3,12 +3,12 @@ use std::path::PathBuf; use std::io::{stdout, BufRead, BufWriter, Write}; use std::io; -use super::{App, Todo, RestrictionFunction}; +use super::{App, Todo, Restriction}; use crate::DisplayArgs; #[derive(Debug,PartialEq, Clone, Default)] pub struct TodoList { - todos: Vec, + pub todos: Vec, } type Output = Todo; @@ -19,7 +19,7 @@ impl TodoList { } } - pub fn index(&self, index:usize, restriction: RestrictionFunction) -> &Output { + pub fn index(&self, index:usize, restriction: Restriction) -> &Output { if let Some(restriction) = restriction { self.todos.iter().filter(|todo| restriction(todo)).nth(index).unwrap() } else { @@ -27,7 +27,7 @@ impl TodoList { } } - pub fn index_mut(&mut self, index:usize, restriction: RestrictionFunction) -> &mut Output { + pub fn index_mut(&mut self, index:usize, restriction: Restriction) -> &mut Output { if let Some(restriction) = restriction { self.todos.iter_mut().filter(|todo| restriction(todo)).nth(index).unwrap() } else { @@ -35,7 +35,7 @@ impl TodoList { } } - pub fn todos(&self, restriction: RestrictionFunction) -> Vec<&Todo> { + pub fn todos(&self, restriction: Restriction) -> Vec<&Todo> { if let Some(restriction) = restriction { self.todos.iter().filter(|todo| restriction(todo)).collect() } else { @@ -165,32 +165,36 @@ impl TodoList { self.todos.iter_mut() } - pub fn messages(&self, restriction: RestrictionFunction) -> Vec { + pub fn messages(&self, restriction: Restriction) -> Vec { self.todos(restriction).iter().map(|todo| todo.message.clone()).collect() } - pub fn display(&self, args: &DisplayArgs, restriction: RestrictionFunction) -> Vec { + pub fn display(&self, args: &DisplayArgs, restriction: Restriction) -> Vec { self.todos(restriction).iter().map(|todo| todo.display(&args)).collect() } - pub fn len(&self, restriction: RestrictionFunction) -> usize { + pub fn len(&self, restriction: Restriction) -> usize { self.todos(restriction).len() } - pub fn is_empty(&self, restriction: RestrictionFunction) -> bool { + pub fn is_empty(&self, restriction: Restriction) -> bool { self.todos(restriction).is_empty() } - pub fn remove(&mut self, index:usize, restriction: RestrictionFunction) { + pub fn remove(&mut self, index:usize, restriction: Restriction) { let mut binding = self.todos(restriction); let filtered:Vec<_> = binding.iter_mut().collect(); self.todos = self.todos.iter().filter(|x| &x != &filtered[index]).cloned().collect(); } - pub fn cut(&mut self, index:usize, restriction: RestrictionFunction) -> Todo{ + pub fn true_position_in_list(&mut self, index:usize, restriction: Restriction) -> usize { let mut binding = self.todos(restriction); let filtered:Vec<_> = binding.iter_mut().collect(); - let index_in_vec = self.todos.iter().position(|x| &x == filtered[index]).unwrap(); + self.todos.iter().position(|x| &x == filtered[index]).unwrap() + } + + pub fn cut(&mut self, index:usize, restriction: Restriction) -> Todo{ + let index_in_vec = self.true_position_in_list(index, restriction); self.todos.remove(index_in_vec) } diff --git a/src/tui_app.rs b/src/tui_app.rs index 50b0756..198bd04 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -367,7 +367,7 @@ impl<'a>TuiApp<'a>{ output = self.update_editor()?; } else { output = self.update_no_editor()?; - self.todo_app.fixed_index(); + self.todo_app.fix_index(); } Ok(output) } From 10470e685921e2f77c5f743ae2296983a83b3f84 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sun, 14 Apr 2024 17:26:10 +0330 Subject: [PATCH 28/53] Add query restriction --- src/todo_app.rs | 8 ++++++++ src/tui_app.rs | 11 +++++++++++ test-aur.sh | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 599462d..4f74341 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -76,6 +76,14 @@ impl App { self.mut_current_list().append_list(todo_list) } + pub fn set_query_restriction(&mut self, query: String) { + if self.show_done() { + self.set_restriction(Rc::new(move |todo| todo.matches(query.as_str()))) + } else { + self.set_restriction(Rc::new(move |todo| todo.matches(query.as_str()) && !todo.done())) + } + } + pub fn do_commands_on_selected(&mut self) { for query in self.args.search_and_select.iter() { if self.args.delete_selected { diff --git a/src/tui_app.rs b/src/tui_app.rs index 198bd04..949497b 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -158,6 +158,11 @@ impl<'a>TuiApp<'a>{ self.set_text_mode(Self::on_search, "Search todo", "Enter search query") } + #[inline] + pub fn restrict_search_prompt(&mut self) { + self.set_text_mode(Self::on_restrict_search, "Restrict search todo", "Enter search query") + } + #[inline] pub fn tree_search_prompt(&mut self) { self.set_text_mode(Self::on_tree_search, "Search the whole tree for todo", "Enter search query") @@ -169,6 +174,11 @@ impl<'a>TuiApp<'a>{ self.todo_app.search_init(); } + #[inline] + fn on_restrict_search(&mut self, str:String) { + self.todo_app.set_query_restriction(str) + } + #[inline] fn on_tree_search(&mut self, str:String) { self.todo_app.tree_search(Some(str)); @@ -444,6 +454,7 @@ impl<'a>TuiApp<'a>{ Char('a') => self.prepend_prompt(), Char('/') => self.search_prompt(), Char('?') => self.tree_search_prompt(), + Char('\\') => self.restrict_search_prompt(), Char('A') => self.append_prompt(), Char('E') | Char('e') => self.edit_prompt(key.code == Char('E')), Char('q') => self.quit_save_prompt(), diff --git a/test-aur.sh b/test-aur.sh index 8cc1a14..4c2e8a8 100755 --- a/test-aur.sh +++ b/test-aur.sh @@ -4,7 +4,7 @@ RED='\033[0;31m' NC='\033[0m' # No Color echo_exit() { - echo -e $RED"Error$NC: $*" + echo -e "${RED}Error$NC: $*" exit 1 } @@ -16,7 +16,7 @@ build_folder() { WD=$PWD cd $1 || echo_exit cd $1 failed makechrootpkg -c -r "$CHROOT" || echo_exit makechrootpkg $CHROOT/root failed - cd $WD + cd "$WD" } build_folder ./aur/c3 From 66b4ce830bb12aa2edf04a68e8bd7bf71a1e2198 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sun, 14 Apr 2024 23:45:50 +0330 Subject: [PATCH 29/53] Fix minor code problems --- src/todo_app.rs | 12 +++++---- src/todo_app/todo.rs | 58 ++++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 4f74341..f126d7c 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -597,7 +597,7 @@ impl App { #[inline] pub fn add_dependency(&mut self) { if let Some(todo) = self.mut_todo() { - let _ = todo.add_todo_dependency(); + todo.add_todo_dependency(); } } @@ -628,8 +628,10 @@ impl App { #[inline] pub fn yank_todo(&mut self) { - let todo_string:String = self.todo().unwrap().into(); - self.clipboard.set_text(todo_string); + if let Some(todo) = self.todo() { + let todo_string:String = todo.into(); + self.clipboard.set_text(todo_string); + } } #[inline] @@ -653,9 +655,9 @@ impl App { #[inline] pub fn add_dependency_traverse_down(&mut self) { if self.is_tree() { - if let Some(todo) = self.todo() { + if let Some(todo) = self.mut_todo() { if todo.dependency.is_none() { - let _ = self.mut_todo().unwrap().add_todo_dependency(); + todo.add_todo_dependency(); } } self.traverse_down() diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index c2cb6ae..8395ab0 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -41,7 +41,6 @@ impl Into for &Todo { pub enum TodoError { ReadFailed, NoteEmpty, - AlreadyExists, DependencyCreationFailed, } @@ -59,6 +58,7 @@ enum State { Priority, Dependency, Message, + End, } impl TryFrom<&str> for Todo { @@ -86,7 +86,7 @@ impl TryFrom<&str> for Todo { if c == '-' { done = true; } else if c.is_digit(10) { - priority = c.to_digit(10).unwrap() as u8; + priority = c.to_digit(10).unwrap() as PriorityType; } else if c == ' ' { state = State::Message; } else if c == '>' { @@ -105,30 +105,33 @@ impl TryFrom<&str> for Todo { } State::Message => { if i == schedule_start_index.unwrap()-1 { - break; + state = State::End } else { message.push(c); } } - } - } + State::End => { + let schedule = Schedule::from(schedule_string); + let dependency = Dependency::from(dependency_string.as_str()); - let schedule = Schedule::from(schedule_string); - - if schedule.should_undone() { - done = false; - } - if schedule.should_done() { - done = true; + if schedule.should_undone() { + done = false; + } + if schedule.should_done() { + done = true; + } + return Ok(Todo { + dependency, + removed_dependency: None, + schedule, + message, + priority, + done, + }) + } + } } - Ok(Todo { - dependency: Dependency::from(dependency_string.as_str()), - removed_dependency: None, - schedule, - message, - priority: priority as u8, - done, - }) + Err(TodoError::ReadFailed) } } @@ -186,12 +189,9 @@ impl Todo { } #[inline] - pub fn add_todo_dependency(&mut self) -> Result<(), TodoError>{ + pub fn add_todo_dependency(&mut self) { if self.dependency.is_none() { self.dependency = Dependency::new_todo_list(self.hash()); - Ok(()) - } else { - Err(TodoError::AlreadyExists) } } @@ -429,7 +429,7 @@ mod tests { fn test_dependency_name() { let mut todo = Todo::default("Test".to_string(), 1); let expected = "900a80c94f076b4ee7006a9747667ccf6878a72b.todo"; - todo.add_todo_dependency().expect("Error setting dependency"); + todo.add_todo_dependency(); let result = &todo.dependency.get_name(); assert_eq!(result, expected); @@ -438,7 +438,7 @@ mod tests { #[test] fn test_dependency_type() { let mut todo = Todo::default("Test".to_string(), 1); - todo.add_todo_dependency().expect("Error setting dependency"); + todo.add_todo_dependency(); assert!(todo.dependency.is_list()); } @@ -447,7 +447,7 @@ mod tests { fn test_add_todo() { let mut todo = Todo::default("Test".to_string(), 1); let expected = "900a80c94f076b4ee7006a9747667ccf6878a72b.todo"; - todo.add_todo_dependency().expect("Error setting dependency"); + todo.add_todo_dependency(); let result = &todo.dependency.get_name(); assert_eq!(result, expected); @@ -482,7 +482,7 @@ mod tests { fn test_add_dependency() { let mut todo = Todo::default("Test".to_string(), 1); - let _ = todo.add_todo_dependency(); + todo.add_todo_dependency(); assert!(todo.dependency.is_list()); } @@ -490,7 +490,7 @@ mod tests { #[test] fn test_remove_dependency() { let mut todo = Todo::default("Test".to_string(), 1); - let _ = todo.add_todo_dependency(); + todo.add_todo_dependency(); todo.remove_dependency(); From 08736ae4bd77cf56574e92755d1321724942901d Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 11:01:22 +0330 Subject: [PATCH 30/53] Fix index problem after set_restriction --- src/todo_app.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index f126d7c..3fe15e7 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -508,12 +508,13 @@ impl App { #[inline(always)] pub fn unset_restriction(&mut self) { - self.restriction = None + self.restriction = None; } #[inline(always)] pub fn set_restriction(&mut self, restriction: RestrictionFunction) { - self.restriction = Some(restriction) + self.restriction = Some(restriction); + self.fix_index(); } #[inline] From a07035e4cdc2c6a6246601e3e142e37cd68de3f5 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 11:06:26 +0330 Subject: [PATCH 31/53] Remove unnecessary comments --- src/todo_app.rs | 8 -------- src/todo_app/todo/schedule.rs | 7 ------- src/todo_app/todo_list.rs | 7 +------ 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 3fe15e7..57bbf1b 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -421,14 +421,6 @@ impl App { for index in self.tree_path.clone() { list = &mut list.todos[index].dependency.todo_list }; - // for index in self.tree_path.iter() { - // let index = Self::fix_index(self.restriction.clone() ,list, *index); - // if let Some(index) = index { - // list = &mut list.index_mut(index, self.restriction.clone()).dependency.todo_list - // } else { - // break; - // } - // }; list } diff --git a/src/todo_app/todo/schedule.rs b/src/todo_app/todo/schedule.rs index ea3c5a9..af6a604 100644 --- a/src/todo_app/todo/schedule.rs +++ b/src/todo_app/todo/schedule.rs @@ -213,13 +213,6 @@ impl Schedule { self.date = Some(date); } - - // pub fn match_message(input: &mut String) -> Self { - // let ScheduleRest { schedule, rest } = ScheduleRest::from(input); - // *input = rest; - // schedule - // } - #[inline(always)] pub fn is_reminder(&self) -> bool { self._type == Type::Reminder diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index a020b7b..ccbccb1 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -241,10 +241,8 @@ impl TodoList { #[inline(always)] fn move_index(&mut self, from: usize, to: usize, shift:usize) -> usize{ - let mut i = from; - if from < to - { + if from < to { for j in from..to { self.todos.swap(j, j+1); i = j+1; @@ -254,7 +252,6 @@ impl TodoList { self.todos.swap(j, j+1); i = j; } - } i } @@ -266,8 +263,6 @@ impl TodoList { #[inline(always)] pub fn sort (&mut self) { - // , ascending:Option - // let ascending = ascending.unwrap_or(false); self.todos.sort_by(|a, b| a.comparison_priority().cmp(&b.comparison_priority())); } } From 9f383501011d175ba2b5d836962c835e31e0a4a1 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 12:32:52 +0330 Subject: [PATCH 32/53] Fix bug in todo string parsing Bug caused by bad code in commit 66b4ce8 (Fix minor code problems, 2024-04-14) in todo::try_from &str. --- src/todo_app/todo.rs | 45 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 8395ab0..5dc64fd 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -52,13 +52,12 @@ impl TryFrom for Todo { } } -#[derive(Default)] +#[derive(Default, PartialEq)] enum State { #[default] Priority, Dependency, Message, - End, } impl TryFrom<&str> for Todo { @@ -105,33 +104,35 @@ impl TryFrom<&str> for Todo { } State::Message => { if i == schedule_start_index.unwrap()-1 { - state = State::End + break; } else { message.push(c); } } - State::End => { - let schedule = Schedule::from(schedule_string); - let dependency = Dependency::from(dependency_string.as_str()); + } + } + if state == State::Message && !message.is_empty() { - if schedule.should_undone() { - done = false; - } - if schedule.should_done() { - done = true; - } - return Ok(Todo { - dependency, - removed_dependency: None, - schedule, - message, - priority, - done, - }) - } + let schedule = Schedule::from(schedule_string); + let dependency = Dependency::from(dependency_string.as_str()); + + if schedule.should_undone() { + done = false; } + if schedule.should_done() { + done = true; + } + Ok(Todo { + dependency, + removed_dependency: None, + schedule, + message, + priority, + done, + }) + } else { + Err(TodoError::ReadFailed) } - Err(TodoError::ReadFailed) } } From 05e2acb9a178258f54feaa4d1cf5e54197f2a077 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 12:53:15 +0330 Subject: [PATCH 33/53] Add responsive priority and query restriction --- src/tui_app.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/tui_app.rs b/src/tui_app.rs index 949497b..ea32e42 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -100,6 +100,7 @@ pub struct TuiApp<'a>{ show_right:bool, text_mode: bool, on_submit: Option()>, + on_input: Option()>, module_enabled: bool, module: &'a mut dyn Module<'a>, textarea: TextArea<'a>, @@ -116,6 +117,7 @@ impl<'a>TuiApp<'a>{ textarea, module, on_submit: None, + on_input: None, show_right: true, text_mode: false, module_enabled, @@ -148,6 +150,17 @@ impl<'a>TuiApp<'a>{ #[inline] pub fn set_text_mode(&mut self, on_submit:fn(&mut Self, String)->(),title: &'a str ,placeholder: &str) { self.on_submit = Some(on_submit); + self.turn_on_text_mode(title, placeholder); + } + + #[inline] + pub fn set_responsive_text_mode(&mut self, on_input:fn(&mut Self, String)->(),title: &'a str ,placeholder: &str) { + self.on_input = Some(on_input); + self.turn_on_text_mode(title, placeholder); + } + + #[inline(always)] + fn turn_on_text_mode(&mut self, title: &'a str ,placeholder: &str) { self.textarea.set_placeholder_text(placeholder); self.textarea.set_block(default_block(title)); self.text_mode = true; @@ -160,7 +173,10 @@ impl<'a>TuiApp<'a>{ #[inline] pub fn restrict_search_prompt(&mut self) { - self.set_text_mode(Self::on_restrict_search, "Restrict search todo", "Enter search query") + const TITLE:&str = "Restrict search todo"; + const PLACEHOLDER:&str = "Enter search query"; + self.set_text_mode(Self::on_restrict_search, TITLE, PLACEHOLDER); + self.set_responsive_text_mode(Self::on_restrict_search, TITLE, PLACEHOLDER); } #[inline] @@ -250,7 +266,10 @@ impl<'a>TuiApp<'a>{ #[inline] pub fn priority_prompt(&mut self) { - self.set_text_mode(Self::on_priority_prompt, "Limit priority", "Enter priority to show"); + const TITLE:&str = "Limit priority"; + const PLACEHOLDER:&str = "Enter priority to show"; + self.set_text_mode(Self::on_priority_prompt, TITLE, PLACEHOLDER); + self.set_responsive_text_mode(Self::on_priority_prompt, TITLE, PLACEHOLDER); } #[inline] @@ -365,6 +384,10 @@ impl<'a>TuiApp<'a>{ }, input => { self.textarea.input(input) ; + if let Some(on_input) = self.on_input { + let message = self.textarea.lines()[0].clone(); + on_input(self, message); + } Ok(None) } } From d25649792ac0a14d011139c5567ea5637c4ca78a Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 12:53:42 +0330 Subject: [PATCH 34/53] Remove temp file from nnn_append_todo method --- src/tui_app.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/tui_app.rs b/src/tui_app.rs index ea32e42..74b4759 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -1,6 +1,6 @@ // vim:fileencoding=utf-8:foldmethod=marker // std {{{ -use std::{fs::{read_to_string, remove_file}, io::{self, stdout}, path::PathBuf, rc::Rc}; +use std::{io::{self, stdout, BufRead, BufReader}, path::PathBuf, process::Stdio, rc::Rc}; use std::process::Command; // }}} // lib {{{ @@ -20,7 +20,7 @@ use modules::{ potato::Potato, }; use super::todo_app::{App, Todo}; -use crate::{date, fileio::temp_path, todo_app::PriorityType}; +use crate::{date, todo_app::PriorityType}; // }}} pub fn default_block<'a, T>(title: T) -> Block<'a> @@ -223,17 +223,22 @@ impl<'a>TuiApp<'a>{ #[inline] pub fn nnn_append_todo(&mut self) { - let path = temp_path("nnn-file-picker"); - if Command::new("nnn").args(["-p", path.to_str().unwrap_or("")]).status().is_err(){ - return + let mut output = Command::new("nnn") + .args(["-p", "-"]) + .stdin(Stdio::inherit()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .expect("Failed to start nnn."); + + let exit_status = output.wait().expect("Failed to wait on nnn."); + + if exit_status.success() { + let reader = BufReader::new(output.stdout.unwrap()); + let first_line = reader.lines().nth(0); + let path = first_line.unwrap().unwrap(); + self.todo_app.append_list_from_path(PathBuf::from(path)); } - let mut output_str = match read_to_string(&path) { - Ok(value) => value, - Err(_) => return, - }; - output_str.pop(); - self.todo_app.append_list_from_path(PathBuf::from(output_str)); - let _ = remove_file(path); } #[inline] From 741501f27a132845345b5f40849090212b7fda2e Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 13:10:33 +0330 Subject: [PATCH 35/53] Fix minor problems in add-traverse-down method --- src/todo_app.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 57bbf1b..24c75f3 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -648,9 +648,9 @@ impl App { #[inline] pub fn add_dependency_traverse_down(&mut self) { if self.is_tree() { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo() { if todo.dependency.is_none() { - todo.add_todo_dependency(); + self.mut_todo().unwrap().add_todo_dependency(); } } self.traverse_down() From 4afa08e2d5981d42d6eb7884ffec7390509e5a56 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 13:16:37 +0330 Subject: [PATCH 36/53] Add some comments --- src/todo_app.rs | 2 ++ src/todo_app/todo/dependency.rs | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 24c75f3..c2ee760 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -648,6 +648,8 @@ impl App { #[inline] pub fn add_dependency_traverse_down(&mut self) { if self.is_tree() { + // The reason we are using a self.todo() here, is that if we don't want to + // change anything, we won't borrow mutable and set the self.changed=true if let Some(todo) = self.todo() { if todo.dependency.is_none() { self.mut_todo().unwrap().add_todo_dependency(); diff --git a/src/todo_app/todo/dependency.rs b/src/todo_app/todo/dependency.rs index 58d52d5..c0825e5 100644 --- a/src/todo_app/todo/dependency.rs +++ b/src/todo_app/todo/dependency.rs @@ -88,13 +88,15 @@ impl Dependency { self.note = std::fs::read_to_string(file_path)?; } DependencyMode::Note | DependencyMode::TodoList + // Sometimes calcurse likes to remove the extra .todo from the file name + // That's why we have the first part of the if statement. c3 itself usually writes + // the list files to a .todo format in notes directory if file_path.is_file() || path.join(&name_todo).is_file() => { - - if self.mode == DependencyMode::Note { - self.name = name_todo; - self.mode = DependencyMode::TodoList; - } - self.todo_list = TodoList::read(&path.join(&self.name), true, false); + if self.mode == DependencyMode::Note { + self.name = name_todo; + self.mode = DependencyMode::TodoList; + } + self.todo_list = TodoList::read(&path.join(&self.name), true, false); } _ => {} }; From b03dbe7a5399d8a396a73ef04f581124210f1a0f Mon Sep 17 00:00:00 2001 From: Nima Askarian <88832088+nimaaskarian@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:02:06 +0330 Subject: [PATCH 37/53] Update README.md Add screenshot of showcase --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a00415e..b98ff49 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ # c3 ![GitHub top language](https://img.shields.io/github/languages/top/nimaaskarian/c3?color=orange) ![AUR version](https://img.shields.io/aur/version/c3?logo=archlinux) +![2024-04-15-132950-snap](https://github.com/nimaaskarian/c3/assets/88832088/f5b38ef0-a37c-4949-9209-8abae5df4775) + A crossplatform to-do list app that uses and extends [calcurse](https://www.calcurse.org/)'s format, to be a tree like to-do list with both sub-dependencies and notes. From 93f5a11146efbde81f4a5b6ba8a9e65ddb022011 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 18:01:42 +0330 Subject: [PATCH 38/53] Add tests to todo_list --- src/todo_app/todo_list.rs | 103 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index ccbccb1..4c887f4 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -266,3 +266,106 @@ impl TodoList { self.todos.sort_by(|a, b| a.comparison_priority().cmp(&b.comparison_priority())); } } + +#[cfg(test)] +mod tests { + use std::fs::{self, remove_dir_all, remove_file}; + + use super::*; + + fn get_todo_list() -> TodoList { + let path = PathBuf::from("tests/TODO_LIST"); + TodoList::read(&path, true, true) + } + + #[test] + fn test_todolist_read_undone() { + let todo_list = get_todo_list(); + let expected_undone = vec![Todo::written("this todo has prio 1".to_string(), 1, false) + ,Todo::written("this one has prio 2".to_string(), 2, false)]; + + assert_eq!(expected_undone, todo_list.todos.iter().filter(|todo| !todo.done()).cloned().collect::>()); + } + + #[test] + fn test_todolist_read_done() { + let todo_list = get_todo_list(); + let expected_done = vec![Todo::written("this one is 2 and done".to_string(), 2, true),Todo::written("this one is 0 and done".to_string(), 0, true)]; + assert_eq!(expected_done, todo_list.todos.iter().filter(|todo| todo.done()).cloned().collect::>()); + } + + #[test] + fn test_len() { + let todo_list = get_todo_list(); + assert_eq!(todo_list.len(None), 4); + } + + #[test] + fn test_write() { + let mut todo_list = get_todo_list(); + let path = PathBuf::from("todo-list-test-write/tmplist"); + let _ = todo_list.write(&path, true); + + + let contents = fs::read_to_string(&path).expect("Reading file failed :("); + let expected = "[1] this todo has prio 1 +[2] this one has prio 2 +[-2] this one is 2 and done +[-0] this one is 0 and done +"; + + remove_dir_all(&path.parent().unwrap()).expect("Remove test failed"); + let _ = remove_file(path); + assert_eq!(contents, expected) + } + + #[test] + fn test_push() { + let mut todo_list = get_todo_list(); + let path = PathBuf::from("todo-list-test-push/tmplist"); + todo_list.push(Todo::default("Show me your warface".to_string(), 0)); + todo_list.reorder_last(); + let _ = todo_list.write(&path, true); + + let contents = fs::read_to_string(&path).expect("Reading file failed :("); + let expected = "[1] this todo has prio 1 +[2] this one has prio 2 +[0] Show me your warface +[-2] this one is 2 and done +[-0] this one is 0 and done +"; + + remove_dir_all(&path.parent().unwrap()).expect("Remove test failed"); + let _ = remove_file(path); + assert_eq!(contents, expected); + } + + #[test] + fn test_initially_sorted() { + let todo_list = get_todo_list(); + let mut sorted_list = todo_list.clone(); + sorted_list.sort(); + + assert_eq!(todo_list, sorted_list) + } + + #[test] + fn test_write_dependencies() -> io::Result<()>{ + let mut todo_list = get_todo_list(); + let _ = todo_list.todos[0].add_todo_dependency(); + let path = PathBuf::from("test-write-dependency/tmplist"); + todo_list.todos[0].dependency.push(Todo::try_from("[0] Some dependency").unwrap()); + let dependency_path = todo_list.write(&path, true)?; + todo_list.write_dependencies(&dependency_path)?; + + let todo_dependency_path = PathBuf::from(format!("test-write-dependency/notes/{}.todo", todo_list.todos[0].hash())); + let contents = fs::read_to_string(&todo_dependency_path).expect("Reading file failed :("); + let expected = "[0] Some dependency\n"; + assert_eq!(contents, expected); + + todo_list.todos[0].remove_dependency(); + todo_list.write(&path, true)?; + remove_dir_all(&path.parent().unwrap())?; + Ok(()) + } +} From 57984bbf8f6b67742550391556aebd263a420d86 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 15 Apr 2024 19:06:46 +0330 Subject: [PATCH 39/53] Change some method names Change mut_current_list to current_list_mut Change mut_todo to todo_mut --- src/todo_app.rs | 52 ++++++++++++++++++++++++------------------------- src/tui_app.rs | 6 +++--- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index c2ee760..7910804 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -73,7 +73,7 @@ impl App { #[inline] pub fn append_list(&mut self, todo_list: TodoList) { - self.mut_current_list().append_list(todo_list) + self.current_list_mut().append_list(todo_list) } pub fn set_query_restriction(&mut self, query: String) { @@ -171,28 +171,28 @@ impl App { #[inline] pub fn increase_day_done(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.schedule.add_days_to_date(-1) } } #[inline] pub fn decrease_day_done(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.schedule.add_days_to_date(1) } } #[inline] pub fn prepend(&mut self, message:String) { - self.mut_current_list().prepend(Todo::default(message, 1)); + self.current_list_mut().prepend(Todo::default(message, 1)); self.go_top(); } #[inline] pub fn append(&mut self, message:String) { - self.mut_current_list().push(Todo::default(message, 0)); - self.index = self.mut_current_list().reorder_last(); + self.current_list_mut().push(Todo::default(message, 0)); + self.index = self.current_list_mut().reorder_last(); } pub fn index(&self) -> usize { @@ -269,11 +269,11 @@ impl App { #[inline] pub fn toggle_current_done(&mut self) { let index = self.index; - self.mut_todo().unwrap().toggle_done(); + self.todo_mut().unwrap().toggle_done(); if self.show_done() { - self.index = self.mut_current_list().reorder(index); + self.index = self.current_list_mut().reorder(index); } else { - self.mut_current_list().sort(); + self.current_list_mut().sort(); } } @@ -379,7 +379,7 @@ impl App { } #[inline] - pub fn mut_todo(&mut self) -> Option<&mut Todo> { + pub fn todo_mut(&mut self) -> Option<&mut Todo> { if self.is_todos_empty() { return None } @@ -388,10 +388,10 @@ impl App { let res_cloned = self.restriction.clone(); if size <= index { - return Some(self.mut_current_list().index_mut(size - 1, res_cloned)); + return Some(self.current_list_mut().index_mut(size - 1, res_cloned)); } - Some(self.mut_current_list().index_mut(index, res_cloned)) + Some(self.current_list_mut().index_mut(index, res_cloned)) } #[inline] @@ -399,7 +399,7 @@ impl App { let restriction = self.restriction.clone(); if !self.is_todos_empty() { let index = self.index; - let todo = self.mut_current_list().cut(index, restriction); + let todo = self.current_list_mut().cut(index, restriction); let todo_string:String = (&todo).into(); self.clipboard.set_text(todo_string); } @@ -411,7 +411,7 @@ impl App { } #[inline] - pub fn mut_current_list(&mut self) -> &mut TodoList { + pub fn current_list_mut(&mut self) -> &mut TodoList { self.changed = true; let is_root = self.is_root(); let mut list = &mut self.todo_list; @@ -476,14 +476,14 @@ impl App { #[inline] pub fn toggle_current_daily(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.toggle_daily() } } #[inline] pub fn toggle_current_weekly(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.toggle_weekly() } } @@ -523,7 +523,7 @@ impl App { #[inline] pub fn set_current_priority(&mut self, priority:PriorityType) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.set_priority(priority); self.reorder_current(); } @@ -557,7 +557,7 @@ impl App { #[inline] pub fn reorder_current(&mut self) { let index = self.index; - self.index = self.mut_current_list().reorder(index); + self.index = self.current_list_mut().reorder(index); } #[inline] @@ -565,7 +565,7 @@ impl App { let restriction = self.restriction.clone(); if !self.is_todos_empty() { let index = self.index; - let todo = self.mut_current_list().cut(index, restriction); + let todo = self.current_list_mut().cut(index, restriction); self.removed_todos.push(todo); } } @@ -582,14 +582,14 @@ impl App { #[inline] pub fn remove_current_dependent(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.remove_dependency(); } } #[inline] pub fn add_dependency(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.add_todo_dependency(); } } @@ -597,7 +597,7 @@ impl App { #[inline] pub fn edit_or_add_note(&mut self) { if self.is_tree() { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { let _ = todo.edit_note(); } } @@ -605,7 +605,7 @@ impl App { #[inline] pub fn decrease_current_priority(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.decrease_priority(); self.reorder_current(); } @@ -613,7 +613,7 @@ impl App { #[inline] pub fn increase_current_priority(&mut self) { - if let Some(todo) = self.mut_todo() { + if let Some(todo) = self.todo_mut() { todo.increase_priority(); self.reorder_current(); } @@ -635,7 +635,7 @@ impl App { let todo_parent = TodoList::dependency_parent(&self.args.todo_path, true); let _ = todo.dependency.read(&todo_parent); let bottom = self.bottom()+1; - let list = &mut self.mut_current_list(); + let list = &mut self.current_list_mut(); list.push(todo); if todos_count != 0 { self.index = list.reorder(bottom); @@ -652,7 +652,7 @@ impl App { // change anything, we won't borrow mutable and set the self.changed=true if let Some(todo) = self.todo() { if todo.dependency.is_none() { - self.mut_todo().unwrap().add_todo_dependency(); + self.todo_mut().unwrap().add_todo_dependency(); } } self.traverse_down() diff --git a/src/tui_app.rs b/src/tui_app.rs index 74b4759..4517b21 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -211,7 +211,7 @@ impl<'a>TuiApp<'a>{ if day.is_none() { return; } - if let Some(todo) = self.todo_app.mut_todo(){ + if let Some(todo) = self.todo_app.todo_mut(){ todo.enable_day(day.unwrap() as i64); } } @@ -244,7 +244,7 @@ impl<'a>TuiApp<'a>{ #[inline] fn on_reminder(&mut self,str:String) { if let Ok(date) = date::parse_user_input(&str) { - if let Some(todo) = self.todo_app.mut_todo() { + if let Some(todo) = self.todo_app.todo_mut() { todo.schedule.enable_reminder(date); } } @@ -336,7 +336,7 @@ impl<'a>TuiApp<'a>{ #[inline] fn on_edit_todo(&mut self,str:String) { if !str.is_empty() { - self.todo_app.mut_todo().unwrap().set_message(str); + self.todo_app.todo_mut().unwrap().set_message(str); } } From 64a1ccec212e9d4cb6461fbe05f867e4e301890c Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 18 Apr 2024 12:50:14 +0330 Subject: [PATCH 40/53] Add unit-tests Improve code in editor output handling --- src/todo_app.rs | 73 ++++++++++++++++++++++++++++++++++++++++- src/tui_app.rs | 87 +++++++++++++++++++++++++++++++------------------ 2 files changed, 128 insertions(+), 32 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 7910804..0a102cf 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -510,7 +510,7 @@ impl App { } #[inline] - pub fn set_priority_limit(&mut self, priority:PriorityType) { + pub fn set_priority_restriction(&mut self, priority:PriorityType) { self.args.display_args.show_done = true; self.set_restriction(Rc::new(move |todo| todo.priority() == priority)) } @@ -705,6 +705,77 @@ mod tests { Ok(app) } + #[test] + fn test_is_changed() -> io::Result<()>{ + let dir = dir("test-is-changed")?; + let mut app = write_test_todos(&dir)?; + assert_eq!(app.is_changed(), false); + app.todo_mut(); + assert_eq!(app.is_changed(), true); + app.write()?; + assert_eq!(app.is_changed(), false); + app.current_list_mut(); + assert_eq!(app.is_changed(), true); + app.read(); + assert_eq!(app.is_changed(), false); + remove_dir_all(dir)?; + Ok(()) + } + + #[test] + fn test_set_restrictions_done() -> io::Result<()>{ + let dir = dir("test-set-restrictions-done")?; + let mut app = write_test_todos(&dir)?; + app.toggle_current_done(); + assert_eq!(app.len(), 0); + app.toggle_show_done(); + assert_eq!(app.len(), 1); + app.toggle_show_done(); + assert_eq!(app.len(), 0); + remove_dir_all(dir)?; + Ok(()) + } + + #[test] + fn test_set_restrictions_query() -> io::Result<()>{ + let dir = dir("test-set-restrictions-query")?; + let mut app = write_test_todos(&dir)?; + assert_eq!(app.len(), 1); + app.set_query_restriction(String::from("hello")); + assert_eq!(app.len(), 1); + app.unset_restriction(); + assert_eq!(app.len(), 1); + remove_dir_all(dir)?; + Ok(()) + } + + #[test] + fn test_set_restrictions_priority() -> io::Result<()>{ + let dir = dir("test-set-restrictions-priority")?; + let mut app = write_test_todos(&dir)?; + app.set_current_priority(2); + assert_eq!(app.len(), 1); + app.set_priority_restriction(2); + assert_eq!(app.len(), 1); + app.set_priority_restriction(0); + assert_eq!(app.len(), 0); + remove_dir_all(dir)?; + Ok(()) + } + + #[test] + fn test_tree_search() -> io::Result<()>{ + let dir = dir("test-tree-search")?; + let mut app = write_test_todos(&dir)?; + remove_dir_all(dir)?; + let query = String::from("nod"); + app.tree_search(Some(query)); + let position = &app.tree_search_positions[1]; + assert_eq!(position.tree_path,vec![0,0]); + assert_eq!(position.matching_indices,vec![0]); + Ok(()) + } + #[test] fn test_write() -> io::Result<()> { let dir = dir("test-write")?; diff --git a/src/tui_app.rs b/src/tui_app.rs index 4517b21..f8ac800 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -96,11 +96,20 @@ pub enum Operation { Restart, } +#[derive(Debug, PartialEq)] +enum EditorOperation { + Cancel, + Submit, + Input, + Ignore, +} + +type HandlerParameter = String; pub struct TuiApp<'a>{ show_right:bool, text_mode: bool, - on_submit: Option()>, - on_input: Option()>, + on_submit: Option()>, + on_input: Option()>, module_enabled: bool, module: &'a mut dyn Module<'a>, textarea: TextArea<'a>, @@ -148,13 +157,14 @@ impl<'a>TuiApp<'a>{ } #[inline] - pub fn set_text_mode(&mut self, on_submit:fn(&mut Self, String)->(),title: &'a str ,placeholder: &str) { + pub fn set_text_mode(&mut self, on_submit:fn(&mut Self, HandlerParameter)->(),title: &'a str ,placeholder: &str) { + self.on_input = None; self.on_submit = Some(on_submit); self.turn_on_text_mode(title, placeholder); } #[inline] - pub fn set_responsive_text_mode(&mut self, on_input:fn(&mut Self, String)->(),title: &'a str ,placeholder: &str) { + pub fn set_responsive_text_mode(&mut self, on_input:fn(&mut Self, HandlerParameter)->(),title: &'a str ,placeholder: &str) { self.on_input = Some(on_input); self.turn_on_text_mode(title, placeholder); } @@ -166,6 +176,13 @@ impl<'a>TuiApp<'a>{ self.text_mode = true; } + #[inline(always)] + fn turn_off_text_mode(&mut self) { + self.textarea.delete_line_by_head(); + self.textarea.delete_line_by_end(); + self.text_mode = false; + } + #[inline] pub fn search_prompt(&mut self) { self.set_text_mode(Self::on_search, "Search todo", "Enter search query") @@ -175,8 +192,8 @@ impl<'a>TuiApp<'a>{ pub fn restrict_search_prompt(&mut self) { const TITLE:&str = "Restrict search todo"; const PLACEHOLDER:&str = "Enter search query"; - self.set_text_mode(Self::on_restrict_search, TITLE, PLACEHOLDER); self.set_responsive_text_mode(Self::on_restrict_search, TITLE, PLACEHOLDER); + self.on_submit = Some(Self::on_restrict_search); } #[inline] @@ -206,7 +223,7 @@ impl<'a>TuiApp<'a>{ } #[inline] - fn on_schedule(&mut self,str:String) { + fn on_schedule(&mut self,str:HandlerParameter) { let day = str.parse::().ok(); if day.is_none() { return; @@ -242,7 +259,7 @@ impl<'a>TuiApp<'a>{ } #[inline] - fn on_reminder(&mut self,str:String) { + fn on_reminder(&mut self,str:HandlerParameter) { if let Ok(date) = date::parse_user_input(&str) { if let Some(todo) = self.todo_app.todo_mut() { todo.schedule.enable_reminder(date); @@ -305,7 +322,7 @@ impl<'a>TuiApp<'a>{ let priority = str.parse::().ok(); if let Some(priority) = priority { if show_done { - self.todo_app.set_priority_limit(priority) + self.todo_app.set_priority_restriction(priority) } else { self.todo_app.set_priority_limit_no_done(priority) } @@ -313,7 +330,7 @@ impl<'a>TuiApp<'a>{ } #[inline] - fn on_save_prompt(&mut self, str:String) { + fn on_save_prompt(&mut self, str:HandlerParameter) { let lower = str.to_lowercase(); if lower.starts_with("y") { let _ = self.todo_app.write(); @@ -324,36 +341,48 @@ impl<'a>TuiApp<'a>{ } #[inline] - fn on_append_todo(&mut self, str:String) { + fn on_append_todo(&mut self, str:HandlerParameter) { self.todo_app.append(str); } #[inline] - fn on_prepend_todo(&mut self,str:String) { + fn on_prepend_todo(&mut self,str:HandlerParameter) { self.todo_app.prepend(str); } #[inline] - fn on_edit_todo(&mut self,str:String) { + fn on_edit_todo(&mut self,str:HandlerParameter) { if !str.is_empty() { self.todo_app.todo_mut().unwrap().set_message(str); } } + #[inline(always)] + fn current_textarea_message(&self) -> String { + self.textarea.lines()[0].clone() + } #[inline] fn enable_text_editor(&mut self) -> io::Result<()>{ - match self.editor()? { - None => {}, - Some(should_add) => { - if should_add { - let todo_message = self.textarea.lines()[0].clone(); - self.on_submit.unwrap()(self, todo_message); + let operation = self.editor()?; + match operation { + EditorOperation::Input => { + if let Some(on_input) = self.on_input { + let message = self.current_textarea_message(); + on_input(self, message); } - self.textarea.delete_line_by_head(); - self.textarea.delete_line_by_end(); - self.text_mode = false; } + EditorOperation::Submit => { + if let Some(on_submit) = self.on_submit { + let message = self.current_textarea_message(); + on_submit(self, message); + } + self.turn_off_text_mode(); + } + EditorOperation::Cancel => { + self.turn_off_text_mode(); + } + EditorOperation::Ignore => {} } Ok(()) } @@ -371,29 +400,25 @@ impl<'a>TuiApp<'a>{ } #[inline] - fn editor(&mut self) -> io::Result> { + fn editor(&mut self) -> io::Result { match crossterm::event::read()?.into() { Input { key: tui_textarea::Key::Esc, .. - } => Ok(Some(false)), + } => Ok(EditorOperation::Cancel), Input { key: tui_textarea::Key::Enter, .. - }=> Ok(Some(true)), + }=> Ok(EditorOperation::Submit), Input { key: tui_textarea::Key::Char('u'), ctrl: true, .. } => { self.textarea.delete_line_by_head(); - Ok(None) + Ok(EditorOperation::Ignore) }, input => { - self.textarea.input(input) ; - if let Some(on_input) = self.on_input { - let message = self.textarea.lines()[0].clone(); - on_input(self, message); - } - Ok(None) + self.textarea.input(input); + Ok(EditorOperation::Input) } } } From 10db314081a0b0ce44b7072b31bb5777c3b66843 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 18 Apr 2024 16:03:20 +0330 Subject: [PATCH 41/53] Add batch edit - Add batch edit - Add batch addition --- src/fileio.rs | 32 +++++++++++++++++++++++++------- src/todo_app.rs | 37 +++++++++++++++++++++++++++++++++++++ src/todo_app/todo.rs | 4 ++-- src/todo_app/todo/note.rs | 32 +++++++++----------------------- src/tui_app.rs | 4 ++++ 5 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/fileio.rs b/src/fileio.rs index 7f3b10b..741d742 100644 --- a/src/fileio.rs +++ b/src/fileio.rs @@ -1,9 +1,32 @@ -use std::fs::{File, remove_dir}; +use std::fs::{File, remove_dir, remove_file}; use std::time::{SystemTime, UNIX_EPOCH}; -use std::io::{prelude::*, self}; +use std::io::{prelude::*, self, Write}; use std::path::PathBuf; use home::home_dir; +use std::process::Command; +use std::env; + +#[inline(always)] +pub fn open_temp_editor(content:Option<&String>, path: PathBuf) -> io::Result{ + let mut file = File::create(&path)?; + if let Some(content) = content { + write!(file, "{content}")?; + } + let editor = if cfg!(windows) { + String::from("notepad") + } else { + match env::var("EDITOR") { + Ok(editor) => editor, + Err(_) => String::from("vi") + } + }; + Command::new(editor).arg(&path).status().expect("Couldn't open the editor."); + let content = file_content(&path)?; + remove_file(path).unwrap(); + Ok(content) +} + #[inline(always)] pub fn append_home_dir(vec:[&str; 4]) -> PathBuf { let mut path = home_dir().unwrap().to_path_buf(); @@ -25,11 +48,6 @@ pub fn get_todo_path() -> io::Result { Ok(file) } -#[inline(always)] -pub fn temp_note_path() -> PathBuf{ - temp_path("note") -} - #[inline(always)] pub fn temp_path(name: &str) -> PathBuf{ let time = match SystemTime::now().duration_since(UNIX_EPOCH) { diff --git a/src/todo_app.rs b/src/todo_app.rs index 0a102cf..cbc52ec 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -8,6 +8,7 @@ use search::Search; use std::rc::Rc; pub use todo::Todo; use crate::Args; +use crate::fileio::{open_temp_editor, temp_path}; pub use self::todo::PriorityType; pub use self::todo_list::TodoList; @@ -149,6 +150,42 @@ impl App { self.search_next(); } + pub fn batch_editor_messages(&mut self) { + let restriction = self.restriction().clone(); + let content = self.current_list().messages(restriction).join("\n"); + let new_messages = open_temp_editor(Some(&content),temp_path("messages")).unwrap(); + let mut new_messages = new_messages.lines(); + if let Some(restriction) = self.restriction.clone() { + for todo in self.todo_list.todos.iter_mut().filter(|todo| restriction(todo)) { + if Self::batch_edit_helper(todo, new_messages.next()) { + self.changed = true + } + } + } else { + for todo in self.todo_list.todos.iter_mut() { + if Self::batch_edit_helper(todo, new_messages.next()) { + self.changed = true + } + } + } + while let Some(message) = new_messages.next() { + self.append(String::from(message)) + } + } + + #[inline(always)] + fn batch_edit_helper(todo: &mut Todo, message: Option<&str>) -> bool { + if let Some(message) = message { + let message = String::from(message); + if todo.message == message { + return false + } + todo.set_message(message); + return true + } + false + } + pub fn print_searched(&mut self) { for position in self.tree_search_positions.iter() { self.tree_path = position.tree_path.clone(); diff --git a/src/todo_app/todo.rs b/src/todo_app/todo.rs index 5dc64fd..268b1ee 100644 --- a/src/todo_app/todo.rs +++ b/src/todo_app/todo.rs @@ -9,7 +9,7 @@ pub mod schedule; mod dependency; use dependency::Dependency; use schedule::Schedule; -use note::{sha1, open_temp_editor}; +use note::{sha1, open_note_temp_editor}; use super::TodoList; use crate::DisplayArgs; //}}} @@ -250,7 +250,7 @@ impl Todo { #[inline] pub fn edit_note(&mut self)-> io::Result<()>{ if !self.dependency.is_list() { - let note = open_temp_editor(self.dependency.note())?; + let note = open_note_temp_editor(self.dependency.note())?; if !note.is_empty() { self.set_note(note)?; } diff --git a/src/todo_app/todo/note.rs b/src/todo_app/todo/note.rs index 8f45d16..73cf636 100644 --- a/src/todo_app/todo/note.rs +++ b/src/todo_app/todo/note.rs @@ -1,29 +1,15 @@ -use std::fs::{File, remove_file}; -use std::io::{self, Write}; +use std::path::PathBuf; +use std::io; use sha1::{Sha1, Digest}; -use std::process::Command; -use std::env; -use crate::fileio::{file_content, temp_note_path}; +use crate::fileio::{temp_path, open_temp_editor}; + +pub fn open_note_temp_editor(content:Option<&String>) -> io::Result{ + open_temp_editor(content,temp_note_path()) +} #[inline(always)] -pub fn open_temp_editor(content:Option<&String>) -> io::Result{ - let path = temp_note_path(); - let mut file = File::create(&path)?; - if let Some(content) = content { - write!(file, "{content}")?; - } - let editor = if cfg!(windows) { - String::from("notepad") - } else { - match env::var("EDITOR") { - Ok(editor) => editor, - Err(_) => String::from("vi") - } - }; - Command::new(editor).arg(&path).status().expect("Couldn't open the editor."); - let content = file_content(&path)?; - remove_file(path).unwrap(); - Ok(content) +pub fn temp_note_path() -> PathBuf{ + temp_path("note") } pub fn sha1(str:&String) -> String{ diff --git a/src/tui_app.rs b/src/tui_app.rs index f8ac800..bda49e5 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -511,6 +511,10 @@ impl<'a>TuiApp<'a>{ Char('A') => self.append_prompt(), Char('E') | Char('e') => self.edit_prompt(key.code == Char('E')), Char('q') => self.quit_save_prompt(), + Char('B') => { + self.todo_app.batch_editor_messages(); + return Ok(Operation::Restart); + }, Char(c) if c.is_digit(10) => { let priority = c.to_digit(10).unwrap(); self.todo_app.set_current_priority(priority as PriorityType); From d5b438f0b9cead81d73880ee249faf4f5abc10fc Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 18 Apr 2024 23:31:05 +0330 Subject: [PATCH 42/53] Fix child list batch edit bug --- src/todo_app.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index cbc52ec..b19f35a 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -156,15 +156,13 @@ impl App { let new_messages = open_temp_editor(Some(&content),temp_path("messages")).unwrap(); let mut new_messages = new_messages.lines(); if let Some(restriction) = self.restriction.clone() { - for todo in self.todo_list.todos.iter_mut().filter(|todo| restriction(todo)) { + for todo in self.current_list_mut().todos.iter_mut().filter(|todo| restriction(todo)) { if Self::batch_edit_helper(todo, new_messages.next()) { - self.changed = true } } } else { - for todo in self.todo_list.todos.iter_mut() { + for todo in self.current_list_mut().todos.iter_mut() { if Self::batch_edit_helper(todo, new_messages.next()) { - self.changed = true } } } From 94b858ab322fb4f9397933b2cc0605dbe572f95b Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 18 Apr 2024 23:32:58 +0330 Subject: [PATCH 43/53] Change batch edit keybind --- src/tui_app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui_app.rs b/src/tui_app.rs index bda49e5..d9fb4da 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -511,7 +511,7 @@ impl<'a>TuiApp<'a>{ Char('A') => self.append_prompt(), Char('E') | Char('e') => self.edit_prompt(key.code == Char('E')), Char('q') => self.quit_save_prompt(), - Char('B') => { + Char('b') => { self.todo_app.batch_editor_messages(); return Ok(Operation::Restart); }, From 17df94cec36d7e61fd288e86c949c137db8f44fc Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Thu, 2 May 2024 11:04:46 +0330 Subject: [PATCH 44/53] Update batch edit code to a more clean one --- src/todo_app.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index b19f35a..8673b60 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -1,3 +1,4 @@ +use std::str::Lines; use std::{io, path::PathBuf}; mod clipboard; use clipboard::Clipboard; @@ -155,20 +156,30 @@ impl App { let content = self.current_list().messages(restriction).join("\n"); let new_messages = open_temp_editor(Some(&content),temp_path("messages")).unwrap(); let mut new_messages = new_messages.lines(); + self.batch_edit_current_list(new_messages) + } + + #[inline(always)] + fn batch_edit_current_list(&mut self, mut messages: Lines<'_>) { + let mut changed = false; if let Some(restriction) = self.restriction.clone() { for todo in self.current_list_mut().todos.iter_mut().filter(|todo| restriction(todo)) { - if Self::batch_edit_helper(todo, new_messages.next()) { + if Self::batch_edit_helper(todo, messages.next()) { + changed = true; } } } else { for todo in self.current_list_mut().todos.iter_mut() { - if Self::batch_edit_helper(todo, new_messages.next()) { + if Self::batch_edit_helper(todo, messages.next()) { + changed = true; } } } - while let Some(message) = new_messages.next() { + while let Some(message) = messages.next() { + changed = true; self.append(String::from(message)) } + self.changed = changed; } #[inline(always)] From b2727ec0a4e92ebaa37207e2b7c53175c37f47e8 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Fri, 3 May 2024 08:23:35 +0330 Subject: [PATCH 45/53] Fix clipboard-patch --- patches/clipboard-patch.diff | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patches/clipboard-patch.diff b/patches/clipboard-patch.diff index 3879e27..7f2f1c7 100644 --- a/patches/clipboard-patch.diff +++ b/patches/clipboard-patch.diff @@ -2,12 +2,11 @@ diff --git a/Cargo.toml b/Cargo.toml index 6269407..db31eef 100644 --- a/Cargo.toml +++ b/Cargo.toml -@@ -4,6 +4,7 @@ version = "0.3.7" +@@ -4,5 +4,6 @@ version = "0.3.7" edition = "2021" [dependencies] +arboard = "3.3.0" - scanf = "1.2.1" sha1 = "0.10.1" home = "0.5.9" diff --git a/src/todo_app/clipboard.rs b/src/todo_app/clipboard.rs From af62316b91b060d3bdf61ab1267b3192ccc3b395 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Mon, 6 May 2024 11:08:18 +0330 Subject: [PATCH 46/53] Fix edit problem --- src/cli_app.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index 477f36c..e377864 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -121,7 +121,7 @@ impl PrintTodoTree { self.is_last = true; let mut lines = note.lines(); - self.print_indention_with_depth(self.depth+1); + self.print_indention_with_depth(self.depth+1, true); if let Some(line) = lines.next() { println!("{}", line); } @@ -144,8 +144,8 @@ impl PrintTodoTree { } #[inline] - fn print_indention_with_depth(&self, depth: usize) { - if self.should_print_indention { + fn print_indention_with_depth(&self, depth: usize, should_print_indention:bool) { + if should_print_indention { return } for i in 1..depth { @@ -164,6 +164,6 @@ impl PrintTodoTree { #[inline] fn print_indention(&self) { - self.print_indention_with_depth(self.depth); + self.print_indention_with_depth(self.depth, self.should_print_indention); } } From ae2863ceb670a34d5fae322fee328a9ef47f9f5a Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Fri, 10 May 2024 13:44:59 +0330 Subject: [PATCH 47/53] Fix bug of true position in deeper lists --- src/todo_app.rs | 5 ++++- src/todo_app/todo_list.rs | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/todo_app.rs b/src/todo_app.rs index 8673b60..4edf253 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -385,7 +385,10 @@ impl App { if self.is_tree() { match self.todo() { Some(todo) if todo.dependency.is_list() => { - self.tree_path.push(self.todo_list.true_position_in_list(self.index, self.restriction.clone())); + let index = self.index; + let restriction = self.restriction.clone(); + let true_index = self.current_list().true_position_in_list(index, restriction); + self.tree_path.push(true_index); self.go_top(); self.search(None); } diff --git a/src/todo_app/todo_list.rs b/src/todo_app/todo_list.rs index 4c887f4..f758c45 100644 --- a/src/todo_app/todo_list.rs +++ b/src/todo_app/todo_list.rs @@ -187,10 +187,14 @@ impl TodoList { self.todos = self.todos.iter().filter(|x| &x != &filtered[index]).cloned().collect(); } - pub fn true_position_in_list(&mut self, index:usize, restriction: Restriction) -> usize { + pub fn true_position_in_list(&self, index:usize, restriction: Restriction) -> usize { let mut binding = self.todos(restriction); let filtered:Vec<_> = binding.iter_mut().collect(); - self.todos.iter().position(|x| &x == filtered[index]).unwrap() + match self.todos.iter().position(|x| &x == filtered[index]) { + Some(position) => position, + None => 0, + } + } pub fn cut(&mut self, index:usize, restriction: Restriction) -> Todo{ From b33bea6b1717ca6a83abb320e0cffc618e9e9955 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sat, 11 May 2024 13:32:33 +0330 Subject: [PATCH 48/53] Fix bugs in print todo --- src/cli_app.rs | 60 ++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/src/cli_app.rs b/src/cli_app.rs index e377864..0128db4 100644 --- a/src/cli_app.rs +++ b/src/cli_app.rs @@ -62,7 +62,7 @@ impl <'a>CliApp <'a>{ // TODO: Use traverse_tree instead of this struct for printing todo tree. #[derive(Clone)] struct PrintTodoTree { - was_last: Vec, + last_stack: Vec, should_print_indention: bool, is_last: bool, depth: usize, @@ -72,7 +72,7 @@ impl PrintTodoTree { #[inline] pub fn new(should_print_indention:bool) -> Self { PrintTodoTree { - was_last: vec![], + last_stack: vec![], is_last: false, depth: 0, should_print_indention, @@ -80,12 +80,12 @@ impl PrintTodoTree { } #[inline] - pub fn tree_child(&self) -> Self { - let mut new_print = self.clone(); - new_print.depth+=1; - new_print.was_last.push(self.is_last); + pub fn tree_child(&self, what_to_push: bool) -> Self { + let mut child = self.clone(); + child.depth+=1; + child.last_stack.push(!self.is_last && what_to_push); - new_print + child } #[inline] @@ -94,13 +94,15 @@ impl PrintTodoTree { for (index, todo) in todos.iter().enumerate() { self.is_last = index == todos.len() - 1; - if self.depth > 0 { + if !self.last_stack.is_empty() { self.print_indention(); } self.print_todo(todo, display_args); if let Some(todo_list) = todo.dependency.todo_list() { - let mut tree_child = self.tree_child(); + let popped = self.last_stack.last(); + let what_to_push = popped.is_some() && !self.last_stack.is_empty(); + let mut tree_child = self.tree_child(what_to_push); tree_child.print_list(todo_list, display_args, restriction.clone()); } @@ -117,42 +119,37 @@ impl PrintTodoTree { #[inline] fn print_note(&mut self, note: &String) { - self.was_last.push(self.is_last); - self.is_last = true; + let mut last_stack = self.last_stack.clone(); + last_stack.push(!self.is_last); - let mut lines = note.lines(); - self.print_indention_with_depth(self.depth+1, true); - if let Some(line) = lines.next() { - println!("{}", line); - } - for line in lines { - self.print_prenote(); + for line in note.lines() { + self.print_prenote(last_stack.clone()); println!("{}", line); } } #[inline] - fn print_prenote(&self) { - for i in 0..self.depth { - if self.was_last[i+1] { - print!(" ") - } else { + fn print_prenote(&self, last_stack: Vec) { + for x in last_stack { + if x { print!("│ ") + } else { + print!(" ") } } print!(" ") } #[inline] - fn print_indention_with_depth(&self, depth: usize, should_print_indention:bool) { - if should_print_indention { + fn print_indention(&self) { + if self.should_print_indention { return } - for i in 1..depth { - if self.was_last[i] { - print!(" ") - } else { + for x in self.last_stack.clone() { + if x { print!("│ ") + } else { + print!(" ") } } if self.is_last { @@ -161,9 +158,4 @@ impl PrintTodoTree { print!("├── "); } } - - #[inline] - fn print_indention(&self) { - self.print_indention_with_depth(self.depth, self.should_print_indention); - } } From 1ddbb77716370191957969de914daeda2207fba2 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sat, 11 May 2024 14:37:14 +0330 Subject: [PATCH 49/53] Change keybinds for nnn and increase day Add output list with nnn and keybind Add open with nnn and keybind --- README.md | 6 ++++-- src/todo_app.rs | 18 +++++++++++++++++- src/tui_app.rs | 41 +++++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b98ff49..2b730e2 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ The default mode of the app is TUI mode. Keybinds are vim-like. Here they are: | D | delete todo | | > | add todo note | | i | increase day done | -| o | decrease day done | +| I | increase day done | | t | add todo dependency | | l | go in depedency/add todo dependency | | h | go back to parent | @@ -71,7 +71,9 @@ The default mode of the app is TUI mode. Keybinds are vim-like. Here they are: | P | enable module | | / | search current list for todo | | ? | search the whole tree for todo | -| O | open nnn file picker for choosing a file to append to current list +| O | open nnn file picker to choose a file to append to current list | +| o | open nnn file picker to choose a file to output current list to | +| Ctrl+o | open nnn file picker to choose a file to open | | n | search next | | N | search previous | | w | write changes to file | diff --git a/src/todo_app.rs b/src/todo_app.rs index 4edf253..16e4e22 100644 --- a/src/todo_app.rs +++ b/src/todo_app.rs @@ -73,6 +73,22 @@ impl App { self.append_list(todo_list) } + #[inline] + pub fn open_path(&mut self, path: PathBuf) { + self.todo_list = TodoList::read(&path, !self.args.no_tree, true); + self.args.todo_path = path; + } + + #[inline] + pub fn output_list_to_path(&mut self, path: PathBuf) -> io::Result<()>{ + let changed = self.changed; + let list = self.current_list_mut(); + let dependency_path = list.write(&path, true)?; + list.write_dependencies(&dependency_path)?; + self.changed = changed; + Ok(()) + } + #[inline] pub fn append_list(&mut self, todo_list: TodoList) { self.current_list_mut().append_list(todo_list) @@ -155,7 +171,7 @@ impl App { let restriction = self.restriction().clone(); let content = self.current_list().messages(restriction).join("\n"); let new_messages = open_temp_editor(Some(&content),temp_path("messages")).unwrap(); - let mut new_messages = new_messages.lines(); + let new_messages = new_messages.lines(); self.batch_edit_current_list(new_messages) } diff --git a/src/tui_app.rs b/src/tui_app.rs index d9fb4da..851f3a0 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -5,9 +5,7 @@ use std::process::Command; // }}} // lib {{{ use crossterm::{ - ExecutableCommand, - terminal::{disable_raw_mode, LeaveAlternateScreen, enable_raw_mode, EnterAlternateScreen}, - event::{self, Event::Key, KeyCode::Char, KeyCode}, + event::{self, Event::Key, KeyCode::{self, Char}, KeyModifiers, ModifierKeyCode}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand }; use tui_textarea::{Input, TextArea, CursorMove}; use ratatui::{prelude::*, widgets::*}; @@ -238,8 +236,7 @@ impl<'a>TuiApp<'a>{ self.set_text_mode(Self::on_reminder, "Date reminder", ""); } - #[inline] - pub fn nnn_append_todo(&mut self) { + fn nnn_path() -> Option { let mut output = Command::new("nnn") .args(["-p", "-"]) .stdin(Stdio::inherit()) @@ -249,15 +246,35 @@ impl<'a>TuiApp<'a>{ .expect("Failed to start nnn."); let exit_status = output.wait().expect("Failed to wait on nnn."); - if exit_status.success() { let reader = BufReader::new(output.stdout.unwrap()); let first_line = reader.lines().nth(0); let path = first_line.unwrap().unwrap(); + return Some(PathBuf::from(path)) + } + None + } + + #[inline] + pub fn nnn_append_todo(&mut self) { + if let Some(path) = Self::nnn_path() { self.todo_app.append_list_from_path(PathBuf::from(path)); } } + pub fn nnn_open(&mut self) { + if let Some(path) = Self::nnn_path() { + self.todo_app.open_path(PathBuf::from(path)); + } + } + + #[inline] + pub fn nnn_output_todo(&mut self) { + if let Some(path) = Self::nnn_path() { + self.todo_app.output_list_to_path(PathBuf::from(path)); + } + } + #[inline] fn on_reminder(&mut self,str:HandlerParameter) { if let Ok(date) = date::parse_user_input(&str) { @@ -461,6 +478,10 @@ impl<'a>TuiApp<'a>{ if let Key(key) = event::read()? { if key.kind == event::KeyEventKind::Press { match key.code { + Char('o') if key.modifiers == KeyModifiers::CONTROL => { + self.nnn_open(); + return Ok(Operation::Restart) + } Char('x') => self.todo_app.cut_todo(), Char('d') => self.todo_app.toggle_current_daily(), Char('W') => self.todo_app.toggle_current_weekly(), @@ -471,11 +492,15 @@ impl<'a>TuiApp<'a>{ Char('y') => self.todo_app.yank_todo(), Char('p') => self.todo_app.paste_todo(), Char('i') => self.todo_app.increase_day_done(), - Char('o') => self.todo_app.decrease_day_done(), - Char('O') => { + Char('I') => self.todo_app.decrease_day_done(), + Char('o') => { self.nnn_append_todo(); return Ok(Operation::Restart) } + Char('O') => { + self.nnn_output_todo(); + return Ok(Operation::Restart) + } KeyCode::Down | Char('j') => self.todo_app.increment(), KeyCode::Up |Char('k') => self.todo_app.decrement(), KeyCode::Right | Char('l') => self.todo_app.add_dependency_traverse_down(), From 4619e65b0fea14bf1fb3d7531cad6f7dc9dc07b0 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sat, 11 May 2024 15:47:50 +0330 Subject: [PATCH 50/53] Fix panic on nnn picker quit Fix keybinds in README.md --- README.md | 4 ++-- src/tui_app.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2b730e2..70fde1e 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ The default mode of the app is TUI mode. Keybinds are vim-like. Here they are: | P | enable module | | / | search current list for todo | | ? | search the whole tree for todo | -| O | open nnn file picker to choose a file to append to current list | -| o | open nnn file picker to choose a file to output current list to | +| o | open nnn file picker to choose a file to append to current list | +| O | open nnn file picker to choose a file to output current list to | | Ctrl+o | open nnn file picker to choose a file to open | | n | search next | | N | search previous | diff --git a/src/tui_app.rs b/src/tui_app.rs index 851f3a0..279bae0 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -248,9 +248,10 @@ impl<'a>TuiApp<'a>{ let exit_status = output.wait().expect("Failed to wait on nnn."); if exit_status.success() { let reader = BufReader::new(output.stdout.unwrap()); - let first_line = reader.lines().nth(0); - let path = first_line.unwrap().unwrap(); - return Some(PathBuf::from(path)) + if let Some(line) = reader.lines().nth(0) { + let path = line.unwrap(); + return Some(PathBuf::from(path)) + } } None } From 2e8d0ed0d8ebcc25ef3278937010f04683f25f01 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sat, 11 May 2024 17:34:37 +0330 Subject: [PATCH 51/53] Remove benchmark.patch --- patches/benchmark.patch | 61 ----------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 patches/benchmark.patch diff --git a/patches/benchmark.patch b/patches/benchmark.patch deleted file mode 100644 index eb3e360..0000000 --- a/patches/benchmark.patch +++ /dev/null @@ -1,61 +0,0 @@ -diff --git a/src/main.rs b/src/main.rs -index f5dcb9f..29dd861 100644 ---- a/src/main.rs -+++ b/src/main.rs -@@ -16,7 +16,7 @@ use todo_app::App; - use fileio::get_todo_path; - //}}} - --#[derive(Parser, Debug)] -+#[derive(Parser, Debug, Clone)] - #[command(author, version, about, long_about = None)] - pub struct DisplayArgs{ - /// Show done todos too -@@ -33,7 +33,7 @@ pub struct DisplayArgs{ - } - - /// A tree-like todo application that makes you smile --#[derive(Parser, Debug)] -+#[derive(Parser, Debug, Clone)] - #[command(author, version, about, long_about = None)] - pub struct Args { - /// Performance mode, don't read dependencies -@@ -101,18 +101,26 @@ impl Args { - - fn main() -> io::Result<()> { - let args = Args::parse(); -- let is_cli = args.is_cli(); -- let mut app = App::new(args); -- -- if is_cli { -- cli_app::run(&mut app) -- } else { -- match tui_app::run(&mut app) { -- Ok(_)=>{Ok(())} -- err => { -- tui_app::shutdown()?; -- err -- } -+ for _ in 0..2 { -+ let mut app = App::new(args.clone()); -+ use std::time::Instant; -+ for _ in 0..1000 { -+ let now = Instant::now(); -+ app.set_current_priority(8); -+ app.reorder_current(); -+ let elapsed = now.elapsed(); -+ println!("{:.2?}", elapsed); - } -+ app.go_bottom(); -+ -+ for _ in 0..1000 { -+ let now = Instant::now(); -+ app.set_current_priority(1); -+ app.reorder_current(); -+ let elapsed = now.elapsed(); -+ println!("{:.2?}", elapsed); -+ } -+ - } -+ Ok(()) - } From 49b75674df632f8b5ae9bfeafb69c648852ba378 Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sat, 11 May 2024 17:36:57 +0330 Subject: [PATCH 52/53] Bumped version 1.0.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c40a47c..40ab8c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,7 +118,7 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "c3" -version = "0.9.0" +version = "1.0.0" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 2da7de8..38af34e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c3" -version = "0.9.0" +version = "1.0.0" edition = "2021" [dependencies] From 4c920b1476a20c94d09c2c94ff0a3caa38e1198d Mon Sep 17 00:00:00 2001 From: nimaaskarian Date: Sat, 11 May 2024 17:37:22 +0330 Subject: [PATCH 53/53] Fix warnings --- src/tui_app.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tui_app.rs b/src/tui_app.rs index 279bae0..829ed2f 100644 --- a/src/tui_app.rs +++ b/src/tui_app.rs @@ -5,7 +5,7 @@ use std::process::Command; // }}} // lib {{{ use crossterm::{ - event::{self, Event::Key, KeyCode::{self, Char}, KeyModifiers, ModifierKeyCode}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand + event::{self, Event::Key, KeyCode::{self, Char}, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand }; use tui_textarea::{Input, TextArea, CursorMove}; use ratatui::{prelude::*, widgets::*}; @@ -272,7 +272,7 @@ impl<'a>TuiApp<'a>{ #[inline] pub fn nnn_output_todo(&mut self) { if let Some(path) = Self::nnn_path() { - self.todo_app.output_list_to_path(PathBuf::from(path)); + let _ = self.todo_app.output_list_to_path(PathBuf::from(path)); } }