Compare commits

..

2 Commits

Author SHA1 Message Date
itsjunetime 01b6c2fa55 Actually reference different version of poppler that we're downloading 2024-11-20 09:43:10 -07:00
itsjunetime c9ef119393 Pull from ubuntu's package archives for version 23.08 2024-11-20 09:40:57 -07:00
12 changed files with 491 additions and 868 deletions
+3 -3
View File
@@ -29,9 +29,9 @@ jobs:
sudo apt-get install -y cmake libjpeg-dev libfontconfig1-dev libopenjp2-7-dev libopenjpip7 libopenjp2-7 libglib2.0-dev libnss3-dev libunwind-dev libgoogle-perftools-dev libboost-dev sudo apt-get install -y cmake libjpeg-dev libfontconfig1-dev libopenjp2-7-dev libopenjpip7 libopenjp2-7 libglib2.0-dev libnss3-dev libunwind-dev libgoogle-perftools-dev libboost-dev
- name: Build newer poppler - name: Build newer poppler
run: | run: |
wget https://poppler.freedesktop.org/poppler-23.10.0.tar.xz wget http://archive.ubuntu.com/ubuntu/pool/main/p/poppler/poppler_23.08.0.orig.tar.xz
tar xf poppler-23.10.0.tar.xz tar xf poppler_23.08.0.orig.tar.xz
cd poppler-23.10.0 cd poppler-23.08.0
mkdir build && cd build mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \ cmake .. -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \ -DCMAKE_C_COMPILER_LAUNCHER=sccache \
-3
View File
@@ -7,9 +7,6 @@
- Move `ratatui-image/vb64` support under `nightly` feature, enabled by default - Move `ratatui-image/vb64` support under `nightly` feature, enabled by default
- Fixed a bug where jumping to a page out of range could result in weird `esc` key behavior - Fixed a bug where jumping to a page out of range could result in weird `esc` key behavior
- Added CI ([#31](https://github.com/itsjunetime/tdf/pull/31), thank you [@Kriejstal](https://github.com/Kreijstal)) - Added CI ([#31](https://github.com/itsjunetime/tdf/pull/31), thank you [@Kriejstal](https://github.com/Kreijstal))
- Changed global allocator to [`mimalloc`](https://github.com/purpleprotocol/mimalloc_rust) for slightly improved performance
- Fixed issue with document reloading not working when files are intermedially deleted
- Fixed a lot of weirdness with bottom message layering/updating
# v0.1.0 # v0.1.0
Generated
+24 -44
View File
@@ -168,7 +168,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"rustversion", "rustversion",
"serde", "serde",
"sync_wrapper 1.0.2", "sync_wrapper 1.0.1",
"tower 0.5.1", "tower 0.5.1",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
@@ -189,7 +189,7 @@ dependencies = [
"mime", "mime",
"pin-project-lite", "pin-project-lite",
"rustversion", "rustversion",
"sync_wrapper 1.0.2", "sync_wrapper 1.0.1",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
] ]
@@ -945,9 +945,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
dependencies = [ dependencies = [
"allocator-api2", "allocator-api2",
"equivalent", "equivalent",
@@ -1141,7 +1141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.2", "hashbrown 0.15.1",
] ]
[[package]] [[package]]
@@ -1244,9 +1244,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.14" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
@@ -1294,9 +1294,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.166" version = "0.2.164"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
[[package]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
@@ -1308,16 +1308,6 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "libmimalloc-sys"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.3" version = "0.1.3"
@@ -1366,7 +1356,7 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
dependencies = [ dependencies = [
"hashbrown 0.15.2", "hashbrown 0.15.1",
] ]
[[package]] [[package]]
@@ -1400,15 +1390,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mimalloc"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
dependencies = [
"libmimalloc-sys",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@@ -1737,9 +1718,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.92" version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -2237,9 +2218,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.89" version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2254,9 +2235,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]] [[package]]
name = "sync_wrapper" name = "sync_wrapper"
version = "1.0.2" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]] [[package]]
name = "system-deps" name = "system-deps"
@@ -2304,7 +2285,6 @@ dependencies = [
"glib", "glib",
"image", "image",
"itertools 0.13.0", "itertools 0.13.0",
"mimalloc",
"notify", "notify",
"poppler-rs", "poppler-rs",
"ratatui", "ratatui",
@@ -2537,9 +2517,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.41" version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [ dependencies = [
"pin-project-lite", "pin-project-lite",
"tracing-attributes", "tracing-attributes",
@@ -2548,9 +2528,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.28" version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2559,9 +2539,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.33" version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"valuable", "valuable",
@@ -2590,9 +2570,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.14" version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
+1 -2
View File
@@ -7,7 +7,7 @@ description = "A terminal viewer for PDFs"
readme = "README.md" readme = "README.md"
homepage = "https://github.com/itsjunetime/tdf" homepage = "https://github.com/itsjunetime/tdf"
repository = "https://github.com/itsjunetime/tdf" repository = "https://github.com/itsjunetime/tdf"
license = "GPL-3.0-or-later" license = "MPL-2.0"
keywords = ["pdf", "tui", "cli", "terminal"] keywords = ["pdf", "tui", "cli", "terminal"]
categories = ["command-line-utilities", "text-processing", "visualization"] categories = ["command-line-utilities", "text-processing", "visualization"]
default-run = "tdf" default-run = "tdf"
@@ -37,7 +37,6 @@ glib = "0.20.0"
itertools = "*" itertools = "*"
flume = { version = "0.11.0", default-features = false, features = ["async"] } flume = { version = "0.11.0", default-features = false, features = ["async"] }
xflags = "0.4.0-pre.2" xflags = "0.4.0-pre.2"
mimalloc = "0.1.43"
# for tracing with tokio-console # for tracing with tokio-console
console-subscriber = { version = "0.4.0", optional = true } console-subscriber = { version = "0.4.0", optional = true }
+373 -674
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -29,5 +29,3 @@ I dunno. Just for fun, mostly.
## Can I contribute? ## Can I contribute?
Yeah, sure. Please do. Yeah, sure. Please do.
Please note, though, that all contributions will be treated as licensed under MPL-2.0. This is so that we can relicense to MPL-2.0 at some point in the future if we manage to move away from poppler as a backend (since that is the only dependency, at time of writing, which requires the GPLv3 license).
+4 -5
View File
@@ -71,7 +71,7 @@ pub async fn render_first_page(path: impl AsRef<Path>) {
} = start_all_rendering(path); } = start_all_rendering(path);
// we only want to render until the first page is ready to be printed // we only want to render until the first page is ready to be printed
while pages.iter().all(Option::is_none) { while pages.iter().all(|p| p.is_none()) {
tokio::select! { tokio::select! {
Some(renderer_msg) = from_render_rx.next() => { Some(renderer_msg) = from_render_rx.next() => {
handle_renderer_msg(renderer_msg, &mut pages, &mut to_converter_tx); handle_renderer_msg(renderer_msg, &mut pages, &mut to_converter_tx);
@@ -94,7 +94,6 @@ async fn render_all_files(path: &'static str) -> Vec<PageInfo> {
while let Some(info) = from_render_rx.next().await { while let Some(info) = from_render_rx.next().await {
match info.expect("Renderer ran into an error while rendering") { match info.expect("Renderer ran into an error while rendering") {
RenderInfo::Reloaded => (),
RenderInfo::NumPages(num) => fill_default(&mut pages, num), RenderInfo::NumPages(num) => fill_default(&mut pages, num),
RenderInfo::Page(page) => { RenderInfo::Page(page) => {
let num = page.page; let num = page.page;
@@ -102,7 +101,7 @@ async fn render_all_files(path: &'static str) -> Vec<PageInfo> {
} }
}; };
if pages.iter().all(Option::is_some) { if pages.iter().all(|p| p.is_some()) {
break; break;
} }
} }
@@ -137,7 +136,7 @@ async fn convert_all_files(files: Vec<PageInfo>) {
} }
} }
while converted.iter().any(Option::is_none) { while converted.iter().any(|p| p.is_none()) {
let page = from_converter_rx let page = from_converter_rx
.next() .next()
.await .await
@@ -158,7 +157,7 @@ impl Profiler for CpuProfiler {
fn start_profiling(&mut self, benchmark_id: &str, _: &std::path::Path) { fn start_profiling(&mut self, benchmark_id: &str, _: &std::path::Path) {
let file = format!( let file = format!(
"./{}-{}.profile", "./{}-{}.profile",
benchmark_id.replace('/', "-"), benchmark_id.replace("/", "-"),
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.unwrap() .unwrap()
-2
View File
@@ -21,8 +21,6 @@ pub fn handle_renderer_msg(
to_converter_tx.send(ConverterMsg::NumPages(num)).unwrap(); to_converter_tx.send(ConverterMsg::NumPages(num)).unwrap();
} }
Ok(RenderInfo::Page(info)) => to_converter_tx.send(ConverterMsg::AddImg(info)).unwrap(), Ok(RenderInfo::Page(info)) => to_converter_tx.send(ConverterMsg::AddImg(info)).unwrap(),
// We can ignore the `Reloaded` variant 'cause that's only used to send info to the TUI
Ok(RenderInfo::Reloaded) => (),
Err(e) => panic!("Got error from renderer: {e:?}") Err(e) => panic!("Got error from renderer: {e:?}")
} }
} }
-3
View File
@@ -1,6 +1,3 @@
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
pub mod converter; pub mod converter;
pub mod renderer; pub mod renderer;
pub mod skip; pub mod skip;
+39 -62
View File
@@ -1,10 +1,10 @@
use std::{ use std::{
ffi::OsString,
io::{stdout, Read, Write}, io::{stdout, Read, Write},
num::NonZeroUsize, num::NonZeroUsize,
path::PathBuf path::PathBuf
}; };
use converter::{run_conversion_loop, ConvertedPage, ConverterMsg};
use crossterm::{ use crossterm::{
execute, execute,
terminal::{ terminal::{
@@ -17,11 +17,13 @@ use glib::{LogField, LogLevel, LogWriterOutput};
use notify::{Event, EventKind, RecursiveMode, Watcher}; use notify::{Event, EventKind, RecursiveMode, Watcher};
use ratatui::{backend::CrosstermBackend, Terminal}; use ratatui::{backend::CrosstermBackend, Terminal};
use ratatui_image::picker::Picker; use ratatui_image::picker::Picker;
use tdf::{ use renderer::{RenderError, RenderInfo, RenderNotif};
converter::{run_conversion_loop, ConvertedPage, ConverterMsg}, use tui::{InputAction, Tui};
renderer::{self, RenderError, RenderInfo, RenderNotif},
tui::{BottomMessage, InputAction, MessageSetting, Tui} mod converter;
}; mod renderer;
mod skip;
mod tui;
// Dummy struct for easy errors in main // Dummy struct for easy errors in main
#[derive(Debug)] #[derive(Debug)]
@@ -58,26 +60,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (render_tx, tui_rx) = flume::unbounded(); let (render_tx, tui_rx) = flume::unbounded();
let watch_to_tui_tx = render_tx.clone(); let watch_to_tui_tx = render_tx.clone();
let mut watcher = notify::recommended_watcher(on_notify_ev( let mut watcher =
watch_to_tui_tx, notify::recommended_watcher(on_notify_ev(watch_to_tui_tx, watch_to_render_tx))?;
watch_to_render_tx,
path.file_name()
.ok_or("Path does not have a last component??")?
.to_owned()
))?;
// So we have to watch the parent directory of the file that we are interested in because the // We're making this nonrecursive 'cause we're just watching a single file, so there's nothing
// `notify` library works on inodes, and if the file is deleted, that inode is gone as well, // to recurse into
// and then the notify library just gives up on trying to watch for the file reappearing. Imo watcher.watch(&path, RecursiveMode::NonRecursive)?;
// they should start watching the parent directory if the file is deleted, and then wait for it
// to reappear and then begin watching it again, but whatever. It seems they've made their
// opinion on this clear
// (https://github.com/notify-rs/notify/issues/113#issuecomment-281836995) so whatever, guess
// we have to do this annoying workaround.
watcher.watch(
path.parent().expect("The root directory is not a PDF"),
RecursiveMode::NonRecursive
)?;
// TODO: Handle non-utf8 file names? Maybe by constructing a CString and passing that in to the // TODO: Handle non-utf8 file names? Maybe by constructing a CString and passing that in to the
// poppler stuff instead of a rust string? // poppler stuff instead of a rust string?
@@ -155,7 +143,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|| "Unknown file".into(), || "Unknown file".into(),
|n| n.to_string_lossy().to_string() |n| n.to_string_lossy().to_string()
); );
let mut tui = Tui::new(file_name, flags.max_wide, flags.r_to_l.unwrap_or_default()); let mut tui = tui::Tui::new(file_name, flags.max_wide, flags.r_to_l.unwrap_or_default());
let backend = CrosstermBackend::new(std::io::stdout()); let backend = CrosstermBackend::new(std::io::stdout());
let mut term = Terminal::new(backend)?; let mut term = Terminal::new(backend)?;
@@ -173,7 +161,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
)?; )?;
enable_raw_mode()?; enable_raw_mode()?;
let mut main_area = Tui::main_layout(&term.get_frame()); let mut main_area = tui::Tui::main_layout(&term.get_frame());
tui_tx.send(RenderNotif::Area(main_area[1]))?; tui_tx.send(RenderNotif::Area(main_area[1]))?;
let mut tui_rx = tui_rx.into_stream(); let mut tui_rx = tui_rx.into_stream();
@@ -202,16 +190,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}, },
Some(renderer_msg) = tui_rx.next() => { Some(renderer_msg) = tui_rx.next() => {
match renderer_msg { match renderer_msg {
Ok(render_info) => match render_info { // if an Ok comes through, we know the error has been resolved ('cause it kinda
RenderInfo::NumPages(num) => { // bails whenever we run into an error) so just clear it
tui.set_n_pages(num); Ok(render_info) => {
to_converter.send(ConverterMsg::NumPages(num))?; match render_info {
}, RenderInfo::NumPages(num) => {
RenderInfo::Page(info) => { tui.set_n_pages(num);
tui.got_num_results_on_page(info.page, info.search_results); to_converter.send(ConverterMsg::NumPages(num))?;
to_converter.send(ConverterMsg::AddImg(info))?; },
}, RenderInfo::Page(info) => {
RenderInfo::Reloaded => tui.set_msg(MessageSetting::Some(BottomMessage::Reloaded)), tui.got_num_results_on_page(info.page, info.search_results);
to_converter.send(ConverterMsg::AddImg(info))?;
},
}
tui.set_bottom_msg(None);
}, },
Err(e) => tui.show_error(e), Err(e) => tui.show_error(e),
} }
@@ -251,8 +243,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
fn on_notify_ev( fn on_notify_ev(
to_tui_tx: flume::Sender<Result<RenderInfo, RenderError>>, to_tui_tx: flume::Sender<Result<RenderInfo, RenderError>>,
to_render_tx: flume::Sender<RenderNotif>, to_render_tx: flume::Sender<RenderNotif>
file_name: OsString
) -> impl Fn(notify::Result<Event>) { ) -> impl Fn(notify::Result<Event>) {
move |res| match res { move |res| match res {
// If we get an error here, and then an error sending, everything's going wrong. Just give // If we get an error here, and then an error sending, everything's going wrong. Just give
@@ -260,29 +251,15 @@ fn on_notify_ev(
Err(e) => to_tui_tx.send(Err(RenderError::Notify(e))).unwrap(), Err(e) => to_tui_tx.send(Err(RenderError::Notify(e))).unwrap(),
// TODO: Should we match EventKind::Rename and propogate that so that the other parts of the // TODO: Should we match EventKind::Rename and propogate that so that the other parts of the
// process know that too? Or should that be // process know that too? Or should that be
Ok(ev) => { Ok(ev) => match ev.kind {
// We only watch the parent directory (see the comment above `watcher.watch` in `fn EventKind::Access(_) => (),
// main`) so we need to filter out events to only ones that pertain to the single file EventKind::Remove(_) =>
// we care about drop(to_tui_tx.send(Err(RenderError::Render("File was deleted".into())))),
if !ev // This shouldn't fail to send unless the receiver gets disconnected. If that's
.paths // happened, then like the main thread has panicked or something, so it doesn't matter
.iter() // we don't handle the error here.
.any(|path| path.file_name().is_some_and(|f| f == file_name)) EventKind::Other | EventKind::Any | EventKind::Create(_) | EventKind::Modify(_) =>
{ drop(to_render_tx.send(renderer::RenderNotif::Reload)),
return;
}
match ev.kind {
EventKind::Access(_) => (),
EventKind::Remove(_) => to_tui_tx
.send(Err(RenderError::Render("File was deleted".into())))
.unwrap(),
// This shouldn't fail to send unless the receiver gets disconnected. If that's
// happened, then like the main thread has panicked or something, so it doesn't matter
// we don't handle the error here.
EventKind::Other | EventKind::Any | EventKind::Create(_) | EventKind::Modify(_) =>
to_render_tx.send(RenderNotif::Reload).unwrap(),
}
} }
} }
} }
+18 -30
View File
@@ -25,8 +25,7 @@ pub enum RenderError {
pub enum RenderInfo { pub enum RenderInfo {
NumPages(usize), NumPages(usize),
Page(PageInfo), Page(PageInfo)
Reloaded
} }
#[derive(Clone)] #[derive(Clone)]
@@ -77,11 +76,13 @@ pub fn start_rendering(
) -> Result<(), SendError<Result<RenderInfo, RenderError>>> { ) -> Result<(), SendError<Result<RenderInfo, RenderError>>> {
// first, wait 'til we get told what the current starting area is so that we can set it to // first, wait 'til we get told what the current starting area is so that we can set it to
// know what to render to // know what to render to
let mut area = loop { let mut area;
loop {
if let RenderNotif::Area(r) = receiver.recv().unwrap() { if let RenderNotif::Area(r) = receiver.recv().unwrap() {
break r; area = r;
break;
} }
}; }
// We want this outside of 'reload so that if the doc reloads, the search term that somebody // We want this outside of 'reload so that if the doc reloads, the search term that somebody
// set will still get highlighted in the reloaded doc // set will still get highlighted in the reloaded doc
@@ -92,37 +93,24 @@ pub fn start_rendering(
let col_w = size.width / size.columns; let col_w = size.width / size.columns;
let col_h = size.height / size.rows; let col_h = size.height / size.rows;
let mut stored_doc = None;
'reload: loop { 'reload: loop {
let doc = match Document::from_file(path, None) { let doc = match Document::from_file(path, None) {
Err(e) => { Err(e) => {
// if there's an error, tell the main loop // if there's an error, tell the main loop
sender.send(Err(RenderError::Doc(e)))?; sender.send(Err(RenderError::Doc(e)))?;
// then wait for a reload notif (since what probably happened is that the file was
match stored_doc { // temporarily removed to facilitate a save or something like that)
Some(ref d) => d, while let Ok(msg) = receiver.recv() {
None => { // and once that comes, just try to reload again
// then wait for a reload notif (since what probably happened is that the file was if let RenderNotif::Reload = msg {
// temporarily removed to facilitate a save or something like that) continue 'reload;
while let Ok(msg) = receiver.recv() {
// and once that comes, just try to reload again
if let RenderNotif::Reload = msg {
continue 'reload;
}
}
// if that while let Ok ever fails and we exit out of that loop, the main thread is
// done, so we're fine to just return
return Ok(());
} }
} }
// if that while let Ok ever fails and we exit out of that loop, the main thread is
// done, so we're fine to just return
return Ok(());
} }
Ok(d) => { Ok(d) => d
if stored_doc.is_some() {
sender.send(Ok(RenderInfo::Reloaded))?;
}
&*stored_doc.insert(d)
}
}; };
let n_pages = doc.n_pages() as usize; let n_pages = doc.n_pages() as usize;
@@ -213,8 +201,8 @@ pub fn start_rendering(
// we only want to continue if one of the following is met: // we only want to continue if one of the following is met:
// 1. It failed to render last time (we want to retry) // 1. It failed to render last time (we want to retry)
// 2. The `contained_term` is set to None (representing 'Unknown'), meaning that we // 2. The `contained_term` is set to None (representing 'Unknown'), meaning that we
// need to at least check if it contains the current term to see if it needs a // need to at least check if it contains the current term to see if it needs a
// re-render // re-render
if rendered.successful && rendered.contained_term.is_some() { if rendered.successful && rendered.contained_term.is_some() {
continue; continue;
} }
+29 -38
View File
@@ -43,8 +43,7 @@ pub enum BottomMessage {
Help, Help,
SearchResults(String), SearchResults(String),
Error(String), Error(String),
Input(InputCommand), Input(InputCommand)
Reloaded
} }
pub enum InputCommand { pub enum InputCommand {
@@ -187,7 +186,6 @@ impl Tui {
Color::Blue Color::Blue
) )
} }
BottomMessage::Reloaded => ("Document was reloaded!".into(), Color::Blue)
}; };
let span = Span::styled(msg_str, Style::new().fg(color)); let span = Span::styled(msg_str, Style::new().fg(color));
@@ -396,13 +394,13 @@ impl Tui {
'k' => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen), 'k' => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
'q' => Some(InputAction::QuitApp), 'q' => Some(InputAction::QuitApp),
'g' => { 'g' => {
self.set_msg(MessageSetting::Some(BottomMessage::Input( self.set_bottom_msg(Some(BottomMessage::Input(
InputCommand::GoToPage(0) InputCommand::GoToPage(0)
))); )));
Some(InputAction::Redraw) Some(InputAction::Redraw)
} }
'/' => { '/' => {
self.set_msg(MessageSetting::Some(BottomMessage::Input( self.set_bottom_msg(Some(BottomMessage::Input(
InputCommand::Search(String::new()) InputCommand::Search(String::new())
))); )));
Some(InputAction::Redraw) Some(InputAction::Redraw)
@@ -453,33 +451,34 @@ impl Tui {
KeyCode::Esc => match self.bottom_msg { KeyCode::Esc => match self.bottom_msg {
BottomMessage::Help => Some(InputAction::QuitApp), BottomMessage::Help => Some(InputAction::QuitApp),
_ => { _ => {
// When we hit escape, we just want to pop off the current message and self.set_bottom_msg(None);
// show the underlying one.
self.set_msg(MessageSetting::Pop);
Some(InputAction::Redraw) Some(InputAction::Redraw)
} }
}, },
KeyCode::Enter => { KeyCode::Enter => {
let mut default = BottomMessage::default(); let BottomMessage::Input(_) = self.bottom_msg else {
std::mem::swap(&mut self.bottom_msg, &mut default);
let BottomMessage::Input(ref cmd) = default else {
std::mem::swap(&mut self.bottom_msg, &mut default);
return None; return None;
}; };
self.set_bottom_msg(None);
let Some(BottomMessage::Input(ref cmd)) = self.prev_msg else {
// We need to verify it's an input msg currently, and only then take it
// and replace it by a default Help message. Don't exactly know how to
// do this otherwise.
unreachable!();
};
match cmd { match cmd {
// Only forward the command if it's within range // Only forward the command if it's within range
InputCommand::GoToPage(page) => { InputCommand::GoToPage(page) => {
// We need to subtract 1 b/c they're tracked internally as let page = *page;
// 0-indexed but input and displayed as 1-indexed
let zero_page = page.saturating_sub(1);
let rendered_len = self.rendered.len(); let rendered_len = self.rendered.len();
if zero_page < rendered_len { if page < rendered_len {
self.set_page(zero_page); self.set_page(page);
Some(InputAction::JumpingToPage(zero_page)) Some(InputAction::JumpingToPage(page))
} else { } else {
self.set_msg(MessageSetting::Some(BottomMessage::Error( self.set_bottom_msg(Some(BottomMessage::Error(
format!("Cannot jump to page {page}; there are only {rendered_len} pages in the document") format!("Cannot jump to page {page}; there are only {rendered_len} pages in the document")
))); )));
Some(InputAction::Redraw) Some(InputAction::Redraw)
@@ -491,14 +490,14 @@ impl Tui {
// We only want to show search results if there would actually be // We only want to show search results if there would actually be
// data to show // data to show
if !term.is_empty() { if !term.is_empty() {
self.set_msg(MessageSetting::Some( self.set_bottom_msg(Some(BottomMessage::SearchResults(
BottomMessage::SearchResults(term.clone()) term.clone()
)); )));
} else { } else {
// else, if it's not empty, we just want to reset the bottom // else, if it's not empty, we just want to reset the bottom
// area to show the default data; we don't want it to like show // area to show the default data; we don't want it to like show
// the data from a previous search // the data from a previous search
self.set_msg(MessageSetting::Reset); self.set_bottom_msg(Some(BottomMessage::Help));
} }
// Reset all the search results // Reset all the search results
@@ -532,7 +531,7 @@ impl Tui {
} }
pub fn show_error(&mut self, err: RenderError) { pub fn show_error(&mut self, err: RenderError) {
self.set_msg(MessageSetting::Some(BottomMessage::Error(match err { self.set_bottom_msg(Some(BottomMessage::Error(match err {
RenderError::Notify(e) => format!("Auto-reload failed: {e}"), RenderError::Notify(e) => format!("Auto-reload failed: {e}"),
RenderError::Doc(e) => format!("Couldn't open document: {e}"), RenderError::Doc(e) => format!("Couldn't open document: {e}"),
RenderError::Render(e) => format!("Couldn't render page: {e}") RenderError::Render(e) => format!("Couldn't render page: {e}")
@@ -549,18 +548,17 @@ impl Tui {
// We have `msg` as optional so that if they reset it to none, it'll replace it with // We have `msg` as optional so that if they reset it to none, it'll replace it with
// `prev_msg`, but if they reset it to something else, it'll put the current thing in prev_msg // `prev_msg`, but if they reset it to something else, it'll put the current thing in prev_msg
pub fn set_msg(&mut self, msg: MessageSetting) { pub fn set_bottom_msg(&mut self, msg: Option<BottomMessage>) {
match msg { match msg {
MessageSetting::Some(mut msg) => { Some(mut msg) => {
std::mem::swap(&mut self.bottom_msg, &mut msg); std::mem::swap(&mut self.bottom_msg, &mut msg);
self.prev_msg = Some(msg); self.prev_msg = Some(msg);
} }
MessageSetting::Default => self.set_msg(MessageSetting::Some(BottomMessage::default())), None => {
MessageSetting::Reset => { let mut new_bottom = self.prev_msg.take().unwrap_or_default();
self.prev_msg = None; std::mem::swap(&mut self.bottom_msg, &mut new_bottom);
self.bottom_msg = BottomMessage::default(); self.prev_msg = Some(new_bottom);
} }
MessageSetting::Pop => self.bottom_msg = self.prev_msg.take().unwrap_or_default()
} }
} }
} }
@@ -583,10 +581,3 @@ enum ChangeAmount {
WholeScreen, WholeScreen,
Single Single
} }
pub enum MessageSetting {
Some(BottomMessage),
Default,
Reset,
Pop
}