feat(report): take terminal width into account for wrapping text

This commit is contained in:
Kat Marchán 2021-09-10 12:39:10 -07:00
parent f482dcec6a
commit bc72532465
No known key found for this signature in database
GPG Key ID: AEB529C08A3C7E9E
4 changed files with 35 additions and 19 deletions

View File

@ -19,6 +19,7 @@ owo-colors = "2.0.0"
atty = "0.2.14" atty = "0.2.14"
ci_info = "0.14.2" ci_info = "0.14.2"
textwrap = "0.14.2" textwrap = "0.14.2"
term_size = "0.3.2"
[dev-dependencies] [dev-dependencies]
semver = "1.0.4" semver = "1.0.4"

View File

@ -95,8 +95,9 @@ fn get_default_printer(_err: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandl
} else { } else {
atty::is(Stream::Stdout) && atty::is(Stream::Stderr) && !ci_info::is_ci() atty::is(Stream::Stdout) && atty::is(Stream::Stderr) && !ci_info::is_ci()
}; };
let size = term_size::dimensions().unwrap_or((80, 0)).0;
if fancy { if fancy {
Box::new(GraphicalReportHandler::new()) Box::new(GraphicalReportHandler::new().with_width(size))
} else { } else {
Box::new(NarratableReportHandler) Box::new(NarratableReportHandler)
} }

View File

