From 80a51848951eac3d9053846a3446616b9147d9dc Mon Sep 17 00:00:00 2001 From: lukeflo Date: Tue, 1 Oct 2024 14:41:55 +0200 Subject: keyword search implemented --- src/frontend/app.rs | 86 +++++++++++++++++++++++++++++++++++++------------ src/frontend/handler.rs | 52 ++++++++++++++++++++++-------- src/frontend/ui.rs | 30 +++++------------ 3 files changed, 112 insertions(+), 56 deletions(-) (limited to 'src/frontend') diff --git a/src/frontend/app.rs b/src/frontend/app.rs index 1105901..ed0318f 100644 --- a/src/frontend/app.rs +++ b/src/frontend/app.rs @@ -15,7 +15,7 @@ // along with this program. If not, see . ///// -use crate::backend::{bib::*, search}; +use crate::backend::{bib::*, search::BibiSearch}; use std::error; use arboard::Clipboard; @@ -51,6 +51,8 @@ pub struct App { pub main_biblio: BibiMain, // bibliographic data pub biblio_data: BibiData, + // search struct: + pub search_struct: BibiSearch, // tag list pub tag_list: TagList, // table items @@ -62,7 +64,7 @@ pub struct App { // mode for popup window pub former_area: Option, // search string - pub search_string: String, + // pub search_string: String, } // Define the fundamental List @@ -75,14 +77,14 @@ pub struct TagList { // Structure of the list items. #[derive(Debug)] pub struct TagListItem { - pub info: String, + pub keyword: String, } // Function to process inputed characters and convert them (to string, or more complex function) impl TagListItem { pub fn new(info: &str) -> Self { Self { - info: info.to_string(), + keyword: info.to_string(), } } } @@ -107,7 +109,7 @@ impl FromIterator> for EntryTable { let entry_table_items = iter .into_iter() .sorted() - .map(|i| EntryTableItem::new(&i[0], &i[1], &i[2], &i[3], &i[4])) + .map(|i| EntryTableItem::new(&i[0], &i[1], &i[2], &i[3], &i[4], &i[5])) .collect(); let entry_table_state = TableState::default().with_selected(0); Self { @@ -131,17 +133,26 @@ pub struct EntryTableItem { pub title: String, pub year: String, pub pubtype: String, + pub keywords: String, pub citekey: String, // pub year: u16, } impl EntryTableItem { - pub fn new(authors: &str, title: &str, year: &str, pubtype: &str, citekey: &str) -> Self { + pub fn new( + authors: &str, + title: &str, + year: &str, + pubtype: &str, + keywords: &str, + citekey: &str, + ) -> Self { Self { authors: authors.to_string(), title: title.to_string(), year: year.to_string(), pubtype: pubtype.to_string(), + keywords: keywords.to_string(), citekey: citekey.to_string(), } } @@ -178,7 +189,8 @@ impl Default for App { let running = true; let main_biblio = BibiMain::new(); let biblio_data = BibiData::new(&main_biblio.bibliography, &main_biblio.citekeys); - let tag_list = TagList::from_iter(main_biblio.citekeys.clone()); + let tag_list = TagList::from_iter(main_biblio.keyword_list.clone()); + let search_struct = BibiSearch::default(); let entry_table = EntryTable::from_iter(biblio_data.entry_list.bibentries.clone()); let current_area = CurrentArea::EntryArea; Self { @@ -186,11 +198,12 @@ impl Default for App { main_biblio, biblio_data, tag_list, + search_struct, entry_table, scroll_info: 0, current_area, former_area: None, - search_string: String::new(), + // search_string: String::new(), } } } @@ -290,8 +303,40 @@ impl App { self.tag_list.tag_list_state.select_last(); } + pub fn get_selected_tag(&self) -> &str { + let idx = self.tag_list.tag_list_state.selected().unwrap(); + let keyword = &self.tag_list.tag_list_items[idx].keyword; + keyword + } + + pub fn search_tags(&mut self) { + let orig_list = &self.main_biblio.keyword_list; + let filtered_list = + BibiSearch::search_tag_list(&self.search_struct.search_string, orig_list.clone()); + self.tag_list = TagList::from_iter(filtered_list) + } + pub fn reset_taglist(&mut self) { - self.tag_list = TagList::from_iter(self.main_biblio.citekeys.clone()) + self.tag_list = TagList::from_iter(self.main_biblio.keyword_list.clone()) + } + + // Filter the entry list by tags + // If already inside a filtered tag or entry list, apply the filtering + // to the already filtered list only + pub fn filter_for_tags(&mut self) { + let orig_list = { + if self.search_struct.inner_search { + let orig_list = &self.search_struct.filtered_entry_list; + orig_list + } else { + let orig_list = &self.biblio_data.entry_list.bibentries; + orig_list + } + }; + let keyword = self.get_selected_tag(); + let filtered_list = BibiSearch::filter_entries_by_tag(&keyword, &orig_list); + self.search_struct.filtered_entry_list = filtered_list; + self.entry_table = EntryTable::from_iter(self.search_struct.filtered_entry_list.clone()); } // Entry Table commands @@ -331,19 +376,18 @@ impl App { // Search entry list pub fn search_entries(&mut self) { - match self.former_area { - Some(FormerArea::EntryArea) => { + let orig_list = { + if self.search_struct.inner_search { + let orig_list = &self.search_struct.filtered_entry_list; + orig_list + } else { let orig_list = &self.biblio_data.entry_list.bibentries; - let filtered_list = - search::search_entry_list(&self.search_string, orig_list.clone()); - self.entry_table = EntryTable::from_iter(filtered_list) - } - Some(FormerArea::TagArea) => { - let orig_list = &self.main_biblio.citekeys; - let filtered_list = search::search_tag_list(&self.search_string, orig_list.clone()); - self.tag_list = TagList::from_iter(filtered_list) + orig_list } - _ => {} - } + }; + let filtered_list = + BibiSearch::search_entry_list(&mut self.search_struct.search_string, orig_list.clone()); + //search::search_entry_list(&self.search_string, orig_list.clone()); + self.entry_table = EntryTable::from_iter(filtered_list) } } diff --git a/src/frontend/handler.rs b/src/frontend/handler.rs index c3eeed5..cdbd981 100644 --- a/src/frontend/handler.rs +++ b/src/frontend/handler.rs @@ -15,10 +15,13 @@ // along with this program. If not, see . ///// -use crate::frontend::app::{App, AppResult}; +use crate::{ + backend::search::BibiSearch, + frontend::app::{App, AppResult}, +}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use super::app::{CurrentArea, EntryTable, FormerArea}; +use super::app::{CurrentArea, FormerArea}; /// Handles the key events and updates the state of [`App`]. pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { @@ -61,14 +64,19 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { KeyCode::Char('/') => { app.former_area = Some(FormerArea::TagArea); app.current_area = CurrentArea::SearchArea; + // app.search_struct.is_search = true; } KeyCode::Tab | KeyCode::BackTab => { app.toggle_area(); } KeyCode::Esc => { - if let Some(FormerArea::SearchArea) = app.former_area { - app.reset_taglist(); - } + app.reset_taglist(); + } + KeyCode::Enter => { + app.filter_for_tags(); + app.toggle_area(); + // app.reset_taglist(); + app.former_area = Some(FormerArea::TagArea); } _ => {} }, @@ -90,6 +98,9 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { App::yank_text(&app.get_selected_citekey()); } KeyCode::Char('/') => { + if let Some(FormerArea::TagArea) = app.former_area { + app.search_struct.inner_search = true; + } app.former_area = Some(FormerArea::EntryArea); app.current_area = CurrentArea::SearchArea; } @@ -97,9 +108,11 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.toggle_area(); } KeyCode::Esc => { - if let Some(FormerArea::SearchArea) = app.former_area { - app.reset_entry_table(); + app.reset_entry_table(); + if app.search_struct.inner_search { + app.reset_taglist(); } + app.former_area = None; } _ => {} }, @@ -113,21 +126,34 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> { app.reset_taglist(); } app.former_area = None; - app.search_string.clear(); + // If search is canceled, reset default status of struct + BibiSearch::default(); + // app.search_struct.search_string.clear(); + // app.search_struct.is_search = false; + // app.search_struct.filtered_entry_list.clear(); } KeyCode::Enter => { // TODO: run function for filtering the list app.toggle_area(); app.former_area = Some(FormerArea::SearchArea); - app.search_string.clear(); + // app.search_string.clear(); + app.search_struct.search_string.clear(); } KeyCode::Backspace => { - app.search_string.pop(); - app.search_entries(); + app.search_struct.search_string.pop(); + if let Some(FormerArea::EntryArea) = app.former_area { + app.search_entries(); + } else if let Some(FormerArea::TagArea) = app.former_area { + app.search_tags(); + } } KeyCode::Char(search_pattern) => { - app.search_string.push(search_pattern); - app.search_entries(); + app.search_struct.search_string.push(search_pattern); + if let Some(FormerArea::EntryArea) = app.former_area { + app.search_entries(); + } else if let Some(FormerArea::TagArea) = app.former_area { + app.search_tags(); + } } _ => {} }, diff --git a/src/frontend/ui.rs b/src/frontend/ui.rs index 0829227..7323917 100644 --- a/src/frontend/ui.rs +++ b/src/frontend/ui.rs @@ -18,15 +18,12 @@ use ratatui::{ buffer::Buffer, layout::{Constraint, Layout, Rect}, - style::{ - palette::tailwind::{GRAY, SLATE}, - Color, Modifier, Style, Stylize, - }, + style::{palette::tailwind::SLATE, Color, Modifier, Style, Stylize}, symbols, text::{Line, Span, Text}, widgets::{ - Block, Borders, Cell, HighlightSpacing, List, ListItem, Padding, Paragraph, Row, - StatefulWidget, Table, Widget, Wrap, + Block, Cell, HighlightSpacing, List, ListItem, Padding, Paragraph, Row, StatefulWidget, + Table, Widget, Wrap, }, }; @@ -38,7 +35,7 @@ use crate::{ use super::app::{CurrentArea, FormerArea}; const MAIN_BLUE_COLOR: Color = Color::Indexed(39); -const MAIN_PURPLE_COLOR: Color = Color::Indexed(129); +// const MAIN_PURPLE_COLOR: Color = Color::Indexed(129); const BOX_BORDER_STYLE_MAIN: Style = Style::new().fg(Color::White).bg(Color::Black); const NORMAL_ROW_BG: Color = Color::Black; const ALT_ROW_BG_COLOR: Color = Color::Indexed(234); @@ -47,7 +44,7 @@ const SELECTED_STYLE: Style = Style::new() .add_modifier(Modifier::BOLD) .add_modifier(Modifier::REVERSED); const TEXT_FG_COLOR: Color = SLATE.c200; -const TEXT_CONFIRMED: Style = Style::new().fg(Color::Green); +// const TEXT_CONFIRMED: Style = Style::new().fg(Color::Green); pub const fn alternate_colors(i: usize) -> Color { if i % 2 == 0 { @@ -59,13 +56,7 @@ pub const fn alternate_colors(i: usize) -> Color { impl From<&TagListItem> for ListItem<'_> { fn from(value: &TagListItem) -> Self { - let line = Line::styled(format!("{}", value.info), TEXT_FG_COLOR); - // match value.status { - // Status::Todo => Line::styled(format!(" ☐ {}", value.todo), TEXT_FG_COLOR), - // Status::Completed => { - // Line::styled(format!(" ✓ {}", value.todo), COMPLETED_TEXT_FG_COLOR) - // } - // }; + let line = Line::styled(format!("{}", value.keyword), TEXT_FG_COLOR); ListItem::new(line) } } @@ -129,7 +120,7 @@ impl App { let block = Block::bordered() .title(search_title) .border_set(symbols::border::ROUNDED); - Paragraph::new(self.search_string.clone()) + Paragraph::new(self.search_struct.search_string.clone()) .block(block) .render(area, buf); } @@ -149,12 +140,7 @@ impl App { pub fn render_entrytable(&mut self, area: Rect, buf: &mut Buffer) { let block = Block::bordered() // can also be Block::new - .title( - Line::raw(" Bibliographic Entries ") - .centered() - .bold() - .fg(Color::Indexed(39)), - ) + .title(Line::raw(" Bibliographic Entries ").centered().bold()) .border_set(symbols::border::ROUNDED) .border_style(BOX_BORDER_STYLE_MAIN) .bg(Color::Black); // .bg(NORMAL_ROW_BG); -- cgit v1.2.3