Ignore BrokenPipe error when piping to pager
If the pager terminates before we wrote everything to stdout, we get a BrokenPipe error. We just want to ignore this error but the print*! macros panic. Therefore we replace the calls to print*! with calls to write*! and ignore the BrokenPipe error kind when handling the error.
This commit is contained in:
parent
02b3116ecd
commit
5d37789837
@ -1,6 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::io::{self, Write};
|
||||
|
||||
use html2text::render::text_renderer;
|
||||
|
||||
use crate::doc;
|
||||
@ -20,35 +22,46 @@ impl RichViewer {
|
||||
}
|
||||
}
|
||||
|
||||
fn print(&self, s: &str) {
|
||||
fn print_doc(&self, doc: &doc::Doc) -> io::Result<()> {
|
||||
self.print_heading(&doc.title, 1)?;
|
||||
self.print_opt(doc.definition.as_deref())?;
|
||||
self.print_opt(doc.description.as_deref())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print(&self, s: &str) -> io::Result<()> {
|
||||
let lines = html2text::from_read_rich(s.as_bytes(), self.line_length);
|
||||
for line in lines {
|
||||
for element in line.iter() {
|
||||
if let text_renderer::TaggedLineElement::Str(ts) = element {
|
||||
self.render_string(ts);
|
||||
self.render_string(ts)?;
|
||||
}
|
||||
}
|
||||
println!();
|
||||
writeln!(io::stdout())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_opt(&self, s: Option<&str>) {
|
||||
fn print_opt(&self, s: Option<&str>) -> io::Result<()> {
|
||||
if let Some(s) = s {
|
||||
println!();
|
||||
self.print(s);
|
||||
writeln!(io::stdout())?;
|
||||
self.print(s)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn print_heading(&self, s: &str, level: usize) {
|
||||
print!("{}{} ", termion::style::Bold, "#".repeat(level));
|
||||
self.print(s);
|
||||
print!("{}", termion::style::Reset);
|
||||
fn print_heading(&self, s: &str, level: usize) -> io::Result<()> {
|
||||
let prefix = "#".repeat(level);
|
||||
write!(io::stdout(), "{}{} ", termion::style::Bold, prefix)?;
|
||||
self.print(s)?;
|
||||
write!(io::stdout(), "{}", termion::style::Reset)
|
||||
}
|
||||
|
||||
fn render_string(&self, ts: &RichString) {
|
||||
fn render_string(&self, ts: &RichString) -> io::Result<()> {
|
||||
let start_style = get_style(ts, get_start_style);
|
||||
let end_style = get_style(ts, get_end_style);
|
||||
print!("{}{}{}", start_style, ts.s, end_style);
|
||||
write!(io::stdout(), "{}{}{}", start_style, ts.s, end_style)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,10 +69,20 @@ impl viewer::Viewer for RichViewer {
|
||||
fn open(&self, doc: &doc::Doc) -> anyhow::Result<()> {
|
||||
viewer::spawn_pager();
|
||||
|
||||
self.print_heading(&doc.title, 1);
|
||||
self.print_opt(doc.definition.as_deref());
|
||||
self.print_opt(doc.description.as_deref());
|
||||
self.print_doc(doc)
|
||||
.or_else(ignore_pipe_error)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn ignore_pipe_error(error: io::Error) -> io::Result<()> {
|
||||
// If the pager is terminated before we can write everything to stdout, we will receive a
|
||||
// BrokenPipe error. But we don’t want to report this error to the user. See also:
|
||||
// https://github.com/rust-lang/rust/issues/46016
|
||||
if error.kind() == io::ErrorKind::BrokenPipe {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user