From 4e03a367a56a2c99152bf6476aeed1a203d649f5 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Thu, 28 Apr 2022 22:43:31 -0400 Subject: [PATCH] tamer: diagnose::report::SourceLine: Separate variants for each line Now `SourceLine` _does_ actually correspond to a line of output, which will allow for better formatting (e.g. collapsing padding) and, importantly, proper management of gutters. Note that the seemingly unnecessary `SectionSourceLine` allows for a subtle consistent formatting for all variants' gutters in `SectionLine`, which will allow us to hoist that rendering out in the next commit. The other option was to include a trailing space for padding and marks, but that is not only sloppy and undesirable, but asking for confusion, especially in editors (like mine) that trim trailing whitespace. DEV-12151 --- tamer/src/diagnose/report.rs | 151 ++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/tamer/src/diagnose/report.rs b/tamer/src/diagnose/report.rs index 9551f80d..c782d0d4 100644 --- a/tamer/src/diagnose/report.rs +++ b/tamer/src/diagnose/report.rs @@ -304,23 +304,26 @@ where let nlines = src.len(); - body.extend(src.into_iter().enumerate().filter_map( - |(i, srcline)| { - let label = - if i == nlines - 1 { olabel.take() } else { None }; + src.into_iter().enumerate().for_each(|(i, srcline)| { + let label = + if i == nlines - 1 { olabel.take() } else { None }; - if let Some(col) = srcline.column() { - Some(SectionLine::SourceLine(SectionSourceLine { - src: srcline, - mark: LineMark { col, level, label }, - })) - } else { - label.map(|l| { - SectionLine::Footnote(SpanLabel(level, l)) - }) - } - }, - )); + if let Some(col) = srcline.column() { + body.extend(vec![ + SectionLine::SourceLinePadding, + SectionLine::SourceLine(srcline.into()), + SectionLine::SourceLineMark(LineMark { + col, + level, + label, + }), + ]); + } else { + body.extend(label.map(|l| { + SectionLine::Footnote(SpanLabel(level, l)) + })); + } + }); (span, level) } @@ -498,24 +501,23 @@ impl<'d> Display for SpanLabel<'d> { } } -/// A possibly-annotated line of output. -/// -/// Note that a section line doesn't necessarily correspond to a single line -/// of output on a terminal; -/// lines are likely to be annotated. +/// Line of output in a [`Section`] body. #[derive(Debug, PartialEq, Eq)] enum SectionLine<'d> { - SourceLine(SectionSourceLine<'d>), + SourceLinePadding, + SourceLine(SectionSourceLine), + SourceLineMark(LineMark<'d>), Footnote(SpanLabel<'d>), } impl<'d> SectionLine<'d> { fn into_footnote(self) -> Option { match self { - Self::SourceLine(SectionSourceLine { - mark: LineMark { level, label, .. }, - .. - }) => label.map(|l| Self::Footnote(SpanLabel(level, l))), + Self::SourceLinePadding => None, + Self::SourceLine(..) => None, + Self::SourceLineMark(LineMark { level, label, .. }) => { + label.map(|l| Self::Footnote(SpanLabel(level, l))) + } Self::Footnote(..) => Some(self), } @@ -525,24 +527,26 @@ impl<'d> SectionLine<'d> { impl<'d> Display for SectionLine<'d> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::SourceLine(line) => line.fmt(f), + Self::SourceLinePadding => write!(f, " |\n"), + Self::SourceLine(line) => write!(f, " |{line}\n"), + Self::SourceLineMark(mark) => write!(f, " |{mark}\n"), Self::Footnote(label) => write!(f, "{label}\n"), } } } -/// A line representing possibly-annotated source code. #[derive(Debug, PartialEq, Eq)] -struct SectionSourceLine<'d> { - src: SourceLine, - mark: LineMark<'d>, +struct SectionSourceLine(SourceLine); + +impl From for SectionSourceLine { + fn from(line: SourceLine) -> Self { + Self(line) + } } -impl<'d> Display for SectionSourceLine<'d> { +impl Display for SectionSourceLine { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, " |\n")?; - write!(f, " | {src}\n", src = self.src)?; - write!(f, " |{}", self.mark) + write!(f, " {line}", line = self.0) } } @@ -575,7 +579,7 @@ impl<'d> Display for LineMark<'d> { write!(f, " {level}: {label}", level = self.level)?; } - write!(f, "\n") + Ok(()) } } @@ -765,23 +769,21 @@ mod test { // Derived from label. level: Level::Note, body: vec![ - SectionLine::SourceLine(SectionSourceLine { - src: src_lines[0].clone(), - mark: LineMark { - level: Level::Note, - col: col_1, - // Label goes on the last source line. - label: None, - } + SectionLine::SourceLinePadding, + SectionLine::SourceLine(src_lines[0].clone().into()), + SectionLine::SourceLineMark(LineMark { + level: Level::Note, + col: col_1, + // Label goes on the last source line. + label: None, }), - SectionLine::SourceLine(SectionSourceLine { - src: src_lines[1].clone(), - mark: LineMark { - level: Level::Note, - col: col_2, - // Label at last source line - label: Some("test label".into()), - } + SectionLine::SourceLinePadding, + SectionLine::SourceLine(src_lines[1].clone().into()), + SectionLine::SourceLineMark(LineMark { + level: Level::Note, + col: col_2, + // Label at last source line + label: Some("test label".into()), }), ], } @@ -882,20 +884,29 @@ mod test { } #[test] - fn section_src_line_with_label_into_footnote() { + fn section_src_line_into_footnote() { assert_eq!( - SectionLine::SourceLine(SectionSourceLine { - src: SourceLine::new_stub( + SectionLine::SourceLine( + SourceLine::new_stub( 1.unwrap_into(), None, DUMMY_SPAN, "discarded".into() - ), - mark: LineMark { - level: Level::Help, - col: Column::Before(1.unwrap_into()), - label: Some("kept label".into()) - } + ) + .into() + ) + .into_footnote(), + None + ); + } + + #[test] + fn section_mark_with_label_into_footnote() { + assert_eq!( + SectionLine::SourceLineMark(LineMark { + level: Level::Help, + col: Column::Before(1.unwrap_into()), + label: Some("kept label".into()) }) .into_footnote(), Some(SectionLine::Footnote(SpanLabel( @@ -906,20 +917,12 @@ mod test { } #[test] - fn section_src_line_without_label_into_footnote() { + fn section_mark_without_label_into_footnote() { assert_eq!( - SectionLine::SourceLine(SectionSourceLine { - src: SourceLine::new_stub( - 1.unwrap_into(), - None, - DUMMY_SPAN, - "discarded".into() - ), - mark: LineMark { - level: Level::Help, - col: Column::Before(1.unwrap_into()), - label: None, - } + SectionLine::SourceLineMark(LineMark { + level: Level::Help, + col: Column::Before(1.unwrap_into()), + label: None, }) .into_footnote(), None