From 0312b8b31160297033e8a435c00e7c92eaa371e6 Mon Sep 17 00:00:00 2001 From: lukeflo Date: Wed, 25 Dec 2024 22:32:42 +0100 Subject: Minor Feature: Sort entries by position in/of file + Hitting now sorts the entries by position in file or position of files + Can be helpful to detect entries newly added via DOI which are appended to EOF --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 9 +++++---- src/app.rs | 3 +++ src/bibiman/bibisetup.rs | 5 ++++- src/bibiman/entries.rs | 50 +++++++++++++++++++++++++++++++++++++----------- src/tui/commands.rs | 4 ++++ src/tui/popup.rs | 4 ++++ src/tui/ui.rs | 18 ++++++++++++----- 9 files changed, 74 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37b48be..b0eec67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,7 +86,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bibiman" -version = "0.9.1" +version = "0.9.2" dependencies = [ "arboard", "biblatex", diff --git a/Cargo.toml b/Cargo.toml index 3e28ba2..4e279df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bibiman" -version = "0.9.1" +version = "0.9.2" authors = ["lukeflo "] license = "GPL-3.0-or-later" repository = "https://codeberg.org/lukeflo/bibiman" diff --git a/README.md b/README.md index 81520ea..6c74510 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,7 @@ Here is how the light terminal scheme looks: ## Features -For now, `bibiman` only has mainly features implemented which are important for -my personal workflow. There are more to come, the list will be updated: +These are the current features, the list will be updated: - [x] **Browse** through the bib entries using _Vim-like keybindings_ and a _fuzzy search_ mode. @@ -114,7 +113,8 @@ my personal workflow. There are more to come, the list will be updated: - [x] **Open related PDF** file (`file` BibLaTeX key) with keypress. - [x] **Open related URL/DOI** with keypress. - [x] **Scrollbar** for better navigating. -- [x] **Sort Entries** by column (`Authors`, `Title`, `Year`, `Pubtype`) +- [x] **Sort Entries** by column (`Authors`, `Title`, `Year`, `Pubtype`), or by + position in bibfile. - [x] **Load multiple files** into one session. - [x] **Add Entry via DOI**. - [ ] **Open related notes file** for specific entry. @@ -138,7 +138,8 @@ Use the following keybindings to manage the TUI: | `Ctrl-d`, `Ctrl-u` | Move down/up by 5 | | `g`, `G` | Go to first/last entry | | `h`, `k` \| `Left`, `Right` | Select previous/next entry column | -| `s` | Sort current column (toggles) | +| `s` | Sort entries by current column (toggles) | +| `S` | Sort entries by position in file | | `PageDown`, `PageUp` \| `Alt-j`, `Alt-k` | Scroll Info window | | `y` | Yank/copy citekey of selected entry | | `e` | Open editor at selected entry | diff --git a/src/app.rs b/src/app.rs index 2240e8f..977cb6c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -280,6 +280,9 @@ impl App { self.bibiman.entry_table.sort_entry_table(true); } } + CmdAction::SortById => { + self.bibiman.entry_table.sort_by_id(); + } CmdAction::YankItem => { if let CurrentArea::EntryArea = self.bibiman.current_area { let citekey: &str = &self.bibiman.entry_table.entry_table_items[self diff --git a/src/bibiman/bibisetup.rs b/src/bibiman/bibisetup.rs index b0049c6..5aa11be 100644 --- a/src/bibiman/bibisetup.rs +++ b/src/bibiman/bibisetup.rs @@ -38,6 +38,7 @@ pub struct BibiSetup { #[derive(Debug, Clone, PartialEq, Eq)] pub struct BibiData { + pub id: u32, pub authors: String, pub title: String, pub year: String, @@ -114,7 +115,9 @@ impl BibiSetup { fn create_entry_list(citekeys: &[String], bibliography: &Bibliography) -> Vec { citekeys .iter() - .map(|k| BibiData { + .enumerate() + .map(|(i, k)| BibiData { + id: i as u32, authors: Self::get_authors(k, bibliography), title: Self::get_title(k, bibliography), year: Self::get_year(k, bibliography), diff --git a/src/bibiman/entries.rs b/src/bibiman/entries.rs index 34020c8..44b8660 100644 --- a/src/bibiman/entries.rs +++ b/src/bibiman/entries.rs @@ -34,7 +34,7 @@ pub struct EntryTable { pub entry_table_items: Vec, pub entry_table_at_search_start: Vec, pub entry_table_selected_column: EntryTableColumn, - pub entry_table_sorted_by_col: EntryTableColumn, + pub entry_table_sorted_by_col: Option, pub entry_table_reversed_sort: bool, pub entry_table_state: TableState, pub entry_scroll_state: ScrollbarState, @@ -55,7 +55,7 @@ impl EntryTable { entry_table_items, entry_table_at_search_start: Vec::new(), entry_table_selected_column: EntryTableColumn::Authors, - entry_table_sorted_by_col: EntryTableColumn::Authors, + entry_table_sorted_by_col: Some(EntryTableColumn::Authors), entry_table_reversed_sort: false, entry_table_state, entry_scroll_state, @@ -68,6 +68,7 @@ impl EntryTable { let mut entry_table: Vec = entry_list .iter() .map(|e| EntryTableItem { + id: e.id, authors: e.authors.clone(), short_author: String::new(), title: e.title.clone(), @@ -86,16 +87,36 @@ impl EntryTable { entry_table } + pub fn sort_by_id(&mut self) { + if self.entry_table_sorted_by_col.is_some() { + self.entry_table_reversed_sort = false + } else { + self.entry_table_reversed_sort = !self.entry_table_reversed_sort; + } + if self.entry_table_reversed_sort { + self.entry_table_items.sort_by(|a, b| b.id.cmp(&a.id)); + } else { + self.entry_table_items.sort_by(|a, b| a.id.cmp(&b.id)); + } + self.entry_table_sorted_by_col = None; + } + // Sort entry table by specific column. // Toggle sorting by hitting same key again pub fn sort_entry_table(&mut self, toggle: bool) { - if toggle { + // Check if table was sorted by visible column before + if toggle && self.entry_table_sorted_by_col.is_some() { self.entry_table_reversed_sort = !self.entry_table_reversed_sort; + } else { + self.entry_table_reversed_sort = false; } - if self.entry_table_selected_column != self.entry_table_sorted_by_col { - self.entry_table_reversed_sort = false + if self.entry_table_sorted_by_col.as_ref().is_some() { + if &self.entry_table_selected_column != self.entry_table_sorted_by_col.as_ref().unwrap() + { + self.entry_table_reversed_sort = false + } } - self.entry_table_sorted_by_col = self.entry_table_selected_column.clone(); + self.entry_table_sorted_by_col = Some(self.entry_table_selected_column.clone()); if self.entry_table_reversed_sort { match self.entry_table_selected_column { EntryTableColumn::Authors => self @@ -133,6 +154,7 @@ impl EntryTable { // Define contents of each entry table row #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct EntryTableItem { + pub id: u32, pub authors: String, pub short_author: String, pub title: String, @@ -152,7 +174,7 @@ impl EntryTableItem { pub fn ref_vec(&mut self) -> Vec<&str> { self.short_author = match self.authors.split_once(",") { Some((first, _rest)) => { - if self.authors.contains("(ed.)") { + if self.authors().contains("(ed.)") { let first_author = format!("{} et al. (ed.)", first); first_author } else { @@ -166,17 +188,21 @@ impl EntryTableItem { vec![ { if self.short_author.is_empty() { - &self.authors + self.authors() } else { &self.short_author } }, - &self.title, - &self.year, - &self.pubtype, + self.title(), + self.year(), + self.pubtype(), ] } + pub fn entry_id(&self) -> &u32 { + &self.id + } + pub fn authors(&self) -> &str { &self.authors } @@ -228,6 +254,7 @@ mod tests { #[test] fn shorten_authors() { let mut entry: EntryTableItem = EntryTableItem { + id: 1, authors: "Miller, Schmitz, Bernard".to_string(), short_author: "".to_string(), title: "A title".to_string(), @@ -244,6 +271,7 @@ mod tests { let entry_vec = EntryTableItem::ref_vec(&mut entry); let mut entry_editors: EntryTableItem = EntryTableItem { + id: 2, authors: "Miller, Schmitz, Bernard (ed.)".to_string(), short_author: "".to_string(), title: "A title".to_string(), diff --git a/src/tui/commands.rs b/src/tui/commands.rs index 0e00f95..08ee677 100644 --- a/src/tui/commands.rs +++ b/src/tui/commands.rs @@ -57,6 +57,8 @@ pub enum CmdAction { Confirm, // Sort table/list SortList, + // + SortById, // Yank selected item YankItem, // Edit file @@ -153,6 +155,8 @@ impl From for CmdAction { KeyCode::Char('y') => Self::YankItem, // Sort entry table by selected col KeyCode::Char('s') => Self::SortList, + // Sort entry table by position in file + KeyCode::Char('S') => Self::SortById, // Show help popup KeyCode::Char('?') => Self::ShowHelp, // Else do nothing diff --git a/src/tui/popup.rs b/src/tui/popup.rs index 78a0719..4ef9fc3 100644 --- a/src/tui/popup.rs +++ b/src/tui/popup.rs @@ -61,6 +61,10 @@ impl PopupArea { ("g|Home: ", "Go to first entry"), ("G|End: ", "Go to last entry"), ("s: ", "sort entries by selected column (toggles reversed)"), + ( + "S: ", + "sort entries by position in/of file (toggles reversed)", + ), ("y: ", "yank/copy citekey of selected entry to clipboard"), ("e: ", "Open editor at selected entry"), ("o: ", "Open with selected entry associated PDF"), diff --git a/src/tui/ui.rs b/src/tui/ui.rs index 4f64338..d85f318 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -563,7 +563,8 @@ pub fn render_entrytable(app: &mut App, args: &CLIArgs, frame: &mut Frame, rect: let header = Row::new(vec![ Cell::from( Line::from(vec![{ Span::raw("Author") }, { - if let EntryTableColumn::Authors = app.bibiman.entry_table.entry_table_sorted_by_col + if let Some(EntryTableColumn::Authors) = + app.bibiman.entry_table.entry_table_sorted_by_col { Span::raw(format!( " {}", @@ -589,7 +590,9 @@ pub fn render_entrytable(app: &mut App, args: &CLIArgs, frame: &mut Frame, rect: ), Cell::from( Line::from(vec![{ Span::raw("Title") }, { - if let EntryTableColumn::Title = app.bibiman.entry_table.entry_table_sorted_by_col { + if let Some(EntryTableColumn::Title) = + app.bibiman.entry_table.entry_table_sorted_by_col + { Span::raw(format!( " {}", if app.bibiman.entry_table.entry_table_reversed_sort { @@ -613,7 +616,9 @@ pub fn render_entrytable(app: &mut App, args: &CLIArgs, frame: &mut Frame, rect: ), Cell::from( Line::from(vec![{ Span::raw("Year") }, { - if let EntryTableColumn::Year = app.bibiman.entry_table.entry_table_sorted_by_col { + if let Some(EntryTableColumn::Year) = + app.bibiman.entry_table.entry_table_sorted_by_col + { Span::raw(format!( " {}", if app.bibiman.entry_table.entry_table_reversed_sort { @@ -637,7 +642,8 @@ pub fn render_entrytable(app: &mut App, args: &CLIArgs, frame: &mut Frame, rect: ), Cell::from( Line::from(vec![{ Span::raw("Pubtype") }, { - if let EntryTableColumn::Pubtype = app.bibiman.entry_table.entry_table_sorted_by_col + if let Some(EntryTableColumn::Pubtype) = + app.bibiman.entry_table.entry_table_sorted_by_col { Span::raw(format!( " {}", @@ -698,7 +704,9 @@ pub fn render_entrytable(app: &mut App, args: &CLIArgs, frame: &mut Frame, rect: Constraint::Percentage(20), Constraint::Fill(1), Constraint::Length( - if let EntryTableColumn::Year = app.bibiman.entry_table.entry_table_sorted_by_col { + if let Some(EntryTableColumn::Year) = + app.bibiman.entry_table.entry_table_sorted_by_col + { 6 } else { 4 -- cgit v1.2.3