From 40629e0c2dc62bfb1786cb1c18bc68ed4c23e9ac Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Sun, 29 Jun 2025 14:20:10 +0200
Subject: working note-opening action. needs refinement: errors and ui
---
src/bibiman.rs | 15 +++++++++++++++
1 file changed, 15 insertions(+)
(limited to 'src/bibiman.rs')
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 21601e3..96a733c 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -692,6 +692,21 @@ impl Bibiman {
None,
)?;
}
+ }
+ if self.popup_area.popup_list[popup_idx].0.contains("Note") {
+ let file = expand_home(&PathBuf::from(popup_entry.clone()));
+ // let object: OsString = popup_entry.into();
+ if file.is_file() {
+ app::open_connected_file(cfg, &file.into_os_string())?;
+ self.close_popup();
+ } else {
+ self.open_popup(
+ PopupKind::MessageError,
+ Some("No valid file path: "),
+ Some(file.to_str().unwrap()),
+ None,
+ )?;
+ }
} else {
eprintln!("Unable to find ressource to open");
};
--
cgit v1.2.3
From ae1667410b0a812fff8d464251548f23f88ae024 Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Sun, 29 Jun 2025 20:31:41 +0200
Subject: some more tests for notes, need to elaborate function for opening
notes
---
src/app.rs | 22 +++++++++++++---------
src/bibiman.rs | 5 ++---
tests/note-files/aristotle:poetics.txt | 0
tests/note-files/aristotle:rhetoric.md | 0
tests/note-files/bertram.txt | 1 +
tests/note-files/betram.txt | 1 -
tests/test-config.toml | 2 +-
7 files changed, 17 insertions(+), 14 deletions(-)
create mode 100644 tests/note-files/aristotle:poetics.txt
create mode 100644 tests/note-files/aristotle:rhetoric.md
create mode 100644 tests/note-files/bertram.txt
delete mode 100644 tests/note-files/betram.txt
(limited to 'src/bibiman.rs')
diff --git a/src/app.rs b/src/app.rs
index 5f2d16b..f015494 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -410,15 +410,6 @@ impl App {
pub fn open_connected_file(cfg: &BibiConfig, file: &OsStr) -> Result<()> {
// Build command to execute pdf-reader. 'xdg-open' is Linux standard
let cmd = &cfg.general.pdf_opener;
- // If necessary, replace ~ with /home dir
- // let file = if cfg.general.file_prefix.is_some() {
- // cfg.general.file_prefix.clone().unwrap().join(file)
- // } else {
- // PathBuf::from(file)
- // };
- // let file = PathBuf::from(file);
-
- // let file = expand_home(&file).into_os_string();
// Pass filepath as argument, pipe stdout and stderr to /dev/null
// to keep the TUI clean (where is it piped on Windows???)
@@ -432,6 +423,19 @@ pub fn open_connected_file(cfg: &BibiConfig, file: &OsStr) -> Result<()> {
Ok(())
}
+pub fn open_connected_note(cfg: &BibiConfig, file: &OsStr) -> Result<()> {
+ // let cmd = cfg.general.editor.as_ref().unwrap();
+
+ let _ = Command::new("xdg-open")
+ .arg(file)
+ .stdout(Stdio::null())
+ .stderr(Stdio::null())
+ .spawn()
+ .wrap_err("Opening file not possible");
+
+ Ok(())
+}
+
pub fn open_connected_link(cfg: &BibiConfig, link: &str) -> Result<()> {
// Build command to execute pdf-reader. 'xdg-open' is Linux standard
let cmd = &cfg.general.url_opener;
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 96a733c..1f19b24 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -692,12 +692,11 @@ impl Bibiman {
None,
)?;
}
- }
- if self.popup_area.popup_list[popup_idx].0.contains("Note") {
+ } else if self.popup_area.popup_list[popup_idx].0.contains("Note") {
let file = expand_home(&PathBuf::from(popup_entry.clone()));
// let object: OsString = popup_entry.into();
if file.is_file() {
- app::open_connected_file(cfg, &file.into_os_string())?;
+ app::open_connected_note(cfg, &file.into_os_string())?;
self.close_popup();
} else {
self.open_popup(
diff --git a/tests/note-files/aristotle:poetics.txt b/tests/note-files/aristotle:poetics.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/note-files/aristotle:rhetoric.md b/tests/note-files/aristotle:rhetoric.md
new file mode 100644
index 0000000..e69de29
diff --git a/tests/note-files/bertram.txt b/tests/note-files/bertram.txt
new file mode 100644
index 0000000..54d31e5
--- /dev/null
+++ b/tests/note-files/bertram.txt
@@ -0,0 +1 @@
+A simple text file with notes about this Betram dude
diff --git a/tests/note-files/betram.txt b/tests/note-files/betram.txt
deleted file mode 100644
index 54d31e5..0000000
--- a/tests/note-files/betram.txt
+++ /dev/null
@@ -1 +0,0 @@
-A simple text file with notes about this Betram dude
diff --git a/tests/test-config.toml b/tests/test-config.toml
index fd39b29..99d1d00 100644
--- a/tests/test-config.toml
+++ b/tests/test-config.toml
@@ -4,7 +4,7 @@
bibfiles = [ "tests/biblatex-test.bib" ]
## Default editor to use when editing files. Arguments are possible
-# editor = "vim" # with args: "vim -y"
+editor = "vim" # with args: "vim -y"
## Default app to open PDFs/Epubs
# pdf_opener = "xdg-open"
--
cgit v1.2.3
From b10615ade6bb2710cf6716f05cc496cb082d24ad Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Mon, 30 Jun 2025 10:43:38 +0200
Subject: opening notes in set editor
---
src/app.rs | 16 +------
src/bibiman.rs | 47 +++++++++++++++++++--
tests/biblatex-test.bib | 12 +++---
tests/note-files/aristotle:poetics.txt | 0
tests/note-files/aristotle:rhetoric.md | 0
tests/note-files/aristotle_poetics.txt | 1 +
tests/note-files/aristotle_rhetoric.md | 0
.../annotated-pdfs/ARIStotle:rheTORIC.PDF | Bin 25294 -> 0 bytes
.../annotated-pdfs/ARIStotle_rheTORIC.PDF | Bin 0 -> 25294 bytes
tests/pdf-files/aristotle:physics.pdf | Bin 25294 -> 0 bytes
tests/pdf-files/aristotle:rhetoric.pdf | Bin 25294 -> 0 bytes
tests/pdf-files/aristotle:rhetoric.txt | 0
tests/pdf-files/aristotle_physics.pdf | Bin 0 -> 25294 bytes
tests/pdf-files/aristotle_rhetoric.pdf | Bin 0 -> 25294 bytes
tests/pdf-files/aristotle_rhetoric.txt | 0
15 files changed, 53 insertions(+), 23 deletions(-)
delete mode 100644 tests/note-files/aristotle:poetics.txt
delete mode 100644 tests/note-files/aristotle:rhetoric.md
create mode 100644 tests/note-files/aristotle_poetics.txt
create mode 100644 tests/note-files/aristotle_rhetoric.md
delete mode 100644 tests/pdf-files/annotated-pdfs/ARIStotle:rheTORIC.PDF
create mode 100644 tests/pdf-files/annotated-pdfs/ARIStotle_rheTORIC.PDF
delete mode 100644 tests/pdf-files/aristotle:physics.pdf
delete mode 100644 tests/pdf-files/aristotle:rhetoric.pdf
delete mode 100644 tests/pdf-files/aristotle:rhetoric.txt
create mode 100644 tests/pdf-files/aristotle_physics.pdf
create mode 100644 tests/pdf-files/aristotle_rhetoric.pdf
create mode 100644 tests/pdf-files/aristotle_rhetoric.txt
(limited to 'src/bibiman.rs')
diff --git a/src/app.rs b/src/app.rs
index f015494..496896a 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -18,6 +18,7 @@
use crate::bibiman::CurrentArea;
use crate::config::BibiConfig;
use color_eyre::eyre::{Context, Ok, Result};
+use editor_command::EditorBuilder;
// use super::Event;
use crate::cliargs::CLIArgs;
use crate::tui::commands::InputCmdAction;
@@ -279,7 +280,7 @@ impl App {
if let Some(PopupKind::Help) = self.bibiman.popup_area.popup_kind {
self.bibiman.close_popup();
} else if let Some(PopupKind::OpenRes) = self.bibiman.popup_area.popup_kind {
- self.bibiman.open_connected_res(cfg)?;
+ self.bibiman.open_connected_res(cfg, tui)?;
} else if let Some(PopupKind::AppendToFile) = self.bibiman.popup_area.popup_kind
{
self.bibiman.append_entry_to_file(cfg)?
@@ -423,19 +424,6 @@ pub fn open_connected_file(cfg: &BibiConfig, file: &OsStr) -> Result<()> {
Ok(())
}
-pub fn open_connected_note(cfg: &BibiConfig, file: &OsStr) -> Result<()> {
- // let cmd = cfg.general.editor.as_ref().unwrap();
-
- let _ = Command::new("xdg-open")
- .arg(file)
- .stdout(Stdio::null())
- .stderr(Stdio::null())
- .spawn()
- .wrap_err("Opening file not possible");
-
- Ok(())
-}
-
pub fn open_connected_link(cfg: &BibiConfig, link: &str) -> Result<()> {
// Build command to execute pdf-reader. 'xdg-open' is Linux standard
let cmd = &cfg.general.url_opener;
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 1f19b24..e1e97ed 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -30,11 +30,12 @@ use crossterm::event::KeyCode;
use editor_command::EditorBuilder;
use ratatui::widgets::ScrollbarState;
use regex::Regex;
+use std::ffi::OsStr;
use std::fs::{self, read_to_string};
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::PathBuf;
-use std::process::Command;
+use std::process::{Command, Stdio};
use std::result::Result::Ok;
use tui_input::Input;
@@ -512,6 +513,46 @@ impl Bibiman {
Ok(())
}
+ pub fn open_connected_note(
+ &mut self,
+ cfg: &BibiConfig,
+ tui: &mut Tui,
+ file: &OsStr,
+ ) -> Result<()> {
+ // get filecontent and citekey for calculating line number
+ let citekey: &str = &self.entry_table.entry_table_items
+ [self.entry_table.entry_table_state.selected().unwrap()]
+ .citekey
+ .clone();
+
+ // Exit TUI to enter editor
+ tui.exit()?;
+ // Use VISUAL or EDITOR. Set "vi" as last fallback
+ let mut cmd: Command = EditorBuilder::new()
+ .source(cfg.general.editor.as_ref())
+ .environment()
+ .source(Some("vi"))
+ .build()
+ .unwrap();
+ // Prepare arguments to open file at specific line
+ let status = cmd.arg(file).status()?;
+ if !status.success() {
+ eprintln!("Spawning editor failed with status {}", status);
+ }
+
+ // Enter TUI again
+ tui.enter()?;
+ tui.terminal.clear()?;
+
+ // Update the database and the lists to show changes
+ // Self::update_lists(self, cfg);
+
+ // Select entry which was selected before entering editor
+ self.select_entry_by_citekey(citekey);
+
+ Ok(())
+ }
+
pub fn add_entry(&mut self) {
if let CurrentArea::EntryArea = self.current_area {
self.former_area = Some(FormerArea::EntryArea);
@@ -662,7 +703,7 @@ impl Bibiman {
Ok(())
}
- pub fn open_connected_res(&mut self, cfg: &BibiConfig) -> Result<()> {
+ pub fn open_connected_res(&mut self, cfg: &BibiConfig, tui: &mut Tui) -> Result<()> {
// Index of selected entry
let entry_idx = self.entry_table.entry_table_state.selected().unwrap();
@@ -696,7 +737,7 @@ impl Bibiman {
let file = expand_home(&PathBuf::from(popup_entry.clone()));
// let object: OsString = popup_entry.into();
if file.is_file() {
- app::open_connected_note(cfg, &file.into_os_string())?;
+ self.open_connected_note(cfg, tui, &file.into_os_string())?;
self.close_popup();
} else {
self.open_popup(
diff --git a/tests/biblatex-test.bib b/tests/biblatex-test.bib
index 4071dcb..692375e 100644
--- a/tests/biblatex-test.bib
+++ b/tests/biblatex-test.bib
@@ -9,7 +9,7 @@
model of particle physics.},
}
-@collection{matuz:doody,
+@collection{matuz_doody,
title = {Contemporary Literary Criticism},
year = {1990},
location = {Detroit},
@@ -54,7 +54,7 @@
field},
}
-@book{aristotle:anima,
+@book{aristotle_anima,
title = {De Anima},
author = {Aristotle},
location = {Cambridge},
@@ -68,7 +68,7 @@
editor}},
}
-@book{aristotle:physics,
+@book{aristotle_physics,
title = {Physics},
shorttitle = {Physics},
author = {Aristotle},
@@ -84,7 +84,7 @@
annotation = {A \texttt{book} entry with a \texttt{translator} field},
}
-@book{aristotle:poetics,
+@book{aristotle_poetics,
title = {Poetics},
shorttitle = {Poetics},
author = {Aristotle},
@@ -100,7 +100,7 @@
editor} as well as a \texttt{series} field},
}
-@mvbook{aristotle:rhetoric,
+@mvbook{aristotle_rhetoric,
title = {The Rhetoric of {Aristotle} with a commentary by the late {Edward
Meredith Cope}},
shorttitle = {Rhetoric},
@@ -441,7 +441,7 @@
@string{pup = {Princeton University Press}}
-@incollection{westfahl:space,
+@incollection{westfahl_space,
title = {The True Frontier},
author = {Westfahl, Gary},
pages = {55--65},
diff --git a/tests/note-files/aristotle:poetics.txt b/tests/note-files/aristotle:poetics.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/note-files/aristotle:rhetoric.md b/tests/note-files/aristotle:rhetoric.md
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/note-files/aristotle_poetics.txt b/tests/note-files/aristotle_poetics.txt
new file mode 100644
index 0000000..a156c76
--- /dev/null
+++ b/tests/note-files/aristotle_poetics.txt
@@ -0,0 +1 @@
+Here some very boring information regarding Aristotle
diff --git a/tests/note-files/aristotle_rhetoric.md b/tests/note-files/aristotle_rhetoric.md
new file mode 100644
index 0000000..e69de29
diff --git a/tests/pdf-files/annotated-pdfs/ARIStotle:rheTORIC.PDF b/tests/pdf-files/annotated-pdfs/ARIStotle:rheTORIC.PDF
deleted file mode 100644
index 6aaba88..0000000
Binary files a/tests/pdf-files/annotated-pdfs/ARIStotle:rheTORIC.PDF and /dev/null differ
diff --git a/tests/pdf-files/annotated-pdfs/ARIStotle_rheTORIC.PDF b/tests/pdf-files/annotated-pdfs/ARIStotle_rheTORIC.PDF
new file mode 100644
index 0000000..6aaba88
Binary files /dev/null and b/tests/pdf-files/annotated-pdfs/ARIStotle_rheTORIC.PDF differ
diff --git a/tests/pdf-files/aristotle:physics.pdf b/tests/pdf-files/aristotle:physics.pdf
deleted file mode 100644
index 6aaba88..0000000
Binary files a/tests/pdf-files/aristotle:physics.pdf and /dev/null differ
diff --git a/tests/pdf-files/aristotle:rhetoric.pdf b/tests/pdf-files/aristotle:rhetoric.pdf
deleted file mode 100644
index 6aaba88..0000000
Binary files a/tests/pdf-files/aristotle:rhetoric.pdf and /dev/null differ
diff --git a/tests/pdf-files/aristotle:rhetoric.txt b/tests/pdf-files/aristotle:rhetoric.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/pdf-files/aristotle_physics.pdf b/tests/pdf-files/aristotle_physics.pdf
new file mode 100644
index 0000000..6aaba88
Binary files /dev/null and b/tests/pdf-files/aristotle_physics.pdf differ
diff --git a/tests/pdf-files/aristotle_rhetoric.pdf b/tests/pdf-files/aristotle_rhetoric.pdf
new file mode 100644
index 0000000..6aaba88
Binary files /dev/null and b/tests/pdf-files/aristotle_rhetoric.pdf differ
diff --git a/tests/pdf-files/aristotle_rhetoric.txt b/tests/pdf-files/aristotle_rhetoric.txt
new file mode 100644
index 0000000..e69de29
--
cgit v1.2.3
From 3a40bbb367a79dc3660c12aa7f62e3efc378ea22 Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Mon, 30 Jun 2025 16:33:01 +0200
Subject: update README: note feature
---
README.md | 33 +++++++++++++++++++++++++--------
src/app.rs | 2 +-
src/bibiman.rs | 32 ++++++++++++++++++++++++++++----
src/bibiman/entries.rs | 5 +++--
src/config.rs | 4 ++--
src/tui/ui.rs | 39 +++++++++++++++++++--------------------
6 files changed, 78 insertions(+), 37 deletions(-)
(limited to 'src/bibiman.rs')
diff --git a/README.md b/README.md
index 5bc40b3..4f0e505 100644
--- a/README.md
+++ b/README.md
@@ -213,6 +213,16 @@ file_prefix = "/some/path/prefix"
# of the format "citekey.pdf". Other PDF basenames are not accepted.
# Use absolute paths (~ for HOME works). Otherwise, loading might not work.
pdf_path = "/path/to/pdf-folder"
+## Path to folder (with subfolders) containing note files with the basename of
+## the format "citekey.extension". Other basenames are not accepted. The possible
+## extensions can be set through the "note_extensions" array.
+note_path = "path/to/note-files"
+note_extensions = [ "md", "txt" ]
+
+## Symbols/chars to show if not has specific attachement
+ file_symbol = " "
+ link_symbol = " "
+ note_symbol = ""
```
`bibfiles`
@@ -246,13 +256,14 @@ pdf_path = "/path/to/pdf-folder"
created through the `pdf_path` variable. Thus, it is safe to mix both
approaches if wanted!
-`pdf_path`
+`pdf_path` and `note_path`
-: The `pdf_path` is used as path wich is recursivley searched for files which
- basename consists of the an entrys `citekey` plus a `.pdf` ending
- (case-insensitive). Every file which matches this pattern for an existing
- `citekey` is associated with the particular entry for the current `bibiman`
- session and can be opened from within.
+: The `pdf_path`/`note_path` is used as path wich is recursivley searched for
+ files which basename consists of the an entrys `citekey` plus a `.pdf` ending
+ or one of the specified note endinfs (case-insensitive). Every file which
+ matches this pattern for an existing `citekey` is associated with the
+ particular entry for the current `bibiman` session and can be opened from
+ within.
### Color Configuration
@@ -272,6 +283,12 @@ warn_color = "124"
bar_bg_color = "234"
popup_bg_color = "234"
selected_row_bg_color = "237"
+note_color = "123"
+file_color = "209"
+link_color = "27"
+author_color = "38"
+title_color = "37"
+year_color = "135"
```
Colors can be set through three different methods:
@@ -309,7 +326,7 @@ These are the current features, the list will be updated:
- [x] **Add Entry via DOI**.
- [x] **Implement config file** for setting some default values like main
bibfile, PDF-opener, or editor
-- [ ] **Open related notes file** for specific entry.
+- [x] **Open related notes file** for specific entry.
- [ ] **Support Hayagriva(`.yaml`)** format as input (_on hold for now_, because
the Hayagriva Yaml style doesn't offer keywords; s. issue in
[Hayagriva repo](https://github.com/typst/hayagriva/issues/240)).
@@ -346,7 +363,7 @@ Use the following keybindings to manage the TUI:
There are some shortcuts to select an item from the opening/yanking popup
without navigating the list:
-- `o-o`|`o-l`: directly opens the first file/link for the selected entry.
+- `o-o`|`o-l`|`o-n`: directly opens the first file|link|note for the selected entry.
- `y-y`: directly yanks the citekey of the selected entry to the clipboard.
## Search
diff --git a/src/app.rs b/src/app.rs
index 496896a..18a97e6 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -83,7 +83,7 @@ impl App {
} else if let Some(PopupKind::YankItem) | Some(PopupKind::OpenRes) =
self.bibiman.popup_area.popup_kind
{
- self.bibiman.fast_selection(cfg, key_event.code)?;
+ self.bibiman.fast_selection(cfg, &mut tui, key_event.code)?;
}
let command = if self.input_mode {
CmdAction::Input(InputCmdAction::parse(key_event, &self.input))
diff --git a/src/bibiman.rs b/src/bibiman.rs
index e1e97ed..9cc9280 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -367,9 +367,9 @@ impl Bibiman {
.entry_table_state
.selected_column()
.unwrap()
- == 3
+ == 4
{
- self.entry_table.entry_table_state.select_first_column();
+ self.entry_table.entry_table_state.select_column(Some(1));
} else {
self.entry_table.entry_table_state.select_next_column();
}
@@ -396,7 +396,7 @@ impl Bibiman {
.entry_table_state
.selected_column()
.unwrap()
- == 0
+ == 1
{
self.entry_table.entry_table_state.select_last_column();
} else {
@@ -788,11 +788,17 @@ impl Bibiman {
///
/// `o` -> opens the first file of the `filepath` `Vec` for the current entry
/// `l` -> opens the link of the current entry
+ /// `n` -> opens the first note
///
/// **Yanking popup**
///
/// `y` -> yanks the citekey for the current entry
- pub fn fast_selection(&mut self, cfg: &BibiConfig, key_code: KeyCode) -> Result<()> {
+ pub fn fast_selection(
+ &mut self,
+ cfg: &BibiConfig,
+ tui: &mut Tui,
+ key_code: KeyCode,
+ ) -> Result<()> {
if let CurrentArea::PopupArea = self.current_area {
let entry_idx = self.entry_table.entry_table_state.selected().unwrap();
match self.popup_area.popup_kind {
@@ -817,6 +823,24 @@ impl Bibiman {
}
}
}
+ KeyCode::Char('n') => {
+ let file = self.entry_table.entry_table_items[entry_idx].notes.clone();
+ if file.is_some() {
+ let file = expand_home(&PathBuf::from(file.unwrap()[0].clone()));
+ // let object: OsString = popup_entry.into();
+ if file.is_file() {
+ self.open_connected_note(cfg, tui, &file.into_os_string())?;
+ self.close_popup();
+ } else {
+ self.open_popup(
+ PopupKind::MessageError,
+ Some("No valid file path: "),
+ Some(file.to_str().unwrap()),
+ None,
+ )?;
+ }
+ }
+ }
KeyCode::Char('l') => {
if self.entry_table.entry_table_items[entry_idx]
.doi_url
diff --git a/src/bibiman/entries.rs b/src/bibiman/entries.rs
index e0c230b..9b536fd 100644
--- a/src/bibiman/entries.rs
+++ b/src/bibiman/entries.rs
@@ -47,8 +47,9 @@ impl EntryTable {
// entry_table
let entry_table_state = TableState::default()
.with_selected(0)
- .with_selected_column(0)
- .with_selected_cell(Some((0, 0)));
+ .with_selected_column(1)
+ // other two values above are ignored, if selected cell isn't fitting
+ .with_selected_cell(Some((0, 1)));
let entry_scroll_state = ScrollbarState::new(entry_table_items.len());
let entry_info_scroll_state = ScrollbarState::default();
Self {
diff --git a/src/config.rs b/src/config.rs
index f67cb9d..278f4b1 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -85,7 +85,7 @@ const DEFAULT_CONFIG: &str = r##"
# selected_row_bg_color = "237"
# note_color = "123"
# file_color = "209"
-# link_color = "27"
+# link_color = "39"
# author_color = "38"
# title_color = "37"
# year_color = "135"
@@ -215,7 +215,7 @@ impl BibiConfig {
selected_row_bg_color: Color::Indexed(237),
note_color: Color::Indexed(123),
file_color: Color::Indexed(209),
- link_color: Color::Indexed(33),
+ link_color: Color::Indexed(39),
author_color: Color::Indexed(38),
title_color: Color::Indexed(37),
year_color: Color::Indexed(135),
diff --git a/src/tui/ui.rs b/src/tui/ui.rs
index ac44d09..3c83935 100644
--- a/src/tui/ui.rs
+++ b/src/tui/ui.rs
@@ -580,7 +580,7 @@ pub fn render_entrytable(app: &mut App, cfg: &BibiConfig, frame: &mut Frame, rec
.bg(cfg.colors.bar_bg_color);
let header = Row::new(vec![
- // Cell::from(Line::from("")).bg(cfg.colors.bar_bg_color),
+ Cell::from(Line::from("")).bg(cfg.colors.bar_bg_color),
Cell::from(
Line::from(vec![{ Span::raw("Author") }, {
if let Some(EntryTableColumn::Authors) =
@@ -704,21 +704,30 @@ pub fn render_entrytable(app: &mut App, cfg: &BibiConfig, frame: &mut Frame, rec
let mut symbol_vec = vec![];
if let Some(f) = &item.symbols[0] {
- symbol_vec.push(Span::styled(f, Style::new().fg(cfg.colors.file_color)));
+ symbol_vec.push(Span::styled(
+ f,
+ Style::new().fg(cfg.colors.file_color).bold(),
+ ));
}
if let Some(l) = &item.symbols[1] {
- symbol_vec.push(Span::styled(l, Style::new().fg(cfg.colors.link_color)));
+ symbol_vec.push(Span::styled(
+ l,
+ Style::new().fg(cfg.colors.link_color).bold(),
+ ));
}
if let Some(n) = &item.symbols[2] {
- symbol_vec.push(Span::styled(n, Style::new().fg(cfg.colors.note_color)))
+ symbol_vec.push(Span::styled(
+ n,
+ Style::new().fg(cfg.colors.note_color).bold(),
+ ))
}
let row = Row::new(vec![
+ Cell::from(Line::from(symbol_vec)),
Cell::from(Line::from(item.authors)),
Cell::from(Line::from(item.title)),
Cell::from(Line::from(item.year)),
Cell::from(Line::from(item.pubtype)),
- Cell::from(Line::from(symbol_vec)),
]);
// let row = item
@@ -738,6 +747,11 @@ pub fn render_entrytable(app: &mut App, cfg: &BibiConfig, frame: &mut Frame, rec
let entry_table = Table::new(
rows,
[
+ Constraint::Length(
+ (cfg.general.file_symbol.chars().count()
+ + cfg.general.link_symbol.chars().count()
+ + cfg.general.note_symbol.chars().count()) as u16,
+ ),
Constraint::Percentage(20),
Constraint::Fill(1),
Constraint::Length(
@@ -750,12 +764,6 @@ pub fn render_entrytable(app: &mut App, cfg: &BibiConfig, frame: &mut Frame, rec
},
),
Constraint::Percentage(10),
- Constraint::Length(
- (cfg.general.file_symbol.chars().count()
- + cfg.general.link_symbol.chars().count()
- + cfg.general.note_symbol.chars().count()
- + 1) as u16,
- ),
],
)
.block(block)
@@ -917,15 +925,6 @@ pub fn render_selected_item(app: &mut App, cfg: &BibiConfig, frame: &mut Frame,
),
]));
}
- // if cur_entry.filepath.is_some() {
- // lines.push(Line::from(vec![
- // Span::styled("File: ", style_value),
- // Span::styled(
- // cur_entry.filepath().to_string_lossy(),
- // Style::new().fg(cfg.colors.main_text_color),
- // ),
- // ]));
- // }
lines.push(Line::from(""));
lines.push(Line::from(vec](https://postimg.cc/ct0W0mK4)
+
+
## Installation
### Crates.io
diff --git a/src/app.rs b/src/app.rs
index 18a97e6..f912614 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -16,15 +16,14 @@
/////
use crate::bibiman::CurrentArea;
-use crate::config::BibiConfig;
-use color_eyre::eyre::{Context, Ok, Result};
-use editor_command::EditorBuilder;
-// use super::Event;
use crate::cliargs::CLIArgs;
+use crate::config::BibiConfig;
use crate::tui::commands::InputCmdAction;
use crate::tui::popup::PopupKind;
use crate::tui::{self, Tui};
use crate::{bibiman::Bibiman, tui::commands::CmdAction};
+use color_eyre::eyre::{Context, Ok, Result};
+use crossterm::event::KeyCode;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::{Command, Stdio};
@@ -84,6 +83,12 @@ impl App {
self.bibiman.popup_area.popup_kind
{
self.bibiman.fast_selection(cfg, &mut tui, key_event.code)?;
+ // if a fast match char was used, restart event-loop.
+ // otherwise, the fast match char will be executed as command
+ match key_event.code {
+ KeyCode::Char('o' | 'l' | 'n' | 'y') => continue,
+ _ => {}
+ }
}
let command = if self.input_mode {
CmdAction::Input(InputCmdAction::parse(key_event, &self.input))
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 9cc9280..f95bbc0 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -35,7 +35,7 @@ use std::fs::{self, read_to_string};
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::PathBuf;
-use std::process::{Command, Stdio};
+use std::process::Command;
use std::result::Result::Ok;
use tui_input::Input;
diff --git a/src/tui/ui.rs b/src/tui/ui.rs
index 3c83935..be53f61 100644
--- a/src/tui/ui.rs
+++ b/src/tui/ui.rs
@@ -300,7 +300,7 @@ pub fn render_popup(app: &mut App, cfg: &BibiConfig, frame: &mut Frame) {
};
let bottom_info = if let Some(PopupKind::OpenRes) = app.bibiman.popup_area.popup_kind {
- " (j,k|↓,↑) ━ (o,l) ━ (ENTER) ━ (ESC) ".bold()
+ " (j,k|↓,↑) ━ (o,l,n) ━ (ENTER) ━ (ESC) ".bold()
} else if let Some(PopupKind::YankItem) = app.bibiman.popup_area.popup_kind {
" (j,k|↓,↑) ━ (y) ━ (ENTER) ━ (ESC) ".bold()
} else {
@@ -580,7 +580,7 @@ pub fn render_entrytable(app: &mut App, cfg: &BibiConfig, frame: &mut Frame, rec
.bg(cfg.colors.bar_bg_color);
let header = Row::new(vec![
- Cell::from(Line::from("")).bg(cfg.colors.bar_bg_color),
+ Cell::from(Line::from("Res.")).bg(cfg.colors.bar_bg_color),
Cell::from(
Line::from(vec![{ Span::raw("Author") }, {
if let Some(EntryTableColumn::Authors) =
@@ -703,23 +703,38 @@ pub fn render_entrytable(app: &mut App, cfg: &BibiConfig, frame: &mut Frame, rec
let mut symbol_vec = vec![];
+ // use default or custom symbols for resources
+ // if an entry has no, replace it with the correct number
+ // of whitespace to align the symbols correct
if let Some(f) = &item.symbols[0] {
symbol_vec.push(Span::styled(
f,
Style::new().fg(cfg.colors.file_color).bold(),
));
+ } else {
+ symbol_vec.push(Span::raw(
+ " ".repeat(cfg.general.file_symbol.chars().count()),
+ ));
}
if let Some(l) = &item.symbols[1] {
symbol_vec.push(Span::styled(
l,
Style::new().fg(cfg.colors.link_color).bold(),
));
+ } else {
+ symbol_vec.push(Span::raw(
+ " ".repeat(cfg.general.link_symbol.chars().count()),
+ ));
}
if let Some(n) = &item.symbols[2] {
symbol_vec.push(Span::styled(
n,
Style::new().fg(cfg.colors.note_color).bold(),
))
+ } else {
+ symbol_vec.push(Span::raw(
+ " ".repeat(cfg.general.note_symbol.chars().count()),
+ ));
}
let row = Row::new(vec![
diff --git a/tests/test-config.toml b/tests/test-config.toml
index 51bd4e6..1d29043 100644
--- a/tests/test-config.toml
+++ b/tests/test-config.toml
@@ -28,9 +28,9 @@ note_path = "tests/note-files"
note_extensions = [ "md", "txt" ]
## Symbols/chars to show if not has specific attachement
- file_symbol = " "
- link_symbol = " "
- note_symbol = ""
+file_symbol = " "
+link_symbol = " "
+note_symbol = ""
# [colors]
## Default values for dark-themed terminal
--
cgit v1.2.3
From 61b22382e0979f756538215047bafd30866ccf1e Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Thu, 3 Jul 2025 15:06:19 +0200
Subject: detach opened note from terminal window running bibiman
---
src/bibiman.rs | 96 +++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 68 insertions(+), 28 deletions(-)
(limited to 'src/bibiman.rs')
diff --git a/src/bibiman.rs b/src/bibiman.rs
index f95bbc0..200db96 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -25,7 +25,7 @@ use crate::tui::Tui;
use crate::{app, cliargs};
use crate::{bibiman::entries::EntryTable, bibiman::keywords::TagList};
use arboard::Clipboard;
-use color_eyre::eyre::{Error, Result};
+use color_eyre::eyre::{Context, Error, Result};
use crossterm::event::KeyCode;
use editor_command::EditorBuilder;
use ratatui::widgets::ScrollbarState;
@@ -35,7 +35,7 @@ use std::fs::{self, read_to_string};
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::PathBuf;
-use std::process::Command;
+use std::process::{Command, Stdio};
use std::result::Result::Ok;
use tui_input::Input;
@@ -520,35 +520,63 @@ impl Bibiman {
file: &OsStr,
) -> Result<()> {
// get filecontent and citekey for calculating line number
- let citekey: &str = &self.entry_table.entry_table_items
- [self.entry_table.entry_table_state.selected().unwrap()]
- .citekey
- .clone();
- // Exit TUI to enter editor
- tui.exit()?;
- // Use VISUAL or EDITOR. Set "vi" as last fallback
- let mut cmd: Command = EditorBuilder::new()
- .source(cfg.general.editor.as_ref())
- .environment()
- .source(Some("vi"))
- .build()
- .unwrap();
- // Prepare arguments to open file at specific line
- let status = cmd.arg(file).status()?;
- if !status.success() {
- eprintln!("Spawning editor failed with status {}", status);
- }
+ match std::env::var("TERM") {
+ Ok(sh) => {
+ let editor = if let Some(e) = cfg.general.editor.clone() {
+ e
+ } else if let Ok(e) = std::env::var("VISUAL") {
+ e
+ } else if let Ok(e) = std::env::var("EDITOR") {
+ e
+ } else {
+ String::from("vi")
+ };
+ let _ = Command::new(sh)
+ .arg("-e")
+ .arg(editor)
+ .arg(file)
+ .stdout(Stdio::null())
+ .stderr(Stdio::null())
+ .spawn()
+ .wrap_err("Couldn't run editor");
+ // Prepare arguments to open file at specific line
+ // let status = note_cmd.status()?;
+ // if !status.success() {
+ // eprintln!("Spawning editor failed with status {}", status);
+ // }
+ }
+ Err(_e) => {
+ let citekey: &str = &self.entry_table.entry_table_items
+ [self.entry_table.entry_table_state.selected().unwrap()]
+ .citekey
+ .clone();
+ // Exit TUI to enter editor
+ tui.exit()?;
+ // Use VISUAL or EDITOR. Set "vi" as last fallback
+ let mut note_cmd: Command = EditorBuilder::new()
+ .source(cfg.general.editor.clone())
+ .environment()
+ .source(Some("vi"))
+ .build()
+ .unwrap();
+ // Prepare arguments to open file at specific line
+ let status = note_cmd.arg(file).status()?;
+ if !status.success() {
+ eprintln!("Spawning editor failed with status {}", status);
+ }
- // Enter TUI again
- tui.enter()?;
- tui.terminal.clear()?;
+ // Enter TUI again
+ tui.enter()?;
+ tui.terminal.clear()?;
- // Update the database and the lists to show changes
- // Self::update_lists(self, cfg);
+ // Update the database and the lists to show changes
+ // Self::update_lists(self, cfg);
- // Select entry which was selected before entering editor
- self.select_entry_by_citekey(citekey);
+ // Select entry which was selected before entering editor
+ self.select_entry_by_citekey(citekey);
+ }
+ }
Ok(())
}
@@ -808,7 +836,19 @@ impl Bibiman {
.filepath
.clone();
if file.is_some() {
- let file = expand_home(&PathBuf::from(file.unwrap()[0].clone()));
+ let file = if self.entry_table.entry_table_items[entry_idx].file_field
+ && cfg.general.file_prefix.is_some()
+ {
+ cfg.general
+ .file_prefix
+ .clone()
+ .unwrap()
+ .join(&file.unwrap()[0])
+ .into_os_string()
+ } else {
+ file.unwrap()[0].clone()
+ };
+ let file = expand_home(&PathBuf::from(file));
// let object: OsString = popup_entry.into();
if file.is_file() {
app::open_connected_file(cfg, &file.into_os_string())?;
--
cgit v1.2.3
From 2594cf34dcf2f04f398dab7b6ecae364eb4c7d17 Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Fri, 4 Jul 2025 13:56:55 +0200
Subject: impl `PopupItem` enum, adapt ui: include colors
---
src/app.rs | 30 ++++++++++++++++++++++++------
src/bibiman.rs | 17 +++++++++++++----
src/tui/popup.rs | 16 +++++++++++++---
src/tui/ui.rs | 16 ++++++++++++----
4 files changed, 62 insertions(+), 17 deletions(-)
(limited to 'src/bibiman.rs')
diff --git a/src/app.rs b/src/app.rs
index f912614..d645dbe 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -19,7 +19,7 @@ use crate::bibiman::CurrentArea;
use crate::cliargs::CLIArgs;
use crate::config::BibiConfig;
use crate::tui::commands::InputCmdAction;
-use crate::tui::popup::PopupKind;
+use crate::tui::popup::{PopupItem, PopupKind};
use crate::tui::{self, Tui};
use crate::{bibiman::Bibiman, tui::commands::CmdAction};
use color_eyre::eyre::{Context, Ok, Result};
@@ -311,13 +311,25 @@ impl App {
.selected()
.unwrap();
let entry = self.bibiman.entry_table.entry_table_items[idx].clone();
- let mut items = vec![("Citekey: ".to_string(), entry.citekey.clone())];
+ let mut items = vec![(
+ "Citekey: ".to_string(),
+ entry.citekey.clone(),
+ PopupItem::Default,
+ )];
if entry.doi_url.is_some() {
- items.push(("Weblink: ".into(), entry.doi_url.unwrap().clone()))
+ items.push((
+ "Weblink: ".into(),
+ entry.doi_url.unwrap().clone(),
+ PopupItem::Link,
+ ))
}
if entry.filepath.is_some() {
entry.filepath.unwrap().iter().for_each(|p| {
- items.push(("Filepath: ".into(), p.clone().into_string().unwrap()))
+ items.push((
+ "Filepath: ".into(),
+ p.clone().into_string().unwrap(),
+ PopupItem::Entryfile,
+ ))
});
// items.push((
// "Filepath: ".into(),
@@ -348,13 +360,14 @@ impl App {
.selected()
.unwrap();
let entry = self.bibiman.entry_table.entry_table_items[idx].clone();
- let mut items: Vec<(String, String)> = vec![];
+ let mut items: Vec<(String, String, PopupItem)> = vec![];
if entry.filepath.is_some() || entry.doi_url.is_some() || entry.notes.is_some()
{
if entry.doi_url.is_some() {
items.push((
"Weblink (DOI/URL): ".into(),
entry.doi_url.unwrap().clone(),
+ PopupItem::Link,
))
}
if entry.filepath.is_some() {
@@ -374,12 +387,17 @@ impl App {
} else {
p.clone().into_string().unwrap()
},
+ PopupItem::Entryfile,
))
});
}
if entry.notes.is_some() {
entry.notes.unwrap().iter().for_each(|n| {
- items.push(("Note: ".into(), n.clone().into_string().unwrap()));
+ items.push((
+ "Note: ".into(),
+ n.clone().into_string().unwrap(),
+ PopupItem::Notefile,
+ ));
});
}
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 200db96..fb72e93 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -20,7 +20,7 @@ use crate::bibiman::entries::EntryTableColumn;
use crate::bibiman::{bibisetup::*, search::BibiSearch};
use crate::cliargs::CLIArgs;
use crate::config::BibiConfig;
-use crate::tui::popup::{PopupArea, PopupKind};
+use crate::tui::popup::{PopupArea, PopupItem, PopupKind};
use crate::tui::Tui;
use crate::{app, cliargs};
use crate::{bibiman::entries::EntryTable, bibiman::keywords::TagList};
@@ -161,7 +161,7 @@ impl Bibiman {
popup_kind: PopupKind,
message: Option<&str>,
object: Option<&str>,
- items: Option>,
+ items: Option>,
) -> Result<()> {
if let CurrentArea::EntryArea = self.current_area {
self.former_area = Some(FormerArea::EntryArea);
@@ -631,10 +631,18 @@ impl Bibiman {
}
pub fn append_to_file(&mut self) {
- let mut items = vec![("Create new file".to_owned(), "".to_string())];
+ let mut items = vec![(
+ "Create new file".to_owned(),
+ "".to_string(),
+ PopupItem::Default,
+ )];
if self.main_bibfiles.len() > 1 {
for f in self.main_bibfiles.clone() {
- items.push(("File: ".into(), f.to_str().unwrap().to_owned()));
+ items.push((
+ "File: ".into(),
+ f.to_str().unwrap().to_owned(),
+ PopupItem::Bibfile,
+ ));
}
} else {
items.push((
@@ -645,6 +653,7 @@ impl Bibiman {
.to_str()
.unwrap()
.to_owned(),
+ PopupItem::Bibfile,
));
}
self.popup_area.popup_selection(items);
diff --git a/src/tui/popup.rs b/src/tui/popup.rs
index 93b01c3..da44744 100644
--- a/src/tui/popup.rs
+++ b/src/tui/popup.rs
@@ -40,13 +40,23 @@ pub enum PopupKind {
YankItem,
}
+#[derive(Debug)]
+pub enum PopupItem {
+ Bibfile,
+ Entryfile,
+ Notefile,
+ Link,
+ Default,
+ None,
+}
+
#[derive(Debug, Default)]
pub struct PopupArea {
pub is_popup: bool,
pub popup_kind: Option,
pub popup_message: String,
pub popup_scroll_pos: u16,
- pub popup_list: Vec<(String, String)>,
+ pub popup_list: Vec<(String, String, PopupItem)>,
pub popup_state: ListState,
pub popup_sel_item: String,
// pub add_entry_input: String,
@@ -116,8 +126,8 @@ impl PopupArea {
/// Opens a popup with a selectable list
///
- /// The list items are passed as argument of the kind `Vec<(String, String)>`.
- pub fn popup_selection(&mut self, items: Vec<(String, String)>) {
+ /// The list items are passed as argument of the kind `Vec<(String, String, PopupItem)>`.
+ pub fn popup_selection(&mut self, items: Vec<(String, String, PopupItem)>) {
self.popup_list = items;
// self.popup_kind = Some(PopupKind::SelectRes);
self.is_popup = true;
diff --git a/src/tui/ui.rs b/src/tui/ui.rs
index be53f61..2c30154 100644
--- a/src/tui/ui.rs
+++ b/src/tui/ui.rs
@@ -17,7 +17,7 @@
use std::path::PathBuf;
-use super::popup::PopupArea;
+use super::popup::{PopupArea, PopupItem};
use crate::bibiman::entries::EntryTableColumn;
use crate::bibiman::{CurrentArea, FormerArea};
use crate::cliargs::CLIArgs;
@@ -280,9 +280,17 @@ pub fn render_popup(app: &mut App, cfg: &BibiConfig, frame: &mut Frame) {
.popup_list
.iter()
.map(
- |(mes, obj)| {
+ |(mes, obj, i)| {
+ let style = match i {
+ PopupItem::Bibfile => Style::new().fg(cfg.colors.entry_color),
+ PopupItem::Entryfile => Style::new().fg(cfg.colors.file_color),
+ PopupItem::Notefile => Style::new().fg(cfg.colors.note_color),
+ PopupItem::Link => Style::new().fg(cfg.colors.link_color),
+ PopupItem::Default => Style::new(),
+ PopupItem::None => Style::new(),
+ };
ListItem::from(Line::from(vec![
- Span::styled(mes, Style::new().bold()),
+ Span::styled(mes, style.bold()),
Span::raw(obj),
]))
}, // ListItem::from(mes.to_owned() + obj)
@@ -333,7 +341,7 @@ pub fn render_popup(app: &mut App, cfg: &BibiConfig, frame: &mut Frame) {
.popup_area
.popup_list
.iter()
- .max_by(|(mes, obj), (m, o)| {
+ .max_by(|(mes, obj, _ik), (m, o, _i)| {
let x = mes.chars().count() + obj.chars().count();
let y = m.chars().count() + o.chars().count();
x.cmp(&y)
--
cgit v1.2.3
From 52079ee745831b06a6c4060f38ee49e42d689dcd Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Fri, 4 Jul 2025 15:05:08 +0200
Subject: use `PopupItem` to determine resource type
---
src/bibiman.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'src/bibiman.rs')
diff --git a/src/bibiman.rs b/src/bibiman.rs
index fb72e93..6aec1fb 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -749,12 +749,12 @@ impl Bibiman {
let popup_entry = self.popup_area.popup_list[popup_idx].1.clone();
// Choose ressource depending an selected popup field
- if self.popup_area.popup_list[popup_idx].0.contains("Weblink") {
+ if let PopupItem::Link = self.popup_area.popup_list[popup_idx].2 {
let object = self.entry_table.entry_table_items[entry_idx].doi_url();
let url = app::prepare_weblink(object);
app::open_connected_link(cfg, &url)?;
self.close_popup();
- } else if self.popup_area.popup_list[popup_idx].0.contains("File") {
+ } else if let PopupItem::Entryfile = self.popup_area.popup_list[popup_idx].2 {
// TODO: Selection for multiple files
// let object = self.entry_table.entry_table_items[entry_idx].filepath()[0];
let file = expand_home(&PathBuf::from(popup_entry.clone()));
@@ -770,7 +770,7 @@ impl Bibiman {
None,
)?;
}
- } else if self.popup_area.popup_list[popup_idx].0.contains("Note") {
+ } else if let PopupItem::Notefile = self.popup_area.popup_list[popup_idx].2 {
let file = expand_home(&PathBuf::from(popup_entry.clone()));
// let object: OsString = popup_entry.into();
if file.is_file() {
--
cgit v1.2.3
From 80c04702012cb1a43711d559e8ffbe9e250b1a57 Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Sat, 5 Jul 2025 20:21:52 +0200
Subject: first steps for create new note
---
src/app.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++---
src/bibiman.rs | 4 ++++
src/tui/commands.rs | 2 ++
src/tui/popup.rs | 2 ++
4 files changed, 67 insertions(+), 3 deletions(-)
(limited to 'src/bibiman.rs')
diff --git a/src/app.rs b/src/app.rs
index d475328..14cc864 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -365,7 +365,7 @@ impl App {
{
if entry.doi_url.is_some() {
items.push((
- format!("{} ", cfg.general.link_symbol.clone().trim()),
+ "Link: ".into(),
entry.doi_url.unwrap().clone(),
PopupItem::Link,
))
@@ -373,7 +373,7 @@ impl App {
if entry.filepath.is_some() {
entry.filepath.unwrap().iter().for_each(|p| {
items.push((
- format!("{} ", cfg.general.file_symbol.clone().trim()),
+ "File: ".into(),
// p.clone().into_string().unwrap(),
if entry.file_field && cfg.general.file_prefix.is_some() {
cfg.general
@@ -394,7 +394,7 @@ impl App {
if entry.notes.is_some() {
entry.notes.unwrap().iter().for_each(|n| {
items.push((
- format!("{} ", cfg.general.note_symbol.clone().trim()),
+ "Note: ".into(),
n.clone().into_string().unwrap(),
PopupItem::Notefile,
));
@@ -419,6 +419,62 @@ impl App {
self.bibiman.add_entry();
}
}
+ CmdAction::CreateNote => {
+ if let CurrentArea::EntryArea = self.bibiman.current_area {
+ if cfg.general.note_path.is_some()
+ && cfg.general.note_extensions.is_some()
+ && self.bibiman.entry_table.entry_table_items[self
+ .bibiman
+ .entry_table
+ .entry_table_state
+ .selected()
+ .unwrap()]
+ .notes
+ .is_none()
+ {
+ let mut items = vec![];
+ for ex in cfg.general.note_extensions.as_ref().unwrap() {
+ items.push((
+ self.bibiman.entry_table.entry_table_items[self
+ .bibiman
+ .entry_table
+ .entry_table_state
+ .selected()
+ .unwrap()]
+ .citekey()
+ .to_string(),
+ ex.clone(),
+ PopupItem::Notefile,
+ ));
+ }
+ self.bibiman
+ .open_popup(PopupKind::CreateNote, None, None, Some(items));
+ } else if cfg.general.note_path.is_some()
+ && self.bibiman.entry_table.entry_table_items[self
+ .bibiman
+ .entry_table
+ .entry_table_state
+ .selected()
+ .unwrap()]
+ .notes
+ .is_some()
+ {
+ self.bibiman.open_popup(
+ PopupKind::MessageError,
+ Some("Selected entry already has a connected note"),
+ None,
+ None,
+ )?;
+ } else {
+ self.bibiman.open_popup(
+ PopupKind::MessageError,
+ Some("No note path found. Set it in config file."),
+ None,
+ None,
+ )?;
+ }
+ }
+ }
CmdAction::ShowHelp => {
self.bibiman.open_popup(PopupKind::Help, None, None, None)?;
}
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 6aec1fb..c92b869 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -740,6 +740,10 @@ impl Bibiman {
Ok(())
}
+ pub fn create_note(&mut self, cfg: &BibiConfig) -> Result<()> {
+ Ok(())
+ }
+
pub fn open_connected_res(&mut self, cfg: &BibiConfig, tui: &mut Tui) -> Result<()> {
// Index of selected entry
let entry_idx = self.entry_table.entry_table_state.selected().unwrap();
diff --git a/src/tui/commands.rs b/src/tui/commands.rs
index 08ee677..47d2802 100644
--- a/src/tui/commands.rs
+++ b/src/tui/commands.rs
@@ -73,6 +73,8 @@ pub enum CmdAction {
ShowHelp,
// Add new entry
AddEntry,
+ // Create note
+ CreateNote,
// Do nothing.
Nothing,
}
diff --git a/src/tui/popup.rs b/src/tui/popup.rs
index 4aaa2c1..46e4792 100644
--- a/src/tui/popup.rs
+++ b/src/tui/popup.rs
@@ -38,6 +38,8 @@ pub enum PopupKind {
AddEntry,
/// select an item of the current entry to yank to clipboard
YankItem,
+ /// Create a new note, select extension
+ CreateNote,
}
#[derive(Debug)]
--
cgit v1.2.3
From 2990df627ff54f01bafdcab767c0a73198e9e6cc Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Sat, 5 Jul 2025 22:29:18 +0200
Subject: create note function impl
---
src/app.rs | 10 ++++-
src/bibiman.rs | 33 +++++++++++++++++
src/tui/commands.rs | 2 +
src/tui/ui.rs | 104 ++++++++++++++++++++++++++++++++++------------------
4 files changed, 111 insertions(+), 38 deletions(-)
(limited to 'src/bibiman.rs')
diff --git a/src/app.rs b/src/app.rs
index 14cc864..01424bc 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -192,7 +192,8 @@ impl App {
}
Some(PopupKind::OpenRes)
| Some(PopupKind::AppendToFile)
- | Some(PopupKind::YankItem) => {
+ | Some(PopupKind::YankItem)
+ | Some(PopupKind::CreateNote) => {
self.bibiman.popup_area.popup_state.scroll_down_by(1)
}
_ => {}
@@ -213,7 +214,8 @@ impl App {
}
Some(PopupKind::OpenRes)
| Some(PopupKind::AppendToFile)
- | Some(PopupKind::YankItem) => {
+ | Some(PopupKind::YankItem)
+ | Some(PopupKind::CreateNote) => {
self.bibiman.popup_area.popup_state.scroll_up_by(1)
}
_ => {}
@@ -273,6 +275,8 @@ impl App {
self.bibiman.close_popup();
} else if let Some(PopupKind::YankItem) = self.bibiman.popup_area.popup_kind {
self.bibiman.close_popup();
+ } else if let Some(PopupKind::CreateNote) = self.bibiman.popup_area.popup_kind {
+ self.bibiman.close_popup();
}
} else {
self.bibiman.reset_current_list();
@@ -291,6 +295,8 @@ impl App {
self.bibiman.append_entry_to_file(cfg)?
} else if let Some(PopupKind::YankItem) = self.bibiman.popup_area.popup_kind {
self.bibiman.yank_entry_field()?
+ } else if let Some(PopupKind::CreateNote) = self.bibiman.popup_area.popup_kind {
+ self.bibiman.create_note(cfg)?
}
}
}
diff --git a/src/bibiman.rs b/src/bibiman.rs
index c92b869..71ac831 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -238,6 +238,18 @@ impl Bibiman {
))
}
}
+ PopupKind::CreateNote => {
+ if items.is_some() {
+ self.popup_area.popup_kind = Some(PopupKind::CreateNote);
+ self.popup_area.popup_selection(items.unwrap());
+ self.popup_area.popup_state.select(Some(0));
+ Ok(())
+ } else {
+ Err(Error::msg(
+ "No Vec<(String, String)> passed as argument to generate the items list",
+ ))
+ }
+ }
}
}
@@ -741,6 +753,27 @@ impl Bibiman {
}
pub fn create_note(&mut self, cfg: &BibiConfig) -> Result<()> {
+ // Index of selected entry
+ let entry_idx = self.entry_table.entry_table_state.selected().unwrap();
+ let citekey = self.entry_table.entry_table_items[entry_idx].citekey();
+
+ // Index of selected popup field
+ let popup_idx = self.popup_area.popup_state.selected().unwrap();
+ let ext = self.popup_area.popup_list[popup_idx].1.clone();
+
+ let basename = PathBuf::from(citekey).with_extension(ext);
+ let path = cfg.general.note_path.as_ref().unwrap();
+
+ let new_file = path.join(basename);
+
+ let new_file = if new_file.starts_with("~") {
+ expand_home(&new_file)
+ } else {
+ new_file
+ };
+
+ File::create_new(new_file).unwrap();
+ self.close_popup();
Ok(())
}
diff --git a/src/tui/commands.rs b/src/tui/commands.rs
index 47d2802..89fcf44 100644
--- a/src/tui/commands.rs
+++ b/src/tui/commands.rs
@@ -151,6 +151,8 @@ impl From for CmdAction {
// Open linked ressource
KeyCode::Char('o') => Self::Open,
// KeyCode::Char('u') => Self::Open(OpenRessource::WebLink),
+ // Create note file
+ KeyCode::Char('n') => Self::CreateNote,
// Edit currently selected entry
KeyCode::Char('e') => Self::EditFile,
// Yank selected item/value
diff --git a/src/tui/ui.rs b/src/tui/ui.rs
index 0a34e51..69ca058 100644
--- a/src/tui/ui.rs
+++ b/src/tui/ui.rs
@@ -273,33 +273,49 @@ pub fn render_popup(app: &mut App, cfg: &BibiConfig, frame: &mut Frame) {
frame.render_widget(Clear, popup_area);
frame.render_widget(&content, popup_area)
}
- Some(PopupKind::OpenRes) | Some(PopupKind::AppendToFile) | Some(PopupKind::YankItem) => {
- let list_items: Vec = app
- .bibiman
- .popup_area
- .popup_list
- .iter()
- .map(
- |(mes, obj, i)| {
- let style: Color = match i {
- PopupItem::Bibfile => cfg.colors.entry_color,
- PopupItem::Citekey => cfg.colors.entry_color,
- PopupItem::Entryfile => cfg.colors.file_color,
- PopupItem::Notefile => cfg.colors.note_color,
- PopupItem::Link => cfg.colors.link_color,
- PopupItem::Default => cfg.colors.main_text_color,
- PopupItem::None => cfg.colors.main_text_color,
- };
- ListItem::from(
- Line::from(vec![
- Span::styled(mes, Style::new().bold()),
- Span::raw(obj),
- ])
- .fg(style),
- )
- }, // ListItem::from(mes.to_owned() + obj)
- )
- .collect();
+ Some(PopupKind::OpenRes)
+ | Some(PopupKind::AppendToFile)
+ | Some(PopupKind::YankItem)
+ | Some(PopupKind::CreateNote) => {
+ let list_items: Vec = if let Some(PopupKind::CreateNote) =
+ app.bibiman.popup_area.popup_kind
+ {
+ app.bibiman
+ .popup_area
+ .popup_list
+ .iter()
+ .map(|(m, o, _i)| {
+ ListItem::from(Line::from(vec![Span::raw(m), Span::raw("."), Span::raw(o)]))
+ .fg(cfg.colors.note_color)
+ })
+ .collect()
+ } else {
+ app.bibiman
+ .popup_area
+ .popup_list
+ .iter()
+ .map(
+ |(mes, obj, i)| {
+ let style: Color = match i {
+ PopupItem::Bibfile => cfg.colors.entry_color,
+ PopupItem::Citekey => cfg.colors.entry_color,
+ PopupItem::Entryfile => cfg.colors.file_color,
+ PopupItem::Notefile => cfg.colors.note_color,
+ PopupItem::Link => cfg.colors.link_color,
+ PopupItem::Default => cfg.colors.main_text_color,
+ PopupItem::None => cfg.colors.main_text_color,
+ };
+ ListItem::from(
+ Line::from(vec![
+ Span::styled(mes, Style::new().bold()),
+ Span::raw(obj),
+ ])
+ .fg(style),
+ )
+ }, // ListItem::from(mes.to_owned() + obj)
+ )
+ .collect()
+ };
let title = if let Some(PopupKind::OpenRes) = app.bibiman.popup_area.popup_kind {
" Open "
@@ -307,21 +323,23 @@ pub fn render_popup(app: &mut App, cfg: &BibiConfig, frame: &mut Frame) {
" Select file to append entry "
} else if let Some(PopupKind::YankItem) = app.bibiman.popup_area.popup_kind {
" Yank to clipboard "
+ } else if let Some(PopupKind::CreateNote) = app.bibiman.popup_area.popup_kind {
+ " Create Note with extension "
} else {
" Select "
};
let bottom_info = if let Some(PopupKind::OpenRes) = app.bibiman.popup_area.popup_kind {
- " (j,k|↓,↑) ━ (o,l,n) ━ (ENTER) ━ (ESC) ".bold()
+ " (j,k|↓,↑) ━ (o,l,n) ━ (ENTER) ━ (ESC) "
} else if let Some(PopupKind::YankItem) = app.bibiman.popup_area.popup_kind {
- " (j,k|↓,↑) ━ (y) ━ (ENTER) ━ (ESC) ".bold()
+ " (j,k|↓,↑) ━ (y) ━ (ENTER) ━ (ESC) "
} else {
- " (j,k|↓,↑) ━ (ENTER) ━ (ESC) ".bold()
+ " (j,k|↓,↑) ━ (ENTER) ━ (ESC) "
};
let block = Block::bordered()
.title_top(title.bold())
- .title_bottom(bottom_info)
+ .title_bottom(bottom_info.bold())
.title_alignment(Alignment::Center)
.style(
Style::new()
@@ -356,15 +374,29 @@ pub fn render_popup(app: &mut App, cfg: &BibiConfig, frame: &mut Frame) {
// Now take the max number for the width of the popup
// let max_item = list_widths.iter().max().unwrap().to_owned();
- let max_item =
- list_widths.0.chars().count() as u16 + list_widths.1.chars().count() as u16;
-
+ let max_item = list_widths.0.clone() + &list_widths.1;
+ // list_widths.0.chars().count() as u16 + list_widths.1.chars().count() as u16;
+
+ let fitting_width: u16 = {
+ let lines = vec![title, bottom_info, &max_item];
+ let lline = lines
+ .iter()
+ .max_by(|a, b| a.chars().count().cmp(&b.chars().count()))
+ .unwrap();
+ // lines.first().unwrap().chars().count() as u16
+ lline.chars().count() as u16
+ };
// Check if the popup would exceed the terminal frame width
- let popup_width = if max_item + 2 > frame.area().width - 2 {
+ let popup_width = if fitting_width + 2 > frame.area().width - 2 {
frame.area().width - 2
} else {
- max_item + 2
+ fitting_width + 2
};
+ // } else if title.chars().count() as u16 > max_item {
+ // (title.chars().count() + 2) as u16
+ // } else {
+ // max_item + 2
+ // };
let popup_heigth = list.len() + 2;
let popup_area = popup_area(frame.area(), popup_width, popup_heigth as u16);
--
cgit v1.2.3
From e27069540a0bb22640974d0bd1a1bdf153b1b40d Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Sat, 5 Jul 2025 22:32:41 +0200
Subject: succesfully tested note creation
---
src/bibiman.rs | 1 +
tests/note-files/doody.md | 0
2 files changed, 1 insertion(+)
create mode 100644 tests/note-files/doody.md
(limited to 'src/bibiman.rs')
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 71ac831..87963dc 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -774,6 +774,7 @@ impl Bibiman {
File::create_new(new_file).unwrap();
self.close_popup();
+ self.update_lists(cfg);
Ok(())
}
diff --git a/tests/note-files/doody.md b/tests/note-files/doody.md
new file mode 100644
index 0000000..e69de29
--
cgit v1.2.3
From e2b4e12cf1ce15a26172ac8f2166c5e02ca89351 Mon Sep 17 00:00:00 2001
From: lukeflo
Date: Sat, 5 Jul 2025 22:55:46 +0200
Subject: quit creating note function if citekey contains special char
---
src/app.rs | 24 ++++++++++++++++++++++--
src/bibiman.rs | 7 +++++--
2 files changed, 27 insertions(+), 4 deletions(-)
(limited to 'src/bibiman.rs')
diff --git a/src/app.rs b/src/app.rs
index 01424bc..708ec37 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -427,7 +427,27 @@ impl App {
}
CmdAction::CreateNote => {
if let CurrentArea::EntryArea = self.bibiman.current_area {
- if cfg.general.note_path.is_some()
+ let citekey = self.bibiman.entry_table.entry_table_items[self
+ .bibiman
+ .entry_table
+ .entry_table_state
+ .selected()
+ .unwrap()]
+ .citekey
+ .clone();
+ if citekey.contains("/")
+ | citekey.contains("|")
+ | citekey.contains("#")
+ | citekey.contains("\\")
+ | citekey.contains("*")
+ {
+ self.bibiman.open_popup(
+ PopupKind::MessageError,
+ Some("Selected entrys citekey contains special char: "),
+ Some(&citekey),
+ None,
+ )?;
+ } else if cfg.general.note_path.is_some()
&& cfg.general.note_extensions.is_some()
&& self.bibiman.entry_table.entry_table_items[self
.bibiman
@@ -454,7 +474,7 @@ impl App {
));
}
self.bibiman
- .open_popup(PopupKind::CreateNote, None, None, Some(items));
+ .open_popup(PopupKind::CreateNote, None, None, Some(items))?;
} else if cfg.general.note_path.is_some()
&& self.bibiman.entry_table.entry_table_items[self
.bibiman
diff --git a/src/bibiman.rs b/src/bibiman.rs
index 87963dc..6d21f8c 100644
--- a/src/bibiman.rs
+++ b/src/bibiman.rs
@@ -755,13 +755,15 @@ impl Bibiman {
pub fn create_note(&mut self, cfg: &BibiConfig) -> Result<()> {
// Index of selected entry
let entry_idx = self.entry_table.entry_table_state.selected().unwrap();
- let citekey = self.entry_table.entry_table_items[entry_idx].citekey();
+ let citekey = self.entry_table.entry_table_items[entry_idx]
+ .citekey
+ .clone();
// Index of selected popup field
let popup_idx = self.popup_area.popup_state.selected().unwrap();
let ext = self.popup_area.popup_list[popup_idx].1.clone();
- let basename = PathBuf::from(citekey).with_extension(ext);
+ let basename = PathBuf::from(&citekey).with_extension(ext);
let path = cfg.general.note_path.as_ref().unwrap();
let new_file = path.join(basename);
@@ -775,6 +777,7 @@ impl Bibiman {
File::create_new(new_file).unwrap();
self.close_popup();
self.update_lists(cfg);
+ self.select_entry_by_citekey(&citekey);
Ok(())
}
--
cgit v1.2.3