diff --git a/src/core/commands.rs b/src/core/commands.rs index 88823e2..dc456a3 100644 --- a/src/core/commands.rs +++ b/src/core/commands.rs @@ -34,6 +34,7 @@ pub enum Command { // Configuration options SetExitStrategy(ExitStrategy), + PersistAlternate(bool), SetInputClassifier(Box), AddExitCallback(Box), #[cfg(feature = "static_output")] @@ -70,6 +71,7 @@ impl PartialEq for Command { impl Debug for Command { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Self::PersistAlternate(persist) => write!(f, "PersistAlternate({persist})"), Self::SetData(text) => write!(f, "SetData({text:?})"), Self::AppendData(text) => write!(f, "AppendData({text:?})"), Self::SetPrompt(text) => write!(f, "SetPrompt({text:?})"), diff --git a/src/core/ev_handler.rs b/src/core/ev_handler.rs index 36ba8cb..2ca27ce 100644 --- a/src/core/ev_handler.rs +++ b/src/core/ev_handler.rs @@ -42,7 +42,7 @@ pub fn handle_event( Command::UserInput(InputEvent::Exit) => { p.exit(); is_exited.store(true, std::sync::atomic::Ordering::SeqCst); - term::cleanup(&mut out, &p.exit_strategy, true)?; + term::cleanup(&mut out, &p.exit_strategy, true, p.persist_alternate)?; } Command::UserInput(InputEvent::UpdateUpperMark(mut um)) => { display::draw_for_change(out, p, &mut um)?; @@ -274,6 +274,7 @@ pub fn handle_event( display::write_prompt(out, &p.displayed_prompt, p.rows.try_into().unwrap())?; } Command::SetExitStrategy(es) => p.exit_strategy = es, + Command::PersistAlternate(b) => p.persist_alternate = b, #[cfg(feature = "static_output")] Command::SetRunNoOverflow(val) => p.run_no_overflow = val, #[cfg(feature = "search")] @@ -475,6 +476,26 @@ mod tests { assert_eq!(ps.exit_strategy, ExitStrategy::PagerQuit); } + #[test] + fn set_persist_screen() { + let mut ps = PagerState::new().unwrap(); + let ev = Command::PersistAlternate(true); + let mut out = Vec::new(); + let mut command_queue = CommandQueue::new_zero(); + + handle_event( + ev, + &mut out, + &mut ps, + &mut command_queue, + &Arc::new(AtomicBool::new(false)), + #[cfg(feature = "search")] + &UIA, + ) + .unwrap(); + assert_eq!(ps.persist_alternate, true); + } + #[test] fn add_exit_callback() { let mut ps = PagerState::new().unwrap(); diff --git a/src/core/init.rs b/src/core/init.rs index 4982274..ca6e606 100644 --- a/src/core/init.rs +++ b/src/core/init.rs @@ -133,6 +133,7 @@ pub fn init_core(pager: &Pager, rm: RunMode) -> std::result::Result<(), MinusErr stdout(), &crate::ExitStrategy::PagerQuit, true, + false, )); panic_hook(pinfo); })); @@ -169,7 +170,7 @@ pub fn init_core(pager: &Pager, rm: RunMode) -> std::result::Result<(), MinusErr let mut rm = RUNMODE.lock(); *rm = RunMode::Uninitialized; drop(rm); - term::cleanup(out.as_ref(), &crate::ExitStrategy::PagerQuit, true)?; + term::cleanup(out.as_ref(), &crate::ExitStrategy::PagerQuit, true, false)?; } res }); @@ -188,7 +189,12 @@ pub fn init_core(pager: &Pager, rm: RunMode) -> std::result::Result<(), MinusErr let mut rm = RUNMODE.lock(); *rm = RunMode::Uninitialized; drop(rm); - term::cleanup(out_copy.as_ref(), &crate::ExitStrategy::PagerQuit, true)?; + term::cleanup( + out_copy.as_ref(), + &crate::ExitStrategy::PagerQuit, + true, + false, + )?; } res }); diff --git a/src/core/utils/term.rs b/src/core/utils/term.rs index 01d9cc2..0eb974b 100644 --- a/src/core/utils/term.rs +++ b/src/core/utils/term.rs @@ -60,6 +60,7 @@ pub fn cleanup( mut out: impl io::Write, es: &crate::ExitStrategy, cleanup_screen: bool, + persist_screen: bool, ) -> std::result::Result<(), CleanupError> { if cleanup_screen { // Reverse order of setup. @@ -67,8 +68,14 @@ pub fn cleanup( terminal::disable_raw_mode().map_err(|e| CleanupError::DisableRawMode(e.into()))?; execute!(out, event::DisableMouseCapture) .map_err(|e| CleanupError::DisableMouseCapture(e.into()))?; - execute!(out, terminal::LeaveAlternateScreen) - .map_err(|e| CleanupError::LeaveAlternateScreen(e.into()))?; + if !persist_screen { + execute!(out, terminal::LeaveAlternateScreen) + .map_err(|e| CleanupError::LeaveAlternateScreen(e.into()))?; + } else { + // Clear out the pager line, it's not something you'd want to keep + queue!(out, Clear(terminal::ClearType::CurrentLine)).unwrap(); + out.flush().unwrap(); + } } if *es == crate::ExitStrategy::ProcessQuit { diff --git a/src/pager.rs b/src/pager.rs index 74ce687..02aa6a7 100644 --- a/src/pager.rs +++ b/src/pager.rs @@ -186,6 +186,15 @@ impl Pager { Ok(self.tx.send(Command::SetExitStrategy(es))?) } + /// Set if minus persists the alternate screen buffer on exit. + /// + /// This controls how the pager will behave with regards to clearing the screen when + /// the user presses `q` or `Ctrl+C'. If it is set, instead of clearing the screen + /// when exited, it will continue to show. + pub fn persist_alternate_screen(&self, b: bool) -> Result<(), MinusError> { + Ok(self.tx.send(Command::PersistAlternate(b))?) + } + /// Set whether to display pager if there's less data than /// available screen height /// diff --git a/src/state.rs b/src/state.rs index 20ed0e2..2014cc4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -143,6 +143,9 @@ pub struct PagerState { /// The behaviour to do when user quits the program using `q` or `Ctrl+C` /// See [`ExitStrategy`] for available options pub(crate) exit_strategy: ExitStrategy, + /// The behaviour to do when user quits the program using `q` or `Ctrl+C` + /// See [persist_alternate_screen](crate::pager::Pager::persist_alternate_screen) for more info. + pub(crate) persist_alternate: bool, /// The prompt that should be displayed to the user, formatted with the /// current search index and number of matches (if the search feature is enabled), /// and the current numbers inputted to scroll @@ -194,6 +197,7 @@ impl PagerState { prompt, running: &minus_core::RUNMODE, exit_strategy: ExitStrategy::ProcessQuit, + persist_alternate: false, input_classifier: Box::>::default(), exit_callbacks: Vec::with_capacity(5), message: None,