aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/bib.rs170
-rw-r--r--src/backend/search.rs23
-rw-r--r--src/frontend/app.rs12
-rw-r--r--src/frontend/entries.rs190
-rw-r--r--src/frontend/handler.rs3
-rw-r--r--src/frontend/keywords.rs32
-rw-r--r--src/frontend/tui.rs71
-rw-r--r--src/frontend/ui.rs45
-rw-r--r--src/main.rs7
9 files changed, 300 insertions, 253 deletions
diff --git a/src/backend/bib.rs b/src/backend/bib.rs
index bfc959d..a7df951 100644
--- a/src/backend/bib.rs
+++ b/src/backend/bib.rs
@@ -17,50 +17,104 @@
use biblatex::{self, Bibliography};
use biblatex::{ChunksExt, Type};
+use itertools::Itertools;
use std::{fs, path::PathBuf};
+#[derive(Debug)]
+pub enum FileFormat {
+ BibLatex,
+ Hayagriva,
+}
+
// Set necessary fields
// TODO: can surely be made more efficient/simpler
#[derive(Debug)]
pub struct BibiMain {
pub bibfile: PathBuf, // path to bibfile
+ pub bibfile_format: FileFormat, // Format of passed file
pub bibfilestring: String, // content of bibfile as string
pub bibliography: Bibliography, // parsed bibliography
pub citekeys: Vec<String>, // list of all citekeys
pub keyword_list: Vec<String>, // list of all available keywords
+ pub entry_list: Vec<BibiData>, // List of all entries
+}
+
+#[derive(Debug, Clone)]
+pub struct BibiData {
+ pub authors: String,
+ pub title: String,
+ pub year: String,
+ pub pubtype: String,
+ pub keywords: String,
+ pub citekey: String,
+ pub abstract_text: String,
+ pub doi_url: String,
+ pub filepath: String,
}
impl BibiMain {
pub fn new(main_bibfile: PathBuf) -> Self {
// TODO: Needs check for config file path as soon as config file is impl
+ let bibfile_format = Self::check_file_format(&main_bibfile);
let bibfile = main_bibfile;
let bibfilestring = fs::read_to_string(&bibfile).unwrap();
let bibliography = biblatex::Bibliography::parse(&bibfilestring).unwrap();
let citekeys = Self::get_citekeys(&bibliography);
let keyword_list = Self::collect_tag_list(&citekeys, &bibliography);
+ let entry_list = Self::create_entry_list(&citekeys, &bibliography);
Self {
bibfile,
+ bibfile_format,
bibfilestring,
bibliography,
citekeys,
keyword_list,
+ entry_list,
+ }
+ }
+
+ // Check which file format the passed file has
+ fn check_file_format(main_bibfile: &PathBuf) -> FileFormat {
+ let extension = main_bibfile.extension().unwrap().to_str();
+
+ match extension {
+ Some("yml") => FileFormat::Hayagriva,
+ Some("yaml") => FileFormat::Hayagriva,
+ Some("bib") => FileFormat::BibLatex,
+ Some(_) => panic!("The extension {:?} is no valid bibfile", extension.unwrap()),
+ None => panic!("The given path {:?} holds no valid file", main_bibfile),
}
}
+ fn create_entry_list(citekeys: &[String], bibliography: &Bibliography) -> Vec<BibiData> {
+ citekeys
+ .into_iter()
+ .map(|k| BibiData {
+ authors: Self::get_authors(&k, &bibliography),
+ title: Self::get_title(&k, &bibliography),
+ year: Self::get_year(&k, &bibliography),
+ pubtype: Self::get_pubtype(&k, &bibliography),
+ keywords: Self::get_keywords(&k, &bibliography),
+ citekey: k.to_owned(),
+ abstract_text: Self::get_abstract(&k, &bibliography),
+ doi_url: Self::get_weblink(&k, &bibliography),
+ filepath: Self::get_filepath(&k, &bibliography),
+ })
+ .collect()
+ }
+
// get list of citekeys from the given bibfile
// this list is the base for further operations on the bibentries
// since it is the entry point of the biblatex crate.
pub fn get_citekeys(bibstring: &Bibliography) -> Vec<String> {
- let mut citekeys: Vec<String> =
- bibstring.iter().map(|entry| entry.to_owned().key).collect();
- citekeys.sort_by_key(|name| name.to_lowercase());
+ let citekeys: Vec<String> = bibstring.keys().map(|k| k.to_owned()).collect();
citekeys
}
// collect all keywords present in the bibliography
// sort them and remove duplicates
// this list is for fast filtering entries by topics/keyowrds
- pub fn collect_tag_list(citekeys: &Vec<String>, biblio: &Bibliography) -> Vec<String> {
+ pub fn collect_tag_list(citekeys: &[String], biblio: &Bibliography) -> Vec<String> {
// Initialize vector collecting all keywords
let mut keyword_list = vec![];
@@ -90,96 +144,40 @@ impl BibiMain {
keyword_list.dedup();
keyword_list
}
-}
-
-#[derive(Debug)]
-pub struct BibiData {
- pub entry_list: BibiDataSets,
-}
-
-impl BibiData {
- pub fn new(biblio: &Bibliography, citekeys: &Vec<String>) -> Self {
- Self {
- entry_list: {
- let bibentries = citekeys
- .into_iter()
- .map(|citekey| BibiEntry::new(&citekey, &biblio))
- .collect();
- BibiDataSets { bibentries }
- },
- }
- }
-}
-
-// Parent struct which keeps the Vector of all bibentries
-// Necessary for implementing FromIterator
-#[derive(Debug)]
-pub struct BibiDataSets {
- pub bibentries: Vec<Vec<String>>,
-}
-
-// Struct which has to be created for every entry of bibdatabase
-#[derive(Debug)]
-pub struct BibiEntry {
- pub authors: String,
- pub title: String,
- pub year: String,
- pub pubtype: String,
- pub keywords: String,
- pub citekey: String,
- pub weblink: String,
- pub filepath: String,
-}
-
-impl BibiEntry {
- pub fn new(citekey: &str, biblio: &Bibliography) -> Vec<String> {
- vec![
- Self::get_authors(&citekey, &biblio),
- Self::get_title(&citekey, &biblio),
- Self::get_year(&citekey, &biblio),
- Self::get_pubtype(&citekey, &biblio),
- Self::get_keywords(&citekey, &biblio),
- citekey.to_string(),
- Self::get_abstract(&citekey, &biblio),
- Self::get_weblink(&citekey, &biblio),
- Self::get_filepath(&citekey, &biblio),
- ]
- }
pub fn get_authors(citekey: &str, biblio: &Bibliography) -> String {
- let authors = {
- if biblio.get(&citekey).unwrap().author().is_ok() {
- let authors = biblio.get(&citekey).unwrap().author().unwrap();
- if authors.len() > 1 {
- let authors = format!("{} et al.", authors[0].name);
- authors
- } else if authors.len() == 1 {
- let authors = authors[0].name.to_string();
- authors
- } else {
- let editors_authors = format!("empty");
- editors_authors
- }
+ if biblio.get(&citekey).unwrap().author().is_ok() {
+ let authors = biblio.get(&citekey).unwrap().author().unwrap();
+ if authors.len() > 1 {
+ let all_authors = authors.iter().map(|a| &a.name).join(", ");
+ all_authors
+ } else if authors.len() == 1 {
+ let authors = authors[0].name.to_string();
+ authors
} else {
- if biblio.get(&citekey).unwrap().editors().is_ok() {
- let editors = biblio.get(&citekey).unwrap().editors().unwrap();
- if editors.len() > 1 {
- let editors = format!("{} (ed.) et al.", editors[0].0[0].name);
- editors
- } else if editors.len() == 1 {
- let editors = format!("{} (ed.)", editors[0].0[0].name);
- editors
- } else {
- let editors_authors = format!("empty");
- editors_authors
- }
+ let editors_authors = format!("empty");
+ editors_authors
+ }
+ } else {
+ if !biblio.get(&citekey).unwrap().editors().unwrap().is_empty() {
+ let editors = biblio.get(&citekey).unwrap().editors().unwrap();
+ if editors[0].0.len() > 1 {
+ // let editors = format!("{} (ed.) et al.", editors[0].0[0].name);
+ let mut editors = editors[0].0.iter().map(|e| &e.name).join(", ");
+ editors.push_str(" (ed.)");
+ editors
+ } else if editors[0].0.len() == 1 {
+ let editors = format!("{} (ed.)", editors[0].0[0].name);
+ editors
} else {
let editors_authors = format!("empty");
editors_authors
}
+ } else {
+ let editors_authors = format!("empty");
+ editors_authors
}
- };
- authors
+ }
}
pub fn get_title(citekey: &str, biblio: &Bibliography) -> String {
diff --git a/src/backend/search.rs b/src/backend/search.rs
index f9062c8..65d97d7 100644
--- a/src/backend/search.rs
+++ b/src/backend/search.rs
@@ -114,17 +114,18 @@ mod tests {
#[test]
fn test_vector_join() {
- let bibvec: EntryTableItem = EntryTableItem::new(
- "Author",
- "Title",
- "1999",
- "article",
- "hello, bye",
- "author_1999",
- "An abstract with multiple sentences. Here is the second",
- "https://www.bibiman.org",
- "/home/file/path.pdf",
- );
+ let bibvec: EntryTableItem = EntryTableItem {
+ authors: "Author".to_string(),
+ short_author: "".to_string(),
+ title: "Title".to_string(),
+ year: "1999".to_string(),
+ pubtype: "article".to_string(),
+ keywords: "hello, bye".to_string(),
+ citekey: "author_1999".to_string(),
+ abstract_text: "An abstract with multiple sentences. Here is the second".to_string(),
+ doi_url: "https://www.bibiman.org".to_string(),
+ filepath: "/home/file/path.pdf".to_string(),
+ };
let joined_vec = BibiSearch::convert_to_string(&bibvec);
diff --git a/src/frontend/app.rs b/src/frontend/app.rs
index 8d6454d..822c6f0 100644
--- a/src/frontend/app.rs
+++ b/src/frontend/app.rs
@@ -53,8 +53,6 @@ pub struct App {
pub main_bibfile: PathBuf,
// main bibliography
pub main_biblio: BibiMain,
- // bibliographic data
- pub biblio_data: BibiData,
// search struct:
pub search_struct: BibiSearch,
// tag list
@@ -76,16 +74,14 @@ impl App {
let running = true;
let main_bibfile = args.bibfilearg;
let main_biblio = BibiMain::new(main_bibfile.clone());
- let biblio_data = BibiData::new(&main_biblio.bibliography, &main_biblio.citekeys);
let tag_list = TagList::new(main_biblio.keyword_list.clone());
let search_struct = BibiSearch::default();
- let entry_table = EntryTable::from_iter(biblio_data.entry_list.bibentries.clone());
+ let entry_table = EntryTable::new(main_biblio.entry_list.clone());
let current_area = CurrentArea::EntryArea;
Ok(Self {
running,
main_bibfile,
main_biblio,
- biblio_data,
tag_list,
search_struct,
entry_table,
@@ -129,11 +125,9 @@ impl App {
pub fn update_lists(&mut self) {
self.main_biblio = BibiMain::new(self.main_bibfile.clone());
- self.biblio_data =
- BibiData::new(&self.main_biblio.bibliography, &self.main_biblio.citekeys);
// self.tag_list = TagList::from_iter(self.main_biblio.keyword_list.clone());
self.tag_list = TagList::new(self.main_biblio.keyword_list.clone());
- self.entry_table = EntryTable::from_iter(self.biblio_data.entry_list.bibentries.clone());
+ self.entry_table = EntryTable::new(self.main_biblio.entry_list.clone());
}
// Toggle moveable list between entries and tags
@@ -157,7 +151,7 @@ impl App {
}
pub fn reset_current_list(&mut self) {
- self.entry_table = EntryTable::from_iter(self.biblio_data.entry_list.bibentries.clone());
+ self.entry_table = EntryTable::new(self.main_biblio.entry_list.clone());
self.tag_list = TagList::new(self.main_biblio.keyword_list.clone());
if let CurrentArea::TagArea = self.current_area {
self.tag_list.tag_list_state.select(Some(0))
diff --git a/src/frontend/entries.rs b/src/frontend/entries.rs
index 98604f9..6c227df 100644
--- a/src/frontend/entries.rs
+++ b/src/frontend/entries.rs
@@ -17,11 +17,10 @@
use super::app::App;
use super::tui::Tui;
-use crate::backend::search::BibiSearch;
+use crate::backend::{bib::BibiData, search::BibiSearch};
use color_eyre::eyre::{Context, Ok, Result};
use core::panic;
use editor_command::EditorBuilder;
-use itertools::Itertools;
use ratatui::widgets::{ScrollbarState, TableState};
use std::process::{Command, Stdio};
@@ -30,44 +29,92 @@ use std::process::{Command, Stdio};
pub struct EntryTable {
pub entry_table_items: Vec<EntryTableItem>,
pub entry_table_at_search_start: Vec<EntryTableItem>,
+ pub entry_table_reversed_sort: bool,
pub entry_table_state: TableState,
pub entry_scroll_state: ScrollbarState,
pub entry_info_scroll: u16,
pub entry_info_scroll_state: ScrollbarState,
}
-impl FromIterator<Vec<String>> for EntryTable {
- fn from_iter<T: IntoIterator<Item = Vec<String>>>(iter: T) -> Self {
- let entry_table_items: Vec<EntryTableItem> = iter
- .into_iter()
- .sorted()
- // 0: authors, 1: title, 2: date, 3: pubtype, 4: keywords, 5: citekey
- // 6: abstract, 7: doi/url, 8: pdf filepath
- // See backend/bib.rs BibiEntry impl
- .map(|i| {
- EntryTableItem::new(
- &i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7], &i[8],
- )
- })
- .collect();
+impl EntryTable {
+ pub fn new(entry_list: Vec<BibiData>) -> Self {
+ let entry_table_items = Self::set_entry_table(entry_list);
let entry_table_state = TableState::default().with_selected(0);
let entry_scroll_state = ScrollbarState::new(entry_table_items.len());
let entry_info_scroll_state = ScrollbarState::default();
Self {
entry_table_items,
entry_table_at_search_start: Vec::new(),
+ entry_table_reversed_sort: false,
entry_table_state,
entry_scroll_state,
entry_info_scroll: 0,
entry_info_scroll_state,
}
}
+
+ pub fn set_entry_table(entry_list: Vec<BibiData>) -> Vec<EntryTableItem> {
+ let mut entry_table: Vec<EntryTableItem> = entry_list
+ .into_iter()
+ .map(|e| EntryTableItem {
+ authors: e.authors,
+ short_author: String::new(),
+ title: e.title,
+ year: e.year,
+ pubtype: e.pubtype,
+ keywords: e.keywords,
+ citekey: e.citekey,
+ abstract_text: e.abstract_text,
+ doi_url: e.doi_url,
+ filepath: e.filepath,
+ })
+ .collect();
+
+ entry_table.sort_by(|a, b| a.authors.to_lowercase().cmp(&b.authors.to_lowercase()));
+ entry_table
+ }
+
+ // Sort entry table by specific column.
+ // Toggle sorting by hitting same key again
+ pub fn sort_entry_table(&mut self, sorting: &str, toggle: bool) {
+ if toggle {
+ self.entry_table_reversed_sort = !self.entry_table_reversed_sort;
+ }
+ if self.entry_table_reversed_sort {
+ match sorting {
+ "author" => self
+ .entry_table_items
+ .sort_by(|a, b| b.authors.to_lowercase().cmp(&a.authors.to_lowercase())),
+ "title" => self
+ .entry_table_items
+ .sort_by(|a, b| b.title.to_lowercase().cmp(&a.title.to_lowercase())),
+ "year" => self
+ .entry_table_items
+ .sort_by(|a, b| b.year.to_lowercase().cmp(&a.year.to_lowercase())),
+ _ => {}
+ }
+ } else if !self.entry_table_reversed_sort {
+ match sorting {
+ "author" => self
+ .entry_table_items
+ .sort_by(|a, b| a.authors.to_lowercase().cmp(&b.authors.to_lowercase())),
+ "title" => self
+ .entry_table_items
+ .sort_by(|a, b| a.title.to_lowercase().cmp(&b.title.to_lowercase())),
+ "year" => self
+ .entry_table_items
+ .sort_by(|a, b| a.year.to_lowercase().cmp(&b.year.to_lowercase())),
+ _ => {}
+ }
+ }
+ }
}
// Define contents of each entry table row
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct EntryTableItem {
pub authors: String,
+ pub short_author: String,
pub title: String,
pub year: String,
pub pubtype: String,
@@ -79,34 +126,34 @@ pub struct EntryTableItem {
}
impl EntryTableItem {
- pub fn new(
- authors: &str,
- title: &str,
- year: &str,
- pubtype: &str,
- keywords: &str,
- citekey: &str,
- abstract_text: &str,
- doi_url: &str,
- filepath: &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(),
- abstract_text: abstract_text.to_string(),
- doi_url: doi_url.to_string(),
- filepath: filepath.to_string(),
- }
- }
-
// This functions decides which fields are rendered in the entry table
// Fields which should be usable but not visible can be left out
- pub fn ref_vec(&self) -> Vec<&String> {
- vec![&self.authors, &self.title, &self.year, &self.pubtype]
+ pub fn ref_vec(&mut self) -> Vec<&str> {
+ self.short_author = match self.authors.split_once(",") {
+ Some((first, _rest)) => {
+ if self.authors.contains("(ed.)") {
+ let first_author = format!("{} et al. (ed.)", first);
+ first_author
+ } else {
+ let first_author = format!("{} et al.", first);
+ first_author
+ }
+ }
+ None => String::from(""),
+ };
+
+ vec![
+ {
+ if self.short_author.is_empty() {
+ &self.authors
+ } else {
+ &self.short_author
+ }
+ },
+ &self.title,
+ &self.year,
+ &self.pubtype,
+ ]
}
pub fn authors(&self) -> &str {
@@ -128,6 +175,14 @@ impl EntryTableItem {
pub fn citekey(&self) -> &str {
&self.citekey
}
+
+ pub fn doi_url(&self) -> &str {
+ &self.doi_url
+ }
+
+ pub fn filepath(&self) -> &str {
+ &self.filepath
+ }
}
impl App {
@@ -256,6 +311,13 @@ impl App {
let filtered_list =
BibiSearch::search_entry_list(&mut self.search_struct.search_string, orig_list.clone());
self.entry_table.entry_table_items = filtered_list;
+ if self.entry_table.entry_table_reversed_sort {
+ self.entry_table.sort_entry_table("author", false);
+ }
+ self.entry_table.entry_scroll_state = ScrollbarState::content_length(
+ self.entry_table.entry_scroll_state,
+ self.entry_table.entry_table_items.len(),
+ );
}
// Open file connected with entry through 'file' or 'pdf' field
@@ -326,6 +388,8 @@ impl App {
#[cfg(test)]
mod tests {
+ use super::EntryTableItem;
+
#[test]
fn check_os() {
let os = std::env::consts::OS;
@@ -336,4 +400,46 @@ mod tests {
std::env::consts::OS
)
}
+
+ #[test]
+ fn shorten_authors() {
+ let mut entry: EntryTableItem = EntryTableItem {
+ authors: "Miller, Schmitz, Bernard".to_string(),
+ short_author: "".to_string(),
+ title: "A title".to_string(),
+ year: "2000".to_string(),
+ pubtype: "article".to_string(),
+ keywords: "key1, key2".to_string(),
+ citekey: "miller_2000".to_string(),
+ abstract_text: "An abstract".to_string(),
+ doi_url: "www.text.org".to_string(),
+ filepath: "/home/test".to_string(),
+ };
+
+ let entry_vec = EntryTableItem::ref_vec(&mut entry);
+
+ let mut entry_editors: EntryTableItem = EntryTableItem {
+ authors: "Miller, Schmitz, Bernard (ed.)".to_string(),
+ short_author: "".to_string(),
+ title: "A title".to_string(),
+ year: "2000".to_string(),
+ pubtype: "article".to_string(),
+ keywords: "key1, key2".to_string(),
+ citekey: "miller_2000".to_string(),
+ abstract_text: "An abstract".to_string(),
+ doi_url: "www.text.org".to_string(),
+ filepath: "/home/test".to_string(),
+ };
+
+ let entry_vec_editors = EntryTableItem::ref_vec(&mut entry_editors);
+
+ assert_eq!(
+ entry_vec,
+ vec!["Miller et al.", "A title", "2000", "article"]
+ );
+ assert_eq!(
+ entry_vec_editors,
+ vec!["Miller et al. (ed.)", "A title", "2000", "article"]
+ )
+ }
}
diff --git a/src/frontend/handler.rs b/src/frontend/handler.rs
index c2cacf5..ec1647e 100644
--- a/src/frontend/handler.rs
+++ b/src/frontend/handler.rs
@@ -121,6 +121,9 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App, tui: &mut Tui) -> R
KeyCode::Char('G') | KeyCode::End => {
app.select_last_entry();
}
+ KeyCode::Char('s') => {
+ app.entry_table.sort_entry_table("author", true);
+ }
KeyCode::Char('y') => {
App::yank_text(&app.get_selected_citekey());
}
diff --git a/src/frontend/keywords.rs b/src/frontend/keywords.rs
index 9432752..ba74b02 100644
--- a/src/frontend/keywords.rs
+++ b/src/frontend/keywords.rs
@@ -42,23 +42,6 @@ impl TagListItem {
}
}
-// impl FromIterator<String> for TagList {
-// fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
-// let tag_list_items: Vec<TagListItem> = iter
-// .into_iter()
-// .map(|info| TagListItem::new(&info))
-// .collect();
-// let tag_list_state = ListState::default(); // for preselection: .with_selected(Some(0));
-// let tag_scroll_state = ScrollbarState::new(tag_list_items.len());
-// Self {
-// tag_list_items,
-// tag_list_state,
-// tag_scroll_state,
-// selected_keyword: String::new(),
-// }
-// }
-// }
-
impl TagList {
pub fn new(keyword_list: Vec<String>) -> Self {
let tag_list_items = keyword_list;
@@ -118,7 +101,11 @@ impl App {
let filtered_list =
BibiSearch::search_tag_list(&self.search_struct.search_string, orig_list.clone());
self.tag_list.tag_list_items = filtered_list;
- // self.tag_list = TagList::from_iter(filtered_list)
+ // Update scrollbar length after filtering list
+ self.tag_list.tag_scroll_state = ScrollbarState::content_length(
+ self.tag_list.tag_scroll_state,
+ self.tag_list.tag_list_items.len(),
+ );
}
pub fn filter_tags_by_entries(&mut self) {
@@ -143,6 +130,10 @@ impl App {
self.search_struct.filtered_tag_list = filtered_keywords.clone();
self.tag_list.tag_list_items = filtered_keywords;
+ self.tag_list.tag_scroll_state = ScrollbarState::content_length(
+ self.tag_list.tag_scroll_state,
+ self.tag_list.tag_list_items.len(),
+ );
}
// Filter the entry list by tags when hitting enter
@@ -154,6 +145,11 @@ impl App {
let filtered_list = BibiSearch::filter_entries_by_tag(&keyword, &orig_list);
self.tag_list.selected_keyword = keyword.to_string();
self.entry_table.entry_table_items = filtered_list;
+ // Update scrollbar state with new lenght of itemlist
+ self.entry_table.entry_scroll_state = ScrollbarState::content_length(
+ self.entry_table.entry_scroll_state,
+ self.entry_table.entry_table_items.len(),
+ );
self.filter_tags_by_entries();
self.toggle_area();
self.entry_table.entry_table_state.select(Some(0));
diff --git a/src/frontend/tui.rs b/src/frontend/tui.rs
index 698407d..e3c9c1a 100644
--- a/src/frontend/tui.rs
+++ b/src/frontend/tui.rs
@@ -24,6 +24,8 @@ use crossterm::{
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
};
// use ratatui::backend::{Backend, CrosstermBackend};
+use color_eyre::eyre::{OptionExt, Result};
+use futures::{FutureExt, StreamExt};
use ratatui::backend::CrosstermBackend as Backend;
use std::io::{stdout, Stdout};
use std::panic;
@@ -31,10 +33,6 @@ use std::{
ops::{Deref, DerefMut},
time::Duration,
};
-
-use color_eyre::config::HookBuilder;
-use color_eyre::eyre::{OptionExt, Result};
-use futures::{FutureExt, StreamExt};
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
@@ -51,14 +49,6 @@ pub enum Event {
Resize(u16, u16),
}
-// pub type IO = std::io::{{crossterm_io | title_case}};
-// pub fn io() -> IO {
-// std::io::{{crossterm_io}}()
-// }
-/// Representation of a terminal user interface.
-///
-/// It is responsible for setting up the terminal,
-/// initializing the interface and handling the draw events.
#[derive(Debug)]
pub struct Tui {
/// Interface to the Terminal.
@@ -73,7 +63,7 @@ pub struct Tui {
}
impl Tui {
- /// Constructs a new instance of [`Tui`].
+ // Constructs a new instance of [`Tui`].
pub fn new() -> Result<Self> {
let terminal = ratatui::Terminal::new(Backend::new(stdout()))?;
let (sender, receiver) = mpsc::unbounded_channel();
@@ -150,26 +140,6 @@ impl Tui {
cancellation_token.cancel();
}
- /// Initializes the terminal interface.
- ///
- /// It enables the raw mode and sets terminal properties.
- // pub fn init(&mut self) -> Result<()> {
- // terminal::enable_raw_mode()?;
- // crossterm::execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?;
-
- // // Define a custom panic hook to reset the terminal properties.
- // // This way, you won't have your terminal messed up if an unexpected error happens.
- // let panic_hook = panic::take_hook();
- // panic::set_hook(Box::new(move |panic| {
- // Self::reset().expect("failed to reset the terminal");
- // panic_hook(panic);
- // }));
-
- // self.terminal.hide_cursor()?;
- // self.terminal.clear()?;
- // Ok(())
- // }
-
pub fn enter(&mut self) -> Result<()> {
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(stdout(), EnterAlternateScreen, cursor::Hide)?;
@@ -216,10 +186,10 @@ impl Tui {
Ok(())
}
- /// [`Draw`] the terminal interface by [`rendering`] the widgets.
- ///
- /// [`Draw`]: ratatui::Terminal::draw
- /// [`rendering`]: crate::ui::render
+ // [`Draw`] the terminal interface by [`rendering`] the widgets.
+ //
+ // [`Draw`]: ratatui::Terminal::draw
+ // [`rendering`]: crate::ui::render
pub fn draw(&mut self, app: &mut App) -> Result<()> {
// self.terminal.draw(|frame| ui::render(app, frame))?;
self.terminal
@@ -230,33 +200,6 @@ impl Tui {
pub async fn next(&mut self) -> Result<Event> {
self.receiver.recv().await.ok_or_eyre("This is an IO error")
}
-
- pub fn init_error_hooks() -> Result<()> {
- let (panic, error) = HookBuilder::default().into_hooks();
- let panic = panic.into_panic_hook();
- let error = error.into_eyre_hook();
- color_eyre::eyre::set_hook(Box::new(move |e| {
- let _ = crossterm::execute!(
- stdout(),
- DisableMouseCapture,
- LeaveAlternateScreen,
- cursor::Show
- );
- let _ = crossterm::terminal::disable_raw_mode();
- error(e)
- }))?;
- std::panic::set_hook(Box::new(move |info| {
- let _ = crossterm::execute!(
- stdout(),
- DisableMouseCapture,
- LeaveAlternateScreen,
- cursor::Show
- );
- let _ = crossterm::terminal::disable_raw_mode();
- panic(info)
- }));
- Ok(())
- }
}
impl Deref for Tui {
diff --git a/src/frontend/ui.rs b/src/frontend/ui.rs
index 08cebcb..4ea275d 100644
--- a/src/frontend/ui.rs
+++ b/src/frontend/ui.rs
@@ -28,7 +28,7 @@ use ratatui::{
},
};
-use crate::{frontend::app::App, frontend::keywords::TagListItem};
+use crate::frontend::{app::App, keywords::TagListItem};
use super::app::{CurrentArea, FormerArea};
@@ -47,6 +47,8 @@ const SELECTED_STYLE: Style = Style::new()
.add_modifier(Modifier::REVERSED);
const TEXT_FG_COLOR: Color = Color::Indexed(252);
const TEXT_UNSELECTED_FG_COLOR: Color = Color::Indexed(245);
+const SORTED_ENTRIES: &str = "▼";
+const SORTED_ENTRIES_REVERSED: &str = "▲";
const SCROLLBAR_UPPER_CORNER: Option<&str> = Some("┓");
const SCROLLBAR_LOWER_CORNER: Option<&str> = Some("┛");
@@ -259,15 +261,22 @@ impl App {
let header_style = Style::default().bold().fg(TEXT_FG_COLOR);
- let header = [
- "Authors".underlined(),
- "Title".underlined(),
- "Year".underlined(),
- "Type".underlined(),
- ]
- .into_iter()
- .map(Cell::from)
- .collect::<Row>()
+ let header = Row::new(vec![
+ Cell::from(Line::from(vec![
+ Span::raw("Author").underlined(),
+ Span::raw(format!(
+ " {}",
+ if self.entry_table.entry_table_reversed_sort {
+ SORTED_ENTRIES_REVERSED
+ } else {
+ SORTED_ENTRIES
+ }
+ )),
+ ])),
+ Cell::from("Title".to_string().underlined()),
+ Cell::from("Year".to_string().underlined()),
+ Cell::from("Type".to_string().underlined()),
+ ])
.style(header_style)
.height(1);
@@ -275,7 +284,7 @@ impl App {
let rows = self
.entry_table
.entry_table_items
- .iter()
+ .iter_mut()
.enumerate()
.map(|(_i, data)| {
let item = data.ref_vec();
@@ -340,15 +349,16 @@ impl App {
let mut lines = vec![];
lines.push(Line::from(vec![
Span::styled("Authors: ", style_value),
- Span::styled(cur_entry.authors.clone(), Style::new().green()),
+ // Span::styled(cur_entry.authors.clone(), Style::new().green()),
+ Span::styled(cur_entry.authors(), Style::new().green()),
]));
lines.push(Line::from(vec![
Span::styled("Title: ", style_value),
- Span::styled(cur_entry.title.clone(), Style::new().magenta()),
+ Span::styled(cur_entry.title(), Style::new().magenta()),
]));
lines.push(Line::from(vec![
Span::styled("Year: ", style_value),
- Span::styled(cur_entry.year.clone(), Style::new().light_magenta()),
+ Span::styled(cur_entry.year(), Style::new().light_magenta()),
]));
if !cur_entry.doi_url.is_empty() || !cur_entry.filepath.is_empty() {
lines.push(Line::raw(""));
@@ -357,7 +367,7 @@ impl App {
lines.push(Line::from(vec![
Span::styled("DOI/URL: ", style_value_sec),
Span::styled(
- cur_entry.doi_url.clone(),
+ cur_entry.doi_url(),
Style::default().fg(TEXT_FG_COLOR).underlined(),
),
]));
@@ -365,10 +375,7 @@ impl App {
if !cur_entry.filepath.is_empty() {
lines.push(Line::from(vec![
Span::styled("File: ", style_value_sec),
- Span::styled(
- cur_entry.filepath.clone(),
- Style::default().fg(TEXT_FG_COLOR),
- ),
+ Span::styled(cur_entry.filepath(), Style::default().fg(TEXT_FG_COLOR)),
]));
}
lines.push(Line::from(""));
diff --git a/src/main.rs b/src/main.rs
index 6ef7ee1..979c4cf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,7 +17,6 @@
use backend::cliargs::{self, CLIArgs};
use color_eyre::eyre::Result;
-use core::panic;
use errorsetup::init_error_hooks;
use frontend::app::App;
@@ -42,9 +41,9 @@ async fn main() -> Result<()> {
std::process::exit(0);
}
- if !parsed_args.bibfilearg.is_file() {
- panic!("No \'.bib\' file passed, aborting")
- }
+ // if !parsed_args.bibfilearg.is_file() {
+ // panic!("No \'.bib\' file passed, aborting")
+ // }
init_error_hooks()?;