use super::*;
pub fn process(vault: &Vault, state: &mut State, key: Key) -> UpdateStatus {
if key != Key::Ctrl('d') {
clear_deletion_flags(state);
}
match key {
Key::Ctrl('q') => UpdateStatus::Quit,
Key::Alt('i') => {
let results = vault.list_ideas().unwrap_or(vec![]);
let new_state = State::SearchIdeas(Query::new(results));
switch(state, new_state)
},
Key::Alt('j') => {
let results = vault.list_journals().unwrap_or(vec![]);
let new_state = State::SearchJournals(Query::new(results));
switch(state, new_state)
}
Key::Alt('n') => {
let results = vault.list_notes(true, &None).unwrap_or(vec![]);
let new_state = State::SearchNotes(Query::new(results));
switch(state, new_state)
},
Key::Alt('t') => {
let results = vault.list_tasks(true, &None).unwrap_or(vec![]);
let new_state = State::SearchTasks(Query::new(results));
switch(state, new_state)
}
Key::Alt('h') | Key::Char('?') => switch(state, State::HelpScreen),
Key::Alt('s') => rotate_task_status(vault, state),
Key::Up => prev_search_result(state),
Key::Down => next_search_result(state),
Key::Char('\n') => schedule_editor(vault, state),
Key::Char(ch) => update_query_and_search(vault, state, ch, append_char, None),
Key::Backspace => update_query_and_search(vault, state, ' ', remove_last_char, None),
Key::Esc => update_query_and_search(vault, state, ' ', clear_string, None),
Key::Ctrl('w') => update_query_and_search(vault, state, ' ', remove_last_word, None),
Key::Ctrl('d') => delete(vault, state),
_ => UpdateStatus::Unchanged,
}
}
fn clear_deletion_flags(state: &mut State) {
let query =
match state {
State::SearchIdeas(query) => Some(query),
State::SearchJournals(query) => Some(query),
State::SearchNotes(query) => Some(query),
State::SearchTasks(query) => Some(query),
_ => None,
};
if let Some(query) = query {
query.deletion_stage = DeletionStage::NotFlagged;
}
}
fn delete(vault: &Vault, state: &mut State) -> UpdateStatus {
match state {
State::SearchIdeas(query) => {
if let Some(index) = query.selected_result_index {
let n = query.results.len();
if index < n {
let (name, _) = &query.results[index];
match query.deletion_stage {
DeletionStage::NotFlagged => {
query.deletion_stage = DeletionStage::Flagged;
return UpdateStatus::Changed;
},
DeletionStage::Flagged => {
if let Ok(true) = ideas::remove_noprompt(vault, name.to_owned()) {
query.results.remove(index);
if index > 0 {
query.selected_result_index = Some(index - 1);
} else if !query.results.is_empty() {
query.selected_result_index = Some(0);
} else {
query.selected_result_index = None;
}
query.deletion_stage = DeletionStage::NotFlagged;
return UpdateStatus::Changed;
}
},
}
}
}
UpdateStatus::Unchanged
},
State::SearchJournals(query) => {
if let Some(index) = query.selected_result_index {
let n = query.results.len();
if index < n {
let (name, _) = &query.results[index];
match query.deletion_stage {
DeletionStage::NotFlagged => {
query.deletion_stage = DeletionStage::Flagged;
return UpdateStatus::Changed;
},
DeletionStage::Flagged => {
if let Some(date) = JustDate::parse(&name) {
if let Ok(true) = journals::remove_noprompt(vault, &date) {
query.results.remove(index);
if index > 0 {
query.selected_result_index = Some(index - 1);
} else if !query.results.is_empty() {
query.selected_result_index = Some(0);
} else {
query.selected_result_index = None;
}
query.deletion_stage = DeletionStage::NotFlagged;
return UpdateStatus::Changed;
}
}
},
}
}
}
UpdateStatus::Unchanged
},
State::SearchNotes(query) => {
if let Some(index) = query.selected_result_index {
let results = query.flatten_results();
let n = results.len();
if index < n {
let (category, name, i, j) = &results[index];
match query.deletion_stage {
DeletionStage::NotFlagged => {
query.deletion_stage = DeletionStage::Flagged;
return UpdateStatus::Changed;
},
DeletionStage::Flagged => {
if let Ok(true) = notes::remove_noprompt(vault, category, name) {
if let Some(j) = j {
let (_, items) = &mut query.results[*i];
items.remove(*j);
if items.is_empty() {
query.results.remove(*i);
}
}
if index > 0 {
query.selected_result_index = Some(index - 1);
} else if !query.results.is_empty() {
query.selected_result_index = Some(0);
} else {
query.selected_result_index = None;
}
query.deletion_stage = DeletionStage::NotFlagged;
return UpdateStatus::Changed;
}
},
}
}
}
UpdateStatus::Unchanged
},
State::SearchTasks(query) => {
if let Some(index) = query.selected_result_index {
let results = query.flatten_results();
let n = results.len();
if index < n {
let (project, task, i, j) = &results[index];
match query.deletion_stage {
DeletionStage::NotFlagged => {
query.deletion_stage = DeletionStage::Flagged;
return UpdateStatus::Changed;
},
DeletionStage::Flagged => {
if let Ok(true) = tasks::remove_noprompt(vault, project, task) {
if let Some(j) = j {
let (_, items) = &mut query.results[*i];
items.remove(*j);
if items.is_empty() {
query.results.remove(*i);
}
}
if index > 0 {
query.selected_result_index = Some(index - 1);
} else if !query.results.is_empty() {
query.selected_result_index = Some(0);
} else {
query.selected_result_index = None;
}
query.deletion_stage = DeletionStage::NotFlagged;
return UpdateStatus::Changed;
}
},
}
}
}
UpdateStatus::Unchanged
},
_ => UpdateStatus::Changed,
}
}
fn schedule_editor(vault: &Vault, state: &mut State) -> UpdateStatus {
match state {
State::SearchIdeas(query) => {
if let Some(index) = query.selected_result_index {
let n = query.results.len();
if index < n {
let (name, _) = &query.results[index];
return UpdateStatus::StartIdeaEditor(name.to_owned());
}
} else {
let name = query.input.as_str().trim().to_owned();
if name != "" {
return UpdateStatus::StartIdeaEditor(name);
}
}
UpdateStatus::Unchanged
},
State::SearchJournals(query) => {
if let Some(index) = query.selected_result_index {
let n = query.results.len();
if index < n {
let (name, _) = &query.results[index];
if let Some(date) = JustDate::parse(&name) {
return UpdateStatus::StartJournalEditor(date);
}
}
} else {
let name = query.input.as_str().trim().to_owned();
if name != "" {
if let Some(date) = JustDate::parse(&name) {
return UpdateStatus::StartJournalEditor(date);
}
}
}
UpdateStatus::Unchanged
},
State::SearchNotes(query) => {
let results = query.flatten_results();
if let Some(index) = query.selected_result_index {
let n = results.len();
if index < n {
let (category, name, _, _) = &results[index];
return UpdateStatus::StartNoteEditor(category.to_owned(), name.to_owned());
}
} else {
let input =
query.input.as_str()
.split(vault.config.input_delimiter.as_str())
.map(|x| x.trim().to_owned())
.collect::<Vec<String>>();
if input.len() == 2 {
let (category, name) = (input[0].to_owned(), input[1].to_owned());
return UpdateStatus::StartNoteEditor(category, name);
}
}
UpdateStatus::Unchanged
},
State::SearchTasks(query) => {
let results = query.flatten_results();
if let Some(index) = query.selected_result_index {
let n = results.len();
if index < n {
let (project, _, _, _) = &results[index];
return UpdateStatus::StartTaskEditor(project.to_owned());
}
} else {
let input =
query.input.as_str()
.split(vault.config.input_delimiter.as_str())
.map(|x| x.trim().to_owned())
.collect::<Vec<String>>();
if input.len() == 2 {
let (project, task) = (input[0].to_owned(), input[1].to_owned());
if tasks::add_task(vault, &project, &task).is_ok() {
let select_item = Some((
project,
Some(format!("{} {}", vault.config.task_not_done_prefix, task)),
));
return update_query_and_search(vault, state, ' ', clear_string, select_item);
}
} else {
let project = query.input.as_str().trim().to_owned();
return UpdateStatus::StartTaskEditor(project);
}
}
UpdateStatus::Unchanged
},
_ => UpdateStatus::Changed,
}
}
fn search(vault: &Vault, state: &mut State, select_item: Option<(String, Option<String>)>) -> UpdateStatus {
match state {
State::SearchIdeas(query) => filter_query_results(vault, query, vault.list_ideas(), select_item, false),
State::SearchJournals(query) => filter_query_results(vault, query, vault.list_journals(), select_item, true),
State::SearchNotes(query) => filter_query_results(vault, query, vault.list_notes(true, &None), select_item, false),
State::SearchTasks(query) => filter_query_results(vault, query, vault.list_tasks(true, &None), select_item, false),
_ => UpdateStatus::Changed,
}
}
fn filter_query_results(vault: &Vault, query: &mut Query, results: Result<QueryResult>, select_item: Option<(String, Option<String>)>, resolve_dates: bool) -> UpdateStatus {
match results {
Ok(results) => {
let mut filtered: QueryResult = vec![];
let mut filter = query.input.as_str().trim().to_lowercase();
if resolve_dates {
if let Some(date) = JustDate::parse(&filter) {
filter = date.format();
}
}
let mut cat_filter = filter.to_owned();
let mut item_filter = filter.to_owned();
let f = filter.as_str();
let delimiter = vault.config.input_delimiter.as_str();
if f.contains(delimiter) {
let items = f.split(delimiter).collect::<Vec<&str>>();
if items.len() == 2 {
cat_filter = items[0].trim().to_owned();
item_filter = items[1].trim().to_owned();
}
}
for (category, items) in results {
let cat = category.as_str().trim().to_lowercase();
if cat_filter == item_filter {
if cat.contains(cat_filter.as_str()) { filtered.push((category, items));
continue;
}
let mut filtered_items: Vec<String> = vec![];
for item in items {
let i = item.as_str().trim().to_lowercase();
if i.contains(item_filter.as_str()) {
filtered_items.push(item);
}
}
if !filtered_items.is_empty() {
filtered.push((category, filtered_items));
}
} else { if cat.contains(cat_filter.as_str()) {
let mut filtered_items: Vec<String> = vec![];
for item in items {
let i = item.as_str().trim().to_lowercase();
if i.contains(item_filter.as_str()) {
filtered_items.push(item);
}
}
if !filtered_items.is_empty() {
filtered.push((category, filtered_items));
}
}
}
}
let is_empty = filtered.is_empty();
query.results = filtered;
query.selected_result_index = None;
if !is_empty {
query.selected_result_index = Some(0);
let mut index = 0;
if let Some((category, item)) = select_item {
for (cat, it, _, _) in query.flatten_results() {
if &category == &cat {
if let Some(item) = &item {
if item == &it {
query.selected_result_index = Some(index);
break;
}
}
}
index += 1;
}
}
}
UpdateStatus::Changed
},
Err(_) => UpdateStatus::Unchanged,
}
}
fn rotate_task_status(vault: &Vault, state: &mut State) -> UpdateStatus {
if let State::SearchTasks(query) = state {
if let Some(index) = query.selected_result_index {
let results = query.flatten_results();
let (project, task, _, _) = &results[index];
if let Ok(new_task) = tasks::rotate_state(vault, &project, &task) {
let select_item = Some((project.to_owned(), Some(new_task)));
return search(vault, state, select_item);
}
}
}
UpdateStatus::Unchanged
}
fn prev_search_result(state: &mut State) -> UpdateStatus {
match state {
State::SearchIdeas(query) => query.select_prev(),
State::SearchJournals(query) => query.select_prev(),
State::SearchNotes(query) => query.select_prev(),
State::SearchTasks(query) => query.select_prev(),
_ => UpdateStatus::Changed,
}
}
fn next_search_result(state: &mut State) -> UpdateStatus {
match state {
State::SearchIdeas(query) => query.select_next(),
State::SearchJournals(query) => query.select_next(),
State::SearchNotes(query) => query.select_next(),
State::SearchTasks(query) => query.select_next(),
_ => UpdateStatus::Changed,
}
}
fn switch(state: &mut State, new_state: State) -> UpdateStatus {
match (&state, &new_state) {
(State::SearchIdeas(_), State::SearchIdeas(_)) => UpdateStatus::Unchanged,
(State::SearchJournals(_), State::SearchJournals(_)) => UpdateStatus::Unchanged,
(State::SearchNotes(_), State::SearchNotes(_)) => UpdateStatus::Unchanged,
(State::SearchTasks(_), State::SearchTasks(_)) => UpdateStatus::Unchanged,
_ => {
*state = new_state;
UpdateStatus::Changed
},
}
}
fn update_query_and_search<F>(vault: &Vault, state: &mut State, ch: char, func: F, select_item: Option<(String, Option<String>)>) -> UpdateStatus
where
F: Fn(&String, char) -> String
{
let status =
if update_query(state, ch, func) == UpdateStatus::Changed {
search(vault, state, select_item)
} else {
UpdateStatus::Unchanged
};
status
}
fn update_query<F>(state: &mut State, ch: char, func: F) -> UpdateStatus
where
F: Fn(&String, char) -> String
{
let query =
match state {
State::SearchIdeas(query) => Some(query),
State::SearchJournals(query) => Some(query),
State::SearchNotes(query) => Some(query),
State::SearchTasks(query) => Some(query),
_ => None,
};
if let Some(query) = query {
let new_input = func(&query.input, ch);
if new_input != query.input {
query.input = new_input;
query.results = vec![];
query.selected_result_index = None;
return UpdateStatus::Changed;
}
}
UpdateStatus::Unchanged
}
fn append_char(input: &String, ch: char) -> String {
format!("{}{}", input, ch)
}
fn remove_last_char(input: &String, _: char) -> String {
let mut result = input.to_owned();
result.pop();
result
}
fn remove_last_word(input: &String, _: char) -> String {
let mut input = input.as_str().trim_end().to_owned();
while input.len() > 0 {
let ch = input.pop();
if let Some(' ') = ch {
break;
}
if let Some('\t') = ch {
break;
}
}
input
}
fn clear_string(_: &String, _: char) -> String {
String::from("")
}