-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
51 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,62 @@ | ||
use crate::process::CommandEx; | ||
use crate::process::Spawned; | ||
use anstream::stream::IsTerminal; | ||
use anyhow::Result; | ||
use std::io::stdout; | ||
use std::path::Path; | ||
use std::process::Child; | ||
use std::panic::resume_unwind; | ||
use std::process::ChildStdin; | ||
use std::process::Command; | ||
use std::process::Stdio; | ||
use std::thread; | ||
use which::which; | ||
|
||
pub fn open() -> Result<Option<Spawned<Child>>> { | ||
if !stdout().is_terminal() { | ||
return Ok(None); | ||
} | ||
pub struct Pager(Command); | ||
|
||
// We could eventually do something more complex, such as parsing PAGER | ||
// env variable like `bat` does https://github.com/sharkdp/bat/issues/158, | ||
// but that would be an overkill for our use case. | ||
if let Ok(path) = which("less") { | ||
// F = Exit immediately if the text fits the entire screen. | ||
// I = Ignore case when searching. | ||
// r = Causes "raw" control characters to be displayed. | ||
// X = Disables sending the termcap (in)itialization. | ||
return spawn(&path, &["-FIrX"]); | ||
} | ||
impl Pager { | ||
pub fn detect() -> Option<Pager> { | ||
if !stdout().is_terminal() { | ||
return None; | ||
} | ||
|
||
// We could eventually do something more complex, such as parsing PAGER | ||
// env variable like `bat` does https://github.com/sharkdp/bat/issues/158, | ||
// but that would be an overkill for our use case. | ||
|
||
if let Ok(path) = which("less") { | ||
let mut command = Command::new(path); | ||
// F = Exit immediately if the text fits the entire screen. | ||
// I = Ignore case when searching. | ||
// r = Causes "raw" control characters to be displayed. | ||
// X = Disables sending the termcap (de)itialization. | ||
command.arg("-FIrX"); | ||
return Some(Pager(command)); | ||
} | ||
|
||
if let Ok(path) = which("more") { | ||
return Some(Pager(Command::new(path))); | ||
} | ||
|
||
if let Ok(path) = which("more") { | ||
return spawn(&path, &[]); | ||
None | ||
} | ||
|
||
Ok(None) | ||
} | ||
pub fn open( | ||
&mut self, | ||
callback: impl Fn(&mut ChildStdin) -> Result<()> + Send + 'static, | ||
) -> Result<()> { | ||
let mut pager = self.0.stdin(Stdio::piped()).spawn_with_context()?; | ||
let mut stdin = pager.take_stdin().expect("could not get pager stdin"); | ||
|
||
let thread = thread::spawn(move || { | ||
callback(&mut stdin.inner).map_err(|err| { | ||
stdin | ||
.context | ||
.apply(err) | ||
.context("failed to write to child process stdin") | ||
}) | ||
}); | ||
|
||
fn spawn(path: &Path, args: &[&str]) -> Result<Option<Spawned<Child>>> { | ||
Command::new(path) | ||
.args(args) | ||
.stdin(Stdio::piped()) | ||
.spawn_with_context() | ||
.map(Some) | ||
pager.wait()?; | ||
thread.join().map_err(resume_unwind)??; | ||
|
||
Ok(()) | ||
} | ||
} |