Skip to content

Commit

Permalink
Merge branch 'release/1.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
nimaaskarian committed Jul 30, 2024
2 parents c80f8bb + db0ce1a commit add5e08
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 94 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "c3"
version = "1.3.2"
version = "1.4.0"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ 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 (tree search) |
| Tab | tree search next |
| n | tree search next |
| 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 |
Expand Down
27 changes: 19 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() -> io::Result<()> {
let mut app = App::new(args.app_args);

if cli_app::run(&mut app, args.cli_args).is_err() {
let output = tui_app::run(&mut app);
let output = tui_app::run(&mut app, args.tui_args);
{
tui_app::shutdown()?;
output
Expand All @@ -35,6 +35,9 @@ pub struct Args {

#[command(flatten)]
cli_args: CliArgs,

#[command(flatten)]
tui_args: TuiArgs,
}

#[derive(ValueEnum, Clone, Debug)]
Expand Down Expand Up @@ -94,21 +97,29 @@ struct CliArgs {

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct AppArgs {
/// Performance mode, don't read dependencies
#[arg(short = 'n', long)]
no_tree: bool,
struct TuiArgs {
/// Alternative way of rendering, render minimum amount of todos
#[arg(long)]
minimal_render: bool,

/// String behind highlighted todo in TUI mode
#[arg(short='H', long, default_value_t=String::from(">>"))]
highlight_string: String,

#[command(flatten)]
display_args: DisplayArgs,

/// Enable TUI module at startup
#[arg(short = 'm', long)]
enable_module: bool,
}

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct AppArgs {
/// Performance mode, don't read dependencies
#[arg(short = 'n', long)]
no_tree: bool,

#[command(flatten)]
display_args: DisplayArgs,

/// Path to todo file (and notes sibling directory)
#[arg(default_value=get_todo_path().unwrap().into_os_string())]
Expand Down
113 changes: 58 additions & 55 deletions src/todo_app.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::VecDeque;
use std::fmt::Write;
use std::fs::create_dir_all;
use std::path::Path;
Expand Down Expand Up @@ -31,7 +32,6 @@ pub struct App {
pub(super) args: AppArgs,
removed_todos: Vec<Todo>,
tree_search_positions: Vec<SearchPosition>,
last_query: String,
x_index: usize,
y_index: usize,
restriction: RestrictionFunction,
Expand Down Expand Up @@ -81,15 +81,11 @@ impl App {
#[inline]
pub(crate) fn new(args: AppArgs) -> Self {
let notes_dir = fileio::append_notes_to_path_parent(&args.todo_path);
let mut todo_list = TodoList::read(&args.todo_path);
if !args.no_tree {
todo_list.read_dependencies(&notes_dir).expect("Failed to read dependencies");
}
let todo_list = Self::read_a_todo_list(&args.todo_path, &notes_dir, &args);
let mut app = App {
notes_dir,
x_index: 0,
y_index: 0,
last_query: String::new(),
tree_search_positions: vec![],
removed_todos: vec![],
todo_list,
Expand All @@ -111,17 +107,18 @@ impl App {
}

#[inline(always)]
fn read_a_todo_list(&self, path: &Path) -> TodoList {
fn read_a_todo_list(path: &Path, notes_dir: &Path, args: &AppArgs) -> TodoList {
let mut todo_list = TodoList::read(path);
if !self.args.no_tree {
todo_list.read_dependencies(&self.notes_dir);
if !args.no_tree {
todo_list.read_dependencies(&notes_dir);
}
todo_list
}

#[inline]
pub fn append_list_from_path(&mut self, path: &Path) {
let todo_list = self.read_a_todo_list(path);
let notes_dir = fileio::append_notes_to_path_parent(path);
let todo_list = Self::read_a_todo_list(path, &notes_dir, &self.args);
self.append_list(todo_list)
}

Expand All @@ -132,7 +129,8 @@ impl App {

#[inline]
pub fn open_path(&mut self, path: PathBuf) {
self.todo_list = self.read_a_todo_list(&path);
self.notes_dir = fileio::append_notes_to_path_parent(&path);
self.todo_list = Self::read_a_todo_list(&path, &self.notes_dir, &self.args);
self.tree_path = vec![];
self.args.todo_path = path;
}
Expand Down Expand Up @@ -164,55 +162,56 @@ impl App {
}))
}

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: &TodoList, prior_indices: &[usize]) {
let mut matching_indices: Vec<usize> = vec![];
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() {
self.tree_search_positions.push(SearchPosition {
tree_path: prior_indices.to_vec(),
matching_indices,
})
}
}

pub fn tree_search(&mut self, query: Option<String>) {
if let Some(query) = query {
self.last_query = query;
}
pub fn tree_search(&mut self, query: String) {
self.tree_search_positions = vec![];
self.y_index = 0;
self.x_index = 0;
if self.last_query.is_empty() {
if query.is_empty() {
return;
}
let before_position = SearchPosition {
tree_path: self.tree_path.clone(),
matching_indices: vec![self.index],
};
self.tree_search_positions.push(before_position);
self.traverse_parents_from_root(Self::add_to_tree_positions);
self.search_next();
let current_not_matches = self.todo().map_or(true, |todo| !todo.matches(&query));
self.search_tree(query);

if current_not_matches {
self.search_next();
}
}

pub fn search_tree(&mut self, query: String) {
let mut lists: VecDeque<(Vec<usize>, &TodoList)> = VecDeque::from([(vec![],&self.todo_list)]);
while let Some((indices, current_list)) = lists.pop_back() {
let mut matching_indices: Vec<usize> = vec![];
for (i,todo) in current_list.filter(&self.restriction).enumerate() {
let mut todo_indices = indices.clone();
todo_indices.push(i);
if todo.matches(&query) {
matching_indices.push(i)
}
if let Some(list) = todo.dependency.as_ref().and_then(|dep| dep.todo_list()) {
lists.push_back((todo_indices,list))
}
}
if !matching_indices.is_empty() {
self.tree_search_positions.push(SearchPosition {
tree_path: indices.to_vec(),
matching_indices,
})
}
}
}

pub fn batch_editor_messages(&mut self) {
let restriction = &self.restriction;

let todos = self.current_list().todos(restriction);
let mut content = if todos.is_empty() {
String::new()
let mut todos_iter = self.current_list().filter(restriction);
let first_item = todos_iter.next();
let mut content = if let Some(item) = first_item {
format!("# INDEX PRIORITY MESSAGE\n{: <7} {: <8} {}\n",0, item.priority(), item.message)
} else {
String::from("# INDEX PRIORITY MESSAGE\n")
String::new()
};
for (i, line) in todos.iter().enumerate() {
writeln!(content, "{i: <7} {: <8} {}", line.priority(), line.message);
for (i, line) in todos_iter.enumerate() {
writeln!(content, "{: <7} {: <8} {}",i+1, line.priority(), line.message);
}
let new_messages = fileio::open_temp_editor(Some(&content), fileio::temp_path("messages")).unwrap();
let new_messages = new_messages.lines();
Expand Down Expand Up @@ -371,17 +370,15 @@ impl App {
} else {
self.current_list_mut().sort();
}
if self.is_undone_empty() {
while self.traverse_up() && !self.is_undone_empty() {
self.toggle_current_done()
}
while self.is_undone_empty() && self.traverse_up() {
self.toggle_current_done()
}
}

#[inline]
pub fn read(&mut self) {
self.changed = false;
self.todo_list = self.read_a_todo_list(&self.args.todo_path);
self.todo_list = Self::read_a_todo_list(&self.args.todo_path, &self.notes_dir, &self.args);
let len = self.max_tree_length();
self.tree_path.truncate(len);
}
Expand Down Expand Up @@ -705,6 +702,12 @@ impl App {
self.display_list(self.current_list())
}

#[inline]
pub fn display_current_slice(&self, min: usize, max: usize) -> Vec<String> {
self.current_list().display_slice(&self.args.display_args, &self.restriction, min, max)
}


#[inline]
pub fn display_list(&self, todo_list: &TodoList) -> Vec<String> {
todo_list.display(&self.args.display_args, &self.restriction)
Expand Down Expand Up @@ -907,8 +910,8 @@ mod tests {
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];
app.tree_search(query);
let position = &app.tree_search_positions[0];
assert_eq!(position.tree_path, vec![2, 0]);
assert_eq!(position.matching_indices, vec![0]);
Ok(())
Expand Down
34 changes: 24 additions & 10 deletions src/todo_app/todo_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,15 @@ impl TodoList {
pub fn traverse_tree(
&self,
callback: fn(&mut App, &TodoList, &[usize]),
prior_indices: Option<Vec<usize>>,
prior_indices: Vec<usize>,
app: &mut App,
) {
let prior_indices = prior_indices.unwrap_or_default();
callback(app, self, prior_indices.as_slice());
callback(app, self, &prior_indices);
for (i, todo) in self.todos.iter().enumerate() {
if let Some(todo_list) = todo.dependency.as_ref().and_then(|dep| dep.todo_list()) {
let mut prior_indices = prior_indices.clone();
prior_indices.push(i);
todo_list.traverse_tree(callback, Some(prior_indices), app);
todo_list.traverse_tree(callback, prior_indices, app);
}
}
}
Expand Down Expand Up @@ -195,25 +194,40 @@ impl TodoList {
}

pub fn messages(&self, restriction: &RestrictionFunction) -> Vec<&str> {
self.todos(restriction)
.iter()
self.todos.iter()
.filter(|todo| restriction(todo))
.map(|todo| todo.message.as_str())
.collect()
}

pub fn filter<'a>(&'a self, restriction: &'a RestrictionFunction) -> std::iter::Filter<std::slice::Iter<Todo>, impl FnMut(&&'a Todo) -> bool> {
self.todos.iter().filter(|todo| restriction(todo))
}

pub fn display(&self, args: &DisplayArgs, restriction: &RestrictionFunction) -> Vec<String> {
self.todos(restriction)
.iter()
self.todos.iter()
.filter(|todo| restriction(todo))
.map(|todo| todo.display(args))
.collect()
}

pub fn display_slice(&self, args: &DisplayArgs, restriction: &RestrictionFunction, min: usize, max: usize) -> Vec<String> {
self.todos.iter()
.filter(|todo| restriction(todo))
.skip(min)
.take(max)
.map(|todo| todo.display(args))
.collect()
}

pub fn len(&self, restriction: &RestrictionFunction) -> usize {
self.todos(restriction).len()
self.todos.iter()
.filter(|todo| restriction(todo))
.count()
}

pub fn is_empty(&self, restriction: &RestrictionFunction) -> bool {
self.todos(restriction).is_empty()
self.len(restriction) == 0
}

pub fn true_position_in_list(&self, index: usize, restriction: &RestrictionFunction) -> usize {
Expand Down
Loading

0 comments on commit add5e08

Please sign in to comment.