diff --git a/Cargo.lock b/Cargo.lock index d5d1e70c79..415ec4d2b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -77,13 +88,12 @@ dependencies = [ ] [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "ci_info" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "1f0e2864372242f01b92c1b882a904f6fb8b57f16e81e148a35b6368b1ea7323" dependencies = [ - "termcolor", - "unicode-width", + "envmnt", ] [[package]] @@ -140,6 +150,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dunce" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" + [[package]] name = "either" version = "1.6.1" @@ -151,7 +167,7 @@ name = "engine-q" version = "0.1.0" dependencies = [ "assert_cmd", - "codespan-reporting", + "miette", "nu-cli", "nu-command", "nu-engine", @@ -163,6 +179,25 @@ dependencies = [ "tempfile", ] +[[package]] +name = "envmnt" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f96dd862f12fac698dec3932dff0e6fb34bffeb5515ae5932d620cfe076571e" +dependencies = [ + "fsio", + "indexmap", +] + +[[package]] +name = "fsio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e87827efaf94c7a44b562ff57de06930712fe21b530c3797cdede26e6377eb" +dependencies = [ + "dunce", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -180,6 +215,31 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.10" @@ -234,6 +294,38 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "miette" +version = "3.0.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043a986fa0bf30fe00f6720e5c298ed1d67fe30ae77659744cbac206e8d2554c" +dependencies = [ + "atty", + "ci_info", + "itertools", + "miette-derive", + "once_cell", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "term_size", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "3.0.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3027fb091be28062da879441b2ab8f43d0013d7cde5fcc3a48fb9da7e22a01" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "mio" version = "0.7.13" @@ -280,12 +372,13 @@ dependencies = [ name = "nu-cli" version = "0.1.0" dependencies = [ - "codespan-reporting", + "miette", "nu-ansi-term", "nu-engine", "nu-parser", "nu-protocol", "reedline", + "thiserror", ] [[package]] @@ -310,15 +403,17 @@ dependencies = [ name = "nu-parser" version = "0.1.0" dependencies = [ - "codespan-reporting", + "miette", "nu-protocol", + "thiserror", ] [[package]] name = "nu-protocol" version = "0.1.0" dependencies = [ - "codespan-reporting", + "miette", + "thiserror", ] [[package]] @@ -349,6 +444,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + [[package]] name = "output_vt100" version = "0.1.2" @@ -364,6 +465,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owo-colors" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fe43bf372b08cc9ccee5144715db59c79ab00168bbe4cf0d274dc0d5f64d7f" + [[package]] name = "parking_lot" version = "0.11.2" @@ -608,6 +715,42 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + +[[package]] +name = "supports-color" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40bc06147993f379a124cc6373ad4022f5d9fd4a80019217c773f81a38e9023c" +dependencies = [ + "atty", + "ci_info", + "lazy_static", + "regex", +] + +[[package]] +name = "supports-hyperlinks" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406" +dependencies = [ + "atty", +] + +[[package]] +name = "supports-unicode" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5fa283a620b255940913bd962cda2e6320e3799041f96ac0d7191ff2b4622f" +dependencies = [ + "atty", +] + [[package]] name = "syn" version = "1.0.76" @@ -634,12 +777,44 @@ dependencies = [ ] [[package]] -name = "termcolor" -version = "1.1.2" +name = "term_size" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" dependencies = [ - "winapi-util", + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -659,6 +834,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" +[[package]] +name = "unicode-linebreak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f" +dependencies = [ + "regex", +] + [[package]] name = "unicode-segmentation" version = "1.8.0" @@ -708,15 +892,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index ccbaff85e0..692383fb5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,13 @@ members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser", "crates/nu-c [dependencies] reedline = { git = "https://github.com/jntrnr/reedline", branch = "main" } -codespan-reporting = "0.11.1" nu-cli = { path="./crates/nu-cli" } nu-command = { path="./crates/nu-command" } nu-engine = { path="./crates/nu-engine" } nu-parser = { path="./crates/nu-parser" } nu-protocol = { path = "./crates/nu-protocol" } nu-table = { path = "./crates/nu-table" } - +miette = { version = "3.0.0-alpha.0" } # mimalloc = { version = "*", default-features = false } [dev-dependencies] diff --git a/crates/nu-cli/Cargo.toml b/crates/nu-cli/Cargo.toml index a00e026d48..042663b801 100644 --- a/crates/nu-cli/Cargo.toml +++ b/crates/nu-cli/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" nu-engine = { path = "../nu-engine" } nu-parser = { path = "../nu-parser" } nu-protocol = { path = "../nu-protocol" } -codespan-reporting = "0.11.1" +miette = { version = "3.0.0-alpha.0", features = ["fancy"] } +thiserror = "1.0.29" nu-ansi-term = "0.36.0" reedline = { git = "https://github.com/jntrnr/reedline", branch = "main" } diff --git a/crates/nu-cli/src/errors.rs b/crates/nu-cli/src/errors.rs index fc63b34714..bfb29d506b 100644 --- a/crates/nu-cli/src/errors.rs +++ b/crates/nu-cli/src/errors.rs @@ -1,414 +1,53 @@ -use core::ops::Range; +use miette::{LabeledSpan, MietteHandler, ReportHandler, Severity, SourceCode}; +use nu_protocol::engine::StateWorkingSet; +use thiserror::Error; -use codespan_reporting::diagnostic::{Diagnostic, Label}; -use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use nu_parser::ParseError; -use nu_protocol::{engine::StateWorkingSet, ShellError, Span}; +/// This error exists so that we can defer SourceCode handling. It simply +/// forwards most methods, except for `.source_code()`, which we provide. +#[derive(Error)] +#[error("{0}")] +struct CliError<'src>( + &'src (dyn miette::Diagnostic + Send + Sync + 'static), + &'src StateWorkingSet<'src>, +); -fn convert_span_to_diag( - working_set: &StateWorkingSet, - span: &Span, -) -> Result<(usize, Range), Box> { - for (file_id, (_, start, end)) in working_set.files().enumerate() { - if span.start >= *start && span.end <= *end { - let new_start = span.start - start; - let new_end = span.end - start; +impl std::fmt::Debug for CliError<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + MietteHandler::default().debug(self, f)?; + Ok(()) + } +} - return Ok((file_id, new_start..new_end)); - } +impl<'src> miette::Diagnostic for CliError<'src> { + fn code<'a>(&'a self) -> Option> { + self.0.code() } - if span.start == working_set.next_span_start() { - // We're trying to highlight the space after the end - if let Some((file_id, (_, _, end))) = working_set.files().enumerate().last() { - return Ok((file_id, *end..(*end + 1))); - } + fn severity(&self) -> Option { + self.0.severity() } - panic!( - "internal error: can't find span in parser state: {:?}", - span - ) + fn help<'a>(&'a self) -> Option> { + self.0.help() + } + + fn url<'a>(&'a self) -> Option> { + self.0.url() + } + + fn labels<'a>(&'a self) -> Option + 'a>> { + self.0.labels() + } + + // Finally, we redirect the source_code method to our own source. + fn source_code(&self) -> Option<&dyn SourceCode> { + Some(&self.1) + } } -pub fn report_parsing_error( +pub fn report_error( working_set: &StateWorkingSet, - error: &ParseError, -) -> Result<(), Box> { - let writer = StandardStream::stderr(ColorChoice::Always); - let config = codespan_reporting::term::Config::default(); - - let diagnostic = - match error { - ParseError::Mismatch(expected, found, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Type mismatch during operation") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("expected {}, found {}", expected, found))]) - } - ParseError::ExtraTokens(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Extra tokens in code") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("extra tokens") - ]) - } - ParseError::ExtraPositional(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Extra positional argument") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("extra positional argument")]) - } - ParseError::UnexpectedEof(s, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unexpected end of code") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("expected {}", s))]) - } - ParseError::Unclosed(delim, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unclosed delimiter") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("unclosed {}", delim))]) - } - ParseError::UnknownStatement(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unknown statement") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("unknown statement") - ]) - } - ParseError::MultipleRestParams(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Multiple rest params") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("multiple rest params")]) - } - ParseError::VariableNotFound(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Variable not found") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("variable not found") - ]) - } - ParseError::UnknownCommand(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unknown command") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("unknown command") - ]) - } - ParseError::UnknownFlag(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unknown flag") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("unknown flag") - ]) - } - ParseError::UnknownType(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unknown type") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("unknown type") - ]) - } - ParseError::MissingFlagParam(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Missing flag param") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("flag missing parameter")]) - } - ParseError::ShortFlagBatchCantTakeArg(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Batches of short flags can't take arguments") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("short flag batches can't take args")]) - } - ParseError::KeywordMissingArgument(name, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message(format!("Missing argument to {}", name)) - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("missing value that follows {}", name))]) - } - ParseError::MissingPositional(name, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Missing required positional arg") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("missing {}", name))]) - } - ParseError::MissingType(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Missing type") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("expected type") - ]) - } - ParseError::MissingColumns(count, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Missing columns") - .with_labels(vec![Label::primary(diag_file_id, diag_range).with_message( - format!( - "expected {} column{}", - count, - if *count == 1 { "" } else { "s" } - ), - )]) - } - ParseError::ExtraColumns(count, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Extra columns") - .with_labels(vec![Label::primary(diag_file_id, diag_range).with_message( - format!( - "expected {} column{}", - count, - if *count == 1 { "" } else { "s" } - ), - )]) - } - ParseError::TypeMismatch(expected, found, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Type mismatch") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("expected {:?}, found {:?}", expected, found))]) - } - ParseError::MissingRequiredFlag(name, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Missing required flag") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("missing required flag {}", name))]) - } - ParseError::IncompleteMathExpression(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Incomplete math expresssion") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("incomplete math expression")]) - } - ParseError::UnknownState(name, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Unknown state") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message(name.to_string()) - ]) - } - ParseError::NonUtf8(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Non-UTF8 code") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("non-UTF8 code") - ]) - } - ParseError::Expected(expected, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Parse mismatch during operation") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("expected {}", expected))]) - } - ParseError::UnsupportedOperation(op_span, lhs_span, lhs_ty, rhs_span, rhs_ty) => { - let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?; - let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?; - let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?; - Diagnostic::error() - .with_message("Unsupported operation") - .with_labels(vec![ - Label::primary(op_file_id, op_range) - .with_message("doesn't support these values"), - Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()), - Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()), - ]) - } - ParseError::ExpectedKeyword(expected, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Expected keyword") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("expected {}", expected))]) - } - ParseError::IncompleteParser(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Parser incomplete") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("parser support missing for this expression")]) - } - ParseError::RestNeedsName(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Rest parameter needs a name") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("needs a parameter name")]) - } - ParseError::AssignmentMismatch(msg, label, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message(msg) - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message(label) - ]) - } - }; - - // println!("DIAG"); - // println!("{:?}", diagnostic); - codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?; - - Ok(()) -} - -pub fn report_shell_error( - working_set: &StateWorkingSet, - error: &ShellError, -) -> Result<(), Box> { - let writer = StandardStream::stderr(ColorChoice::Always); - let config = codespan_reporting::term::Config::default(); - - let diagnostic = - match error { - ShellError::OperatorMismatch { - op_span, - lhs_ty, - lhs_span, - rhs_ty, - rhs_span, - } => { - let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?; - let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?; - let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?; - Diagnostic::error() - .with_message("Type mismatch during operation") - .with_labels(vec![ - Label::primary(op_file_id, op_range) - .with_message("type mismatch for operator"), - Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()), - Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()), - ]) - } - ShellError::UnsupportedOperator(op, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message(format!("Unsupported operator: {}", op)) - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("unsupported operator")]) - } - ShellError::UnknownOperator(op, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message(format!("Unsupported operator: {}", op)) - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("unsupported operator")]) - } - ShellError::ExternalNotSupported(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("External commands not yet supported") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("external not supported")]) - } - ShellError::InternalError(s) => { - Diagnostic::error().with_message(format!("Internal error: {}", s)) - } - ShellError::VariableNotFoundAtRuntime(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Variable not found") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("variable not found") - ]) - } - ShellError::CantConvert(s, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message(format!("Can't convert to {}", s)) - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("can't convert to {}", s))]) - } - ShellError::CannotCreateRange(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - Diagnostic::error() - .with_message("Can't convert range to countable values") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("can't convert to countable values")]) - } - ShellError::DivisionByZero(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - - Diagnostic::error() - .with_message("Division by zero") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("division by zero") - ]) - } - ShellError::AccessBeyondEnd(len, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - - Diagnostic::error() - .with_message("Row number too large") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("row number too large (max: {})", *len))]) - } - ShellError::AccessBeyondEndOfStream(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - - Diagnostic::error() - .with_message("Row number too large") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message("row number too large")]) - } - ShellError::IncompatiblePathAccess(name, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - - Diagnostic::error() - .with_message("Data cannot be accessed with a cell path") - .with_labels(vec![Label::primary(diag_file_id, diag_range) - .with_message(format!("{} doesn't support cell paths", name))]) - } - ShellError::CantFindColumn(span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - - //FIXME: add "did you mean" - Diagnostic::error() - .with_message("Cannot find column") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message("cannot find column") - ]) - } - ShellError::ExternalCommand(error, span) => { - let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; - - Diagnostic::error() - .with_message("External command") - .with_labels(vec![ - Label::primary(diag_file_id, diag_range).with_message(error.to_string()) - ]) - } - }; - - // println!("DIAG"); - // println!("{:?}", diagnostic); - codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?; - - Ok(()) + error: &(dyn miette::Diagnostic + Send + Sync + 'static), +) { + eprintln!("Error: {:?}", CliError(error, working_set)); } diff --git a/crates/nu-cli/src/lib.rs b/crates/nu-cli/src/lib.rs index 111a74c961..f0f8a4ab54 100644 --- a/crates/nu-cli/src/lib.rs +++ b/crates/nu-cli/src/lib.rs @@ -3,5 +3,5 @@ mod errors; mod syntax_highlight; pub use completions::NuCompleter; -pub use errors::{report_parsing_error, report_shell_error}; +pub use errors::report_error; pub use syntax_highlight::NuHighlighter; diff --git a/crates/nu-parser/Cargo.toml b/crates/nu-parser/Cargo.toml index f0ca45a1fb..4221db951e 100644 --- a/crates/nu-parser/Cargo.toml +++ b/crates/nu-parser/Cargo.toml @@ -4,5 +4,6 @@ version = "0.1.0" edition = "2018" [dependencies] -codespan-reporting = "0.11.1" -nu-protocol = { path = "../nu-protocol"} \ No newline at end of file +miette = { version = "3.0.0-alpha.0" } +thiserror = "1.0.29" +nu-protocol = { path = "../nu-protocol"} diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index 2965d5f149..7de6db4999 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -1,34 +1,150 @@ +use miette::Diagnostic; use nu_protocol::{Span, Type}; +use thiserror::Error; -#[derive(Debug)] +#[derive(Clone, Debug, Error, Diagnostic)] pub enum ParseError { - ExtraTokens(Span), - ExtraPositional(Span), - UnexpectedEof(String, Span), - Unclosed(String, Span), - UnknownStatement(Span), - Expected(String, Span), - Mismatch(String, String, Span), // expected, found, span - UnsupportedOperation(Span, Span, Type, Span, Type), - ExpectedKeyword(String, Span), - MultipleRestParams(Span), - VariableNotFound(Span), - UnknownCommand(Span), - NonUtf8(Span), - UnknownFlag(Span), - UnknownType(Span), - MissingFlagParam(Span), - ShortFlagBatchCantTakeArg(Span), - MissingPositional(String, Span), - KeywordMissingArgument(String, Span), - MissingType(Span), - TypeMismatch(Type, Type, Span), // expected, found, span - MissingRequiredFlag(String, Span), - IncompleteMathExpression(Span), - UnknownState(String, Span), - IncompleteParser(Span), - RestNeedsName(Span), - ExtraColumns(usize, Span), - MissingColumns(usize, Span), - AssignmentMismatch(String, String, Span), + /// The parser encountered unexpected tokens, when the code should have + /// finished. You should remove these or finish adding what you intended + /// to add. + #[error("Extra tokens in code.")] + #[diagnostic( + code(nu::parser::extra_tokens), + url(docsrs), + help("Try removing them.") + )] + ExtraTokens(#[label = "extra tokens"] Span), + + #[error("Extra positional argument.")] + #[diagnostic(code(nu::parser::extra_positional), url(docsrs))] + ExtraPositional(#[label = "extra positional argument"] Span), + + #[error("Unexpected end of code.")] + #[diagnostic(code(nu::parser::unexpected_eof), url(docsrs))] + UnexpectedEof(String, #[label("expected {0}")] Span), + + #[error("Unclosed delimiter.")] + #[diagnostic(code(nu::parser::unclosed_delimiter), url(docsrs))] + Unclosed(String, #[label("unclosed {0}")] Span), + + #[error("Unknown statement.")] + #[diagnostic(code(nu::parser::unknown_statement), url(docsrs))] + UnknownStatement(#[label("unknown statement")] Span), + + #[error("Parse mismatch during operation.")] + #[diagnostic(code(nu::parser::parse_mismatch), url(docsrs))] + Expected(String, #[label("expected {0}")] Span), + + #[error("Type mismatch during operation.")] + #[diagnostic(code(nu::parser::type_mismatch), url(docsrs))] + Mismatch(String, String, #[label("expected {0}, found {1}")] Span), // expected, found, span + + #[error("Unsupported operation.")] + #[diagnostic( + code(nu::parser::unsupported_operation), + url(docsrs), + help("Change {2} or {4} to be the right types and try again.") + )] + UnsupportedOperation( + #[label = "doesn't support these values."] Span, + #[label("{2}")] Span, + Type, + #[label("{4}")] Span, + Type, + ), + + #[error("Expected keyword.")] + #[diagnostic(code(nu::parser::expected_keyword), url(docsrs))] + ExpectedKeyword(String, #[label("expected {0}")] Span), + + #[error("Multiple rest params.")] + #[diagnostic(code(nu::parser::multiple_rest_params), url(docsrs))] + MultipleRestParams(#[label = "multiple rest params"] Span), + + #[error("Variable not found.")] + #[diagnostic(code(nu::parser::variable_not_found), url(docsrs))] + VariableNotFound(#[label = "variable not found"] Span), + + #[error("Unknown command.")] + #[diagnostic( + code(nu::parser::unknown_command), + url(docsrs), + // TODO: actual suggestions + // help("Did you mean `foo`?") + )] + UnknownCommand(#[label = "unknown command"] Span), + + #[error("Non-UTF8 code.")] + #[diagnostic(code(nu::parser::non_utf8), url(docsrs))] + NonUtf8(#[label = "non-UTF8 code"] Span), + + #[error("Unknown flag.")] + #[diagnostic(code(nu::parser::unknown_flag), url(docsrs))] + UnknownFlag(#[label = "unknown flag"] Span), + + #[error("Unknown type.")] + #[diagnostic(code(nu::parser::unknown_type), url(docsrs))] + UnknownType(#[label = "unknown type"] Span), + + #[error("Missing flag param.")] + #[diagnostic(code(nu::parser::missing_flag_param), url(docsrs))] + MissingFlagParam(#[label = "flag missing param"] Span), + + #[error("Batches of short flags can't take arguments.")] + #[diagnostic(code(nu::parser::short_flag_arg_cant_take_arg), url(docsrs))] + ShortFlagBatchCantTakeArg(#[label = "short flag batches can't take args"] Span), + + #[error("Missing required positional argument.")] + #[diagnostic(code(nu::parser::missing_positional), url(docsrs))] + MissingPositional(String, #[label("missing {0}")] Span), + + #[error("Missing argument to `{0}`.")] + #[diagnostic(code(nu::parser::keyword_missing_arg), url(docsrs))] + KeywordMissingArgument(String, #[label("missing value that follows {0}")] Span), + + #[error("Missing type.")] + #[diagnostic(code(nu::parser::missing_type), url(docsrs))] + MissingType(#[label = "expected type"] Span), + + #[error("Type mismatch.")] + #[diagnostic(code(nu::parser::type_mismatch), url(docsrs))] + TypeMismatch(Type, Type, #[label("expected {0:?}, found {1:?}")] Span), // expected, found, span + + #[error("Missing required flag.")] + #[diagnostic(code(nu::parser::missing_required_flag), url(docsrs))] + MissingRequiredFlag(String, #[label("missing required flag {0}")] Span), + + #[error("Incomplete math expression.")] + #[diagnostic(code(nu::parser::incomplete_math_expression), url(docsrs))] + IncompleteMathExpression(#[label = "incomplete math expression"] Span), + + #[error("Unknown state.")] + #[diagnostic(code(nu::parser::unknown_state), url(docsrs))] + UnknownState(String, #[label("{0}")] Span), + + #[error("Parser incomplete.")] + #[diagnostic(code(nu::parser::parser_incomplete), url(docsrs))] + IncompleteParser(#[label = "parser support missing for this expression"] Span), + + #[error("Rest parameter needs a name.")] + #[diagnostic(code(nu::parser::rest_needs_name), url(docsrs))] + RestNeedsName(#[label = "needs a parameter name"] Span), + + #[error("Extra columns.")] + #[diagnostic(code(nu::parser::extra_columns), url(docsrs))] + ExtraColumns( + usize, + #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span, + ), + + #[error("Missing columns.")] + #[diagnostic(code(nu::parser::missing_columns), url(docsrs))] + MissingColumns( + usize, + #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span, + ), + + #[error("{0}")] + #[diagnostic(code(nu::parser::assignment_mismatch), url(docsrs))] + AssignmentMismatch(String, String, #[label("{1}")] Span), } diff --git a/crates/nu-protocol/Cargo.toml b/crates/nu-protocol/Cargo.toml index d6c800e68e..2949bff8fd 100644 --- a/crates/nu-protocol/Cargo.toml +++ b/crates/nu-protocol/Cargo.toml @@ -6,4 +6,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -codespan-reporting = "0.11.1" \ No newline at end of file +thiserror = "1.0.29" +miette = { version = "3.0.0-alpha.0" } diff --git a/crates/nu-protocol/src/engine/command.rs b/crates/nu-protocol/src/engine/command.rs index adac4ec916..ce322eb873 100644 --- a/crates/nu-protocol/src/engine/command.rs +++ b/crates/nu-protocol/src/engine/command.rs @@ -2,7 +2,7 @@ use crate::{ast::Call, value::Value, BlockId, Example, ShellError, Signature}; use super::EvaluationContext; -pub trait Command { +pub trait Command: Send + Sync { fn name(&self) -> &str; fn signature(&self) -> Signature { diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 771bac24bb..6f7e1f2d66 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -1,7 +1,7 @@ use super::Command; use crate::{ast::Block, BlockId, DeclId, Span, Type, VarId}; use core::panic; -use std::{collections::HashMap, ops::Range, slice::Iter}; +use std::{collections::HashMap, slice::Iter}; pub struct EngineState { files: Vec<(String, usize, usize)>, @@ -549,95 +549,42 @@ impl<'a> StateWorkingSet<'a> { } } -impl<'a> codespan_reporting::files::Files<'a> for StateWorkingSet<'a> { - type FileId = usize; - - type Name = String; - - type Source = String; - - fn name(&'a self, id: Self::FileId) -> Result { - Ok(self.get_filename(id)) - } - - fn source( - &'a self, - id: Self::FileId, - ) -> Result { - Ok(self.get_file_source(id)) - } - - fn line_index( - &'a self, - id: Self::FileId, - byte_index: usize, - ) -> Result { - let source = self.get_file_source(id); - - let mut count = 0; - - for byte in source.bytes().enumerate() { - if byte.0 == byte_index { - // println!("count: {} for file: {} index: {}", count, id, byte_index); - return Ok(count); - } - if byte.1 == b'\n' { - count += 1; +impl<'a> miette::SourceCode for &StateWorkingSet<'a> { + fn read_span<'b>( + &'b self, + span: &miette::SourceSpan, + context_lines_before: usize, + context_lines_after: usize, + ) -> Result, miette::MietteError> { + for (filename, start, end) in self.files() { + if span.offset() >= *start && span.offset() + span.len() <= *end { + let our_span = Span { + start: *start, + end: *end, + }; + // We need to move to a local span because we're only reading + // the specific file contents via self.get_span_contents. + let local_span = (span.offset() - *start, span.len()).into(); + let span_contents = self.get_span_contents(our_span); + let span_contents = span_contents.read_span( + &local_span, + context_lines_before, + context_lines_after, + )?; + let content_span = span_contents.span(); + // Back to "global" indexing + let retranslated = (content_span.offset() + start, content_span.len()).into(); + return Ok(Box::new(miette::MietteSpanContents::new_named( + filename.clone(), + span_contents.data(), + retranslated, + span_contents.line(), + span_contents.column(), + span_contents.line_count(), + ))); } } - - // println!("count: {} for file: {} index: {}", count, id, byte_index); - Ok(count) - } - - fn line_range( - &'a self, - id: Self::FileId, - line_index: usize, - ) -> Result, codespan_reporting::files::Error> { - let source = self.get_file_source(id); - - let mut count = 0; - - let mut start = Some(0); - let mut end = None; - - for byte in source.bytes().enumerate() { - #[allow(clippy::comparison_chain)] - if count > line_index { - let start = start.expect("internal error: couldn't find line"); - let end = end.expect("internal error: couldn't find line"); - - // println!( - // "Span: {}..{} for fileid: {} index: {}", - // start, end, id, line_index - // ); - return Ok(start..end); - } else if count == line_index { - end = Some(byte.0 + 1); - } - - #[allow(clippy::comparison_chain)] - if byte.1 == b'\n' { - count += 1; - if count > line_index { - break; - } else if count == line_index { - start = Some(byte.0 + 1); - } - } - } - - match (start, end) { - (Some(start), Some(end)) => { - // println!( - // "Span: {}..{} for fileid: {} index: {}", - // start, end, id, line_index - // ); - Ok(start..end) - } - _ => Err(codespan_reporting::files::Error::FileMissing), - } + Err(miette::MietteError::OutOfBounds) } } diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 1f18799883..9d1677116f 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -1,25 +1,72 @@ +use miette::Diagnostic; +use thiserror::Error; + use crate::{ast::Operator, Span, Type}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Error, Diagnostic)] pub enum ShellError { + #[error("Type mismatch during operation.")] + #[diagnostic(code(nu::shell::type_mismatch), url(docsrs))] OperatorMismatch { + #[label = "type mismatch for operator"] op_span: Span, lhs_ty: Type, + #[label("{lhs_ty}")] lhs_span: Span, rhs_ty: Type, + #[label("{rhs_ty}")] rhs_span: Span, }, - UnsupportedOperator(Operator, Span), - UnknownOperator(String, Span), - ExternalNotSupported(Span), + + #[error("Unsupported operator: {0}.")] + #[diagnostic(code(nu::shell::unsupported_operator), url(docsrs))] + UnsupportedOperator(Operator, #[label = "unsupported operator"] Span), + + #[error("Unsupported operator: {0}.")] + #[diagnostic(code(nu::shell::unknown_operator), url(docsrs))] + UnknownOperator(String, #[label = "unsupported operator"] Span), + + #[error("External commands not yet supported")] + #[diagnostic(code(nu::shell::external_commands), url(docsrs))] + ExternalNotSupported(#[label = "external not supported"] Span), + + #[error("Internal error: {0}.")] + #[diagnostic(code(nu::shell::internal_error), url(docsrs))] InternalError(String), - VariableNotFoundAtRuntime(Span), - CantConvert(String, Span), - DivisionByZero(Span), - CannotCreateRange(Span), - AccessBeyondEnd(usize, Span), - AccessBeyondEndOfStream(Span), - IncompatiblePathAccess(String, Span), - CantFindColumn(Span), - ExternalCommand(String, Span), + + #[error("Variable not found")] + #[diagnostic(code(nu::shell::variable_not_found), url(docsrs))] + VariableNotFoundAtRuntime(#[label = "variable not found"] Span), + + #[error("Can't convert to {0}.")] + #[diagnostic(code(nu::shell::cant_convert), url(docsrs))] + CantConvert(String, #[label("can't convert to {0}")] Span), + + #[error("Division by zero.")] + #[diagnostic(code(nu::shell::division_by_zero), url(docsrs))] + DivisionByZero(#[label("division by zero")] Span), + + #[error("Can't convert range to countable values")] + #[diagnostic(code(nu::shell::range_to_countable), url(docsrs))] + CannotCreateRange(#[label = "can't convert to countable values"] Span), + + #[error("Row number too large (max: {0}).")] + #[diagnostic(code(nu::shell::access_beyond_end), url(docsrs))] + AccessBeyondEnd(usize, #[label = "too large"] Span), + + #[error("Row number too large.")] + #[diagnostic(code(nu::shell::access_beyond_end_of_stream), url(docsrs))] + AccessBeyondEndOfStream(#[label = "too large"] Span), + + #[error("Data cannot be accessed with a cell path")] + #[diagnostic(code(nu::shell::incompatible_path_access), url(docsrs))] + IncompatiblePathAccess(String, #[label("{0} doesn't support cell paths")] Span), + + #[error("Cannot find column")] + #[diagnostic(code(nu::shell::column_not_found), url(docsrs))] + CantFindColumn(#[label = "cannot find column"] Span), + + #[error("External command")] + #[diagnostic(code(nu::shell::external_command), url(docsrs))] + ExternalCommand(String, #[label("{0}")] Span), } diff --git a/crates/nu-protocol/src/span.rs b/crates/nu-protocol/src/span.rs index f1fd064355..0a43a1239d 100644 --- a/crates/nu-protocol/src/span.rs +++ b/crates/nu-protocol/src/span.rs @@ -1,9 +1,17 @@ +use miette::SourceSpan; + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Span { pub start: usize, pub end: usize, } +impl From for SourceSpan { + fn from(s: Span) -> Self { + Self::new(s.start.into(), (s.end - s.start).into()) + } +} + impl Span { pub fn new(start: usize, end: usize) -> Span { Span { start, end } diff --git a/src/main.rs b/src/main.rs index 32e0fb4c6c..510cddfddb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ -use nu_cli::{report_parsing_error, report_shell_error, NuCompleter, NuHighlighter}; +use miette::{IntoDiagnostic, Result}; +use nu_cli::{report_error, NuCompleter, NuHighlighter}; use nu_command::create_default_context; use nu_engine::eval_block; use nu_parser::parse; @@ -11,18 +12,18 @@ use reedline::DefaultCompletionActionHandler; #[cfg(test)] mod tests; -fn main() -> std::io::Result<()> { +fn main() -> Result<()> { let engine_state = create_default_context(); if let Some(path) = std::env::args().nth(1) { - let file = std::fs::read(&path)?; + let file = std::fs::read(&path).into_diagnostic()?; let (block, delta) = { let engine_state = engine_state.borrow(); let mut working_set = StateWorkingSet::new(&*engine_state); let (output, err) = parse(&mut working_set, Some(&path), &file, false); if let Some(err) = err { - let _ = report_parsing_error(&working_set, &err); + report_error(&working_set, &err); std::process::exit(1); } @@ -44,7 +45,7 @@ fn main() -> std::io::Result<()> { let engine_state = engine_state.borrow(); let working_set = StateWorkingSet::new(&*engine_state); - let _ = report_shell_error(&working_set, &err); + report_error(&working_set, &err); std::process::exit(1); } @@ -56,11 +57,12 @@ fn main() -> std::io::Result<()> { let completer = NuCompleter::new(engine_state.clone()); - let mut line_editor = Reedline::create()? - .with_history(Box::new(FileBackedHistory::with_file( - 1000, - "history.txt".into(), - )?))? + let mut line_editor = Reedline::create() + .into_diagnostic()? + .with_history(Box::new( + FileBackedHistory::with_file(1000, "history.txt".into()).into_diagnostic()?, + )) + .into_diagnostic()? .with_highlighter(Box::new(NuHighlighter { engine_state: engine_state.clone(), })) @@ -102,7 +104,7 @@ fn main() -> std::io::Result<()> { false, ); if let Some(err) = err { - let _ = report_parsing_error(&working_set, &err); + report_error(&working_set, &err); continue; } (output, working_set.render()) @@ -123,7 +125,7 @@ fn main() -> std::io::Result<()> { let engine_state = engine_state.borrow(); let working_set = StateWorkingSet::new(&*engine_state); - let _ = report_shell_error(&working_set, &err); + report_error(&working_set, &err); } } } @@ -134,7 +136,7 @@ fn main() -> std::io::Result<()> { break; } Ok(Signal::CtrlL) => { - line_editor.clear_screen()?; + line_editor.clear_screen().into_diagnostic()?; } Err(err) => { let message = err.to_string(); diff --git a/src/tests.rs b/src/tests.rs index 7a04df6320..90cb52013b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -133,7 +133,7 @@ fn if_elseif4() -> TestResult { fn no_scope_leak1() -> TestResult { fail_test( "if $false { let $x = 10 } else { let $x = 20 }; $x", - "variable not found", + "Variable not found", ) }