From 67fc2abec66a6ec94eb6e8d08f087cc761dc4394 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Wed, 9 Oct 2024 21:57:12 -0700 Subject: [PATCH 1/2] Add ariadne --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + 2 files changed, 12 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 80a7276d..0951f406 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,6 +84,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ariadne" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44055e597c674aef7cb903b2b9f6e4cba1277ed0d2d61dae7cd52d7ffa81f8e2" +dependencies = [ + "unicode-width 0.1.14", + "yansi", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -524,6 +534,7 @@ name = "just" version = "1.36.0" dependencies = [ "ansi_term", + "ariadne", "blake3", "camino", "chrono", diff --git a/Cargo.toml b/Cargo.toml index f74367d0..c6727267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [".", "crates/*"] [dependencies] ansi_term = "0.12.0" +ariadne = "0.4.1" blake3 = { version = "1.5.0", features = ["rayon", "mmap"] } camino = "1.0.4" chrono = "0.4.38" From 40133571de0bb6457feb5639f7296c9efca6eb54 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Wed, 9 Oct 2024 23:13:09 -0700 Subject: [PATCH 2/2] Use ariadne https://github.com/casey/just/issues/1323 --- justfile | 3 +- src/compile_error.rs | 108 +++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 7 +++ src/run.rs | 2 +- 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/justfile b/justfile index d0ff455c..bd304ccd 100755 --- a/justfile +++ b/justfile @@ -12,7 +12,7 @@ export JUST_LOG := log watch +args='test': cargo watch --clear --exec '{{ args }}' -[group: 'test'] +[group('test')] test: cargo test --all @@ -34,6 +34,7 @@ run: filter PATTERN: cargo test {{PATTERN}} +[group: 'misc'] [group: 'misc'] build: cargo build diff --git a/src/compile_error.rs b/src/compile_error.rs index 8b4ec8de..37c63e8e 100644 --- a/src/compile_error.rs +++ b/src/compile_error.rs @@ -19,6 +19,114 @@ impl<'src> CompileError<'src> { } } +pub(crate) fn render_compile_error(error: &CompileError) { + use ariadne::{Label, Report, ReportKind, Source}; + + let token = error.token; + let source = Source::from(token.src); + + let start = token.offset; + let end = token.offset + token.length; + + let path = format!("{}", token.path.display()); + let label = Label::new((&path, start..end)); + + let report = Report::build(ReportKind::Error, &path, start); + + let report = match &*error.kind { + CompileErrorKind::AttributeArgumentCountMismatch { + attribute, + found, + min, + max, + } => { + let label_msg = format!("Found {found} {}", Count("argument", *found)); + + let note = if min == max { + format!("`{attribute}` takes {min} {}", Count("argument", *min)) + } else { + format!("`{attribute}` takes between {min} and {max} arguments") + }; + + report + .with_code("E01") + .with_message("Attribute argument count mismatch") + .with_label(label.with_message(label_msg)) + .with_note(note) + .finish() + } + /* + CompileErrorKind::BacktickShebang => todo!(), + CompileErrorKind::CircularRecipeDependency { recipe, circle } => todo!(), + CompileErrorKind::CircularVariableDependency { variable, circle } => todo!(), + CompileErrorKind::DependencyArgumentCountMismatch { dependency, found, min, max } => todo!(), + CompileErrorKind::Redefinition { first, first_type, name, second_type } => todo!(), + */ + CompileErrorKind::DuplicateAttribute { attribute, first } => { + let original_label = source + .line(*first) + .map(|line| Label::new((&path, line.span())).with_message("original")); + + let mut report = report + .with_code("E02") + .with_message(format!("Duplicate attribute `{attribute}`")); + if let Some(original) = original_label { + report = report.with_label(original); + } + report.with_label(label.with_message("duplicate")).finish() + } + _ => { + let message = format!("{error}"); + report.with_message(message).with_label(label).finish() + } /* + CompileErrorKind::DuplicateParameter { recipe, parameter } => todo!(), + CompileErrorKind::DuplicateSet { setting, first } => todo!(), + CompileErrorKind::DuplicateVariable { variable } => todo!(), + CompileErrorKind::DuplicateUnexport { variable } => todo!(), + CompileErrorKind::ExpectedKeyword { expected, found } => todo!(), + CompileErrorKind::ExportUnexported { variable } => todo!(), + CompileErrorKind::ExtraLeadingWhitespace => todo!(), + CompileErrorKind::ExtraneousAttributes { count } => todo!(), + CompileErrorKind::FunctionArgumentCountMismatch { function, found, expected } => todo!(), + CompileErrorKind::Include => todo!(), + CompileErrorKind::InconsistentLeadingWhitespace { expected, found } => todo!(), + CompileErrorKind::Internal { message } => todo!(), + CompileErrorKind::InvalidAttribute { item_kind, item_name, attribute } => todo!(), + CompileErrorKind::InvalidEscapeSequence { character } => todo!(), + CompileErrorKind::MismatchedClosingDelimiter { close, open, open_line } => todo!(), + CompileErrorKind::MixedLeadingWhitespace { whitespace } => todo!(), + CompileErrorKind::ParameterFollowsVariadicParameter { parameter } => todo!(), + CompileErrorKind::ParsingRecursionDepthExceeded => todo!(), + CompileErrorKind::RequiredParameterFollowsDefaultParameter { parameter } => todo!(), + CompileErrorKind::ShebangAndScriptAttribute { recipe } => todo!(), + CompileErrorKind::ShellExpansion { err } => todo!(), + CompileErrorKind::UndefinedVariable { variable } => todo!(), + CompileErrorKind::UnexpectedCharacter { expected } => todo!(), + CompileErrorKind::UnexpectedClosingDelimiter { close } => todo!(), + CompileErrorKind::UnexpectedEndOfToken { expected } => todo!(), + CompileErrorKind::UnexpectedToken { expected, found } => todo!(), + CompileErrorKind::UnicodeEscapeCharacter { character } => todo!(), + CompileErrorKind::UnicodeEscapeDelimiter { character } => todo!(), + CompileErrorKind::UnicodeEscapeEmpty => todo!(), + CompileErrorKind::UnicodeEscapeLength { hex } => todo!(), + CompileErrorKind::UnicodeEscapeRange { hex } => todo!(), + CompileErrorKind::UnicodeEscapeUnterminated => todo!(), + CompileErrorKind::UnknownAliasTarget { alias, target } => todo!(), + CompileErrorKind::UnknownAttribute { attribute } => todo!(), + CompileErrorKind::UnknownDependency { recipe, unknown } => todo!(), + CompileErrorKind::UnknownFunction { function } => todo!(), + CompileErrorKind::UnknownSetting { setting } => todo!(), + CompileErrorKind::UnknownStartOfToken => todo!(), + CompileErrorKind::UnpairedCarriageReturn => todo!(), + CompileErrorKind::UnterminatedBacktick => todo!(), + CompileErrorKind::UnterminatedInterpolation => todo!(), + CompileErrorKind::UnterminatedString => todo!(), + */ + }; + + report.eprint((&path, source)).unwrap(); +} + fn capitalize(s: &str) -> String { let mut chars = s.chars(); match chars.next() { diff --git a/src/error.rs b/src/error.rs index dd6955fe..03f75794 100644 --- a/src/error.rs +++ b/src/error.rs @@ -504,6 +504,13 @@ impl<'src> ColorDisplay for Error<'src> { } } +pub(crate) fn render_error(error: &Error, color: Color) { + match error { + Error::Compile { compile_error } => compile_error::render_compile_error(compile_error), + _ => eprintln!("{}", error.color_display(color.stderr())), + } +} + fn format_cmd(binary: &OsString, arguments: &Vec) -> String { iter::once(binary) .chain(arguments) diff --git a/src/run.rs b/src/run.rs index a07b73bb..c17ba502 100644 --- a/src/run.rs +++ b/src/run.rs @@ -29,7 +29,7 @@ pub fn run(args: impl Iterator + Clone>) -> Result<() }) .map_err(|error| { if !verbosity.quiet() && error.print_message() { - eprintln!("{}", error.color_display(color.stderr())); + crate::error::render_error(&error, color); } error.code().unwrap_or(EXIT_FAILURE) })