1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
// When no feature is active this crate is unusable but contains lots of
// unused imports and dead code. To avoid useless warnings about this they
// are allowed when no feature is active.
//! `minus`: A library for asynchronous terminal [paging], written in Rust.
//!
//! If you want to learn about its motivation and features, please take a look into it's [README].
//!
//! # Overview
//! When getting started with minus, the two most important concepts to get familier with are:
//! * The [Pager] type: which acts as a bridge between your application and minus. It is used
//! to pass data and configure minus before and after starting the pager.
//! * Initialization functions: This includes the [dynamic_paging] and [page_all] functions which
//! take a [Pager] as argument. They are responsible for generating the initial state and starting
//! the pager.
//!
//! See the docs for the respective items to learn more on its usage.
//!
//! # Examples
//!
//! ## Threads
//!
//! ```rust,no_run
//! use minus::{dynamic_paging, MinusError, Pager};
//! use std::{
//! fmt::Write,
//! thread::{spawn, sleep},
//! time::Duration
//! };
//!
//! fn main() -> Result<(), MinusError> {
//! // Initialize the pager
//! let mut pager = Pager::new();
//! // Run the pager in a separate thread
//! let pager2 = pager.clone();
//! let pager_thread = spawn(move || dynamic_paging(pager2));
//!
//! for i in 0..=100_u32 {
//! writeln!(pager, "{}", i);
//! sleep(Duration::from_millis(100));
//! }
//! pager_thread.join().unwrap()?;
//! Ok(())
//! }
//! ```
//!
//! ## tokio
//!
//! ```rust,no_run
//! use minus::{dynamic_paging, MinusError, Pager};
//! use std::time::Duration;
//! use std::fmt::Write;
//! use tokio::{join, task::spawn_blocking, time::sleep};
//!
//! #[tokio::main]
//! async fn main() -> Result<(), MinusError> {
//! // Initialize the pager
//! let mut pager = Pager::new();
//! // Asynchronously send data to the pager
//! let increment = async {
//! let mut pager = pager.clone();
//! for i in 0..=100_u32 {
//! writeln!(pager, "{}", i);
//! sleep(Duration::from_millis(100)).await;
//! }
//! Result::<_, MinusError>::Ok(())
//! };
//! // spawn_blocking(dynamic_paging(...)) creates a separate thread managed by the tokio
//! // runtime and runs the async_paging inside it
//! let pager = pager.clone();
//! let (res1, res2) = join!(spawn_blocking(move || dynamic_paging(pager)), increment);
//! // .unwrap() unwraps any error while creating the tokio task
//! // The ? mark unpacks any error that might have occurred while the
//! // pager is running
//! res1.unwrap()?;
//! res2?;
//! Ok(())
//! }
//! ```
//!
//! ## Static output
//! ```rust,no_run
//! use std::fmt::Write;
//! use minus::{MinusError, Pager, page_all};
//!
//! fn main() -> Result<(), MinusError> {
//! // Initialize a default static configuration
//! let mut output = Pager::new();
//! // Push numbers blockingly
//! for i in 0..=30 {
//! writeln!(output, "{}", i)?;
//! }
//! // Run the pager
//! minus::page_all(output)?;
//! // Return Ok result
//! Ok(())
//! }
//! ```
//!
//! **Note:**
//! In static mode, `minus` doesn't start the pager and just prints the content if the current terminal size can
//! display all lines. You can of course change this behaviour.
//!
//! ## Default keybindings
//!
//! Here is the list of default key/mouse actions handled by `minus`.
//!
//! **A `[n] key` means that you can precede the key by an integer**.
//!
//! | Action | Description |
//! |---------------------|------------------------------------------------------------------------------|
//! | Ctrl+C/q | Quit the pager |
//! | \[n\] Arrow Up/k | Scroll up by n number of line(s). If n is omitted, scroll up by 1 line |
//! | \[n\] Arrow Down/j | Scroll down by n number of line(s). If n is omitted, scroll down by 1 line |
//! | Ctrl+h | Turn off line wrapping and allow horizontal scrolling |
//! | \[n\] Arrow left/h | Scroll left by n number of line(s). If n is omitted, scroll up by 1 line |
//! | \[n\] Arrow right/l | Scroll right by n number of line(s). If n is omitted, scroll down by 1 line |
//! | Page Up | Scroll up by entire page |
//! | Page Down | Scroll down by entire page |
//! | \[n\] Enter | Scroll down by n number of line(s). |
//! | 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 |
//! | \[n\] G | Go to the very bottom of the output. If n is present, goes to that line |
//! | 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 |
//! | Ctrl+f | Toggle [follow-mode] |
//! | / | Start forward search |
//! | ? | Start backward search |
//! | Esc | Cancel search input |
//! | n | Go to the next search match |
//! | p | Go to the next previous match |
//!
//! End-applications are free to change these bindings to better suit their needs. See docs for
//! [Pager::set_input_classifier] function and [input] module.
//!
//! ## Key Bindings Available at Search Prompt
//!
//! | Key Bindings | Description |
//! |-------------------|-----------------------------------------------------|
//! | Esc | Cancel the search |
//! | Enter | Confirm the search query |
//! | Backspace | Remove the character before the cursor |
//! | Delete | Remove the character under the cursor |
//! | Arrow Left | Move cursor towards left |
//! | Arrow right | Move cursor towards right |
//! | Ctrl+Arrow left | Move cursor towards left word by word |
//! | Ctrl+Arrow right | Move cursor towards right word by word |
//! | Home | Move cursor at the beginning pf search query |
//! | End | Move cursor at the end pf search query |
//!
//! Currently these cannot be changed by applications but this may be supported in the future.
//!
//! [`tokio`]: https://docs.rs/tokio
//! [`async-std`]: https://docs.rs/async-std
//! [`Threads`]: std::thread
//! [follow-mode]: struct.Pager.html#method.follow_output
//! [paging]: https://en.wikipedia.org/wiki/Terminal_pager
//! [README]: https://github.com/arijit79/minus#motivation
pub use dynamic_paging;
pub use page_all;
pub use RunMode;
pub use SearchMode;
pub use MinusError;
pub use Pager;
pub use PagerState;
/// A convenient type for `Vec<Box<dyn FnMut() + Send + Sync + 'static>>`
pub type ExitCallbacks = ;
/// Result type returned by most minus's functions
type Result<T = , E = MinusError> = Result;
/// Behaviour that happens when the pager is exited
/// Enum indicating whether to display the line numbers or not.
///
/// Note that displaying line numbers may be less performant than not doing it.
/// `minus` tries to do as quickly as possible but the numbers and padding
/// still have to be computed.
///
/// This implements [`Not`](std::ops::Not) to allow turning on/off line numbers
/// when they where not locked in by the binary displaying the text.