mirror of
https://github.com/itsjunetime/tdf.git
synced 2026-06-02 08:01:47 -04:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0129c498c2 | |||
| b9a12650c6 | |||
| 65e1f1a205 | |||
| cc46791627 | |||
| 9cf4a8e0d8 | |||
| 25d98c3776 | |||
| 03c2f381d9 | |||
| eb5ee99eec | |||
| 34b42cb1b2 | |||
| e3ccb26d66 | |||
| 1aa26b8e8c | |||
| 1402db3eba | |||
| 2d43c1e513 |
@@ -0,0 +1,56 @@
|
|||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Setup sccache
|
||||||
|
if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
|
||||||
|
uses: mozilla-actions/sccache-action@v0.0.6
|
||||||
|
- name: Configure sccache
|
||||||
|
if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
|
||||||
|
run: |
|
||||||
|
echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
|
||||||
|
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
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
|
||||||
|
run: |
|
||||||
|
wget https://poppler.freedesktop.org/poppler-23.10.0.tar.xz
|
||||||
|
tar xf poppler-23.10.0.tar.xz
|
||||||
|
cd poppler-23.10.0
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
|
||||||
|
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
|
||||||
|
-DENABLE_UNSTABLE_API_ABI_HEADERS=ON \
|
||||||
|
-DENABLE_GPGME=OFF \
|
||||||
|
-DENABLE_QT5=OFF \
|
||||||
|
-DENABLE_QT6=OFF \
|
||||||
|
-DENABLE_SPLASH=OFF \
|
||||||
|
-DENABLE_LIBCURL=OFF
|
||||||
|
make -j$(nproc)
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clippy
|
||||||
|
run: cargo clippy -- -D warnings
|
||||||
|
- name: Check fmt
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --benches -- adobe_example
|
||||||
|
- name: Build
|
||||||
|
run: cargo build
|
||||||
@@ -5,6 +5,11 @@
|
|||||||
- Small internal changes to accomodate a few more clippy lints
|
- Small internal changes to accomodate a few more clippy lints
|
||||||
- Update `ratatui` and `ratatui-image` git dependencies to latest upstream
|
- Update `ratatui` and `ratatui-image` git dependencies to latest upstream
|
||||||
- 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
|
||||||
|
- 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
+69
-49
@@ -149,9 +149,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.7"
|
version = "0.7.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
|
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
@@ -168,7 +168,7 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"serde",
|
"serde",
|
||||||
"sync_wrapper 1.0.1",
|
"sync_wrapper 1.0.2",
|
||||||
"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.1",
|
"sync_wrapper 1.0.2",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
@@ -253,9 +253,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.19.0"
|
version = "1.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
@@ -321,9 +321,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.0"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
|
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -342,9 +342,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-expr"
|
name = "cfg-expr"
|
||||||
version = "0.17.0"
|
version = "0.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c"
|
checksum = "c360837f8f19e2e4468275138f1c0dec1647d1e17bb7c0215fe3cd7530e93c25"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
@@ -385,18 +385,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.20"
|
version = "4.5.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.20"
|
version = "4.5.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
@@ -404,9 +404,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compact_str"
|
name = "compact_str"
|
||||||
@@ -681,9 +681,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.34"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
|
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
@@ -910,9 +910,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.6"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
|
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -945,9 +945,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.1"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"equivalent",
|
"equivalent",
|
||||||
@@ -1039,9 +1039,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
|
checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@@ -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.1",
|
"hashbrown 0.15.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1244,9 +1244,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
@@ -1294,9 +1294,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.162"
|
version = "0.2.166"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
@@ -1308,6 +1308,16 @@ 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"
|
||||||
@@ -1356,7 +1366,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.1",
|
"hashbrown 0.15.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1390,6 +1400,15 @@ 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"
|
||||||
@@ -1718,9 +1737,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.89"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -2009,9 +2028,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.40"
|
version = "0.38.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
|
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
@@ -2069,9 +2088,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.132"
|
version = "1.0.133"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -2218,9 +2237,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.87"
|
version = "2.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2235,9 +2254,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sync_wrapper"
|
name = "sync_wrapper"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
|
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-deps"
|
name = "system-deps"
|
||||||
@@ -2258,7 +2277,7 @@ version = "7.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005"
|
checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-expr 0.17.0",
|
"cfg-expr 0.17.1",
|
||||||
"heck",
|
"heck",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"toml",
|
"toml",
|
||||||
@@ -2285,6 +2304,7 @@ dependencies = [
|
|||||||
"glib",
|
"glib",
|
||||||
"image",
|
"image",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
|
"mimalloc",
|
||||||
"notify",
|
"notify",
|
||||||
"poppler-rs",
|
"poppler-rs",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
@@ -2517,9 +2537,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.40"
|
version = "0.1.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-attributes",
|
"tracing-attributes",
|
||||||
@@ -2528,9 +2548,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.27"
|
version = "0.1.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2539,9 +2559,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.32"
|
version = "0.1.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"valuable",
|
"valuable",
|
||||||
@@ -2570,9 +2590,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
|
|||||||
+92
-1
@@ -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 = "MPL-2.0"
|
license = "GPL-3.0-or-later"
|
||||||
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,6 +37,7 @@ 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 }
|
||||||
@@ -61,3 +62,93 @@ harness = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "for_profiling"
|
name = "for_profiling"
|
||||||
path = "./benches/for_profiling.rs"
|
path = "./benches/for_profiling.rs"
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
uninlined_format_args = "warn"
|
||||||
|
redundant_closure_for_method_calls = "warn"
|
||||||
|
cast_lossless = "warn"
|
||||||
|
single_char_pattern = "warn"
|
||||||
|
manual_let_else = "warn"
|
||||||
|
ignored_unit_patterns = "warn"
|
||||||
|
range_plus_one = "warn"
|
||||||
|
unreadable_literal = "warn"
|
||||||
|
redundant_else = "warn"
|
||||||
|
assigning_clones = "warn"
|
||||||
|
bool_to_int_with_if = "warn"
|
||||||
|
borrow_as_ptr = "warn"
|
||||||
|
cast_ptr_alignment = "warn"
|
||||||
|
checked_conversions = "warn"
|
||||||
|
copy_iterator = "warn"
|
||||||
|
default_trait_access = "warn"
|
||||||
|
doc_link_with_quotes = "warn"
|
||||||
|
empty_enum = "warn"
|
||||||
|
explicit_into_iter_loop = "warn"
|
||||||
|
explicit_iter_loop = "warn"
|
||||||
|
filter_map_next = "warn"
|
||||||
|
flat_map_option = "warn"
|
||||||
|
fn_params_excessive_bools = "warn"
|
||||||
|
from_iter_instead_of_collect = "warn"
|
||||||
|
implicit_clone = "warn"
|
||||||
|
index_refutable_slice = "warn"
|
||||||
|
inefficient_to_string = "warn"
|
||||||
|
invalid_upcast_comparisons = "warn"
|
||||||
|
iter_filter_is_ok = "warn"
|
||||||
|
iter_filter_is_some = "warn"
|
||||||
|
iter_not_returning_iterator = "warn"
|
||||||
|
large_futures = "warn"
|
||||||
|
large_stack_arrays = "warn"
|
||||||
|
large_types_passed_by_value = "warn"
|
||||||
|
linkedlist = "warn"
|
||||||
|
macro_use_imports = "warn"
|
||||||
|
manual_assert = "warn"
|
||||||
|
manual_instant_elapsed = "warn"
|
||||||
|
manual_is_power_of_two = "warn"
|
||||||
|
manual_is_variant_and = "warn"
|
||||||
|
manual_ok_or = "warn"
|
||||||
|
manual_string_new = "warn"
|
||||||
|
many_single_char_names = "warn"
|
||||||
|
manual_unwrap_or = "warn"
|
||||||
|
match_on_vec_items = "warn"
|
||||||
|
match_same_arms = "warn"
|
||||||
|
match_wildcard_for_single_variants = "warn"
|
||||||
|
maybe_infinite_iter = "warn"
|
||||||
|
mismatching_type_param_order = "warn"
|
||||||
|
missing_fields_in_debug = "warn"
|
||||||
|
mut_mut = "warn"
|
||||||
|
needless_bitwise_bool = "warn"
|
||||||
|
needless_continue = "warn"
|
||||||
|
needless_for_each = "warn"
|
||||||
|
needless_pass_by_value = "warn"
|
||||||
|
needless_raw_string_hashes = "warn"
|
||||||
|
no_effect_underscore_binding = "warn"
|
||||||
|
no_mangle_with_rust_abi = "warn"
|
||||||
|
option_as_ref_cloned = "warn"
|
||||||
|
option_option = "warn"
|
||||||
|
ptr_as_ptr = "warn"
|
||||||
|
ptr_cast_constness = "warn"
|
||||||
|
range_minus_one = "warn"
|
||||||
|
ref_as_ptr = "warn"
|
||||||
|
ref_binding_to_reference = "warn"
|
||||||
|
ref_option = "warn"
|
||||||
|
ref_option_ref = "warn"
|
||||||
|
return_self_not_must_use = "warn"
|
||||||
|
same_functions_in_if_condition = "warn"
|
||||||
|
should_panic_without_expect = "warn"
|
||||||
|
similar_names = "warn"
|
||||||
|
stable_sort_primitive = "warn"
|
||||||
|
str_split_at_newline = "warn"
|
||||||
|
struct_excessive_bools = "warn"
|
||||||
|
struct_field_names = "warn"
|
||||||
|
transmute_ptr_to_ptr = "warn"
|
||||||
|
trivially_copy_pass_by_ref = "warn"
|
||||||
|
unicode_not_nfc = "warn"
|
||||||
|
unnecessary_box_returns = "warn"
|
||||||
|
unnecessary_join = "warn"
|
||||||
|
unnecessary_literal_bound = "warn"
|
||||||
|
unnecessary_wraps = "warn"
|
||||||
|
unnested_or_patterns = "warn"
|
||||||
|
unused_async = "warn"
|
||||||
|
unused_self = "warn"
|
||||||
|
used_underscore_binding = "warn"
|
||||||
|
used_underscore_items = "warn"
|
||||||
|
zero_sized_map_values = "warn"
|
||||||
|
|||||||
@@ -29,3 +29,5 @@ 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).
|
||||||
|
|||||||
@@ -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(|p| p.is_none()) {
|
while pages.iter().all(Option::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,6 +94,7 @@ 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;
|
||||||
@@ -101,7 +102,7 @@ async fn render_all_files(path: &'static str) -> Vec<PageInfo> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if pages.iter().all(|p| p.is_some()) {
|
if pages.iter().all(Option::is_some) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +137,7 @@ async fn convert_all_files(files: Vec<PageInfo>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while converted.iter().any(|p| p.is_none()) {
|
while converted.iter().any(Option::is_none) {
|
||||||
let page = from_converter_rx
|
let page = from_converter_rx
|
||||||
.next()
|
.next()
|
||||||
.await
|
.await
|
||||||
@@ -157,7 +158,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()
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ 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:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#[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;
|
||||||
|
|||||||
+63
-40
@@ -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,13 +17,11 @@ 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 renderer::{RenderError, RenderInfo, RenderNotif};
|
use tdf::{
|
||||||
use tui::{InputAction, Tui};
|
converter::{run_conversion_loop, ConvertedPage, ConverterMsg},
|
||||||
|
renderer::{self, RenderError, RenderInfo, RenderNotif},
|
||||||
mod converter;
|
tui::{BottomMessage, InputAction, MessageSetting, Tui}
|
||||||
mod renderer;
|
};
|
||||||
mod skip;
|
|
||||||
mod tui;
|
|
||||||
|
|
||||||
// Dummy struct for easy errors in main
|
// Dummy struct for easy errors in main
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -60,12 +58,26 @@ 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 =
|
let mut watcher = notify::recommended_watcher(on_notify_ev(
|
||||||
notify::recommended_watcher(on_notify_ev(watch_to_tui_tx, watch_to_render_tx))?;
|
watch_to_tui_tx,
|
||||||
|
watch_to_render_tx,
|
||||||
|
path.file_name()
|
||||||
|
.ok_or("Path does not have a last component??")?
|
||||||
|
.to_owned()
|
||||||
|
))?;
|
||||||
|
|
||||||
// We're making this nonrecursive 'cause we're just watching a single file, so there's nothing
|
// So we have to watch the parent directory of the file that we are interested in because the
|
||||||
// to recurse into
|
// `notify` library works on inodes, and if the file is deleted, that inode is gone as well,
|
||||||
watcher.watch(&path, RecursiveMode::NonRecursive)?;
|
// and then the notify library just gives up on trying to watch for the file reappearing. Imo
|
||||||
|
// 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?
|
||||||
@@ -123,7 +135,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
// We need to create `picker` on this thread because if we create it on the `renderer` thread,
|
// We need to create `picker` on this thread because if we create it on the `renderer` thread,
|
||||||
// it messes up something with user input. Input never makes it to the crossterm thing
|
// it messes up something with user input. Input never makes it to the crossterm thing
|
||||||
let picker = Picker::from_query_stdio()?;
|
let picker = Picker::from_query_stdio()?;
|
||||||
|
|
||||||
// then we want to spawn off the rendering task
|
// then we want to spawn off the rendering task
|
||||||
// We need to use the thread::spawn API so that this exists in a thread not owned by tokio,
|
// We need to use the thread::spawn API so that this exists in a thread not owned by tokio,
|
||||||
@@ -143,7 +155,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::Tui::new(file_name, flags.max_wide, flags.r_to_l.unwrap_or_default());
|
let mut 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)?;
|
||||||
@@ -161,7 +173,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
)?;
|
)?;
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
let mut main_area = tui::Tui::main_layout(&term.get_frame());
|
let mut main_area = 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();
|
||||||
@@ -190,20 +202,16 @@ 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 {
|
||||||
// if an Ok comes through, we know the error has been resolved ('cause it kinda
|
Ok(render_info) => match render_info {
|
||||||
// bails whenever we run into an error) so just clear it
|
RenderInfo::NumPages(num) => {
|
||||||
Ok(render_info) => {
|
tui.set_n_pages(num);
|
||||||
match render_info {
|
to_converter.send(ConverterMsg::NumPages(num))?;
|
||||||
RenderInfo::NumPages(num) => {
|
},
|
||||||
tui.set_n_pages(num);
|
RenderInfo::Page(info) => {
|
||||||
to_converter.send(ConverterMsg::NumPages(num))?;
|
tui.got_num_results_on_page(info.page, info.search_results);
|
||||||
},
|
to_converter.send(ConverterMsg::AddImg(info))?;
|
||||||
RenderInfo::Page(info) => {
|
},
|
||||||
tui.got_num_results_on_page(info.page, info.search_results);
|
RenderInfo::Reloaded => tui.set_msg(MessageSetting::Some(BottomMessage::Reloaded)),
|
||||||
to_converter.send(ConverterMsg::AddImg(info))?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tui.set_bottom_msg(None);
|
|
||||||
},
|
},
|
||||||
Err(e) => tui.show_error(e),
|
Err(e) => tui.show_error(e),
|
||||||
}
|
}
|
||||||
@@ -243,7 +251,8 @@ 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
|
||||||
@@ -251,15 +260,29 @@ 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) => match ev.kind {
|
Ok(ev) => {
|
||||||
EventKind::Access(_) => (),
|
// We only watch the parent directory (see the comment above `watcher.watch` in `fn
|
||||||
EventKind::Remove(_) =>
|
// main`) so we need to filter out events to only ones that pertain to the single file
|
||||||
drop(to_tui_tx.send(Err(RenderError::Render("File was deleted".into())))),
|
// we care about
|
||||||
// This shouldn't fail to send unless the receiver gets disconnected. If that's
|
if !ev
|
||||||
// happened, then like the main thread has panicked or something, so it doesn't matter
|
.paths
|
||||||
// we don't handle the error here.
|
.iter()
|
||||||
EventKind::Other | EventKind::Any | EventKind::Create(_) | EventKind::Modify(_) =>
|
.any(|path| path.file_name().is_some_and(|f| f == file_name))
|
||||||
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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-18
@@ -25,7 +25,8 @@ pub enum RenderError {
|
|||||||
|
|
||||||
pub enum RenderInfo {
|
pub enum RenderInfo {
|
||||||
NumPages(usize),
|
NumPages(usize),
|
||||||
Page(PageInfo)
|
Page(PageInfo),
|
||||||
|
Reloaded
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -76,13 +77,11 @@ 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;
|
let mut area = loop {
|
||||||
loop {
|
|
||||||
if let RenderNotif::Area(r) = receiver.recv().unwrap() {
|
if let RenderNotif::Area(r) = receiver.recv().unwrap() {
|
||||||
area = r;
|
break 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
|
||||||
@@ -93,24 +92,37 @@ 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
|
|
||||||
// temporarily removed to facilitate a save or something like that)
|
match stored_doc {
|
||||||
while let Ok(msg) = receiver.recv() {
|
Some(ref d) => d,
|
||||||
// and once that comes, just try to reload again
|
None => {
|
||||||
if let RenderNotif::Reload = msg {
|
// then wait for a reload notif (since what probably happened is that the file was
|
||||||
continue 'reload;
|
// temporarily removed to facilitate a save or something like that)
|
||||||
|
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) => d
|
Ok(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;
|
||||||
@@ -201,8 +213,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;
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-46
@@ -1,4 +1,4 @@
|
|||||||
use std::{io::stdout, num::NonZeroUsize, rc::Rc};
|
use std::{borrow::Cow, io::stdout, num::NonZeroUsize, rc::Rc};
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{Event, KeyCode, MouseEventKind},
|
event::{Event, KeyCode, MouseEventKind},
|
||||||
@@ -43,7 +43,8 @@ pub enum BottomMessage {
|
|||||||
Help,
|
Help,
|
||||||
SearchResults(String),
|
SearchResults(String),
|
||||||
Error(String),
|
Error(String),
|
||||||
Input(InputCommand)
|
Input(InputCommand),
|
||||||
|
Reloaded
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum InputCommand {
|
pub enum InputCommand {
|
||||||
@@ -152,18 +153,18 @@ impl Tui {
|
|||||||
let rendered_span = Span::styled(&rendered_str, Style::new().fg(Color::Cyan));
|
let rendered_span = Span::styled(&rendered_str, Style::new().fg(Color::Cyan));
|
||||||
frame.render_widget(rendered_span, bottom_layout[1]);
|
frame.render_widget(rendered_span, bottom_layout[1]);
|
||||||
|
|
||||||
let (msg_str, color) = match self.bottom_msg {
|
let (msg_str, color): (Cow<'_, str>, _) = match self.bottom_msg {
|
||||||
BottomMessage::Help => (
|
BottomMessage::Help => (
|
||||||
"/: Search, g: Go To Page, n: Next Search Result, N: Previous Search Result"
|
"/: Search, g: Go To Page, n: Next Search Result, N: Previous Search Result".into(),
|
||||||
.to_string(),
|
|
||||||
Color::Blue
|
Color::Blue
|
||||||
),
|
),
|
||||||
BottomMessage::Error(ref e) => (format!("Couldn't render a page: {e}"), Color::Red),
|
BottomMessage::Error(ref e) => (e.as_str().into(), Color::Red),
|
||||||
BottomMessage::Input(ref input_state) => (
|
BottomMessage::Input(ref input_state) => (
|
||||||
match input_state {
|
match input_state {
|
||||||
InputCommand::GoToPage(page) => format!("Go to: {page}"),
|
InputCommand::GoToPage(page) => format!("Go to: {page}"),
|
||||||
InputCommand::Search(s) => format!("Search: {s}")
|
InputCommand::Search(s) => format!("Search: {s}")
|
||||||
},
|
}
|
||||||
|
.into(),
|
||||||
Color::Blue
|
Color::Blue
|
||||||
),
|
),
|
||||||
BottomMessage::SearchResults(ref term) => {
|
BottomMessage::SearchResults(ref term) => {
|
||||||
@@ -181,10 +182,12 @@ impl Tui {
|
|||||||
format!(
|
format!(
|
||||||
"Results for '{term}': {num_found} (searched: {}%)",
|
"Results for '{term}': {num_found} (searched: {}%)",
|
||||||
num_searched / self.rendered.len()
|
num_searched / self.rendered.len()
|
||||||
),
|
)
|
||||||
|
.into(),
|
||||||
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));
|
||||||
@@ -370,12 +373,16 @@ impl Tui {
|
|||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
// TODO: refactor back to `if let` arm guards when those are stabilized
|
// TODO: refactor back to `if let` arm guards when those are stabilized
|
||||||
if let BottomMessage::Input(InputCommand::Search(ref mut term)) = self.bottom_msg {
|
if let BottomMessage::Input(InputCommand::Search(ref mut term)) =
|
||||||
|
self.bottom_msg
|
||||||
|
{
|
||||||
term.push(c);
|
term.push(c);
|
||||||
return Some(InputAction::Redraw);
|
return Some(InputAction::Redraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let BottomMessage::Input(InputCommand::GoToPage(ref mut page)) = self.bottom_msg {
|
if let BottomMessage::Input(InputCommand::GoToPage(ref mut page)) =
|
||||||
|
self.bottom_msg
|
||||||
|
{
|
||||||
return c.to_digit(10).map(|input_num| {
|
return c.to_digit(10).map(|input_num| {
|
||||||
*page = (*page * 10) + input_num as usize;
|
*page = (*page * 10) + input_num as usize;
|
||||||
InputAction::Redraw
|
InputAction::Redraw
|
||||||
@@ -389,13 +396,15 @@ 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_bottom_msg(Some(BottomMessage::Input(InputCommand::GoToPage(0))));
|
self.set_msg(MessageSetting::Some(BottomMessage::Input(
|
||||||
|
InputCommand::GoToPage(0)
|
||||||
|
)));
|
||||||
Some(InputAction::Redraw)
|
Some(InputAction::Redraw)
|
||||||
}
|
}
|
||||||
'/' => {
|
'/' => {
|
||||||
self.set_bottom_msg(Some(BottomMessage::Input(InputCommand::Search(
|
self.set_msg(MessageSetting::Some(BottomMessage::Input(
|
||||||
String::new()
|
InputCommand::Search(String::new())
|
||||||
))));
|
)));
|
||||||
Some(InputAction::Redraw)
|
Some(InputAction::Redraw)
|
||||||
}
|
}
|
||||||
'n' if self.page < self.rendered.len() - 1 => {
|
'n' if self.page < self.rendered.len() - 1 => {
|
||||||
@@ -424,49 +433,57 @@ impl Tui {
|
|||||||
});
|
});
|
||||||
|
|
||||||
jump_to_page(&mut self.page, &mut self.last_render.rect, prev_page)
|
jump_to_page(&mut self.page, &mut self.last_render.rect, prev_page)
|
||||||
},
|
}
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
KeyCode::Backspace => {
|
KeyCode::Backspace => {
|
||||||
if let BottomMessage::Input(InputCommand::Search(ref mut term)) = self.bottom_msg {
|
if let BottomMessage::Input(InputCommand::Search(ref mut term)) =
|
||||||
|
self.bottom_msg
|
||||||
|
{
|
||||||
term.pop();
|
term.pop();
|
||||||
return Some(InputAction::Redraw);
|
return Some(InputAction::Redraw);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
KeyCode::Right => self.change_page(PageChange::Next, ChangeAmount::Single),
|
KeyCode::Right => self.change_page(PageChange::Next, ChangeAmount::Single),
|
||||||
KeyCode::Down => self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
|
KeyCode::Down => self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
|
||||||
KeyCode::Left => self.change_page(PageChange::Prev, ChangeAmount::Single),
|
KeyCode::Left => self.change_page(PageChange::Prev, ChangeAmount::Single),
|
||||||
KeyCode::Up => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
|
KeyCode::Up => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
|
||||||
KeyCode::Esc => match self.bottom_msg {
|
KeyCode::Esc => match self.bottom_msg {
|
||||||
BottomMessage::Input(_) => {
|
BottomMessage::Help => Some(InputAction::QuitApp),
|
||||||
self.set_bottom_msg(None);
|
_ => {
|
||||||
|
// When we hit escape, we just want to pop off the current message and
|
||||||
|
// show the underlying one.
|
||||||
|
self.set_msg(MessageSetting::Pop);
|
||||||
Some(InputAction::Redraw)
|
Some(InputAction::Redraw)
|
||||||
}
|
}
|
||||||
_ => Some(InputAction::QuitApp)
|
|
||||||
},
|
},
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
let BottomMessage::Input(_) = self.bottom_msg else {
|
let mut default = BottomMessage::default();
|
||||||
|
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) => {
|
||||||
let page = *page;
|
// We need to subtract 1 b/c they're tracked internally as
|
||||||
(page < self.rendered.len()).then(|| {
|
// 0-indexed but input and displayed as 1-indexed
|
||||||
self.set_page(page);
|
let zero_page = page.saturating_sub(1);
|
||||||
InputAction::JumpingToPage(page)
|
let rendered_len = self.rendered.len();
|
||||||
})
|
|
||||||
|
if zero_page < rendered_len {
|
||||||
|
self.set_page(zero_page);
|
||||||
|
Some(InputAction::JumpingToPage(zero_page))
|
||||||
|
} else {
|
||||||
|
self.set_msg(MessageSetting::Some(BottomMessage::Error(
|
||||||
|
format!("Cannot jump to page {page}; there are only {rendered_len} pages in the document")
|
||||||
|
)));
|
||||||
|
Some(InputAction::Redraw)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
InputCommand::Search(term) => {
|
InputCommand::Search(term) => {
|
||||||
let term = term.clone();
|
let term = term.clone();
|
||||||
@@ -474,14 +491,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_bottom_msg(Some(BottomMessage::SearchResults(
|
self.set_msg(MessageSetting::Some(
|
||||||
term.clone()
|
BottomMessage::SearchResults(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_bottom_msg(Some(BottomMessage::Help));
|
self.set_msg(MessageSetting::Reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all the search results
|
// Reset all the search results
|
||||||
@@ -515,7 +532,7 @@ impl Tui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_error(&mut self, err: RenderError) {
|
pub fn show_error(&mut self, err: RenderError) {
|
||||||
self.set_bottom_msg(Some(BottomMessage::Error(match err {
|
self.set_msg(MessageSetting::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}")
|
||||||
@@ -532,17 +549,18 @@ 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_bottom_msg(&mut self, msg: Option<BottomMessage>) {
|
pub fn set_msg(&mut self, msg: MessageSetting) {
|
||||||
match msg {
|
match msg {
|
||||||
Some(mut msg) => {
|
MessageSetting::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);
|
||||||
}
|
}
|
||||||
None => {
|
MessageSetting::Default => self.set_msg(MessageSetting::Some(BottomMessage::default())),
|
||||||
let mut new_bottom = self.prev_msg.take().unwrap_or_default();
|
MessageSetting::Reset => {
|
||||||
std::mem::swap(&mut self.bottom_msg, &mut new_bottom);
|
self.prev_msg = None;
|
||||||
self.prev_msg = Some(new_bottom);
|
self.bottom_msg = BottomMessage::default();
|
||||||
}
|
}
|
||||||
|
MessageSetting::Pop => self.bottom_msg = self.prev_msg.take().unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -565,3 +583,10 @@ enum ChangeAmount {
|
|||||||
WholeScreen,
|
WholeScreen,
|
||||||
Single
|
Single
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum MessageSetting {
|
||||||
|
Some(BottomMessage),
|
||||||
|
Default,
|
||||||
|
Reset,
|
||||||
|
Pop
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user