diff --git a/logparse/src/ast.rs b/logparse/src/ast.rs index 20a884e..c413531 100644 --- a/logparse/src/ast.rs +++ b/logparse/src/ast.rs @@ -156,6 +156,28 @@ pub enum Token<'a> { /// ``` None, + /// A lifetime, a la rust. The rules for this are pretty restrictive. + /// A lifetime must be, a single quote, followed by no more than 3 ascii lowercase alphabetic characters, + /// followed by *not* a closing quote, and any nonalphabetic character. Like a comma, + /// whitespace, eof, etc. + /// + /// ``` + /// # logparse::generate_ast_recognizer!(is_lt, Token::Lifetime(_)); + /// + /// assert!(is_lt("'a")); + /// assert!(is_lt("'tcx")); + /// + /// // some counterexamples + /// + /// assert!(!is_lt("'verylong")); + /// assert!(!is_lt("'foo'")); + /// assert!(!is_lt("'a'")); + /// assert!(!is_lt("'a longer single-quoted string'")); + /// assert!(!is_lt("a")); + /// assert!(!is_lt("13")); + /// `` + Lifetime(Cow<'a, str>), + /// A path, anything that looks vaguely path-like. /// For example: /// diff --git a/logparse/src/display.rs b/logparse/src/display.rs index 57b84aa..2bb2bc0 100644 --- a/logparse/src/display.rs +++ b/logparse/src/display.rs @@ -165,6 +165,7 @@ impl<'a> Display for Token<'a> { } => write!(f, "{before}{space_before}{separator}{after}"), Token::Delimited(delimited) => write!(f, "{delimited}"), Token::String(s) => write!(f, "{s}"), + Token::Lifetime(s) => write!(f, "'{s}"), } } } diff --git a/logparse/src/parse.rs b/logparse/src/parse.rs index 8aa80b1..78dac59 100644 --- a/logparse/src/parse.rs +++ b/logparse/src/parse.rs @@ -263,11 +263,30 @@ impl<'a> Atom<'a> { impl<'a> Token<'a> { fn parse_without_separator + 'a>() -> impl Parser<&'a str, Self, E> { - use winnow::{combinator::*, prelude::*}; + use winnow::{combinator::*, prelude::*, token::*}; let delimited: Box> = Box::new(Delimited::parse().map(Self::Delimited)); + let lifetime = ( + "'", + alt(( + repeat::<_, _, Cow<'a, str>, _, _>( + 1..=3, + any.verify(|i: &char| i.is_ascii_lowercase() || *i == '_'), + ) + .take(), + "{erased}", + )), + peek(alt(( + any::<&str, _> + .verify(|i: &char| !i.is_alphabetic() && *i != '\'') + .value(()), + eof::<&str, _>.value(()), + ))), + ) + .map(|(_, lifetime, _)| Token::Lifetime(lifetime.into())); + trace( "token-without-sep", alt(( @@ -276,6 +295,7 @@ impl<'a> Token<'a> { "None".value(Self::None), Number::parse().map(Self::Number), Path::parse().map(Self::Path), + lifetime, AnyString::parse().map(Self::String), delimited, Atom::parse(|| { @@ -1116,14 +1136,8 @@ mod tests { leading_space: Space( "", ), - token: String( - AnyString { - prefix: "", - ty: Single, - contents: "_, ", - num_hashtags: 0, - suffix: "", - }, + token: Lifetime( + "_", ), }, Segment { @@ -1132,10 +1146,18 @@ mod tests { ), token: Atom( Text( - "_", + ",", ), ), }, + Segment { + leading_space: Space( + " ", + ), + token: Lifetime( + "_", + ), + }, ], trailing_space: Space( "", @@ -1251,4 +1273,342 @@ mod tests { } "#); } + + #[test] + fn parse_ex2() { + assert_debug_snapshot!(parse(r#"super_combine_consts::, rustc_middle::ty::context::TyCtxt<'_>>>(?1c, ?2c)"#), @r#" + Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: Separated { + before: Atom( + Text( + "super_combine_consts", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Delimited( + Delimited { + prefix: None, + delimiter: Angle, + contents: Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: Separated { + before: Atom( + Text( + "rustc_type_ir", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Separated { + before: Atom( + Text( + "relate", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Separated { + before: Atom( + Text( + "solver_relating", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Delimited( + Delimited { + prefix: Some( + ( + Text( + "SolverRelating", + ), + Space( + "", + ), + ), + ), + delimiter: Angle, + contents: Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: Lifetime( + "_", + ), + }, + Segment { + leading_space: Space( + "", + ), + token: Atom( + Text( + ",", + ), + ), + }, + Segment { + leading_space: Space( + " ", + ), + token: Separated { + before: Atom( + Text( + "rustc_infer", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Separated { + before: Atom( + Text( + "infer", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Delimited( + Delimited { + prefix: Some( + ( + Text( + "InferCtxt", + ), + Space( + "", + ), + ), + ), + delimiter: Angle, + contents: Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: Lifetime( + "_", + ), + }, + ], + trailing_space: Space( + "", + ), + }, + }, + ), + }, + }, + }, + }, + }, + Segment { + leading_space: Space( + "", + ), + token: Atom( + Text( + ",", + ), + ), + }, + Segment { + leading_space: Space( + " ", + ), + token: Separated { + before: Atom( + Text( + "rustc_middle", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Separated { + before: Atom( + Text( + "ty", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Separated { + before: Atom( + Text( + "context", + ), + ), + space_before: Space( + "", + ), + separator: DoubleColon, + after: Segment { + leading_space: Space( + "", + ), + token: Delimited( + Delimited { + prefix: Some( + ( + Text( + "TyCtxt", + ), + Space( + "", + ), + ), + ), + delimiter: Angle, + contents: Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: Lifetime( + "_", + ), + }, + ], + trailing_space: Space( + "", + ), + }, + }, + ), + }, + }, + }, + }, + }, + }, + }, + ], + trailing_space: Space( + "", + ), + }, + }, + ), + }, + }, + }, + }, + }, + }, + }, + ], + trailing_space: Space( + "", + ), + }, + }, + ), + }, + }, + }, + Segment { + leading_space: Space( + "", + ), + token: Delimited( + Delimited { + prefix: None, + delimiter: Paren, + contents: Segments { + segments: [ + Segment { + leading_space: Space( + "", + ), + token: Atom( + Text( + "?1c,", + ), + ), + }, + Segment { + leading_space: Space( + " ", + ), + token: Atom( + Text( + "?2c", + ), + ), + }, + ], + trailing_space: Space( + "", + ), + }, + }, + ), + }, + ], + trailing_space: Space( + "", + ), + } + "#) + } } diff --git a/logparse/src/spans.rs b/logparse/src/spans.rs index aec5a0a..b696069 100644 --- a/logparse/src/spans.rs +++ b/logparse/src/spans.rs @@ -14,6 +14,8 @@ pub enum SpanKind { Number, /// Known literals, like `true`, `false`, `None`, `Ok`, `Err` Literal, + /// Lifetimes (`'foo`) + Lifetime, /// Strings String, /// Paths @@ -193,6 +195,10 @@ mod private { Token::Atom(atom) => { atom.into_spans(cx); } + Token::Lifetime(lifetime) => { + cx.push("'", SpanKind::Surroundings); + cx.push(lifetime, SpanKind::Lifetime); + } } } } diff --git a/src/tui/reader.rs b/src/tui/reader.rs index 401ce32..b4ab2a8 100644 --- a/src/tui/reader.rs +++ b/src/tui/reader.rs @@ -424,7 +424,7 @@ mod tests { println!("undo"); f.undo(); c.update_with_parents(&f); - assert_eq!(c.curr().message_or_name(), Some("nest")); + assert_eq!(c.curr().message_or_name(), Some("enter")); assert!(c.next(&f)); assert_eq!(c.curr().message_or_name(), Some("bar")); assert!(!c.next(&f)); diff --git a/src/tui/widgets/line_text.rs b/src/tui/widgets/line_text.rs index 11563c4..12c67a9 100644 --- a/src/tui/widgets/line_text.rs +++ b/src/tui/widgets/line_text.rs @@ -165,12 +165,13 @@ pub fn style_span(kind: SpanKind, style: Style, styles: &Styles) -> Style { SpanKind::Delimiter(_) => style.fg(styles.delimiter).bold(), SpanKind::Separator => style.fg(styles.faded), SpanKind::Number => style.fg(styles.literal), - SpanKind::Literal => style.fg(styles.literal).dim(), + SpanKind::Literal => style.fg(styles.literal), SpanKind::String => style.fg(styles.string), SpanKind::Path => style.fg(styles.literal).underlined(), SpanKind::Space(_) => style, SpanKind::Constructor => style.fg(styles.literal), SpanKind::Surroundings => style.fg(styles.faded), + SpanKind::Lifetime => style.fg(styles.faded), SpanKind::Text => style, } }