Skip to content

Commit

Permalink
Restructured config file into less-responsibility structs
Browse files Browse the repository at this point in the history
  • Loading branch information
Linus-Mussmaecher committed Jun 10, 2024
1 parent f96596a commit 5a2306a
Show file tree
Hide file tree
Showing 17 changed files with 1,161 additions and 1,200 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ Here is a list of all possible configuration settings:
- `Relevant`: When you have no filter set, the global stats are shown.
Otherwise, local stats are shown.
This setting therefore avoids showing duplicating stats at all times.
- `continous_filter` is set to `true` by default, but can be set to `false` to cause your select view to only filter upon pressing enter and not while typing.
- `editor` configures the command to edit your notes.
This can be a terminal application or an external application.
- `viewer` configures the command for your HTML viewing application (I use `google-chrome-stable`). If unconfigured, tries to use your systems default application for HTML files.
The following configuration options manage the HTML files created by rucola:
- `continuous_html` is set to `true` by default, causing all your notes to be converted to HTML files on program start and for those HTMLs to be continuously kept up-to-date in case of file changes.
Set to `false` to create HTMLs only on demand, which may cause links in them to be un-followable.
- `enable_html` is set to `true` by default, causing all your notes to be converted to HTML files on program start and for those HTMLs to be continuously kept up-to-date in case of file changes.
Set to `false` to never create HTMLs in the background.
HTMLs can still be created by choosing the view option on a single file, in which case this file - and only this file, in particular none of the files linked from it - will be converted.
- `mathjax` is set to `true` by default, but can be set to `false` to never prepend a MathJax preamble to HTML files. While set to `true`, the preamble is only appended if math blocks (delimited by `$...$` and `$$...$$` are detected).
- `math_replacments` is a vector of pairs of strings.
In math mode, every appearance of the first string will be replaced by the second one.
Expand Down
115 changes: 48 additions & 67 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use super::{config, data, error, ui, ui::Screen};
use notify::{self, Watcher};
use super::{data, error, files, ui, ui::Screen};
use ratatui::prelude::*;
use std::{cell::RefCell, rc::Rc, sync::mpsc};

/// The main state of the application.
/// Consists of a select screen that is always existent, a stack of notes the user has navigated through and that he can navigate through by popping, reversing its navigation. Lastly, there is a display screen of the currently displayed note, which should always correspond to the top of the stack.
Expand All @@ -12,82 +10,84 @@ pub struct App {
display: Option<ui::screen::DisplayScreen>,
/// The ids of note on the display stack
display_stack: Vec<String>,
/// Stored config data
config: config::Config,
/// Index note data
index: data::NoteIndexContainer,
/// Watcher that checks for file changes in the vault directory and needs to be kept alive with this index.
/// Can be unused because it is just here for RAII.
#[allow(unused)]
watcher: notify::INotifyWatcher,
/// Channel from which file change events in the vault directory are deposited by the watcher and can be requested.
file_change_channel: mpsc::Receiver<Result<notify::Event, notify::Error>>,

// === Config ===
/// The file manager this app's screens use to enact the user's file system requests on the file system.
manager: files::FileManager,
/// The HtmlBuider this app's screens use to continuously build html files.
builder: files::HtmlBuilder,
/// The styles used by this app's screens.
styles: ui::UiStyles,
}

impl App {
/// Creates a new application state. This includes
/// - Loading a config file
/// - Indexing notes from the given path
/// - Creating an initial select screen and empty display stack
pub fn new(config: config::Config) -> Self {
// Index all files in path
let index = Rc::new(RefCell::new(data::NoteIndex::new(&config)));

// Create asynchronous channel for file events.
let (sender, receiver) = mpsc::channel();
pub fn new(args: crate::Arguments) -> Self {
// Create all configs
let (styles, builder, manager, tracker, stats_show) =
files::load_configurations(args).unwrap();

// Create watcher so we can store it in the file, delaying its drop (which stops its function) until the end of the lifetime of this index.
let mut watcher = notify::recommended_watcher(move |res| {
sender.send(res).unwrap();
})
.unwrap();

// Start watching the vault.
watcher
.watch(
&config.create_vault_path(),
notify::RecursiveMode::Recursive,
)
.expect("Fixed config does not fail.");
// Index all files in path
let index = std::rc::Rc::new(std::cell::RefCell::new(data::NoteIndex::new(
tracker,
builder.clone(),
)));

// Initialize app state
Self {
select: ui::screen::SelectScreen::new(index.clone(), &config),
select: ui::screen::SelectScreen::new(
index.clone(),
manager.clone(),
builder.clone(),
styles.clone(),
stats_show,
),
display: None,
display_stack: Vec::new(),
index,
watcher,
file_change_channel: receiver,
config,
styles,
manager,
builder,
}
}

/// Reads the top of the display stack, creates a new display screen from it and sets that as the currently active display screen.
/// If the display stack is empty, clears the display screen.
fn set_display_to_top(&mut self) -> Result<(), error::RucolaError> {
self.display = match self.display_stack.last() {
Some(id) => Some(ui::screen::DisplayScreen::new(
id,
self.index.clone(),
self.manager.clone(),
self.builder.clone(),
self.styles.clone(),
)?),
None => None,
};
Ok(())
}

// Updates the app with the given key.
pub fn update(
&mut self,
key: Option<crossterm::event::KeyEvent>,
) -> Result<ui::TerminalMessage, error::RucolaError> {
// Check for file changes
let mut modifications = false;
let mut index = self.index.borrow_mut();
for event in self.file_change_channel.try_iter().flatten() {
modifications |= index.handle_file_event(event, &self.config)?;
}
let modifications = index.handle_file_events()?;
drop(index);

// if anything happened in the file system, better refresh the environment
if modifications {
self.select.refresh_env_stats();
// if we are currently in a display screen, also refresh it (by creating it anew)
if self.display.is_some() {
self.display = match self.display_stack.last() {
Some(id) => Some(ui::screen::DisplayScreen::new(
id,
self.index.clone(),
&self.config,
)?),
None => None,
};
self.set_display_to_top()?;
}
}

Expand Down Expand Up @@ -117,31 +117,12 @@ impl App {
ui::Message::DisplayStackPop => {
// Pop the top of the stack - which should correspond to the currently displayed note.
self.display_stack.pop();
// Attempt to read the top of the stack again.
// Replace the display screen with the one created from this result - either a valid display screen that will be displayed or none, causing the select screen to show.
self.display = match self.display_stack.last() {
Some(id) => Some(ui::screen::DisplayScreen::new(
id,
self.index.clone(),
&self.config,
)?),
None => None,
};
self.set_display_to_top()?;
}
ui::Message::DisplayStackPush(new_id) => {
// Push a new id on top of the display stack.
self.display_stack.push(new_id.clone());

// Attempt to read the top of the stack again.
// Replace the display screen with the one created from this result, which should always be a valid display screen created from the id we just pushed.
self.display = match self.display_stack.last() {
Some(id) => Some(ui::screen::DisplayScreen::new(
id,
self.index.clone(),
&self.config,
)?),
None => None,
};
self.set_display_to_top()?;
}
}

Expand Down
Loading

0 comments on commit 5a2306a

Please sign in to comment.