@ -21,6 +21,7 @@ See [crate::set_hook] for more details on customizing your global printer.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GraphicalReportHandler { pub struct GraphicalReportHandler {
pub(crate) linkify_code: bool, pub(crate) linkify_code: bool,
pub(crate) termwidth: usize,
pub(crate) theme: GraphicalTheme, pub(crate) theme: GraphicalTheme,
} }
@ -30,6 +31,7 @@ impl GraphicalReportHandler {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
linkify_code: true, linkify_code: true,
termwidth: 200,
theme: GraphicalTheme::default(), theme: GraphicalTheme::default(),
} }
} }
@ -38,6 +40,7 @@ impl GraphicalReportHandler {
pub fn new_themed(theme: GraphicalTheme) -> Self { pub fn new_themed(theme: GraphicalTheme) -> Self {
Self { Self {
linkify_code: true, linkify_code: true,
termwidth: 200,
theme, theme,
} }
} }
@ -47,6 +50,18 @@ impl GraphicalReportHandler {
self.linkify_code = false; self.linkify_code = false;
self self
} }
/// Set a theme for this handler.
pub fn with_theme(mut self, theme: GraphicalTheme) -> Self {
self.theme = theme;
self
}
/// Sets the width to wrap the report at.
pub fn with_width(mut self, width: usize) -> Self {
self.termwidth = width;
self
}
} }
impl Default for GraphicalReportHandler { impl Default for GraphicalReportHandler {
@ -66,6 +81,7 @@ impl GraphicalReportHandler {
diagnostic: &(dyn Diagnostic), diagnostic: &(dyn Diagnostic),
) -> fmt::Result { ) -> fmt::Result {
self.render_header(f, diagnostic)?; self.render_header(f, diagnostic)?;
writeln!(f)?;
self.render_causes(f, diagnostic)?; self.render_causes(f, diagnostic)?;
if let Some(snippets) = diagnostic.snippets() { if let Some(snippets) = diagnostic.snippets() {
@ -80,11 +96,6 @@ impl GraphicalReportHandler {
} }
fn render_header(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { fn render_header(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result {
let (severity_style, severity_icon) = match diagnostic.severity() {
Some(Severity::Error) | None => (self.theme.styles.error, self.theme.characters.x),
Some(Severity::Warning) => (self.theme.styles.warning, self.theme.characters.warning),
Some(Severity::Advice) => (self.theme.styles.advice, self.theme.characters.point_right),
};
write!(f, "{}", self.theme.characters.hbar.to_string().repeat(4))?; write!(f, "{}", self.theme.characters.hbar.to_string().repeat(4))?;
if self.linkify_code && diagnostic.url().is_some() && diagnostic.code().is_some() { if self.linkify_code && diagnostic.url().is_some() && diagnostic.code().is_some() {
let url = diagnostic.url().unwrap(); // safe let url = diagnostic.url().unwrap(); // safe
@ -100,24 +111,25 @@ impl GraphicalReportHandler {
write!(f, "[{}]", code.style(self.theme.styles.code))?; write!(f, "[{}]", code.style(self.theme.styles.code))?;
} }
writeln!(f, "{}", self.theme.characters.hbar.to_string().repeat(20),)?; writeln!(f, "{}", self.theme.characters.hbar.to_string().repeat(20),)?;
writeln!(f)?;
// TODO: terminal width support
let initial_indent = format!(" {} ", severity_icon.style(severity_style));
let rest_indent = format!(" {} ", self.theme.characters.vbar.style(severity_style));
let opts = textwrap::Options::new(80)
.initial_indent(&initial_indent)
.subsequent_indent(&rest_indent);
writeln!(f, "{}", textwrap::fill(&diagnostic.to_string(), opts))?;
Ok(()) Ok(())
} }
fn render_causes(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { fn render_causes(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result {
let severity_style = match diagnostic.severity() { let (severity_style, severity_icon) = match diagnostic.severity() {
Some(Severity::Error) | None => self.theme.styles.error, Some(Severity::Error) | None => (self.theme.styles.error, self.theme.characters.x),
Some(Severity::Warning) => self.theme.styles.warning, Some(Severity::Warning) => (self.theme.styles.warning, self.theme.characters.warning),
Some(Severity::Advice) => self.theme.styles.advice, Some(Severity::Advice) => (self.theme.styles.advice, self.theme.characters.point_right),
}; };
let initial_indent = format!(" {} ", severity_icon.style(severity_style));
let rest_indent = format!(" {} ", self.theme.characters.vbar.style(severity_style));
let width = self.termwidth.saturating_sub(4);
let opts = textwrap::Options::new(width)
.initial_indent(&initial_indent)
.subsequent_indent(&rest_indent);
writeln!(f, "{}", textwrap::fill(&diagnostic.to_string(), opts))?;
if let Some(cause) = diagnostic.source() { if let Some(cause) = diagnostic.source() {
let mut cause_iter = Chain::new(cause).peekable(); let mut cause_iter = Chain::new(cause).peekable();
while let Some(error) = cause_iter.next() { while let Some(error) = cause_iter.next() {
@ -135,7 +147,7 @@ impl GraphicalReportHandler {
let rest_indent = format!(" {} ", self.theme.characters.vbar) let rest_indent = format!(" {} ", self.theme.characters.vbar)
.style(severity_style) .style(severity_style)
.to_string(); .to_string();
let opts = textwrap::Options::new(80) let opts = textwrap::Options::new(width)
.initial_indent(&initial_indent) .initial_indent(&initial_indent)
.subsequent_indent(&rest_indent); .subsequent_indent(&rest_indent);
writeln!(f, "{}", textwrap::fill(&error.to_string(), opts))?; writeln!(f, "{}", textwrap::fill(&error.to_string(), opts))?;

View File

@ -9,6 +9,7 @@ fn fmt_report(diag: Report) -> String {
// Mostly for dev purposes. // Mostly for dev purposes.
if std::env::var("STYLE").is_ok() { if std::env::var("STYLE").is_ok() {
GraphicalReportHandler::new_themed(GraphicalTheme::unicode()) GraphicalReportHandler::new_themed(GraphicalTheme::unicode())
.with_width(80)
.render_report(&mut out, diag.as_ref()) .render_report(&mut out, diag.as_ref())
.unwrap(); .unwrap();
} else if std::env::var("NARRATED").is_ok() { } else if std::env::var("NARRATED").is_ok() {
@ -17,6 +18,7 @@ fn fmt_report(diag: Report) -> String {
.unwrap(); .unwrap();
} else { } else {
GraphicalReportHandler::new_themed(GraphicalTheme::unicode_nocolor()) GraphicalReportHandler::new_themed(GraphicalTheme::unicode_nocolor())
.with_width(80)
.render_report(&mut out, diag.as_ref()) .render_report(&mut out, diag.as_ref())
.unwrap(); .unwrap();
}; };