Use ColorDisplay trait to print objects to the terminal (#926)

This commit is contained in:
Casey Rodarmor 2021-07-28 18:06:57 -07:00 committed by GitHub
parent 1f20ca6481
commit 27cf2b96df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 107 additions and 98 deletions

View File

@ -30,14 +30,6 @@ impl Color {
}
}
pub(crate) fn fmt(fmt: &Formatter) -> Self {
if fmt.alternate() {
Self::always()
} else {
Self::never()
}
}
pub(crate) fn auto() -> Self {
Self {
use_color: UseColor::Auto,

20
src/color_display.rs Normal file
View File

@ -0,0 +1,20 @@
use crate::common::*;
pub(crate) trait ColorDisplay {
fn color_display<'a>(&'a self, color: Color) -> Wrapper<'a>
where
Self: Sized,
{
Wrapper(self, color)
}
fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result;
}
pub(crate) struct Wrapper<'a>(&'a dyn ColorDisplay, Color);
impl<'a> Display for Wrapper<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f, self.1)
}
}

View File

@ -38,8 +38,8 @@ pub(crate) use crate::{load_dotenv::load_dotenv, output::output, unindent::unind
// traits
pub(crate) use crate::{
command_ext::CommandExt, keyed::Keyed, ordinal::Ordinal, platform_interface::PlatformInterface,
range_ext::RangeExt,
color_display::ColorDisplay, command_ext::CommandExt, keyed::Keyed, ordinal::Ordinal,
platform_interface::PlatformInterface, range_ext::RangeExt,
};
// structs and enums

View File

@ -160,30 +160,6 @@ impl<'src> Error<'src> {
message: message.into(),
}
}
pub(crate) fn write(&self, w: &mut dyn Write, color: Color) -> io::Result<()> {
let color = color.stderr();
if color.active() {
writeln!(
w,
"{}: {}{:#}{}",
color.error().paint("error"),
color.message().prefix(),
self,
color.message().suffix()
)?;
} else {
writeln!(w, "error: {}", self)?;
}
if let Some(token) = self.context() {
token.write_context(w, color.error())?;
writeln!(w)?;
}
Ok(())
}
}
impl<'src> From<CompileError<'src>> for Error<'src> {
@ -210,10 +186,17 @@ impl<'src> From<SearchError> for Error<'src> {
}
}
impl<'src> Display for Error<'src> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl<'src> ColorDisplay for Error<'src> {
fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
use Error::*;
write!(
f,
"{}: {}",
color.error().paint("error"),
color.message().prefix()
)?;
match self {
ArgumentCountMismatch {
recipe,
@ -254,7 +237,7 @@ impl<'src> Display for Error<'src> {
}
write!(f, "\nusage:\n just {}", recipe)?;
for param in parameters {
write!(f, " {}", param)?;
write!(f, " {}", param.color_display(color))?;
}
},
Backtick { output_error, .. } => match output_error {
@ -619,6 +602,13 @@ impl<'src> Display for Error<'src> {
},
}
write!(f, "{}", color.message().suffix())?;
if let Some(token) = self.context() {
writeln!(f)?;
write!(f, "{}", token.color_display(color.error()))?;
}
Ok(())
}
}

View File

