mirror of
https://github.com/nushell/nushell
synced 2024-11-11 07:34:14 +00:00
Merge pull request #52 from zkat/main
replace codespan-reporting with miette 3.0
This commit is contained in:
commit
e5aa8b9d3f
14 changed files with 514 additions and 578 deletions
219
Cargo.lock
generated
219
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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<usize>), Box<dyn std::error::Error>> {
|
||||
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<Box<dyn std::fmt::Display + 'a>> {
|
||||
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<Severity> {
|
||||
self.0.severity()
|
||||
}
|
||||
|
||||
panic!(
|
||||
"internal error: can't find span in parser state: {:?}",
|
||||
span
|
||||
)
|
||||
fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
|
||||
self.0.help()
|
||||
}
|
||||
|
||||
fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
|
||||
self.0.url()
|
||||
}
|
||||
|
||||
fn labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '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<dyn std::error::Error>> {
|
||||
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<dyn std::error::Error>> {
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -4,5 +4,6 @@ version = "0.1.0"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
codespan-reporting = "0.11.1"
|
||||
nu-protocol = { path = "../nu-protocol"}
|
||||
miette = { version = "3.0.0-alpha.0" }
|
||||
thiserror = "1.0.29"
|
||||
nu-protocol = { path = "../nu-protocol"}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
thiserror = "1.0.29"
|
||||
miette = { version = "3.0.0-alpha.0" }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<Self::Name, codespan_reporting::files::Error> {
|
||||
Ok(self.get_filename(id))
|
||||
}
|
||||
|
||||
fn source(
|
||||
&'a self,
|
||||
id: Self::FileId,
|
||||
) -> Result<Self::Source, codespan_reporting::files::Error> {
|
||||
Ok(self.get_file_source(id))
|
||||
}
|
||||
|
||||
fn line_index(
|
||||
&'a self,
|
||||
id: Self::FileId,
|
||||
byte_index: usize,
|
||||
) -> Result<usize, codespan_reporting::files::Error> {
|
||||
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<Box<dyn miette::SpanContents + 'b>, 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<Range<usize>, 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
use miette::SourceSpan;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
impl From<Span> 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 }
|
||||
|
|
28
src/main.rs
28
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();
|
||||
|
|
|
@ -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",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue