feat(printer): rename default printer and consistify some naming conventions with printing

BREAKING CHANGE: This is a significant API break.
This commit is contained in:
Kat Marchán 2021-08-21 22:52:06 -07:00
parent e980b72373
commit aafa4a3de1
No known key found for this signature in database
GPG Key ID: AEB529C08A3C7E9E
6 changed files with 113 additions and 51 deletions

View File

@ -8,34 +8,52 @@ use crate::protocol::{Diagnostic, DiagnosticReportPrinter, DiagnosticSnippet, Se
use crate::{SourceSpan, SpanContents}; use crate::{SourceSpan, SpanContents};
/** /**
Reference implementation of the [DiagnosticReportPrinter] trait. This is generally A [DiagnosticReportPrinter] that displays a given [crate::DiagnosticReport] in a quasi-graphical way, using terminal colors, unicode drawing characters, and other such things.
good enough for simple use-cases, and is the default one installed with `miette`,
but you might want to implement your own if you want custom reporting for your This is the default reporter bundled with `miette`.
tool or app.
This printer can be customized by using `new_themed()` and handing it a [GraphicalTheme] of your own creation (or using one of its own defaults!)
See [crate::set_printer] for more details on customizing your global printer.
## Example
```
use miette::{GraphicalReportPrinter, GraphicalTheme};
miette::set_printer(GraphicalReportPrinter::new_themed(GraphicalTheme::unicode_nocolor()));
```
*/ */
pub struct DefaultReportPrinter { #[derive(Debug, Clone)]
pub(crate) theme: MietteTheme, pub struct GraphicalReportPrinter {
pub(crate) theme: GraphicalTheme,
} }
impl DefaultReportPrinter { impl GraphicalReportPrinter {
/// Create a new [GraphicalReportPrinter] with the default
/// [GraphicalTheme]. This will use both unicode characters and colors.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
theme: MietteTheme::default(), theme: GraphicalTheme::default(),
} }
} }
pub fn new_themed(theme: MietteTheme) -> Self { ///Create a new [GraphicalReportPrinter] with a given [GraphicalTheme].
pub fn new_themed(theme: GraphicalTheme) -> Self {
Self { theme } Self { theme }
} }
} }
impl Default for DefaultReportPrinter { impl Default for GraphicalReportPrinter {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl DefaultReportPrinter { impl GraphicalReportPrinter {
/// Render a [Diagnostic]. This function is mostly internal and meant to
/// be called by the toplevel [DiagnosticReportPrinter] handler, but is
/// made public to make it easier (possible) to test in isolation from
/// global state.
pub fn render_report( pub fn render_report(
&self, &self,
f: &mut impl fmt::Write, f: &mut impl fmt::Write,
@ -116,7 +134,7 @@ impl DefaultReportPrinter {
Ok(()) Ok(())
} }
fn render_snippet(&self, f: &mut impl fmt::Write, snippet: &DiagnosticSnippet) -> fmt::Result { fn render_snippet(&self, f: &mut impl fmt::Write, snippet: &DiagnosticSnippet<'_>) -> fmt::Result {
let (contents, lines) = self.get_lines(snippet)?; let (contents, lines) = self.get_lines(snippet)?;
// Highlights are the bits we're going to underline in our overall // Highlights are the bits we're going to underline in our overall
@ -408,7 +426,7 @@ impl DefaultReportPrinter {
fn get_lines<'a>( fn get_lines<'a>(
&'a self, &'a self,
snippet: &'a DiagnosticSnippet, snippet: &'a DiagnosticSnippet<'_>,
) -> Result<(Box<dyn SpanContents + 'a>, Vec<Line>), fmt::Error> { ) -> Result<(Box<dyn SpanContents + 'a>, Vec<Line>), fmt::Error> {
let context_data = snippet let context_data = snippet
.source .source
@ -463,7 +481,7 @@ impl DefaultReportPrinter {
} }
} }
impl DiagnosticReportPrinter for DefaultReportPrinter { impl DiagnosticReportPrinter for GraphicalReportPrinter {
fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result { fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() { if f.alternate() {
return fmt::Debug::fmt(diagnostic, f); return fmt::Debug::fmt(diagnostic, f);

View File

@ -1,6 +1,5 @@
/*! /*!
Basic reporter for Diagnostics. Probably good enough for most use-cases, Reporters included with `miette`.
but largely meant to be an example.
*/ */
use std::fmt; use std::fmt;
@ -10,11 +9,11 @@ use once_cell::sync::OnceCell;
use crate::protocol::{Diagnostic, DiagnosticReportPrinter, Severity}; use crate::protocol::{Diagnostic, DiagnosticReportPrinter, Severity};
use crate::MietteError; use crate::MietteError;
pub use default_printer::*; pub use graphical_printer::*;
pub use narratable_printer::*; pub use narratable_printer::*;
pub use theme::*; pub use theme::*;
mod default_printer; mod graphical_printer;
mod narratable_printer; mod narratable_printer;
mod theme; mod theme;
@ -22,8 +21,8 @@ static REPORTER: OnceCell<Box<dyn DiagnosticReportPrinter + Send + Sync + 'stati
OnceCell::new(); OnceCell::new();
/// Set the global [DiagnosticReportPrinter] that will be used when you report /// Set the global [DiagnosticReportPrinter] that will be used when you report
/// using [DiagnosticReport]. /// using [crate::DiagnosticReport].
pub fn set_reporter( pub fn set_printer(
reporter: impl DiagnosticReportPrinter + Send + Sync + 'static, reporter: impl DiagnosticReportPrinter + Send + Sync + 'static,
) -> Result<(), MietteError> { ) -> Result<(), MietteError> {
REPORTER REPORTER
@ -31,9 +30,9 @@ pub fn set_reporter(
.map_err(|_| MietteError::SetPrinterFailure) .map_err(|_| MietteError::SetPrinterFailure)
} }
/// Used by [DiagnosticReport] to fetch the reporter that will be used to /// Used by [crate::DiagnosticReport] to fetch the reporter that will be used to
/// print stuff out. /// print stuff out.
pub fn get_reporter() -> &'static (dyn DiagnosticReportPrinter + Send + Sync + 'static) { pub(crate) fn get_printer() -> &'static (dyn DiagnosticReportPrinter + Send + Sync + 'static) {
&**REPORTER.get_or_init(get_default_printer) &**REPORTER.get_or_init(get_default_printer)
} }
@ -46,8 +45,8 @@ fn get_default_printer() -> Box<dyn DiagnosticReportPrinter + Send + Sync + 'sta
atty::is(Stream::Stdout) && atty::is(Stream::Stderr) && !ci_info::is_ci() atty::is(Stream::Stdout) && atty::is(Stream::Stderr) && !ci_info::is_ci()
}; };
if fancy { if fancy {
Box::new(DefaultReportPrinter { Box::new(GraphicalReportPrinter {
theme: MietteTheme::default(), theme: GraphicalTheme::default(),
}) })
} else { } else {
Box::new(NarratableReportPrinter) Box::new(NarratableReportPrinter)
@ -55,9 +54,9 @@ fn get_default_printer() -> Box<dyn DiagnosticReportPrinter + Send + Sync + 'sta
} }
/// Literally what it says on the tin. /// Literally what it says on the tin.
pub struct JokeReporter; pub struct JokePrinter;
impl DiagnosticReportPrinter for JokeReporter { impl DiagnosticReportPrinter for JokePrinter {
fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result { fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() { if f.alternate() {
return fmt::Debug::fmt(diagnostic, f); return fmt::Debug::fmt(diagnostic, f);

View File

@ -10,6 +10,7 @@ good enough for simple use-cases, and is the default one installed with `miette`
but you might want to implement your own if you want custom reporting for your but you might want to implement your own if you want custom reporting for your
tool or app. tool or app.
*/ */
#[derive(Debug, Clone)]
pub struct NarratableReportPrinter; pub struct NarratableReportPrinter;
impl NarratableReportPrinter { impl NarratableReportPrinter {

View File

@ -1,55 +1,91 @@
use atty::Stream; use atty::Stream;
use owo_colors::Style; use owo_colors::Style;
pub struct MietteTheme { /**
pub characters: MietteCharacters, Theme used by [crate::GraphicalReportPrinter] to render fancy [crate::Diagnostic] reports.
pub styles: MietteStyles,
A theme consists of two things: the set of characters to be used for drawing,
and the [owo_colors::Style]s to be used to paint various items.
You can create your own custom graphical theme using this type, or you can use
one of the predefined ones using the methods below.
*/
#[derive(Debug, Clone)]
pub struct GraphicalTheme {
/// Characters to be used for drawing.
pub characters: ThemeCharacters,
/// Styles to be used for painting.
pub styles: ThemeStyles,
} }
impl MietteTheme { impl GraphicalTheme {
pub fn basic() -> Self { /// ASCII-art-based graphical drawing, with ANSI styling.
pub fn ascii() -> Self {
Self { Self {
characters: MietteCharacters::ascii(), characters: ThemeCharacters::ascii(),
styles: MietteStyles::ansi(), styles: ThemeStyles::ansi(),
} }
} }
/// Graphical theme that draws using both ansi colors and unicode characters.
pub fn unicode() -> Self { pub fn unicode() -> Self {
Self { Self {
characters: MietteCharacters::unicode(), characters: ThemeCharacters::unicode(),
styles: MietteStyles::ansi(), styles: ThemeStyles::ansi(),
} }
} }
/// Graphical theme that draws in monochrome, while still using unicode
/// characters.
pub fn unicode_nocolor() -> Self { pub fn unicode_nocolor() -> Self {
Self { Self {
characters: MietteCharacters::unicode(), characters: ThemeCharacters::unicode(),
styles: MietteStyles::none(), styles: ThemeStyles::none(),
} }
} }
/// A "basic" graphical theme that skips colors and unicode characters and
/// just does monochrome ascii art. If you want a completely non-graphical
/// rendering of your `Diagnostic`s, check out
/// [crate::NarratableReportPrinter], or write your own
/// [crate::DiagnosticReportPrinter]!
pub fn none() -> Self { pub fn none() -> Self {
Self { Self {
characters: MietteCharacters::ascii(), characters: ThemeCharacters::ascii(),
styles: MietteStyles::none(), styles: ThemeStyles::none(),
} }
} }
} }
impl Default for MietteTheme { impl Default for GraphicalTheme {
fn default() -> Self { fn default() -> Self {
match std::env::var("NO_COLOR") { match std::env::var("NO_COLOR") {
_ if !atty::is(Stream::Stdout) || !atty::is(Stream::Stderr) => Self::basic(), _ if !atty::is(Stream::Stdout) || !atty::is(Stream::Stderr) => Self::ascii(),
Ok(string) if string != "0" => Self::unicode_nocolor(), Ok(string) if string != "0" => Self::unicode_nocolor(),
_ => Self::unicode(), _ => Self::unicode(),
} }
} }
} }
pub struct MietteStyles { /**
Styles for various parts of graphical rendering for the [crate::GraphicalReportPrinter].
*/
#[derive(Debug, Clone)]
pub struct ThemeStyles {
/// Style to apply to things highlighted as "error".
pub error: Style, pub error: Style,
/// Style to apply to things highlighted as "warning".
pub warning: Style, pub warning: Style,
/// Style to apply to things highlighted as "advice".
pub advice: Style, pub advice: Style,
/// Style to apply to the diagnostic code.
pub code: Style, pub code: Style,
/// Style to apply to the help text.
pub help: Style, pub help: Style,
/// Style to apply to the filename/source name.
pub filename: Style, pub filename: Style,
/// Styles to cycle through (using `.iter().cycle()`), to render the lines
/// and text for diagnostic highlights.
pub highlights: Vec<Style>, pub highlights: Vec<Style>,
} }
@ -57,7 +93,8 @@ fn style() -> Style {
Style::new() Style::new()
} }
impl MietteStyles { impl ThemeStyles {
/// ANSI color-based styles.
pub fn ansi() -> Self { pub fn ansi() -> Self {
Self { Self {
error: style().red(), error: style().red(),
@ -74,6 +111,7 @@ impl MietteStyles {
} }
} }
/// No styling. Just regular ol' monochrome.
pub fn none() -> Self { pub fn none() -> Self {
Self { Self {
error: style(), error: style(),
@ -88,9 +126,13 @@ impl MietteStyles {
} }
// --------------------------------------- // ---------------------------------------
// All code below here was taken from ariadne here: // Most of these characters were taken from
// https://github.com/zesterer/ariadne/blob/e3cb394cb56ecda116a0a1caecd385a49e7f6662/src/draw.rs // https://github.com/zesterer/ariadne/blob/e3cb394cb56ecda116a0a1caecd385a49e7f6662/src/draw.rs
pub struct MietteCharacters {
/// Characters to be used when drawing when using [crate::GraphicalReportPrinter].
#[allow(missing_docs)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ThemeCharacters {
pub hbar: char, pub hbar: char,
pub vbar: char, pub vbar: char,
pub xbar: char, pub xbar: char,
@ -121,7 +163,8 @@ pub struct MietteCharacters {
pub point_right: char, pub point_right: char,
} }
impl MietteCharacters { impl ThemeCharacters {
/// Fancy unicode-based graphical elements.
pub fn unicode() -> Self { pub fn unicode() -> Self {
Self { Self {
hbar: '', hbar: '',
@ -149,6 +192,7 @@ impl MietteCharacters {
} }
} }
/// ASCII-art-based graphical elements. Works well on older terminals.
pub fn ascii() -> Self { pub fn ascii() -> Self {
Self { Self {
hbar: '-', hbar: '-',

View File

@ -1,5 +1,5 @@
use miette::{ use miette::{
DefaultReportPrinter, Diagnostic, DiagnosticReport, MietteError, MietteTheme, GraphicalReportPrinter, Diagnostic, DiagnosticReport, MietteError, GraphicalTheme,
NarratableReportPrinter, SourceSpan, NarratableReportPrinter, SourceSpan,
}; };
use thiserror::Error; use thiserror::Error;
@ -8,7 +8,7 @@ fn fmt_report(diag: DiagnosticReport) -> String {
let mut out = String::new(); let mut out = String::new();
// Mostly for dev purposes. // Mostly for dev purposes.
if std::env::var("STYLE").is_ok() { if std::env::var("STYLE").is_ok() {
DefaultReportPrinter::new_themed(MietteTheme::unicode()) GraphicalReportPrinter::new_themed(GraphicalTheme::unicode())
.render_report(&mut out, diag.inner()) .render_report(&mut out, diag.inner())
.unwrap(); .unwrap();
} else { } else {

View File

@ -1,5 +1,5 @@
use miette::{ use miette::{
DefaultReportPrinter, Diagnostic, DiagnosticReport, MietteError, MietteTheme, GraphicalReportPrinter, Diagnostic, DiagnosticReport, MietteError, GraphicalTheme,
NarratableReportPrinter, SourceSpan, NarratableReportPrinter, SourceSpan,
}; };
use thiserror::Error; use thiserror::Error;
@ -8,7 +8,7 @@ fn fmt_report(diag: DiagnosticReport) -> String {
let mut out = String::new(); let mut out = String::new();
// Mostly for dev purposes. // Mostly for dev purposes.
if std::env::var("STYLE").is_ok() { if std::env::var("STYLE").is_ok() {
DefaultReportPrinter::new_themed(MietteTheme::unicode()) GraphicalReportPrinter::new_themed(GraphicalTheme::unicode())
.render_report(&mut out, diag.inner()) .render_report(&mut out, diag.inner())
.unwrap(); .unwrap();
} else if std::env::var("NARRATED").is_ok() { } else if std::env::var("NARRATED").is_ok() {
@ -16,7 +16,7 @@ fn fmt_report(diag: DiagnosticReport) -> String {
.render_report(&mut out, diag.inner()) .render_report(&mut out, diag.inner())
.unwrap(); .unwrap();
} else { } else {
DefaultReportPrinter::new_themed(MietteTheme::unicode_nocolor()) GraphicalReportPrinter::new_themed(GraphicalTheme::unicode_nocolor())
.render_report(&mut out, diag.inner()) .render_report(&mut out, diag.inner())
.unwrap(); .unwrap();
}; };