From c4fd29541b049f38a9a898974b0b098f6bffe777 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Sun, 1 Sep 2024 18:52:26 -0600 Subject: [PATCH 01/10] added a way to search through list, ref #2093 --- src/exercise.rs | 1 + src/list.rs | 55 +++++++++++++++++++++++++++++++++++++--- src/list/scroll_state.rs | 2 +- src/list/state.rs | 37 +++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 11eea63..6806807 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -55,6 +55,7 @@ fn run_bin( } /// See `info_file::ExerciseInfo` +#[derive(Debug)] pub struct Exercise { pub dir: Option<&'static str>, pub name: &'static str, diff --git a/src/list.rs b/src/list.rs index 481fb2f..8364da7 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Ok, Result}; use crossterm::{ cursor, event::{ @@ -21,6 +21,7 @@ mod state; fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> { let mut list_state = ListState::new(app_state, stdout)?; + let mut is_searching = false; loop { match event::read().context("Failed to read terminal event")? { @@ -31,9 +32,50 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } list_state.message.clear(); + + let curr_key = key.code; + + if is_searching { + match curr_key { + KeyCode::Esc | KeyCode::Enter => { + is_searching = false; // not sure why rust analyzer thinks this is unused + list_state.search_query.clear(); + return Ok(()); + } + KeyCode::Char(k) => { + eprintln!("pressed while searching {:?}", curr_key); + + list_state.search_query.push(k); + list_state.message.push_str("search:"); + list_state.message.push_str(&list_state.search_query); + list_state.message.push_str("|"); + + list_state.select_if_matches_search_query(); + + list_state.draw(stdout)?; + continue; + } + KeyCode::Backspace => { + list_state.search_query.pop(); + list_state.message.push_str("search:"); + list_state.message.push_str(&list_state.search_query); + list_state.message.push_str("|"); + + list_state.select_if_matches_search_query(); + + list_state.draw(stdout)?; + continue; + } + _ => { + continue; + } + } + } match key.code { - KeyCode::Char('q') => return Ok(()), + KeyCode::Char('q') => { + return Ok(()); + } KeyCode::Down | KeyCode::Char('j') => list_state.select_next(), KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => list_state.select_first(), @@ -66,9 +108,16 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> return Ok(()); } } + KeyCode::Char('s') | KeyCode::Char('/') => { + eprintln!("starting search"); + list_state.message.push_str("search:|"); + is_searching = true; + } // Redraw to remove the message. KeyCode::Esc => (), - _ => continue, + _ => { + continue; + } } } Event::Mouse(event) => match event.kind { diff --git a/src/list/scroll_state.rs b/src/list/scroll_state.rs index 25a7373..2c02ed4 100644 --- a/src/list/scroll_state.rs +++ b/src/list/scroll_state.rs @@ -46,7 +46,7 @@ impl ScrollState { self.selected } - fn set_selected(&mut self, selected: usize) { + pub fn set_selected(&mut self, selected: usize) { self.selected = Some(selected); self.update_offset(); } diff --git a/src/list/state.rs b/src/list/state.rs index 49b6d5d..117740c 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -45,6 +45,7 @@ pub struct ListState<'a> { term_height: u16, separator_line: Vec, show_footer: bool, + pub search_query: String, } impl<'a> ListState<'a> { @@ -78,6 +79,7 @@ impl<'a> ListState<'a> { term_height: 0, separator_line: Vec::new(), show_footer: true, + search_query: String::new(), }; slf.set_term_size(width, height); @@ -356,6 +358,41 @@ impl<'a> ListState<'a> { Ok(()) } + + pub fn select_if_matches_search_query(&mut self) { + eprintln!("search query: {:?}", self.search_query); + + let idx = self + .app_state + .exercises() + .iter() + .enumerate() + .find_map(|(i, s)| { + if s.name.contains(self.search_query.as_str()) { + Some(i) + } else { + None + } + }); + eprintln!("idx: {:?}", idx); + + match idx { + Some(i) => { + // ? do we need this function call? + // let exercise_ind = self.selected_to_exercise_ind(i).unwrap(); + let exercise_ind = i; + self.scroll_state.set_selected(exercise_ind); + eprintln!("exercise_ind: {:?}", exercise_ind); + self.update_rows(); + } + None => { + let msg = String::from("[NOT FOUND]") + &self.message.clone(); + self.message.clear(); + self.message.push_str(&msg); + } + } + + } // Return `true` if there was something to select. pub fn selected_to_current_exercise(&mut self) -> Result { From 388f8da97f2ff47011d1bebbf0d153ea85741562 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Sun, 1 Sep 2024 19:03:33 -0600 Subject: [PATCH 02/10] removed debug statements --- src/exercise.rs | 1 - src/list.rs | 7 +------ src/list/state.rs | 4 ---- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 6806807..11eea63 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -55,7 +55,6 @@ fn run_bin( } /// See `info_file::ExerciseInfo` -#[derive(Debug)] pub struct Exercise { pub dir: Option<&'static str>, pub name: &'static str, diff --git a/src/list.rs b/src/list.rs index 8364da7..351b0b5 100644 --- a/src/list.rs +++ b/src/list.rs @@ -43,8 +43,6 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> return Ok(()); } KeyCode::Char(k) => { - eprintln!("pressed while searching {:?}", curr_key); - list_state.search_query.push(k); list_state.message.push_str("search:"); list_state.message.push_str(&list_state.search_query); @@ -109,15 +107,12 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } } KeyCode::Char('s') | KeyCode::Char('/') => { - eprintln!("starting search"); list_state.message.push_str("search:|"); is_searching = true; } // Redraw to remove the message. KeyCode::Esc => (), - _ => { - continue; - } + _ => continue, } } Event::Mouse(event) => match event.kind { diff --git a/src/list/state.rs b/src/list/state.rs index 53fed9a..7dfd6b5 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -348,8 +348,6 @@ impl<'a> ListState<'a> { } pub fn select_if_matches_search_query(&mut self) { - eprintln!("search query: {:?}", self.search_query); - let idx = self .app_state .exercises() @@ -362,7 +360,6 @@ impl<'a> ListState<'a> { None } }); - eprintln!("idx: {:?}", idx); match idx { Some(i) => { @@ -370,7 +367,6 @@ impl<'a> ListState<'a> { // let exercise_ind = self.selected_to_exercise_ind(i).unwrap(); let exercise_ind = i; self.scroll_state.set_selected(exercise_ind); - eprintln!("exercise_ind: {:?}", exercise_ind); self.update_rows(); } None => { From 92a1214dcdbead48f520cb4f1d8e53f59b5619e1 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Sun, 1 Sep 2024 19:05:23 -0600 Subject: [PATCH 03/10] passes clippy lints --- src/list.rs | 12 ++++++------ src/list/state.rs | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/list.rs b/src/list.rs index 351b0b5..0f0643c 100644 --- a/src/list.rs +++ b/src/list.rs @@ -32,9 +32,9 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } list_state.message.clear(); - + let curr_key = key.code; - + if is_searching { match curr_key { KeyCode::Esc | KeyCode::Enter => { @@ -46,10 +46,10 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.search_query.push(k); list_state.message.push_str("search:"); list_state.message.push_str(&list_state.search_query); - list_state.message.push_str("|"); - + list_state.message.push('|'); + list_state.select_if_matches_search_query(); - + list_state.draw(stdout)?; continue; } @@ -57,7 +57,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.search_query.pop(); list_state.message.push_str("search:"); list_state.message.push_str(&list_state.search_query); - list_state.message.push_str("|"); + list_state.message.push('|'); list_state.select_if_matches_search_query(); diff --git a/src/list/state.rs b/src/list/state.rs index 7dfd6b5..be05f3b 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -346,7 +346,7 @@ impl<'a> ListState<'a> { Ok(()) } - + pub fn select_if_matches_search_query(&mut self) { let idx = self .app_state @@ -360,7 +360,7 @@ impl<'a> ListState<'a> { None } }); - + match idx { Some(i) => { // ? do we need this function call? @@ -375,7 +375,6 @@ impl<'a> ListState<'a> { self.message.push_str(&msg); } } - } // Return `true` if there was something to select. From 547a9d947bf62052c263a6ee935b5451d3c0dbf1 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Mon, 2 Sep 2024 10:45:45 -0600 Subject: [PATCH 04/10] escape/enter no longer exits the list, exits only the search --- src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index 0f0643c..b41c891 100644 --- a/src/list.rs +++ b/src/list.rs @@ -40,7 +40,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> KeyCode::Esc | KeyCode::Enter => { is_searching = false; // not sure why rust analyzer thinks this is unused list_state.search_query.clear(); - return Ok(()); + continue; } KeyCode::Char(k) => { list_state.search_query.push(k); From abf1228a0a837e71d744f5f2881f386387802cc7 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Mon, 2 Sep 2024 10:59:23 -0600 Subject: [PATCH 05/10] search now filters the list first --- src/list.rs | 2 +- src/list/state.rs | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/list.rs b/src/list.rs index b41c891..069cdda 100644 --- a/src/list.rs +++ b/src/list.rs @@ -38,7 +38,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> if is_searching { match curr_key { KeyCode::Esc | KeyCode::Enter => { - is_searching = false; // not sure why rust analyzer thinks this is unused + is_searching = false; list_state.search_query.clear(); continue; } diff --git a/src/list/state.rs b/src/list/state.rs index be05f3b..8d5bf5b 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -352,6 +352,27 @@ impl<'a> ListState<'a> { .app_state .exercises() .iter() + .filter_map(|exercise| { + match self.filter() { + Filter::None => { + Some(exercise) + }, + Filter::Done => { + if exercise.done { + Some(exercise) + } else { + None + } + }, + Filter::Pending => { + if !exercise.done { + Some(exercise) + } else { + None + } + } + } + }) .enumerate() .find_map(|(i, s)| { if s.name.contains(self.search_query.as_str()) { @@ -363,8 +384,6 @@ impl<'a> ListState<'a> { match idx { Some(i) => { - // ? do we need this function call? - // let exercise_ind = self.selected_to_exercise_ind(i).unwrap(); let exercise_ind = i; self.scroll_state.set_selected(exercise_ind); self.update_rows(); From 71494264ca7303071cfeafbdbf137e11e653190f Mon Sep 17 00:00:00 2001 From: Adhyan Date: Mon, 2 Sep 2024 11:02:17 -0600 Subject: [PATCH 06/10] fixed clippy lints --- src/list/state.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 8d5bf5b..9e813a0 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -352,26 +352,10 @@ impl<'a> ListState<'a> { .app_state .exercises() .iter() - .filter_map(|exercise| { - match self.filter() { - Filter::None => { - Some(exercise) - }, - Filter::Done => { - if exercise.done { - Some(exercise) - } else { - None - } - }, - Filter::Pending => { - if !exercise.done { - Some(exercise) - } else { - None - } - } - } + .filter(|exercise| match self.filter() { + Filter::None => true, + Filter::Done => exercise.done, + Filter::Pending => !exercise.done, }) .enumerate() .find_map(|(i, s)| { From 948e16e3c783bff20736b356d9f961af3bb00784 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 14:40:24 -0600 Subject: [PATCH 07/10] moved continue to end of if-block --- src/list.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/list.rs b/src/list.rs index 069cdda..dc224f0 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Ok, Result}; +use anyhow::{Context, Result}; use crossterm::{ cursor, event::{ @@ -40,7 +40,6 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> KeyCode::Esc | KeyCode::Enter => { is_searching = false; list_state.search_query.clear(); - continue; } KeyCode::Char(k) => { list_state.search_query.push(k); @@ -51,7 +50,6 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.select_if_matches_search_query(); list_state.draw(stdout)?; - continue; } KeyCode::Backspace => { list_state.search_query.pop(); @@ -62,12 +60,10 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.select_if_matches_search_query(); list_state.draw(stdout)?; - continue; - } - _ => { - continue; } + _ => {} } + continue; } match key.code { From fea917c8f2a8e9464ab5625f29f06fd622a26a04 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 14:52:09 -0600 Subject: [PATCH 08/10] removed unnecessary update_rows() call and minor refactoring --- src/list.rs | 6 ++---- src/list/state.rs | 14 ++++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/list.rs b/src/list.rs index dc224f0..857d0ce 100644 --- a/src/list.rs +++ b/src/list.rs @@ -67,9 +67,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } match key.code { - KeyCode::Char('q') => { - return Ok(()); - } + KeyCode::Char('q') => return Ok(()), KeyCode::Down | KeyCode::Char('j') => list_state.select_next(), KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => list_state.select_first(), @@ -102,7 +100,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> return Ok(()); } } - KeyCode::Char('s') | KeyCode::Char('/') => { + KeyCode::Char('s' | '/') => { list_state.message.push_str("search:|"); is_searching = true; } diff --git a/src/list/state.rs b/src/list/state.rs index 8d5bf5b..f150766 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -374,23 +374,21 @@ impl<'a> ListState<'a> { } }) .enumerate() - .find_map(|(i, s)| { - if s.name.contains(self.search_query.as_str()) { - Some(i) + .find_map(|(idx, exercise)| { + if exercise.name.contains(self.search_query.as_str()) { + Some(idx) } else { None } }); match idx { - Some(i) => { - let exercise_ind = i; + Some(x) => { + let exercise_ind = x; self.scroll_state.set_selected(exercise_ind); - self.update_rows(); } None => { - let msg = String::from("[NOT FOUND]") + &self.message.clone(); - self.message.clear(); + let msg = String::from(" (not found)"); self.message.push_str(&msg); } } From 47148e78a32dc39001ec69642eeb2d87f4b4e1ad Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 15:03:25 -0600 Subject: [PATCH 09/10] replaced enumerate() with position(); converted select_if_matches_search_query to apply_search_query --- src/list.rs | 14 ++--------- src/list/state.rs | 62 ++++++++++++++++++++++------------------------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/list.rs b/src/list.rs index 857d0ce..5d7c8dd 100644 --- a/src/list.rs +++ b/src/list.rs @@ -43,22 +43,12 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } KeyCode::Char(k) => { list_state.search_query.push(k); - list_state.message.push_str("search:"); - list_state.message.push_str(&list_state.search_query); - list_state.message.push('|'); - - list_state.select_if_matches_search_query(); - + list_state.apply_search_query(); list_state.draw(stdout)?; } KeyCode::Backspace => { list_state.search_query.pop(); - list_state.message.push_str("search:"); - list_state.message.push_str(&list_state.search_query); - list_state.message.push('|'); - - list_state.select_if_matches_search_query(); - + list_state.apply_search_query(); list_state.draw(stdout)?; } _ => {} diff --git a/src/list/state.rs b/src/list/state.rs index f150766..be6c42e 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -347,40 +347,36 @@ impl<'a> ListState<'a> { Ok(()) } - pub fn select_if_matches_search_query(&mut self) { + pub fn apply_search_query(&mut self) { + self.message.push_str("search:"); + self.message.push_str(&self.search_query); + self.message.push('|'); + + if self.search_query.is_empty() { return; } + let idx = self - .app_state - .exercises() - .iter() - .filter_map(|exercise| { - match self.filter() { - Filter::None => { - Some(exercise) - }, - Filter::Done => { - if exercise.done { - Some(exercise) - } else { - None - } - }, - Filter::Pending => { - if !exercise.done { - Some(exercise) - } else { - None - } - } - } - }) - .enumerate() - .find_map(|(idx, exercise)| { - if exercise.name.contains(self.search_query.as_str()) { - Some(idx) - } else { - None - } - }); + .app_state + .exercises() + .iter() + .filter_map(|exercise| { + match self.filter() { + Filter::None => Some(exercise), + Filter::Done if exercise.done => Some(exercise), + Filter::Pending if !exercise.done => Some(exercise), + _ => None, + } + }) + .position(|exercise| exercise.name.contains(self.search_query.as_str())); + + match idx { + Some(exercise_ind) => { + self.scroll_state.set_selected(exercise_ind); + } + None => { + let msg = String::from(" (not found)"); + self.message.push_str(&msg); + } + } match idx { Some(x) => { From f463cf86627411696922bd703e8c875eec7b367b Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 15:10:44 -0600 Subject: [PATCH 10/10] passes clippy lints and removed extra code from the merge --- src/list/state.rs | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index be6c42e..60077c7 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -352,21 +352,20 @@ impl<'a> ListState<'a> { self.message.push_str(&self.search_query); self.message.push('|'); - if self.search_query.is_empty() { return; } + if self.search_query.is_empty() { + return; + } let idx = self - .app_state - .exercises() - .iter() - .filter_map(|exercise| { - match self.filter() { - Filter::None => Some(exercise), - Filter::Done if exercise.done => Some(exercise), - Filter::Pending if !exercise.done => Some(exercise), - _ => None, - } - }) - .position(|exercise| exercise.name.contains(self.search_query.as_str())); + .app_state + .exercises() + .iter() + .filter(|exercise| match self.filter() { + Filter::None => true, + Filter::Done => exercise.done, + Filter::Pending => !exercise.done, + }) + .position(|exercise| exercise.name.contains(self.search_query.as_str())); match idx { Some(exercise_ind) => { @@ -377,17 +376,6 @@ impl<'a> ListState<'a> { self.message.push_str(&msg); } } - - match idx { - Some(x) => { - let exercise_ind = x; - self.scroll_state.set_selected(exercise_ind); - } - None => { - let msg = String::from(" (not found)"); - self.message.push_str(&msg); - } - } } // Return `true` if there was something to select.