Skip to content

Add quit_if_one_screen option for dynamic_paging mode#157

Open
keith-hall wants to merge 3 commits intoAMythicDev:mainfrom
forkeith:copilot/enable-auto-quit-dynamic-paging
Open

Add quit_if_one_screen option for dynamic_paging mode#157
keith-hall wants to merge 3 commits intoAMythicDev:mainfrom
forkeith:copilot/enable-auto-quit-dynamic-paging

Conversation

@keith-hall
Copy link
Copy Markdown

Adds a less -F-style auto-quit for dynamic_paging: when content fits on one screen and the end of output is signalled, the pager exits automatically and preserves the content on the terminal. Unlike set_run_no_overflow (static mode only), this supports buffering use cases where the total line count is unknown upfront.

New API

  • Pager::set_quit_if_one_screen(bool) — enables the feature. When true, auto-quits with content preserved if it fits on screen once end-of-output is signalled. Otherwise the pager stays open for user scrolling/quitting.
  • Pager::end_of_output() — explicitly signals end-of-output without dropping the Pager handle. Preferred when the caller needs to keep the handle alive (e.g. bat's use case).
  • Drop-based signal — dropping all application-side Pager clones also fires the signal automatically via an AliveGuard RAII type.

Internals

  • AliveGuard holds a channel sender and fires Command::CheckQuitIfOneScreen on drop; dynamic_paging receives its own independent Arc<AliveGuard> so only the caller's clones count toward the signal.
  • PagerState::quit_if_one_screen: bool tracks the setting; the CheckQuitIfOneScreen handler restores the terminal, writes formatted lines to the main screen, then respects the configured ExitStrategy.

Usage

let pager = Pager::new();
pager.set_quit_if_one_screen(true)?;

let pager2 = pager.clone();
let t = std::thread::spawn(move || dynamic_paging(pager2));

pager.push_str("Hello\nWorld\n")?;

// Option A: explicit signal, handle stays alive
pager.end_of_output()?;

// Option B: implicit signal via drop
drop(pager);

t.join().unwrap()?;

@academician
Copy link
Copy Markdown
Collaborator

@keith-hall Thanks for the contribution! I'm still reviewing the code, but small question for you - why create a new setting instead of reusing set_run_no_overflow/Command::SetRunNoOverflow? Maybe there's some nuance I'm not understanding, but they seem like they communicate basically the same thing - just with different behavior in dynamic vs static paging mode.

@keith-hall
Copy link
Copy Markdown
Author

keith-hall commented Apr 1, 2026

Hi, thanks for taking a look, and good question. Mainly I just wasn't sure whether it would be desirable to re-use the same setting/command when it feels like a different situation - with set_run_no_overflow, it already knows whether the content will overflow and decides not to change screen mode and avoids initializing the pager. With quit_if_one_screen, apart from it borrowing less terminology which many users are likely familiar with, it does indeed do just that - as I understand it, minus has already at this point changed screen mode and initialized the paging, but then sees the end of the input and quits itself again, to output the content "normally" to stdout.

Edit: I forgot to mention, I would be happy to unify and re-use the set_run_no_overflow naming if you are fine with it 👍

@keith-hall
Copy link
Copy Markdown
Author

I suspect it is the AliveGuard which has caused the CI test failure? Maybe it makes sense to drop that and just use the explicit end_of_output signal?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants