A fast, asynchronous terminal paging library for Rust. minus
provides high
level functions to easily embed a pager for any terminal application. Due
to the asynchronous nature of minus
, the pager's data and configuration can be updated at any time.
minus
supports both [tokio
] as well as [async-std
] runtimes. What's more,
if you only want to use minus
for serving static output, you can simply opt
out of these dynamic features, see the Usage section below.
minus
was started by me for my work on [pijul
]. I was unsatisfied with the
existing options like pager
and moins
.
pager
:
more
or less
.moins
:
tokio
] for your application ? Use the tokio_lib
feature.async-std
] for your application ? Use the async_std_lib
feature.static_output
feature.In your Cargo.toml
file:
```toml [dependencies.minus] version = "4.0.0"
features = ["tokio_lib"]
features = ["asyncstdlib"]
features = ["static_output"]
features = ["search"] ```
All examples are available in the examples
directory and you can run them
using cargo
. Remember to set the correct feature for the targeted example
(e.g.: cargo run --example=dyn_tokio --features=tokio_lib
).
Using [tokio
]:
```rust use futures::join; use tokio::time::sleep; use minus::{Pager, asyncstdupdating}; use std::fmt::Write; use std::time::Duration;
async fn main() -> Result<(), Box
// Asynchronously push numbers to the output
let increment = async {
for i in 0..=30_u32 {
let mut guard = pager.lock().await;
writeln!(guard, "{}", i)?;
// Also you can use this syntax
// guard.push_str(&format("{}\n", i));
drop(guard);
sleep(Duration::from_millis(100)).await;
}
// Dynamic paging should hint the pager that it's stream of data has
// ended
let mut guard.lock().await;
guard.end_data_stream();
// Return an Ok result
Result::<_, std::fmt::Error>::Ok(())
};
// Join the futures
let (res1, res2) = join!(
minus::tokio_updating(pager.clone()),
increment
);
// Check for errors
res1?;
res2?;
// Return Ok result
Ok(())
} ```
Using [async-std
]:
```rust use asyncstd::task::sleep; use futures::join; use minus::{Pager, asyncstd_updating}; use std::fmt::Write; use std::time::Duration;
async fn main() -> Result<(), Box
// Asynchronously push numbers to the output
let increment = async {
for i in 0..=30_u32 {
let mut guard = pager.lock().await;
writeln!(guard, "{}", i)?;
// Also you can use this syntax
// guard.push_str(&format("{}\n", i));
drop(guard);
sleep(Duration::from_millis(100)).await;
}
// Dynamic paging should hint the pager that it's stream of data has
// ended
let mut guard.lock().await;
guard.end_data_stream();
// Return an Ok result
Result::<_, std::fmt::Error>::Ok(())
};
// Join the futures
let (res1, res2) = join!(
minus::async_std_updating(guard.clone()), increment);
// Check for errors
res1?;
res2?;
// Return Ok result
Ok(())
} ```
Some static output:
```rust use std::fmt::Write; use minus::{Pager, page_all};
fn main() -> Result<(), Box
If there are more rows in the terminal than the number of lines in the given
data, minus
will simply print the data and quit. This only works in static
paging since asynchronous paging could still receive more data that makes it
pass the limit.
Here is the list of default key bindings.
| Action | Description | | ---------- | ------------- | | Ctrl+C/q | Quit the pager | | Arrow Up/k | Scroll up by one line | | Arrow Down/j | Scroll down by one line | | Page Up | Scroll up by entire page | | Page Down | Scroll down by entire page | | Enter | Scroll down by one line or clear prompt messages | | Space | Scroll down by one page | | Ctrl+U/u | Scroll up by half a screen | | Ctrl+D/d | Scroll down by half a screen | | g | Go to the very top of the output | | G | Go to the very bottom of the output | | Mouse scroll Up | Scroll up by 5 lines | | Mouse scroll Down | Scroll down by 5 lines | | Ctrl+L | Toggle line numbers if not forced enabled/disabled | | / | Start forward search | | ? | Start backward search | | Esc | Cancel search input | | n | Go to the next search match | | p | Go to the next previous match |
Applications can customize these keybindings to better suite there needs
Unless explicitly stated, all works to minus
are dual licensed under the
MIT License and Apache License 2.0
Issues and pull requests are more than welcome.
See CONTRIBUTING.md on how to contribute to minus
.
Thank you to everyone here for giving there time and contribution to minus
* @rezural
* @poliorcetics
* @danieleades
* @mark-a
* @mkatychev
* @tomstoneham
* @Hardy7cc
* @tomstoneham
We are open to discussion and thoughts om improving minus
. Join us at
Zulip