Skip to main content

logparse/
spans.rs

1use super::ast::*;
2use std::borrow::Cow;
3
4/// Text categories, based on the parsing.
5#[derive(Copy, Clone, PartialEq, Eq, Debug)]
6pub enum SpanKind {
7    /// Parentheses e.g.
8    ///
9    /// Stores the delimiter depth, for e.g. rainbow delimiters.
10    Delimiter(usize),
11    /// Separators like `=`, ':' and ','
12    Separator,
13    /// Numbers
14    Number,
15    /// Known literals, like `true`, `false`, `None`, `Ok`, `Err`
16    Literal,
17    /// Lifetimes (`'foo`)
18    Lifetime,
19    /// Strings
20    String,
21    /// Paths
22    Path,
23    /// Spaces (returns original number of spaces)
24    Space(usize),
25
26    /// Constructor: the prefix of a delimited block.
27    /// i.e. `Some` in `Some(3)`
28    Constructor,
29
30    /// String prefix, suffix, hashtags, etc.
31    /// Also number suffix
32    Surroundings,
33
34    /// Any other text (the default)
35    Text,
36}
37
38/// A `Span` is a piece of categorized text, based on the parsing done by
39/// [`parse_input`](crate::parse_input).
40#[derive(Clone, PartialEq, Eq, Debug)]
41pub struct Span<'a> {
42    /// The segment of text.
43    pub text: Cow<'a, str>,
44    /// Its category.
45    pub kind: SpanKind,
46}
47
48/// Configuration options for [`into_spans`]
49pub struct Config {
50    /// Turn sequences of more than 1 space into exactly 1 space.
51    pub collapse_space: bool,
52}
53
54pub trait IntoSpans<'a>: private::IntoSpansImpl<'a> {}
55
56/// Turn an ast node into [`Span`]s.
57pub fn into_spans<'a>(ast: impl IntoSpans<'a>, config: Config) -> Vec<Span<'a>> {
58    let mut cx = private::Context {
59        config,
60        res: Vec::new(),
61        depth: 0,
62    };
63    ast.into_spans(&mut cx);
64    cx.res
65}
66
67mod private {
68    use super::*;
69
70    pub struct Context<'a> {
71        pub config: Config,
72        pub res: Vec<Span<'a>>,
73        pub depth: usize,
74    }
75
76    impl<'a> Context<'a> {
77        fn push(&mut self, text: impl Into<Cow<'a, str>>, kind: SpanKind) {
78            self.res.push(Span {
79                text: text.into(),
80                kind,
81            })
82        }
83    }
84
85    pub trait IntoSpansImpl<'a> {
86        fn into_spans(self, cx: &mut Context<'a>);
87    }
88
89    impl<'a, T> IntoSpans<'a> for T where T: IntoSpansImpl<'a> {}
90
91    impl<'a> IntoSpansImpl<'a> for Separator {
92        fn into_spans(self, cx: &mut Context<'a>) {
93            match self {
94                Separator::Eq => cx.push("=", SpanKind::Separator),
95                Separator::Colon => cx.push(":", SpanKind::Separator),
96                Separator::DoubleColon => cx.push("::", SpanKind::Separator),
97            }
98        }
99    }
100
101    impl<'a> IntoSpansImpl<'a> for QuoteType {
102        fn into_spans(self, cx: &mut Context<'a>) {
103            match self {
104                QuoteType::Single => cx.push("'", SpanKind::Separator),
105                QuoteType::Double => cx.push("\"", SpanKind::Separator),
106                QuoteType::Backtick => cx.push("`", SpanKind::Separator),
107            }
108        }
109    }
110
111    impl<'a> IntoSpansImpl<'a> for AnyString<'a> {
112        fn into_spans(self, cx: &mut Context<'a>) {
113            let Self {
114                prefix,
115                ty,
116                contents,
117                num_hashtags,
118                suffix,
119            } = self;
120            cx.push(prefix, SpanKind::Surroundings);
121            for _ in 0..num_hashtags {
122                cx.push("#", SpanKind::Surroundings)
123            }
124
125            ty.into_spans(cx);
126            cx.push(contents, SpanKind::String);
127            ty.into_spans(cx);
128
129            for _ in 0..num_hashtags {
130                cx.push("#", SpanKind::Surroundings)
131            }
132            cx.push(suffix, SpanKind::Surroundings);
133        }
134    }
135
136    impl<'a> IntoSpansImpl<'a> for Path<'a> {
137        fn into_spans(self, cx: &mut Context<'a>) {
138            cx.push(self.to_string(), SpanKind::Path)
139        }
140    }
141
142    impl<'a> IntoSpansImpl<'a> for Number<'a> {
143        fn into_spans(self, cx: &mut Context<'a>) {
144            cx.push(self.number, SpanKind::Number);
145
146            if let Some(suffix) = self.suffix_without_underscore {
147                cx.push("_", SpanKind::Surroundings);
148                cx.push(suffix, SpanKind::Surroundings);
149            }
150        }
151    }
152
153    impl<'a> IntoSpansImpl<'a> for Atom<'a> {
154        fn into_spans(self, cx: &mut Context<'a>) {
155            match self {
156                Atom::Text(text) => cx.push(text, SpanKind::Text),
157            }
158        }
159    }
160
161    impl<'a> IntoSpansImpl<'a> for Space<'a> {
162        fn into_spans(self, cx: &mut Context<'a>) {
163            match self.0.len() {
164                0 => {}
165                1 => cx.push(self.0, SpanKind::Space(1)),
166                n if cx.config.collapse_space => cx.push(" ", SpanKind::Space(n)),
167                n => cx.push(self.0, SpanKind::Space(n)),
168            }
169        }
170    }
171
172    impl<'a> IntoSpansImpl<'a> for Token<'a> {
173        fn into_spans(self, cx: &mut Context<'a>) {
174            match self {
175                Token::True => cx.push("true", SpanKind::Literal),
176                Token::False => cx.push("false", SpanKind::Literal),
177                Token::None => cx.push("None", SpanKind::Literal),
178                Token::Path(path) => path.into_spans(cx),
179                Token::String(string) => string.into_spans(cx),
180                Token::Number(number) => number.into_spans(cx),
181                Token::Separated {
182                    before,
183                    space_before,
184                    separator,
185                    after,
186                } => {
187                    before.into_spans(cx);
188                    space_before.into_spans(cx);
189                    separator.into_spans(cx);
190                    after.into_spans(cx);
191                }
192                Token::Delimited(delimited) => {
193                    delimited.into_spans(cx);
194                }
195                Token::Atom(atom) => {
196                    atom.into_spans(cx);
197                }
198                Token::Lifetime(lifetime) => {
199                    cx.push("'", SpanKind::Surroundings);
200                    cx.push(lifetime, SpanKind::Lifetime);
201                }
202            }
203        }
204    }
205
206    impl<'a> IntoSpansImpl<'a> for Segment<'a> {
207        fn into_spans(self, cx: &mut Context<'a>) {
208            let Self {
209                leading_space,
210                token,
211            } = self;
212            leading_space.into_spans(cx);
213            token.into_spans(cx);
214        }
215    }
216
217    impl<'a> IntoSpansImpl<'a> for Segments<'a> {
218        fn into_spans(self, cx: &mut Context<'a>) {
219            let Self {
220                segments,
221                trailing_space,
222            } = self;
223            for segment in segments {
224                segment.into_spans(cx);
225            }
226            trailing_space.into_spans(cx);
227        }
228    }
229
230    impl<'a> IntoSpansImpl<'a> for Delimited<'a> {
231        fn into_spans(self, cx: &mut Context<'a>) {
232            let Self {
233                prefix,
234                delimiter,
235                contents,
236            } = self;
237
238            match prefix {
239                Some((Atom::Text(text), space)) => {
240                    cx.push(
241                        text,
242                        match delimiter {
243                            Delimiter::Brace => SpanKind::Constructor,
244                            Delimiter::Paren if space.0.is_empty() => SpanKind::Constructor,
245                            Delimiter::Paren => SpanKind::Text,
246                            Delimiter::Bracket => SpanKind::Text,
247                            Delimiter::Angle if space.0.is_empty() => SpanKind::Constructor,
248                            Delimiter::Angle => SpanKind::Text,
249                        },
250                    );
251                    space.into_spans(cx);
252                }
253                None => {}
254            }
255
256            match delimiter {
257                Delimiter::Paren => cx.push("(", SpanKind::Delimiter(cx.depth)),
258                Delimiter::Bracket => cx.push("[", SpanKind::Delimiter(cx.depth)),
259                Delimiter::Brace => cx.push("{", SpanKind::Delimiter(cx.depth)),
260                Delimiter::Angle => cx.push("<", SpanKind::Delimiter(cx.depth)),
261            }
262
263            cx.depth += 1;
264            contents.into_spans(cx);
265            cx.depth -= 1;
266
267            match delimiter {
268                Delimiter::Paren => cx.push(")", SpanKind::Delimiter(cx.depth)),
269                Delimiter::Bracket => cx.push("]", SpanKind::Delimiter(cx.depth)),
270                Delimiter::Brace => cx.push("}", SpanKind::Delimiter(cx.depth)),
271                Delimiter::Angle => cx.push(">", SpanKind::Delimiter(cx.depth)),
272            }
273        }
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use insta::assert_debug_snapshot;
280
281    use super::SpanKind;
282    use crate::{Config, into_spans, parse_input};
283
284    fn spans(input: &str) -> Vec<(String, SpanKind)> {
285        let res = parse_input(input).unwrap();
286        into_spans(
287            res,
288            Config {
289                collapse_space: true,
290            },
291        )
292        .into_iter()
293        .map(|i| (i.text.into_owned(), i.kind))
294        .collect()
295    }
296
297    #[test]
298    fn spans_ex1() {
299        assert_debug_snapshot!(spans(
300            r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#
301        ), @r#"
302        [
303            (
304                "def_id",
305                Text,
306            ),
307            (
308                "=",
309                Separator,
310            ),
311            (
312                "DefId",
313                Constructor,
314            ),
315            (
316                "(",
317                Delimiter(
318                    0,
319                ),
320            ),
321            (
322                "0",
323                Number,
324            ),
325            (
326                ":",
327                Separator,
328            ),
329            (
330                "3",
331                Number,
332            ),
333            (
334                " ",
335                Space(
336                    1,
337                ),
338            ),
339            (
340                "~",
341                Text,
342            ),
343            (
344                " ",
345                Space(
346                    1,
347                ),
348            ),
349            (
350                "unsized_coercion",
351                Text,
352            ),
353            (
354                "[",
355                Delimiter(
356                    1,
357                ),
358            ),
359            (
360                "10fa",
361                Text,
362            ),
363            (
364                "]",
365                Delimiter(
366                    1,
367                ),
368            ),
369            (
370                "::",
371                Separator,
372            ),
373            (
374                "Trait",
375                Text,
376            ),
377            (
378                ")",
379                Delimiter(
380                    0,
381                ),
382            ),
383        ]
384        "#)
385    }
386
387    #[test]
388    fn spans_ex2() {
389        assert_debug_snapshot!(spans(
390            r#"data=TypeNs("MetaSized") visible_parent=DefId(2:3984 ~ core[bcc4]::marker) actual_parent=Some(DefId(2:3984 ~ core[bcc4]::marker))"#
391        ), @r#"
392        [
393            (
394                "data",
395                Text,
396            ),
397            (
398                "=",
399                Separator,
400            ),
401            (
402                "TypeNs",
403                Constructor,
404            ),
405            (
406                "(",
407                Delimiter(
408                    0,
409                ),
410            ),
411            (
412                "",
413                Surroundings,
414            ),
415            (
416                "\"",
417                Separator,
418            ),
419            (
420                "MetaSized",
421                String,
422            ),
423            (
424                "\"",
425                Separator,
426            ),
427            (
428                "",
429                Surroundings,
430            ),
431            (
432                ")",
433                Delimiter(
434                    0,
435                ),
436            ),
437            (
438                " ",
439                Space(
440                    1,
441                ),
442            ),
443            (
444                "visible_parent",
445                Text,
446            ),
447            (
448                "=",
449                Separator,
450            ),
451            (
452                "DefId",
453                Constructor,
454            ),
455            (
456                "(",
457                Delimiter(
458                    0,
459                ),
460            ),
461            (
462                "2",
463                Number,
464            ),
465            (
466                ":",
467                Separator,
468            ),
469            (
470                "3984",
471                Number,
472            ),
473            (
474                " ",
475                Space(
476                    1,
477                ),
478            ),
479            (
480                "~",
481                Text,
482            ),
483            (
484                " ",
485                Space(
486                    1,
487                ),
488            ),
489            (
490                "core",
491                Text,
492            ),
493            (
494                "[",
495                Delimiter(
496                    1,
497                ),
498            ),
499            (
500                "bcc4",
501                Text,
502            ),
503            (
504                "]",
505                Delimiter(
506                    1,
507                ),
508            ),
509            (
510                "::",
511                Separator,
512            ),
513            (
514                "marker",
515                Text,
516            ),
517            (
518                ")",
519                Delimiter(
520                    0,
521                ),
522            ),
523            (
524                " ",
525                Space(
526                    1,
527                ),
528            ),
529            (
530                "actual_parent",
531                Text,
532            ),
533            (
534                "=",
535                Separator,
536            ),
537            (
538                "Some",
539                Constructor,
540            ),
541            (
542                "(",
543                Delimiter(
544                    0,
545                ),
546            ),
547            (
548                "DefId",
549                Constructor,
550            ),
551            (
552                "(",
553                Delimiter(
554                    1,
555                ),
556            ),
557            (
558                "2",
559                Number,
560            ),
561            (
562                ":",
563                Separator,
564            ),
565            (
566                "3984",
567                Number,
568            ),
569            (
570                " ",
571                Space(
572                    1,
573                ),
574            ),
575            (
576                "~",
577                Text,
578            ),
579            (
580                " ",
581                Space(
582                    1,
583                ),
584            ),
585            (
586                "core",
587                Text,
588            ),
589            (
590                "[",
591                Delimiter(
592                    2,
593                ),
594            ),
595            (
596                "bcc4",
597                Text,
598            ),
599            (
600                "]",
601                Delimiter(
602                    2,
603                ),
604            ),
605            (
606                "::",
607                Separator,
608            ),
609            (
610                "marker",
611                Text,
612            ),
613            (
614                ")",
615                Delimiter(
616                    1,
617                ),
618            ),
619            (
620                ")",
621                Delimiter(
622                    0,
623                ),
624            ),
625        ]
626        "#)
627    }
628
629    #[test]
630    fn spans_ex3() {
631        assert_debug_snapshot!(spans(
632            r#"insert(DefId(0:4 ~ unsized_coercion[10fa]::{impl#0})): inserting TraitRef <u32 as Trait> into specialization graph"#
633        ), @r#"
634        [
635            (
636                "insert",
637                Constructor,
638            ),
639            (
640                "(",
641                Delimiter(
642                    0,
643                ),
644            ),
645            (
646                "DefId",
647                Constructor,
648            ),
649            (
650                "(",
651                Delimiter(
652                    1,
653                ),
654            ),
655            (
656                "0",
657                Number,
658            ),
659            (
660                ":",
661                Separator,
662            ),
663            (
664                "4",
665                Number,
666            ),
667            (
668                " ",
669                Space(
670                    1,
671                ),
672            ),
673            (
674                "~",
675                Text,
676            ),
677            (
678                " ",
679                Space(
680                    1,
681                ),
682            ),
683            (
684                "unsized_coercion",
685                Text,
686            ),
687            (
688                "[",
689                Delimiter(
690                    2,
691                ),
692            ),
693            (
694                "10fa",
695                Text,
696            ),
697            (
698                "]",
699                Delimiter(
700                    2,
701                ),
702            ),
703            (
704                "::",
705                Separator,
706            ),
707            (
708                "{",
709                Delimiter(
710                    2,
711                ),
712            ),
713            (
714                "impl#0",
715                Text,
716            ),
717            (
718                "}",
719                Delimiter(
720                    2,
721                ),
722            ),
723            (
724                ")",
725                Delimiter(
726                    1,
727                ),
728            ),
729            (
730                ")",
731                Delimiter(
732                    0,
733                ),
734            ),
735            (
736                ":",
737                Separator,
738            ),
739            (
740                " ",
741                Space(
742                    1,
743                ),
744            ),
745            (
746                "inserting",
747                Text,
748            ),
749            (
750                " ",
751                Space(
752                    1,
753                ),
754            ),
755            (
756                "TraitRef",
757                Text,
758            ),
759            (
760                " ",
761                Space(
762                    1,
763                ),
764            ),
765            (
766                "<",
767                Delimiter(
768                    0,
769                ),
770            ),
771            (
772                "u32",
773                Text,
774            ),
775            (
776                " ",
777                Space(
778                    1,
779                ),
780            ),
781            (
782                "as",
783                Text,
784            ),
785            (
786                " ",
787                Space(
788                    1,
789                ),
790            ),
791            (
792                "Trait",
793                Text,
794            ),
795            (
796                ">",
797                Delimiter(
798                    0,
799                ),
800            ),
801            (
802                " ",
803                Space(
804                    1,
805                ),
806            ),
807            (
808                "into",
809                Text,
810            ),
811            (
812                " ",
813                Space(
814                    1,
815                ),
816            ),
817            (
818                "specialization",
819                Text,
820            ),
821            (
822                " ",
823                Space(
824                    1,
825                ),
826            ),
827            (
828                "graph",
829                Text,
830            ),
831        ]
832        "#)
833    }
834
835    #[test]
836    fn spans_ex4() {
837        assert_debug_snapshot!(spans(
838        r#"inspecting def_id=DefId(3:662 ~ alloc[ef11]::boxed::Box) span=tests/ui/impl-trait/unsized_coercion.rs:12:15: 12:30 (#0)"#
839        ), @r##"
840        [
841            (
842                "inspecting",
843                Text,
844            ),
845            (
846                " ",
847                Space(
848                    1,
849                ),
850            ),
851            (
852                "def_id",
853                Text,
854            ),
855            (
856                "=",
857                Separator,
858            ),
859            (
860                "DefId",
861                Constructor,
862            ),
863            (
864                "(",
865                Delimiter(
866                    0,
867                ),
868            ),
869            (
870                "3",
871                Number,
872            ),
873            (
874                ":",
875                Separator,
876            ),
877            (
878                "662",
879                Number,
880            ),
881            (
882                " ",
883                Space(
884                    1,
885                ),
886            ),
887            (
888                "~",
889                Text,
890            ),
891            (
892                " ",
893                Space(
894                    1,
895                ),
896            ),
897            (
898                "alloc",
899                Text,
900            ),
901            (
902                "[",
903                Delimiter(
904                    1,
905                ),
906            ),
907            (
908                "ef11",
909                Text,
910            ),
911            (
912                "]",
913                Delimiter(
914                    1,
915                ),
916            ),
917            (
918                "::",
919                Separator,
920            ),
921            (
922                "boxed",
923                Text,
924            ),
925            (
926                "::",
927                Separator,
928            ),
929            (
930                "Box",
931                Text,
932            ),
933            (
934                ")",
935                Delimiter(
936                    0,
937                ),
938            ),
939            (
940                " ",
941                Space(
942                    1,
943                ),
944            ),
945            (
946                "span",
947                Text,
948            ),
949            (
950                "=",
951                Separator,
952            ),
953            (
954                "tests/ui/impl-trait/unsized_coercion.rs:12:15",
955                Path,
956            ),
957            (
958                ":",
959                Separator,
960            ),
961            (
962                " ",
963                Space(
964                    1,
965                ),
966            ),
967            (
968                "12",
969                Number,
970            ),
971            (
972                ":",
973                Separator,
974            ),
975            (
976                "30",
977                Number,
978            ),
979            (
980                " ",
981                Space(
982                    1,
983                ),
984            ),
985            (
986                "(",
987                Delimiter(
988                    0,
989                ),
990            ),
991            (
992                "#0",
993                Text,
994            ),
995            (
996                ")",
997                Delimiter(
998                    0,
999                ),
1000            ),
1001        ]
1002        "##)
1003    }
1004}