Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,12 @@ pub fn launch_action(action: &Action) -> anyhow::Result<()> {
return Ok(());
}
if let Some(text) = action.action.strip_prefix("todo:add:") {
crate::plugins::todo::append_todo(crate::plugins::todo::TODO_FILE, text)?;
crate::plugins::todo::append_todo(
crate::plugins::todo::TODO_FILE,
text,
0,
&[],
)?;
return Ok(());
}
if let Some(idx) = action.action.strip_prefix("todo:remove:") {
Expand Down
30 changes: 28 additions & 2 deletions src/plugins/todo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ pub const TODO_FILE: &str = "todo.json";
pub struct TodoEntry {
pub text: String,
pub done: bool,
#[serde(default)]
pub priority: u8,
#[serde(default)]
pub tags: Vec<String>,
}

/// Load todo entries from `path`.
Expand All @@ -31,12 +35,14 @@ pub fn save_todos(path: &str, todos: &[TodoEntry]) -> anyhow::Result<()> {
Ok(())
}

/// Append a new todo entry with `text`.
pub fn append_todo(path: &str, text: &str) -> anyhow::Result<()> {
/// Append a new todo entry with `text`, `priority` and `tags`.
pub fn append_todo(path: &str, text: &str, priority: u8, tags: &[String]) -> anyhow::Result<()> {
let mut list = load_todos(path).unwrap_or_default();
list.push(TodoEntry {
text: text.to_string(),
done: false,
priority,
tags: tags.to_vec(),
});
save_todos(path, &list)
}
Expand All @@ -61,6 +67,26 @@ pub fn mark_done(path: &str, index: usize) -> anyhow::Result<()> {
Ok(())
}

/// Set the priority of the todo at `index` in `path`.
pub fn set_priority(path: &str, index: usize, priority: u8) -> anyhow::Result<()> {
let mut list = load_todos(path).unwrap_or_default();
if let Some(entry) = list.get_mut(index) {
entry.priority = priority;
save_todos(path, &list)?;
}
Ok(())
}

/// Replace the tags of the todo at `index` in `path`.
pub fn set_tags(path: &str, index: usize, tags: &[String]) -> anyhow::Result<()> {
let mut list = load_todos(path).unwrap_or_default();
if let Some(entry) = list.get_mut(index) {
entry.tags = tags.to_vec();
save_todos(path, &list)?;
}
Ok(())
}

/// Remove all completed todos from `path`.
pub fn clear_done(path: &str) -> anyhow::Result<()> {
let mut list = load_todos(path).unwrap_or_default();
Expand Down
44 changes: 42 additions & 2 deletions src/todo_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ pub struct TodoDialog {
pub open: bool,
entries: Vec<TodoEntry>,
text: String,
priority: u8,
tags: String,
}

impl TodoDialog {
pub fn open(&mut self) {
self.entries = load_todos(TODO_FILE).unwrap_or_default();
self.open = true;
self.text.clear();
self.priority = 0;
self.tags.clear();
}

fn save(&mut self, app: &mut LauncherApp) {
Expand All @@ -39,10 +43,33 @@ impl TodoDialog {
ui.horizontal(|ui| {
ui.label("New");
ui.text_edit_singleline(&mut self.text);
ui.label("Priority");
if ui
.add(egui::DragValue::new(&mut self.priority).clamp_range(0..=255))
.changed()
{
// no action
}
ui.label("Tags");
ui.text_edit_singleline(&mut self.tags);
if ui.button("Add").clicked() {
if !self.text.trim().is_empty() {
self.entries.push(TodoEntry { text: self.text.clone(), done: false });
let tag_list: Vec<String> = self
.tags
.split(',')
.map(|t| t.trim())
.filter(|t| !t.is_empty())
.map(|t| t.to_string())
.collect();
self.entries.push(TodoEntry {
text: self.text.clone(),
done: false,
priority: self.priority,
tags: tag_list,
});
self.text.clear();
self.priority = 0;
self.tags.clear();
save_now = true;
}
}
Expand All @@ -65,7 +92,20 @@ impl TodoDialog {
save_now = true;
}
ui.label(entry.text.replace('\n', " "));
if ui.button("Remove").clicked() { remove = Some(idx); }
ui.add(egui::DragValue::new(&mut entry.priority).clamp_range(0..=255));
let mut tag_str = entry.tags.join(", ");
if ui.text_edit_singleline(&mut tag_str).changed() {
entry.tags = tag_str
.split(',')
.map(|t| t.trim())
.filter(|t| !t.is_empty())
.map(|t| t.to_string())
.collect();
save_now = true;
}
if ui.button("Remove").clicked() {
remove = Some(idx);
}
});
}
});
Expand Down
29 changes: 23 additions & 6 deletions tests/todo_plugin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use multi_launcher::plugin::Plugin;
use multi_launcher::plugins::todo::{append_todo, load_todos, remove_todo, mark_done, TodoPlugin, TODO_FILE};
use multi_launcher::plugins::todo::{
append_todo, load_todos, remove_todo, mark_done, set_priority, set_tags,
TodoPlugin, TODO_FILE,
};
use once_cell::sync::Lazy;
use std::sync::Mutex;
use tempfile::tempdir;
Expand Down Expand Up @@ -30,8 +33,8 @@ fn list_returns_saved_items() {
let dir = tempdir().unwrap();
std::env::set_current_dir(dir.path()).unwrap();

append_todo(TODO_FILE, "alpha").unwrap();
append_todo(TODO_FILE, "beta").unwrap();
append_todo(TODO_FILE, "alpha", 0, &[]).unwrap();
append_todo(TODO_FILE, "beta", 0, &[]).unwrap();

let plugin = TodoPlugin::default();
let results = plugin.search("todo list");
Expand All @@ -45,8 +48,8 @@ fn remove_action_deletes_entry() {
let dir = tempdir().unwrap();
std::env::set_current_dir(dir.path()).unwrap();

append_todo(TODO_FILE, "remove me").unwrap();
append_todo(TODO_FILE, "keep").unwrap();
append_todo(TODO_FILE, "remove me", 0, &[]).unwrap();
append_todo(TODO_FILE, "keep", 0, &[]).unwrap();

let plugin = TodoPlugin::default();
let results = plugin.search("todo rm remove");
Expand Down Expand Up @@ -81,7 +84,7 @@ fn mark_done_toggles_status() {
let dir = tempdir().unwrap();
std::env::set_current_dir(dir.path()).unwrap();

append_todo(TODO_FILE, "task").unwrap();
append_todo(TODO_FILE, "task", 0, &[]).unwrap();

mark_done(TODO_FILE, 0).unwrap();
let todos = load_todos(TODO_FILE).unwrap();
Expand All @@ -91,3 +94,17 @@ fn mark_done_toggles_status() {
let todos = load_todos(TODO_FILE).unwrap();
assert!(!todos[0].done);
}

#[test]
fn set_priority_and_tags_update_entry() {
let _lock = TEST_MUTEX.lock().unwrap();
let dir = tempdir().unwrap();
std::env::set_current_dir(dir.path()).unwrap();

append_todo(TODO_FILE, "task", 0, &[]).unwrap();
set_priority(TODO_FILE, 0, 5).unwrap();
set_tags(TODO_FILE, 0, &["a".into(), "b".into()]).unwrap();
let todos = load_todos(TODO_FILE).unwrap();
assert_eq!(todos[0].priority, 5);
assert_eq!(todos[0].tags, vec!["a", "b"]);
}