diff --git a/Cargo.lock b/Cargo.lock index e1afeb7..9f472e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,6 +248,17 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "windows-sys", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -431,6 +442,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "equivalent" version = "1.0.2" @@ -634,6 +651,18 @@ dependencies = [ "rustversion", ] +[[package]] +name = "insta" +version = "1.47.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" +dependencies = [ + "console", + "once_cell", + "similar", + "tempfile", +] + [[package]] name = "instability" version = "0.3.11" @@ -1387,6 +1416,7 @@ dependencies = [ "clap", "crossterm", "dumpster", + "insta", "itertools", "jiff", "nix 0.31.1", @@ -1566,6 +1596,12 @@ dependencies = [ "libc", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "siphasher" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index 127af5b..ad95fc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,4 @@ dumpster = "2.1" winnow = {version="1", features=["parser"]} proptest = "1" proptest-derive = "0.8" +insta = "1" diff --git a/proptest-regressions/format_debug_output/proptesting.txt b/proptest-regressions/format_debug_output/proptesting.txt index 1fe09c7..3af969d 100644 --- a/proptest-regressions/format_debug_output/proptesting.txt +++ b/proptest-regressions/format_debug_output/proptesting.txt @@ -5,3 +5,5 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc 1dac24f74cdeb63f61d662876f276058bc71481f1df552aeea1293e22b682d59 # shrinks to original = "ยก{}" +cc f7a17a233c11246ea8182505c41e30dc2a2f1c9020d5108f95403eb2de179fac # shrinks to original = ")" +cc 7bfbe4d3505dc0e94e5b87ae86e4b4554d9af477f4d0770161c2403ce39627f3 # shrinks to original = "!/\t" diff --git a/src/format_debug_output/ast.rs b/src/format_debug_output/ast.rs index 913c0f6..5ecd58f 100644 --- a/src/format_debug_output/ast.rs +++ b/src/format_debug_output/ast.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; pub enum Separator { Eq, Colon, + DoubleColon, } #[derive(Copy, Clone, Debug, Arbitrary, PartialEq)] @@ -36,6 +37,8 @@ pub struct Space<'a>(pub Cow<'a, str>); #[derive(Copy, Clone, Debug, PartialEq, Arbitrary)] pub enum PathSep { + /// Happens at the start of paths, for the no leading / case + None, Slash, Backslash, } @@ -63,6 +66,7 @@ pub struct FileName<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Path<'a> { pub drive_excluding_colon: Option, + pub segments: Vec>, pub filename: FileName<'a>, } @@ -101,7 +105,7 @@ pub enum Token<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Delimited<'a> { - pub prefix: Atom<'a>, + pub prefix: Option>, pub delimiter: Delimiter, pub contents: Segments<'a>, } diff --git a/src/format_debug_output/display.rs b/src/format_debug_output/display.rs index 89620fd..9bc0634 100644 --- a/src/format_debug_output/display.rs +++ b/src/format_debug_output/display.rs @@ -6,6 +6,7 @@ impl Display for Separator { match self { Separator::Eq => write!(f, "="), Separator::Colon => write!(f, ":"), + Separator::DoubleColon => write!(f, "::"), } } } @@ -68,6 +69,7 @@ impl Display for PathSep { match self { PathSep::Slash => write!(f, "/"), PathSep::Backslash => write!(f, "\\"), + PathSep::None => Ok(()), } } } @@ -105,13 +107,20 @@ impl<'a> Display for FileName<'a> { impl<'a> Display for Path<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(drive) = &self.drive_excluding_colon { + let Self { + drive_excluding_colon, + segments, + filename, + } = self; + + if let Some(drive) = &drive_excluding_colon { write!(f, "{drive}:")?; } - for segment in &self.segments { + + for segment in segments { write!(f, "{segment}")?; } - write!(f, "{}", self.filename) + write!(f, "{filename}") } } @@ -152,7 +161,9 @@ impl<'a> Display for Token<'a> { impl<'a> Display for Delimited<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.prefix)?; + if let Some(prefix) = &self.prefix { + write!(f, "{prefix}")?; + } self.delimiter.fmt_start(f)?; write!(f, "{}", self.contents)?; self.delimiter.fmt_end(f) diff --git a/src/format_debug_output/parse.rs b/src/format_debug_output/parse.rs index bda7e01..731c6fa 100644 --- a/src/format_debug_output/parse.rs +++ b/src/format_debug_output/parse.rs @@ -7,15 +7,24 @@ impl<'a> AnyString<'a> { fn parse>() -> impl Parser<&'a str, Self, E> { use winnow::{combinator::*, prelude::*, token::*}; - // let (prefix, num_hashtags, quote) = + let quote = alt(( + '`'.value(QuoteType::Backtick), + '\''.value(QuoteType::Single), + '\"'.value(QuoteType::Double), + )); + + macro_rules! surrounding { + () => { + take_while(0.., |b: char| { + !b.is_whitespace() && b.is_alphabetic() && !['\'', '"', '`', '#'].contains(&b) + }) + }; + } + let preamble = ( - take_while(0.., |b: char| !b.is_whitespace()), + surrounding!(), take_while(0.., |c| c == '#').map(|i: &'a str| i.len()), - alt(( - '`'.value(QuoteType::Backtick), - '\''.value(QuoteType::Single), - '\"'.value(QuoteType::Double), - )), + quote, ); trace( @@ -35,17 +44,16 @@ impl<'a> AnyString<'a> { ); let contents = repeat_till(0.., any, end).map(|(contents, _)| contents); - let suffix = take_while(0.., |b: char| !b.is_whitespace()); - (contents, suffix).map(move |(contents, suffix): (Cow<'a, str>, &'a str)| { - Self { + (contents, surrounding!()).map( + move |(contents, suffix): (Cow<'a, str>, &'a str)| Self { prefix: prefix.into(), ty: quote, contents, num_hashtags, suffix: suffix.into(), - } - }) + }, + ) }, ), ) @@ -75,14 +83,15 @@ impl PathSep { } impl Separator { - fn parser<'a, E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> { + fn parse<'a, E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> { use winnow::{combinator::*, prelude::*, token::*}; trace( "separator", alt(( - literal('=').value(Self::Eq), - literal(':').value(Self::Colon), + "::".value(Self::DoubleColon), + (literal('=')).value(Self::Eq), + (literal(':')).value(Self::Colon), )), ) } @@ -147,95 +156,159 @@ impl<'a> Path<'a> { fn parse>() -> impl Parser<&'a str, Self, E> { use winnow::{combinator::*, prelude::*, token::*}; - let till_next_sep = repeat_till( - 0.., - any::<&'a str, E>.verify(|i: &char| !(*i).is_whitespace()), - peek(alt(( - PathSep::parse().value(()), + let terminator = || { + alt(( + eof.value(()), any::<&'a str, E> - .verify(|i: &char| (*i).is_whitespace()) + .verify(|i: &char| { + (*i).is_whitespace() + || !(i.is_alphanumeric() + || ['_', '-', '\"', '\'', '.', '/', '\\'].contains(i)) + }) .value(()), - ))), - ) - .map(|(segment, _)| segment); + )) + }; + + let terminator_or_sep = || alt((PathSep::parse().value(()), terminator())); + + let till_next_sep = || { + trace( + "till next sep", + repeat_till(0.., any::<&'a str, E>, peek(terminator_or_sep())) + .map(|(segment, _)| segment), + ) + }; let sep_and_next = - (PathSep::parse(), till_next_sep).map(|(leading_separator, segment)| PathSegment { + (PathSep::parse(), till_next_sep()).map(|(leading_separator, segment)| PathSegment { leading_separator, segment, }); + let opt_sep_and_next = + (opt(PathSep::parse()), till_next_sep()).map(|(leading_separator, segment)| { + PathSegment { + leading_separator: leading_separator.unwrap_or(PathSep::None), + segment, + } + }); + let drive = opt(( - any::<&'a str, E>.verify(|x: &char| matches!(*x, 'A'..='Z')), + any::<&'a str, E>.verify(|x: &char| matches!(*x, 'A'..='Z' | 'a' ..= 'z')), ':', )) .map(|i| i.map(|(letter, _): (char, char)| letter)); let drive_and_segments = ( drive, - repeat_till( - 1.., - sep_and_next, - peek(any.verify(|i: &char| (*i).is_whitespace())), - ) - .map(|(segments, _): (Vec, _)| { - let (rest, last) = { - let mut segments = segments; - let last = segments.pop().unwrap(); - (segments, last) - }; - - (rest, FileName::parse(last)) - }), + opt_sep_and_next, + repeat_till(0.., sep_and_next, peek(terminator())) + .map(|(segments, _): (Vec, _)| segments), ); trace( "path", - drive_and_segments.map(|(drive, (segments, filename))| Self { - drive_excluding_colon: drive, - segments, - filename, - }), + drive_and_segments + .map(|(drive, segment, segments)| { + let (segments, last) = { + let mut segments = segments; + segments.insert(0, segment); + let last = segments.pop().unwrap(); + (segments, last) + }; + + let filename = FileName::parse(last); + + Self { + drive_excluding_colon: drive, + segments, + filename, + } + }) + .verify(|i| { + !i.segments.is_empty() + || i.drive_excluding_colon.is_some() + || i.filename.ext_excluding_dot.is_some() + || !matches!(i.filename.leading_separator, PathSep::None) + }), ) } } impl<'a> Atom<'a> { - fn parse>( - except_chars: &'static [char], + fn parse, T: 'a>( + terminated_by: impl Parser<&'a str, T, E>, ) -> impl Parser<&'a str, Self, E> { use winnow::{combinator::*, prelude::*, token::*}; - trace( - "atom", - alt((repeat( - 1.., - any.verify(move |i: &char| !(*i).is_whitespace() && !except_chars.contains(i)), + let text = repeat::<_, _, Cow<'a, str>, _, _>( + 1.., + ( + peek(not(terminated_by)), + any::<&str, _>.verify(move |i: &char| !(*i).is_whitespace()), ) - .map(Self::Text),)), + .map(|(_, i)| i), ) + .map(Self::Text); + + trace("atom", alt((text,))) } } impl<'a> Token<'a> { - fn parse + 'a>() -> impl Parser<&'a str, Self, E> { + fn parse_without_separator + 'a>() -> impl Parser<&'a str, Self, E> { use winnow::{combinator::*, prelude::*}; let delimited: Box> = Box::new(Delimited::parse().map(Self::Delimited)); trace( - "token", + "token-without-sep", alt(( "true".value(Self::True), "false".value(Self::False), "None".value(Self::None), Path::parse().map(Self::Path), - AnyString::parse().map(Self::String), Number::parse().map(Self::Number), + AnyString::parse().map(Self::String), delimited, - Atom::parse(&[]).map(Self::Atom), + Atom::parse(alt((Separator::parse().value(""), ")", "]", "}"))).map(Self::Atom), )), ) } + + fn parse + 'a>() -> Box + 'a> { + use winnow::{combinator::*, prelude::*}; + + let before = Self::parse_without_separator(); + + Box::new(trace( + "token", + alt(( + ( + before, + opt( + (Space::parse(), Separator::parse()).flat_map(|(space, sep)| { + let box_dyn_segment: Box> = + Box::new(Segment::parse()); + box_dyn_segment.map(move |segment| (space.clone(), sep, segment)) + }), + ), + ) + .map(|(before, trailer)| { + if let Some((space_before, separator, after)) = trailer { + Token::Separated { + before: Box::new(before), + space_before, + separator, + after: Box::new(after), + } + } else { + before + } + }), + Atom::parse(fail::<_, (), _>).map(Self::Atom), + )), + )) + } } impl<'a> Delimited<'a> { @@ -245,7 +318,12 @@ impl<'a> Delimited<'a> { trace( "delimited", ( - Atom::parse(&['(', '[', '{']), + opt(Atom::parse(alt(( + "(", + "[", + "{", + Separator::parse().value(""), + )))), alt(( literal('(').map(|_| literal(')').value(Delimiter::Paren)), literal('[').map(|_| literal(']').value(Delimiter::Bracket)), @@ -264,11 +342,11 @@ impl<'a> Delimited<'a> { impl<'a> Segments<'a> { fn parse + 'a, End: 'a>( - end: impl Parser<&'a str, End, E>, - ) -> impl Parser<&'a str, (Self, End), E> { + end: impl Parser<&'a str, End, E> + 'a, + ) -> Box + 'a> { use winnow::{combinator::*, prelude::*}; - trace( + Box::new(trace( "segments", repeat_till(0.., Segment::parse(), (Space::parse(), end)).map( |(segments, (trailing_space, end)): (Vec<_>, _)| { @@ -281,18 +359,21 @@ impl<'a> Segments<'a> { ) }, ), - ) + )) } } impl<'a> Number<'a> { fn parse>() -> impl Parser<&'a str, Self, E> { - use winnow::{ascii::*, combinator::*, prelude::*}; + use winnow::{ascii::*, combinator::*, prelude::*, token::*}; trace( "number", - alt((float::<_, f64, _>.take(), dec_int::<_, i64, _>.take())) - .map(|i: &str| Self(i.into())), + ( + alt((float::<_, f64, _>.take(), dec_int::<_, i64, _>.take())), + peek(not(any::<&'a str, E>.verify(|x: &char| x.is_alphabetic()))), + ) + .map(|(i, _): (&str, _)| Self(i.into())), ) } } @@ -318,3 +399,371 @@ pub fn parse_input<'a>(i: &'a str) -> Result, String> { .parse(i) .map_err(|e| e.to_string()) } + +#[cfg(test)] +mod tests { + use insta::assert_debug_snapshot; + use winnow::Parser; + + use crate::format_debug_output::{ + ast::{Path, Segments}, + parse_input, + }; + + fn parse_path_only<'a>(i: &'a str) -> Path<'a> { + Path::parse::().parse(i).unwrap() + } + + fn parse<'a>(input: &'a str) -> Segments<'a> { + parse_input(input).unwrap() + } + + #[test] + fn parse_path() { + assert_debug_snapshot!(parse_path_only(r#"tests/ui/impl-trait/unsized_coercion.rs"#), @r#" + Path { + drive_excluding_colon: None, + segments: [ + PathSegment { + leading_separator: None, + segment: "tests", + }, + PathSegment { + leading_separator: Slash, + segment: "ui", + }, + PathSegment { + leading_separator: Slash, + segment: "impl-trait", + }, + ], + filename: FileName { + leading_separator: Slash, + segment: "unsized_coercion", + ext_excluding_dot: Some( + "rs", + ), + location: None, + }, + } + "#); + } + + #[test] + fn parse_path_with_file_line() { + assert_debug_snapshot!(parse_path_only(r#"tests/ui/impl-trait/unsized_coercion.rs:3:4"#), @r#" + Path { + drive_excluding_colon: None, + segments: [ + PathSegment { + leading_separator: None, + segment: "tests", + }, + PathSegment { + leading_separator: Slash, + segment: "ui", + }, + PathSegment { + leading_separator: Slash, + segment: "impl-trait", + }, + ], + filename: FileName { + leading_separator: Slash, + segment: "unsized_coercion", + ext_excluding_dot: Some( + "rs", + ), + location: Some( + FileLocation { + line: "3", + offset: Some( + "4", + ), + }, + ), + }, + } + "#); + } + + #[test] + fn parse_empty() { + assert_debug_snapshot!(parse(r#""#), @r#" + Segments { + segments: [], + trailing_space: Space( + "", + ), + } + "#) + } + + #[test] + fn parse_text() { + assert_debug_snapshot!(parse(r#"abc"#), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: Atom( + Text( + "abc", + ), + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#) + } + + #[test] + fn parse_boolean() { + assert_debug_snapshot!(parse(r#"true"#), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: True, + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r#"false"#), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: False, + }, + ], + trailing_space: Space( + "", + ), + } + "#); + } + + #[test] + fn parse_string() { + assert_debug_snapshot!(parse(r##""foo""##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "", + ty: Double, + contents: "foo", + num_hashtags: 0, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"#"foo"#"##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "", + ty: Double, + contents: "foo", + num_hashtags: 1, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"r#"foo"#"##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "r", + ty: Double, + contents: "foo", + num_hashtags: 1, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"c"foo""##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "c", + ty: Double, + contents: "foo", + num_hashtags: 0, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"b"foo""##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "b", + ty: Double, + contents: "foo", + num_hashtags: 0, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"'a'"##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "", + ty: Single, + contents: "a", + num_hashtags: 0, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"`b`"##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "", + ty: Backtick, + contents: "b", + num_hashtags: 0, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"b'foo'"##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "b", + ty: Single, + contents: "foo", + num_hashtags: 0, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + assert_debug_snapshot!(parse(r##"b`foo`"##), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: String( + AnyString { + prefix: "b", + ty: Backtick, + contents: "foo", + num_hashtags: 0, + suffix: "", + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#); + } +} diff --git a/src/format_debug_output/proptesting.rs b/src/format_debug_output/proptesting.rs index ec3816a..c5fbdff 100644 --- a/src/format_debug_output/proptesting.rs +++ b/src/format_debug_output/proptesting.rs @@ -1,6 +1,6 @@ use super::ast::*; +use crate::format_debug_output::parse_input; use crate::format_debug_output::{Config, into_spans}; -use crate::format_debug_output::{ast::*, parse_input}; use proptest::prelude::*; use proptest::proptest; @@ -155,7 +155,8 @@ impl Token<'static> { impl Delimited<'static> { #[cfg(test)] fn arb(token: impl Strategy>) -> impl Strategy { - (Atom::arb(), any::(), Segments::arb(token)).prop_map( + use proptest::option::*; + (of(Atom::arb()), any::(), Segments::arb(token)).prop_map( |(prefix, delimiter, contents)| Self { prefix, delimiter, diff --git a/src/format_debug_output/spans.rs b/src/format_debug_output/spans.rs index 73e5650..79a66e9 100644 --- a/src/format_debug_output/spans.rs +++ b/src/format_debug_output/spans.rs @@ -82,6 +82,7 @@ mod private { match self { Separator::Eq => cx.push("=", Kind::Separator), Separator::Colon => cx.push(":", Kind::Separator), + Separator::DoubleColon => cx.push("::", Kind::Separator), } } } @@ -166,7 +167,12 @@ mod private { space_before, separator, after, - } => todo!(), + } => { + before.into_spans(cx); + space_before.into_spans(cx); + separator.into_spans(cx); + after.into_spans(cx); + } Token::Delimited(delimited) => { delimited.into_spans(cx); } @@ -210,7 +216,8 @@ mod private { } = self; match prefix { - Atom::Text(text) => cx.push(text, Kind::Constructor), + Some(Atom::Text(text)) => cx.push(text, Kind::Constructor), + None => {} } match delimiter { @@ -233,3 +240,736 @@ mod private { } } } + +#[cfg(test)] +mod tests { + use insta::assert_debug_snapshot; + + use super::Kind; + use crate::format_debug_output::{Config, into_spans, parse_input}; + + fn spans(input: &str) -> Vec<(String, Kind)> { + let res = parse_input(input).unwrap(); + into_spans( + res, + Config { + collapse_space: true, + }, + ) + .into_iter() + .map(|i| (i.text.into_owned(), i.kind)) + .collect() + } + + #[test] + fn spans_ex1() { + assert_debug_snapshot!(spans( + r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"# + ), @r#" + [ + ( + "def_id", + Text, + ), + ( + "=", + Separator, + ), + ( + "DefId", + Constructor, + ), + ( + "(", + Delimiter( + 0, + ), + ), + ( + "0", + Number, + ), + ( + ":", + Separator, + ), + ( + "3", + Number, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "~", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "unsized_coercion", + Constructor, + ), + ( + "[", + Delimiter( + 1, + ), + ), + ( + "10fa", + Text, + ), + ( + "]", + Delimiter( + 1, + ), + ), + ( + "::", + Separator, + ), + ( + "Trait", + Text, + ), + ( + ")", + Delimiter( + 0, + ), + ), + ] + "#) + } + + #[test] + fn spans_ex2() { + assert_debug_snapshot!(spans( + r#"data=TypeNs("MetaSized") visible_parent=DefId(2:3984 ~ core[bcc4]::marker) actual_parent=Some(DefId(2:3984 ~ core[bcc4]::marker))"# + ), @r#" + [ + ( + "data", + Text, + ), + ( + "=", + Separator, + ), + ( + "TypeNs", + Constructor, + ), + ( + "(", + Delimiter( + 0, + ), + ), + ( + "", + StringSurroundings, + ), + ( + "\"", + Separator, + ), + ( + "MetaSized", + String, + ), + ( + "\"", + Separator, + ), + ( + "", + StringSurroundings, + ), + ( + ")", + Delimiter( + 0, + ), + ), + ( + " ", + Space( + 1, + ), + ), + ( + "visible_parent", + Text, + ), + ( + "=", + Separator, + ), + ( + "DefId", + Constructor, + ), + ( + "(", + Delimiter( + 0, + ), + ), + ( + "2", + Number, + ), + ( + ":", + Separator, + ), + ( + "3984", + Number, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "~", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "core", + Constructor, + ), + ( + "[", + Delimiter( + 1, + ), + ), + ( + "bcc4", + Text, + ), + ( + "]", + Delimiter( + 1, + ), + ), + ( + "::", + Separator, + ), + ( + "marker", + Text, + ), + ( + ")", + Delimiter( + 0, + ), + ), + ( + " ", + Space( + 1, + ), + ), + ( + "actual_parent", + Text, + ), + ( + "=", + Separator, + ), + ( + "Some", + Constructor, + ), + ( + "(", + Delimiter( + 0, + ), + ), + ( + "DefId", + Constructor, + ), + ( + "(", + Delimiter( + 1, + ), + ), + ( + "2", + Number, + ), + ( + ":", + Separator, + ), + ( + "3984", + Number, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "~", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "core", + Constructor, + ), + ( + "[", + Delimiter( + 2, + ), + ), + ( + "bcc4", + Text, + ), + ( + "]", + Delimiter( + 2, + ), + ), + ( + "::", + Separator, + ), + ( + "marker", + Text, + ), + ( + ")", + Delimiter( + 1, + ), + ), + ( + ")", + Delimiter( + 0, + ), + ), + ] + "#) + } + + #[test] + fn spans_ex3() { + assert_debug_snapshot!(spans( + r#"insert(DefId(0:4 ~ unsized_coercion[10fa]::{impl#0})): inserting TraitRef into specialization graph"# + ), @r#" + [ + ( + "insert", + Constructor, + ), + ( + "(", + Delimiter( + 0, + ), + ), + ( + "DefId", + Constructor, + ), + ( + "(", + Delimiter( + 1, + ), + ), + ( + "0", + Number, + ), + ( + ":", + Separator, + ), + ( + "4", + Number, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "~", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "unsized_coercion", + Constructor, + ), + ( + "[", + Delimiter( + 2, + ), + ), + ( + "10fa", + Text, + ), + ( + "]", + Delimiter( + 2, + ), + ), + ( + "::", + Separator, + ), + ( + "{", + Delimiter( + 2, + ), + ), + ( + "impl#0", + Text, + ), + ( + "}", + Delimiter( + 2, + ), + ), + ( + ")", + Delimiter( + 1, + ), + ), + ( + ")", + Delimiter( + 0, + ), + ), + ( + ":", + Separator, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "inserting", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "TraitRef", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "into", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "specialization", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "graph", + Text, + ), + ] + "#) + } + + #[test] + fn spans_ex4() { + assert_debug_snapshot!(spans( + r#"inspecting def_id=DefId(3:662 ~ alloc[ef11]::boxed::Box) span=tests/ui/impl-trait/unsized_coercion.rs:12:15: 12:30 (#0)"# + ), @r##" + [ + ( + "inspecting", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "def_id", + Text, + ), + ( + "=", + Separator, + ), + ( + "DefId", + Constructor, + ), + ( + "(", + Delimiter( + 0, + ), + ), + ( + "3", + Number, + ), + ( + ":", + Separator, + ), + ( + "662", + Number, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "~", + Text, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "alloc", + Constructor, + ), + ( + "[", + Delimiter( + 1, + ), + ), + ( + "ef11", + Text, + ), + ( + "]", + Delimiter( + 1, + ), + ), + ( + "::", + Separator, + ), + ( + "boxed", + Text, + ), + ( + "::", + Separator, + ), + ( + "Box", + Text, + ), + ( + ")", + Delimiter( + 0, + ), + ), + ( + " ", + Space( + 1, + ), + ), + ( + "span", + Text, + ), + ( + "=", + Separator, + ), + ( + "tests/ui/impl-trait/unsized_coercion.rs", + Path, + ), + ( + ":", + Separator, + ), + ( + "12", + Number, + ), + ( + ":", + Separator, + ), + ( + "15", + Number, + ), + ( + ":", + Separator, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "12", + Number, + ), + ( + ":", + Separator, + ), + ( + "30", + Number, + ), + ( + " ", + Space( + 1, + ), + ), + ( + "(", + Delimiter( + 0, + ), + ), + ( + "#0", + Text, + ), + ( + ")", + Delimiter( + 0, + ), + ), + ] + "##) + } +} diff --git a/src/tui/widgets/line_text.rs b/src/tui/widgets/line_text.rs index 6723f47..0708cc3 100644 --- a/src/tui/widgets/line_text.rs +++ b/src/tui/widgets/line_text.rs @@ -215,10 +215,10 @@ impl Into> for Styled<'_, LineText> { let style = match kind { SpanKind::Delimiter(_) => style.fg(self.styles.delimiter).bold(), SpanKind::Separator => style.fg(self.styles.faded), - SpanKind::Number => style.fg(self.styles.literal).underlined(), - SpanKind::Literal => style.fg(self.styles.literal).underlined(), - SpanKind::String => style.fg(self.styles.literal).italic(), - SpanKind::Path => style.fg(self.styles.literal).italic(), + SpanKind::Number => style.fg(self.styles.literal), + SpanKind::Literal => style.fg(self.styles.literal).dim(), + SpanKind::String => style.fg(self.styles.literal), + SpanKind::Path => style.fg(self.styles.literal).underlined(), SpanKind::Space(_) => style, SpanKind::Constructor => style.fg(self.styles.literal), SpanKind::StringSurroundings => style.fg(self.styles.faded),