@ -21,9 +21,13 @@ impl InterruptHandler {
match INSTANCE.lock() {
Ok(guard) => guard,
Err(poison_error) => {
eprintln!("{}", Error::Internal {
message: format!("interrupt handler mutex poisoned: {}", poison_error),
});
eprintln!(
"{}",
Error::Internal {
message: format!("interrupt handler mutex poisoned: {}", poison_error),
}
.color_display(Color::auto().stderr())
);
std::process::exit(EXIT_FAILURE);
},
}
@ -58,9 +62,14 @@ impl InterruptHandler {
pub(crate) fn unblock(&mut self) {
if self.blocks == 0 {
if self.verbosity.loud() {
eprintln!("{}", Error::Internal {
message: "attempted to unblock interrupt handler, but handler was not blocked".to_owned(),
});
eprintln!(
"{}",
Error::Internal {
message: "attempted to unblock interrupt handler, but handler was not blocked"
.to_owned(),
}
.color_display(Color::auto().stderr())
);
}
std::process::exit(EXIT_FAILURE);
}

View File

@ -16,7 +16,7 @@ impl<'src> Display for Item<'src> {
Item::Alias(alias) => write!(f, "{}", alias),
Item::Assignment(assignment) => write!(f, "{}", assignment),
Item::Comment(comment) => write!(f, "{}", comment),
Item::Recipe(recipe) => write!(f, "{}", recipe),
Item::Recipe(recipe) => write!(f, "{}", recipe.color_display(Color::never())),
Item::Set(set) => write!(f, "{}", set),
}
}

View File

@ -375,8 +375,8 @@ impl<'src> Justfile<'src> {
}
}
impl<'src> Display for Justfile<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
impl<'src> ColorDisplay for Justfile<'src> {
fn fmt(&self, f: &mut Formatter, color: Color) -> Result<(), fmt::Error> {
let mut items = self.recipes.len() + self.assignments.len() + self.aliases.len();
for (name, assignment) in &self.assignments {
if assignment.export {
@ -396,7 +396,7 @@ impl<'src> Display for Justfile<'src> {
}
}
for recipe in self.recipes.values() {
write!(f, "{}", recipe)?;
write!(f, "{}", recipe.color_display(color))?;
items -= 1;
if items != 0 {
write!(f, "\n\n")?;
@ -683,11 +683,11 @@ mod tests {
fn test(input: &str, expected: &str) {
let justfile = compile(input);
let actual = format!("{:#}", justfile);
let actual = format!("{}", justfile.color_display(Color::never()));
assert_eq!(actual, expected);
println!("Re-parsing...");
let reparsed = compile(&actual);
let redumped = format!("{:#}", reparsed);
let redumped = format!("{}", reparsed.color_display(Color::never()));
assert_eq!(redumped, actual);
}

View File

@ -2298,17 +2298,13 @@ mod tests {
} if message == "Lexer presumed character `-`"
);
let mut cursor = Cursor::new(Vec::new());
Error::Compile { compile_error }
.write(&mut cursor, Color::never())
.unwrap();
assert_eq!(
str::from_utf8(&cursor.into_inner()).unwrap(),
Error::Compile { compile_error }
.color_display(Color::never())
.to_string(),
"error: Internal error, this may indicate a bug in just: \
Lexer presumed character `-`\nconsider filing an issue: \
https://github.com/casey/just/issues/new\n |\n1 | !\n | ^\n"
https://github.com/casey/just/issues/new\n |\n1 | !\n | ^"
);
}
}

View File

@ -65,6 +65,7 @@ mod assignment_resolver;
mod ast;
mod binding;
mod color;
mod color_display;
mod command_ext;
mod common;
mod compile_error;

View File

@ -23,9 +23,10 @@ pub(crate) fn load_dotenv(
.map(|val| val.as_os_str().to_str() == Some("1"))
.unwrap_or(false)
{
Warning::DotenvLoad
.write(&mut io::stderr(), config.color.stderr())
.ok();
eprintln!(
"{}",
Warning::DotenvLoad.color_display(config.color.stderr())
);
}
let iter = dotenv::from_path_iter(&path)?;

View File

@ -13,9 +13,8 @@ pub(crate) struct Parameter<'src> {
pub(crate) export: bool,
}
impl<'src> Display for Parameter<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let color = Color::fmt(f);
impl<'src> ColorDisplay for Parameter<'src> {
fn fmt(&self, f: &mut Formatter, color: Color) -> Result<(), fmt::Error> {
if self.export {
write!(f, "$")?;
}

View File

@ -309,8 +309,8 @@ impl<'src, D> Keyed<'src> for Recipe<'src, D> {
}
}
impl<'src, D: Display> Display for Recipe<'src, D> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
impl<'src, D: Display> ColorDisplay for Recipe<'src, D> {
fn fmt(&self, f: &mut Formatter, color: Color) -> Result<(), fmt::Error> {
if let Some(doc) = self.doc {
writeln!(f, "# {}", doc)?;
}
@ -322,7 +322,7 @@ impl<'src, D: Display> Display for Recipe<'src, D> {
}
for parameter in &self.parameters {
write!(f, " {}", parameter)?;
write!(f, " {}", parameter.color_display(color))?;
}
write!(f, ":")?;

View File

@ -30,7 +30,7 @@ pub fn run() -> Result<(), i32> {
})
.map_err(|error| {
if !verbosity.quiet() {
error.write(&mut io::stderr(), color).ok();
eprintln!("{}", error.color_display(color));
}
error.code().unwrap_or(EXIT_FAILURE)
})

View File

@ -62,24 +62,24 @@ impl Subcommand {
if config.verbosity.loud() {
for warning in &justfile.warnings {
warning.write(&mut io::stderr(), config.color.stderr()).ok();
eprintln!("{}", warning.color_display(config.color.stderr()));
}
}
match self {
Choose { overrides, chooser } =>
Self::choose(&config, justfile, &search, overrides, chooser.as_deref())?,
Command { overrides, .. } => justfile.run(&config, &search, overrides, &[])?,
Self::choose(config, justfile, &search, overrides, chooser.as_deref())?,
Command { overrides, .. } => justfile.run(config, &search, overrides, &[])?,
Dump => Self::dump(ast),
Evaluate { overrides, .. } => justfile.run(&config, &search, overrides, &[])?,
Format => Self::format(&config, ast, &search)?,
List => Self::list(&config, justfile),
Evaluate { overrides, .. } => justfile.run(config, &search, overrides, &[])?,
Format => Self::format(config, ast, &search)?,
List => Self::list(config, justfile),
Run {
arguments,
overrides,
} => justfile.run(&config, &search, overrides, arguments)?,
Show { ref name } => Self::show(&name, justfile)?,
Summary => Self::summary(&config, justfile),
} => justfile.run(config, &search, overrides, arguments)?,
Show { ref name } => Self::show(config, &name, justfile)?,
Summary => Self::summary(config, justfile),
Variables => Self::variables(justfile),
Completions { .. } | Edit | Init => unreachable!(),
}
@ -308,7 +308,9 @@ impl Subcommand {
let mut line_width = UnicodeWidthStr::width(*name);
for parameter in &recipe.parameters {
line_width += UnicodeWidthStr::width(format!(" {}", parameter).as_str());
line_width += UnicodeWidthStr::width(
format!(" {}", parameter.color_display(Color::never())).as_str(),
);
}
if line_width <= 30 {
@ -331,11 +333,7 @@ impl Subcommand {
{
print!("{}{}", config.list_prefix, name);
for parameter in &recipe.parameters {
if config.color.stdout().active() {
print!(" {:#}", parameter);
} else {
print!(" {}", parameter);
}
print!(" {}", parameter.color_display(config.color.stdout()));
}
// Declaring this outside of the nested loops will probably be more efficient,
@ -365,14 +363,14 @@ impl Subcommand {
}
}
fn show<'src>(name: &str, justfile: Justfile<'src>) -> Result<(), Error<'src>> {
fn show<'src>(config: &Config, name: &str, justfile: Justfile<'src>) -> Result<(), Error<'src>> {
if let Some(alias) = justfile.get_alias(name) {
let recipe = justfile.get_recipe(alias.target.name.lexeme()).unwrap();
println!("{}", alias);
println!("{}", recipe);
println!("{}", recipe.color_display(config.color.stdout()));
Ok(())
} else if let Some(recipe) = justfile.get_recipe(name) {
println!("{}", recipe);
println!("{}", recipe.color_display(config.color.stdout()));
Ok(())
} else {
Err(Error::UnknownRecipes {

View File

@ -18,8 +18,10 @@ impl<'src> Token<'src> {
pub(crate) fn error(&self, kind: CompileErrorKind<'src>) -> CompileError<'src> {
CompileError { token: *self, kind }
}
}
pub(crate) fn write_context(&self, w: &mut dyn Write, color: Color) -> io::Result<()> {
impl<'src> ColorDisplay for Token<'src> {
fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
let width = if self.length == 0 { 1 } else { self.length };
let line_number = self.line.ordinal();
@ -50,11 +52,11 @@ impl<'src> Token<'src> {
i += c.len_utf8();
}
let line_number_width = line_number.to_string().len();
writeln!(w, "{0:1$} |", "", line_number_width)?;
writeln!(w, "{} | {}", line_number, space_line)?;
write!(w, "{0:1$} |", "", line_number_width)?;
writeln!(f, "{0:1$} |", "", line_number_width)?;
writeln!(f, "{} | {}", line_number, space_line)?;
write!(f, "{0:1$} |", "", line_number_width)?;
write!(
w,
f,
" {0:1$}{2}{3:^<4$}{5}",
"",
space_column,
@ -67,7 +69,7 @@ impl<'src> Token<'src> {
None =>
if self.offset != self.src.len() {
write!(
w,
f,
"internal error: Error has invalid line number: {}",
line_number
)?;

View File

@ -2,8 +2,6 @@ use crate::common::*;
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Warning {
// Remove this on 2021-07-01.
#[allow(dead_code)]
DotenvLoad,
}
@ -13,17 +11,19 @@ impl Warning {
Self::DotenvLoad => None,
}
}
}
pub(crate) fn write(&self, w: &mut dyn Write, color: Color) -> io::Result<()> {
impl ColorDisplay for Warning {
fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
let warning = color.warning();
let message = color.message();
write!(w, "{} {}", warning.paint("warning:"), message.prefix())?;
write!(f, "{} {}", warning.paint("warning:"), message.prefix())?;
match self {
Self::DotenvLoad => {
#[rustfmt::skip]
write!(w, "\
write!(f, "\
A `.env` file was found and loaded, but this behavior will change in the future.
To silence this warning and continue loading `.env` files, add:
@ -44,10 +44,11 @@ See https://github.com/casey/just/issues/469 for more details.")?;
},
}
writeln!(w, "{}", message.suffix())?;
write!(f, "{}", message.suffix())?;
if let Some(token) = self.context() {
token.write_context(w, warning)?;
writeln!(f)?;
write!(f, "{}", token.color_display(color))?;
}
Ok(())