From eeb7486ecc81ebf360f16e21d972cf5ae695deeb Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 17 May 2020 11:41:44 +1000 Subject: [PATCH 1/4] Simplify some methods in renderer --- codespan-reporting/src/term/renderer.rs | 42 ++++++------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/codespan-reporting/src/term/renderer.rs b/codespan-reporting/src/term/renderer.rs index 7ecd32b..8d7ee7a 100644 --- a/codespan-reporting/src/term/renderer.rs +++ b/codespan-reporting/src/term/renderer.rs @@ -511,36 +511,6 @@ impl<'writer, 'config> Renderer<'writer, 'config> { Ok(()) } - /// The top of a multi-line label. - fn label_multi_top_line( - &mut self, - severity: Severity, - label_style: LabelStyle, - len: usize, - ) -> io::Result<()> { - self.set_color(self.styles().label(severity, label_style))?; - for _ in 0..len { - write!(self, "{}", self.config.chars.multi_top)?; - } - self.reset()?; - Ok(()) - } - - /// The top of a multi-line label. - fn label_multi_bottom_line( - &mut self, - severity: Severity, - label_style: LabelStyle, - len: usize, - ) -> io::Result<()> { - self.set_color(self.styles().label(severity, label_style))?; - for _ in 0..len { - write!(self, "{}", self.config.chars.multi_bottom)?; - } - self.reset()?; - Ok(()) - } - /// The top-left of a multi-line label. /// /// ```text @@ -633,8 +603,16 @@ impl<'writer, 'config> Renderer<'writer, 'config> { ) -> io::Result<()> { match underline { None => self.inner_gutter_space(), - Some((ls, VerticalBound::Top)) => self.label_multi_top_line(severity, ls, 2), - Some((ls, VerticalBound::Bottom)) => self.label_multi_bottom_line(severity, ls, 2), + Some((label_style, vertical_bound)) => { + self.set_color(self.styles().label(severity, label_style))?; + let ch = match vertical_bound { + VerticalBound::Top => self.config.chars.multi_top, + VerticalBound::Bottom => self.config.chars.multi_bottom, + }; + write!(self, "{0}{0}", ch)?; + self.reset()?; + Ok(()) + } } } From 124b6de617af0bb25476947a542e104af2a8828c Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Thu, 26 Mar 2020 14:36:12 +1100 Subject: [PATCH 2/4] Remove obsolete comment --- codespan-reporting/src/term/views.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/codespan-reporting/src/term/views.rs b/codespan-reporting/src/term/views.rs index 695e9d3..204bdf4 100644 --- a/codespan-reporting/src/term/views.rs +++ b/codespan-reporting/src/term/views.rs @@ -231,8 +231,6 @@ where } } - // TODO: Insert `None` spaces in `labeled_files` - // Header and message // // ```text From 7082032a2041347a8fbd29797e542016619f66ca Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Thu, 14 May 2020 13:30:38 +1000 Subject: [PATCH 3/4] Split unicode span test over multiple lines --- .../term__unicode_spans__rich_no_color.snap | 17 ++++++++++++++++- .../term__unicode_spans__short_no_color.snap | 3 +++ codespan-reporting/tests/term.rs | 16 ++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap b/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap index 371f1e0..3609f0f 100644 --- a/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap @@ -7,8 +7,23 @@ error[E01]: cow may not jump during new moon. โ”‚ 1 โ”‚ ๐Ÿ„๐ŸŒ‘๐Ÿ„๐ŸŒ’๐Ÿ„๐ŸŒ“๐Ÿ„๐ŸŒ”๐Ÿ„๐ŸŒ•๐Ÿ„๐ŸŒ–๐Ÿ„๐ŸŒ—๐Ÿ„๐ŸŒ˜๐Ÿ„ โ”‚ ^^ Invalid jump + +note: invalid unicode range + โ”Œโ”€ moon_jump.rs:1:1 + โ”‚ +1 โ”‚ ๐Ÿ„๐ŸŒ‘๐Ÿ„๐ŸŒ’๐Ÿ„๐ŸŒ“๐Ÿ„๐ŸŒ”๐Ÿ„๐ŸŒ•๐Ÿ„๐ŸŒ–๐Ÿ„๐ŸŒ—๐Ÿ„๐ŸŒ˜๐Ÿ„ โ”‚ -- Cow range does not start at boundary. - โ”‚ ------ Cow does not start or end at boundary. + +note: invalid unicode range + โ”Œโ”€ moon_jump.rs:1:3 + โ”‚ +1 โ”‚ ๐Ÿ„๐ŸŒ‘๐Ÿ„๐ŸŒ’๐Ÿ„๐ŸŒ“๐Ÿ„๐ŸŒ”๐Ÿ„๐ŸŒ•๐Ÿ„๐ŸŒ–๐Ÿ„๐ŸŒ—๐Ÿ„๐ŸŒ˜๐Ÿ„ โ”‚ -- Cow range does not end at boundary. +note: invalid unicode range + โ”Œโ”€ moon_jump.rs:1:1 + โ”‚ +1 โ”‚ ๐Ÿ„๐ŸŒ‘๐Ÿ„๐ŸŒ’๐Ÿ„๐ŸŒ“๐Ÿ„๐ŸŒ”๐Ÿ„๐ŸŒ•๐Ÿ„๐ŸŒ–๐Ÿ„๐ŸŒ—๐Ÿ„๐ŸŒ˜๐Ÿ„ + โ”‚ ------ Cow does not start or end at boundary. + diff --git a/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap b/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap index 9085082..1b19451 100644 --- a/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap @@ -3,4 +3,7 @@ source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- moon_jump.rs:1:1: error[E01]: cow may not jump during new moon. +note: invalid unicode range +note: invalid unicode range +note: invalid unicode range diff --git a/codespan-reporting/tests/term.rs b/codespan-reporting/tests/term.rs index 0714c56..5b6acef 100644 --- a/codespan-reporting/tests/term.rs +++ b/codespan-reporting/tests/term.rs @@ -741,14 +741,26 @@ mod unicode_spans { .with_labels(vec![ Label::primary((), invalid_start..invalid_end) .with_message("Invalid jump"), + ]), + Diagnostic::note() + .with_message("invalid unicode range") + .with_labels(vec![ Label::secondary((), invalid_start.."๐Ÿ„".len()) .with_message("Cow range does not start at boundary."), + ]), + Diagnostic::note() + .with_message("invalid unicode range") + .with_labels(vec![ Label::secondary((), "๐Ÿ„๐ŸŒ‘".len().."๐Ÿ„๐ŸŒ‘๐Ÿ„".len() - 1) .with_message("Cow range does not end at boundary."), + ]), + Diagnostic::note() + .with_message("invalid unicode range") + .with_labels(vec![ Label::secondary((), invalid_start.."๐Ÿ„๐ŸŒ‘๐Ÿ„".len() - 1) .with_message("Cow does not start or end at boundary."), - - ])]; + ]), + ]; TestData{files: file, diagnostics } }; } From 0f76247b80e785af326482cec7619d8e3ed125da Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Thu, 14 May 2020 16:56:14 +1000 Subject: [PATCH 4/4] Improve single line label rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the rendering of carets smarter when there are multiple labels on the same line. Instead of this: ``` โ”Œโ”€ one_line.rs:3:5 โ”‚ 3 โ”‚ v.push(v.pop().unwrap()); โ”‚ - first borrow later used by call โ”‚ ---- first mutable borrow occurs here โ”‚ ^ second mutable borrow occurs here โ”‚ ``` ...we now render the following: ``` โ”Œโ”€ one_line.rs:3:5 โ”‚ 3 โ”‚ v.push(v.pop().unwrap()); โ”‚ - ---- ^ second mutable borrow occurs here โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ first mutable borrow occurs here โ”‚ first borrow later used by call โ”‚ ``` --- codespan-reporting/src/term/config.rs | 41 +- codespan-reporting/src/term/renderer.rs | 412 +++++++++++++----- codespan-reporting/src/term/views.rs | 17 +- .../term__empty_ranges__rich_color.snap | 14 +- .../term__empty_ranges__rich_no_color.snap | 10 +- .../term__empty_ranges__short_color.snap | 3 +- .../term__empty_ranges__short_no_color.snap | 3 +- .../term__fizz_buzz__rich_color.snap | 14 +- .../term__multifile__rich_color.snap | 8 +- ...rm__multiline_overlapping__rich_color.snap | 4 +- .../term__overlapping__rich_color.snap | 24 +- .../term__overlapping__rich_no_color.snap | 20 +- .../term__same_line__rich_color.snap | 7 +- .../term__same_line__rich_no_color.snap | 7 +- .../term__same_ranges__rich_color.snap | 6 +- .../term__same_ranges__rich_no_color.snap | 6 +- .../term__unicode_spans__rich_no_color.snap | 6 +- codespan-reporting/tests/term.rs | 5 +- 18 files changed, 407 insertions(+), 200 deletions(-) diff --git a/codespan-reporting/src/term/config.rs b/codespan-reporting/src/term/config.rs index a3bd03a..8c68ac7 100644 --- a/codespan-reporting/src/term/config.rs +++ b/codespan-reporting/src/term/config.rs @@ -32,16 +32,14 @@ impl Default for Config { } impl Config { - /// Measure the width of a string, taking into account the tab width. - pub fn width(&self, s: &str) -> usize { + /// Measure the unicode width of a character, taking into account the tab width. + pub fn width(&self, ch: char) -> usize { use unicode_width::UnicodeWidthChar; - s.chars() - .map(|ch| match ch { - '\t' => self.tab_width, - _ => ch.width().unwrap_or(0), - }) - .sum() + match ch { + '\t' => self.tab_width, + _ => ch.width().unwrap_or(0), + } } /// Construct a source writer using the current config. @@ -279,29 +277,10 @@ pub struct Chars { /// The character to use for the left of a multi-line label. /// Defaults to: `'โ”‚'`. pub multi_left: char, -} -impl Chars { - pub fn single_caret_char(&self, label_style: LabelStyle) -> char { - match label_style { - LabelStyle::Primary => self.single_primary_caret, - LabelStyle::Secondary => self.single_secondary_caret, - } - } - - pub fn multi_caret_char_start(&self, label_style: LabelStyle) -> char { - match label_style { - LabelStyle::Primary => self.multi_primary_caret_start, - LabelStyle::Secondary => self.multi_secondary_caret_start, - } - } - - pub fn multi_caret_char_end(&self, label_style: LabelStyle) -> char { - match label_style { - LabelStyle::Primary => self.multi_primary_caret_end, - LabelStyle::Secondary => self.multi_secondary_caret_end, - } - } + /// The character to use for the left of a pointer underneath a caret. + /// Defaults to: `'โ”‚'`. + pub pointer_left: char, } impl Default for Chars { @@ -326,6 +305,8 @@ impl Default for Chars { multi_bottom_left: 'โ•ฐ', multi_bottom: 'โ”€', multi_left: 'โ”‚', + + pointer_left: 'โ”‚', } } } diff --git a/codespan-reporting/src/term/renderer.rs b/codespan-reporting/src/term/renderer.rs index 8d7ee7a..fb4029b 100644 --- a/codespan-reporting/src/term/renderer.rs +++ b/codespan-reporting/src/term/renderer.rs @@ -84,8 +84,10 @@ type Underline = (LabelStyle, VerticalBound); /// snippet break โ”€โ”€ โ”‚ ยท โ”‚ /// snippet line โ”€โ”€ โ”‚ 38 โ”‚ โ”‚ Brownie lemon drops chocolate jelly-o candy canes. Danish marzipan /// snippet line โ”€โ”€ โ”‚ 39 โ”‚ โ”‚ jujubes soufflรฉ carrot cake marshmallow tiramisu caramels candy canes. -/// โ”‚ โ”‚ โ”‚ ^^^^^^^^^^^^^^^^^^ blah blah -/// โ”‚ โ”‚ โ”‚ -------------------- blah blah +/// โ”‚ โ”‚ โ”‚ ^^^^^^^^^^^^^^^^^^^ -------------------- blah blah +/// โ”‚ โ”‚ โ”‚ โ”‚ +/// โ”‚ โ”‚ โ”‚ blah blah +/// โ”‚ โ”‚ โ”‚ note: this is a note /// snippet line โ”€โ”€ โ”‚ 40 โ”‚ โ”‚ Fruitcake jelly-o danish toffee. Tootsie roll pastry cheesecake /// snippet line โ”€โ”€ โ”‚ 41 โ”‚ โ”‚ soufflรฉ marzipan. Chocolate bar oat cake jujubes lollipop pastry /// snippet line โ”€โ”€ โ”‚ 42 โ”‚ โ”‚ cupcake. Candy canes cupcake toffee gingerbread candy canes muffin @@ -229,6 +231,10 @@ impl<'writer, 'config> Renderer<'writer, 'config> { num_multi_labels: usize, multi_labels: &[(usize, MultiLabel<'_>)], ) -> io::Result<()> { + // Trim trailing newlines, linefeeds, and null chars from source, if they exist. + // FIXME: Use the number of trimmed placeholders when rendering single line carets + let source = source.trim_end_matches(['\n', '\r', '\0'].as_ref()); + // Write source line // // ```text @@ -260,20 +266,223 @@ impl<'writer, 'config> Renderer<'writer, 'config> { } // Write source - write!(self.config.source(self.writer), " {}", source.trim_end())?; + write!(self.config.source(self.writer), " {}", source)?; write!(self, "\n")?; } // Write single labels underneath source // // ```text - // โ”‚ โ”‚ โ”‚ ^^^^ oh noes + // โ”‚ - ---- ^^^ second mutable borrow occurs here + // โ”‚ โ”‚ โ”‚ + // โ”‚ โ”‚ first mutable borrow occurs here + // โ”‚ first borrow later used by call + // โ”‚ help: some help here // ``` - for (label_style, range, message) in single_labels.iter() { + if !single_labels.is_empty() { + // Our plan is as follows: + // + // 1. Do an initial scan to find: + // - The number of non-empty messages. + // - The right-most start and end positions of labels. + // - A candidate for a trailing label (where the label's message + // is printed to the left of the caret). + // 2. Check if the trailing label candidate overlaps another label - + // if so we print it underneath the carets with the other labels. + // 3. Print a line of carets, and (possibly) the trailing message + // to the left. + // 4. Print vertical lines pointing to the carets, and the messages + // for those carets. + // + // We try our best avoid introducing new dynamic allocations, + // instead preferring to iterate over the labels multiple times. It + // is unclear what the performance tradeoffs are however, so further + // investigation may be required. + + // The number of non-empty messages to print. + let mut num_messages = 0; + // The right-most start position, eg: + // + // ```text + // -^^^^---- ^^^^^^^ + // โ”‚ + // right-most start position + // ``` + let mut max_label_start = 0; + // The right-most end position, eg: + // + // ```text + // -^^^^---- ^^^^^^^ + // โ”‚ + // right-most end position + // ``` + let mut max_label_end = 0; + // A trailing message, eg: + // + // ```text + // ^^^ second mutable borrow occurs here + // ``` + let mut trailing_label = None; + + for (label_index, label) in single_labels.iter().enumerate() { + let (_, range, message) = label; + if !message.is_empty() { + num_messages += 1; + } + max_label_start = std::cmp::max(max_label_start, range.start); + max_label_end = std::cmp::max(max_label_end, range.end); + // This is a candidate for the trailing label, so let's record it. + if range.end == max_label_end { + if message.is_empty() { + trailing_label = None; + } else { + trailing_label = Some((label_index, label)); + } + } + } + if let Some((trailing_label_index, (_, trailing_range, _))) = trailing_label { + // Check to see if the trailing label candidate overlaps any of + // the other labels on the current line. + if single_labels + .iter() + .enumerate() + .filter(|(label_index, _)| *label_index != trailing_label_index) + .any(|(_, (_, range, _))| is_overlapping(trailing_range, range)) + { + // If it does, we'll instead want to render it below the + // carets along with the other hanging labels. + trailing_label = None; + } + } + + // Write a line of carets + // + // ```text + // โ”‚ ^^^^^^ -------^^^^^^^^^-------^^^^^----- ^^^^ trailing label message + // ``` self.outer_gutter(outer_padding)?; self.border_left()?; self.inner_gutter(severity, num_multi_labels, multi_labels)?; - self.label_single(severity, *label_style, source, range.clone(), message)?; + write!(self, " ")?; + + let mut previous_label_style = None; + for (byte_index, ch) in source + .char_indices() + // Add a placeholder source column at the end to allow for + // printing carets at the end of lines, eg: + // + // ```text + // 1 โ”‚ Hello world! + // โ”‚ ^ + // ``` + // + // ' ' is used because its unicode width is equal to 1 according + // to `unicode_width::UnicodeWidthChar`. + .chain(std::iter::once((source.len(), ' '))) + { + // Find the current label style at this column + let column_range = byte_index..(byte_index + ch.len_utf8()); + let current_label_style = single_labels + .iter() + .filter(|(_, range, _)| is_overlapping(range, &column_range)) + .map(|(label_style, _, _)| *label_style) + .max_by_key(label_priority_key); + + // Update writer style if necessary + if previous_label_style != current_label_style { + match current_label_style { + None => self.reset()?, + Some(label_style) => { + self.set_color(self.styles().label(severity, label_style))?; + } + } + } + + let caret_ch = match current_label_style { + Some(LabelStyle::Primary) => Some(self.chars().single_primary_caret), + Some(LabelStyle::Secondary) => Some(self.chars().single_secondary_caret), + // Only print padding if we are before the end of the last single line caret + None if byte_index < max_label_end => Some(' '), + None => None, + }; + if let Some(caret_ch) = caret_ch { + // FIXME: improve rendering for carets that occurring within character boundaries + for _ in 0..self.config.width(ch) { + write!(self, "{}", caret_ch)?; + } + } + + previous_label_style = current_label_style; + } + // Reset style if it was previously set + if previous_label_style.is_some() { + self.reset()?; + } + // Write first trailing label message + if let Some((_, (label_style, _, message))) = trailing_label { + write!(self, " ")?; + self.set_color(self.styles().label(severity, *label_style))?; + write!(self, "{}", message)?; + self.reset()?; + } + write!(self, "\n")?; + + // Write hanging labels pointing to carets + // + // ```text + // โ”‚ โ”‚ โ”‚ + // โ”‚ โ”‚ first mutable borrow occurs here + // โ”‚ first borrow later used by call + // โ”‚ help: some help here + // ``` + if num_messages > trailing_label.iter().count() { + // Write first set of vertical lines before hanging labels + // + // ```text + // โ”‚ โ”‚ โ”‚ + // ``` + self.outer_gutter(outer_padding)?; + self.border_left()?; + self.inner_gutter(severity, num_multi_labels, multi_labels)?; + write!(self, " ")?; + self.caret_pointers( + severity, + max_label_start, + single_labels, + trailing_label, + source.char_indices(), + )?; + write!(self, "\n")?; + + // Write hanging labels pointing to carets + // + // ```text + // โ”‚ โ”‚ first mutable borrow occurs here + // โ”‚ first borrow later used by call + // โ”‚ help: some help here + // ``` + for (label_style, range, message) in + hanging_labels(single_labels, trailing_label).rev() + { + self.outer_gutter(outer_padding)?; + self.border_left()?; + self.inner_gutter(severity, num_multi_labels, multi_labels)?; + write!(self, " ")?; + self.caret_pointers( + severity, + max_label_start, + single_labels, + trailing_label, + source + .char_indices() + .take_while(|(byte_index, _)| *byte_index < range.start), + )?; + self.set_color(self.styles().label(severity, *label_style))?; + write!(self, "{}", message)?; + self.reset()?; + write!(self, "\n")?; + } + } } // Write top or bottom label carets underneath source @@ -454,34 +663,39 @@ impl<'writer, 'config> Renderer<'writer, 'config> { Ok(()) } - /// Single-line label with a message. - /// - /// ```text - /// ^^ expected `Int` but found `String` - /// ``` - fn label_single( + /// Write vertical lines pointing to carets. + fn caret_pointers( &mut self, severity: Severity, - label_style: LabelStyle, - source: &str, - range: Range, - message: &str, + max_label_start: usize, + single_labels: &[SingleLabel<'_>], + trailing_label: Option<(usize, &SingleLabel<'_>)>, + char_indices: impl Iterator, ) -> io::Result<()> { - let space_source = slice_at_char_boundaries(source, 0..range.start); - let space_len = self.config.width(space_source); - write!(self, " {space: >width$}", space = "", width = space_len)?; - self.set_color(self.styles().label(severity, label_style))?; - let source = slice_at_char_boundaries(source, range); - // We use `usize::max` here to ensure that we print at least one - // label character - even when we have a zero-length span. - for _ in 0..usize::max(self.config.width(source), 1) { - write!(self, "{}", self.chars().single_caret_char(label_style))?; + for (byte_index, ch) in char_indices { + let column_range = byte_index..(byte_index + ch.len_utf8()); + let label_style = hanging_labels(single_labels, trailing_label) + .filter(|(_, range, _)| column_range.contains(&range.start)) + .map(|(label_style, _, _)| *label_style) + .max_by_key(label_priority_key); + + let spaces = match label_style { + None => 0..self.config.width(ch), + Some(label_style) => { + self.set_color(self.styles().label(severity, label_style))?; + write!(self, "{}", self.chars().pointer_left)?; + self.reset()?; + 1..self.config.width(ch) + } + }; + // Only print padding if we are before the end of the last single line caret + if byte_index <= max_label_start { + for _ in spaces { + write!(self, " ")?; + } + } } - if !message.is_empty() { - write!(self, " {}", message)?; - } - self.reset()?; - write!(self, "\n")?; + Ok(()) } @@ -558,11 +772,22 @@ impl<'writer, 'config> Renderer<'writer, 'config> { range: RangeTo, ) -> io::Result<()> { self.set_color(self.styles().label(severity, label_style))?; - let source = slice_at_char_boundaries(source, 0..range.end); - for _ in 0..(self.config.width(source) + 1) { - write!(self, "{}", self.chars().multi_top)?; + + for (_, ch) in source + .char_indices() + .take_while(|(byte_index, _)| *byte_index < range.end + 1) + { + // FIXME: improve rendering for carets that occurring within character boundaries + for _ in 0..self.config.width(ch) { + write!(self, "{}", self.chars().multi_top)?; + } } - write!(self, "{}", self.chars().multi_caret_char_start(label_style))?; + + let caret_start = match label_style { + LabelStyle::Primary => self.config.chars.multi_primary_caret_start, + LabelStyle::Secondary => self.config.chars.multi_secondary_caret_start, + }; + write!(self, "{}", caret_start)?; self.reset()?; write!(self, "\n")?; Ok(()) @@ -582,11 +807,22 @@ impl<'writer, 'config> Renderer<'writer, 'config> { message: &str, ) -> io::Result<()> { self.set_color(self.styles().label(severity, label_style))?; - let source = slice_at_char_boundaries(source, 0..range.end); - for _ in 0..self.config.width(source) { - write!(self, "{}", self.chars().multi_bottom)?; + + for (_, ch) in source + .char_indices() + .take_while(|(byte_index, _)| *byte_index < range.end) + { + // FIXME: improve rendering for carets that occurring within character boundaries + for _ in 0..self.config.width(ch) { + write!(self, "{}", self.chars().multi_bottom)?; + } } - write!(self, "{}", self.chars().multi_caret_char_end(label_style))?; + + let caret_end = match label_style { + LabelStyle::Primary => self.config.chars.multi_primary_caret_start, + LabelStyle::Secondary => self.config.chars.multi_secondary_caret_start, + }; + write!(self, "{}", caret_end)?; if !message.is_empty() { write!(self, " {}", message)?; } @@ -679,83 +915,31 @@ impl<'writer, 'config> WriteColor for Renderer<'writer, 'config> { } } -/// Searches for character boundary from byte_index towards the end of the string. -fn closest_char_boundary(s: &str, byte_index: usize) -> usize { - let length = s.len(); - for index in byte_index..=length { - if s.is_char_boundary(index) { - return index; - } - } - length +/// Check if two ranges overlap +fn is_overlapping(range0: &Range, range1: &Range) -> bool { + let start = std::cmp::max(range0.start, range1.start); + let end = std::cmp::min(range0.end, range1.end); + start < end } -/// Searches for character boundary from byte_index towards the start of the string. -fn closest_char_boundary_rev(s: &str, byte_index: usize) -> usize { - for index in (0..=byte_index).rev() { - if s.is_char_boundary(index) { - return index; - } - } - 0 -} - -/// Finds a valid unicode boundaries looking from `range.start` towards the beginning of the string. -/// From `range.end` towards the end of the string. Returning a `&str` of all characters -/// that overlapping the range. -fn slice_at_char_boundaries<'a>(s: &'a str, range: Range) -> &'a str { - let start = closest_char_boundary_rev(s, range.start); - let end = closest_char_boundary(s, range.end); - &s[start..end] -} - -#[cfg(test)] -mod test { - use super::*; - use std::iter::repeat; - - #[test] - fn test_boundary() { - let s = "๐ŸŒž"; - assert_eq!(closest_char_boundary(s, 0), 0); - assert_eq!(closest_char_boundary_rev(s, 0), 0); - for i in 1..s.len() { - assert_eq!(closest_char_boundary_rev(s, i), 0); - assert_eq!(closest_char_boundary(s, i), s.len()); - } - } - - #[test] - fn test_boundaries() { - let s = "๐ŸŒ‘๐ŸŒ’๐ŸŒ“๐ŸŒ”"; - let individually = ["๐ŸŒ‘", "๐ŸŒ’", "๐ŸŒ“", "๐ŸŒ”"]; - - let mut expect = Vec::new(); - // [(0, 0, "", ""), - // (0, 4, "๐ŸŒ‘", "๐ŸŒ‘"), repeated 4 times, - // (4, 4, "", ""), once - // (4, 8, "๐ŸŒ’", "๐ŸŒ’"), repeated 4 times, and so on (+4, +4) - // ... - // (16, 16, "", ""); 21] - expect.push((0, 0, "", "")); - for (idx, &char) in individually.iter().enumerate() { - let n = char.len(); - assert_eq!(n, 4); - let expected_start = (idx % n) * n; - let expected_end = (idx % n) * n + n; - expect.extend(repeat((expected_start, expected_end, char, char)).take(n - 1)); - expect.push((expected_end, expected_end, "", "")); - } - - // drop mut. - let expect = expect; - let mut found = Vec::new(); - for i in 0..=s.len() { - let sliced = slice_at_char_boundaries(s, i..i); - let prev = closest_char_boundary_rev(s, i); - let next = closest_char_boundary(s, i); - found.push((prev, next, &s[prev..next], sliced)); - } - assert_eq!(found, expect); +/// For prioritizing primary labels over secondary labels when rendering carets. +fn label_priority_key(label_style: &LabelStyle) -> u8 { + match label_style { + LabelStyle::Secondary => 0, + LabelStyle::Primary => 1, } } + +/// Return an iterator that yields the labels that require hanging messages +/// rendered underneath them. +fn hanging_labels<'labels, 'diagnostic>( + single_labels: &'labels [SingleLabel<'diagnostic>], + trailing_label: Option<(usize, &'labels SingleLabel<'diagnostic>)>, +) -> impl 'labels + DoubleEndedIterator> { + single_labels + .iter() + .enumerate() + .filter(|(_, (_, _, message))| !message.is_empty()) + .filter(move |(i, _)| trailing_label.map_or(true, |(j, _)| *i != j)) + .map(|(_, label)| label) +} diff --git a/codespan-reporting/src/term/views.rs b/codespan-reporting/src/term/views.rs index 204bdf4..8422ede 100644 --- a/codespan-reporting/src/term/views.rs +++ b/codespan-reporting/src/term/views.rs @@ -143,14 +143,21 @@ where // to piggyback off its lexicographic comparison implementation. (range.start, range.end).cmp(&(label_start, label_end)) }) { - // If the ranges are the same, order the labels as they - // originally were specified in the diagnostic. - Ok(index) => index + 1, - Err(index) => index, + // If the ranges are the same, order the labels in reverse + // to how they were originally specified in the diagnostic. + // This helps with printing in the renderer. + Ok(index) | Err(index) => index, }; + // Ensure that we print at least one caret, even when we + // have a zero-length source range. + let mut label_range = label_start..label_end; + if label_range.len() == 0 { + label_range.end = label_range.start + 1; + } + line.single_labels - .insert(index, (label.style, label_start..label_end, &label.message)); + .insert(index, (label.style, label_range, &label.message)); } else { // Multiple lines // diff --git a/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap b/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap index 4608113..b467854 100644 --- a/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap +++ b/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap @@ -6,18 +6,24 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} hello:1:7 {fg:Blue}โ”‚{/} {fg:Blue}1{/} {fg:Blue}โ”‚{/} Hello world! - {fg:Blue}โ”‚{/} {fg:Green}^ middle{/} + {fg:Blue}โ”‚{/} {fg:Green}^{/} {fg:Green}middle{/} {fg:Green bold bright}note{bold bright}: end of line{/} {fg:Blue}โ”Œโ”€{/} hello:1:13 {fg:Blue}โ”‚{/} {fg:Blue}1{/} {fg:Blue}โ”‚{/} Hello world! - {fg:Blue}โ”‚{/} {fg:Green}^ end of line{/} + {fg:Blue}โ”‚{/} {fg:Green}^{/} {fg:Green}end of line{/} -{fg:Green bold bright}note{bold bright}: end of file{/} +{fg:Green bold bright}note{bold bright}: end of line{/} {fg:Blue}โ”Œโ”€{/} hello:2:11 {fg:Blue}โ”‚{/} {fg:Blue}2{/} {fg:Blue}โ”‚{/} Bye world! - {fg:Blue}โ”‚{/} {fg:Green}^ end of file{/} + {fg:Blue}โ”‚{/} {fg:Green}^{/} {fg:Green}end of line{/} + +{fg:Green bold bright}note{bold bright}: end of file{/} + {fg:Blue}โ”Œโ”€{/} hello:3:4 + {fg:Blue}โ”‚{/} +{fg:Blue}3{/} {fg:Blue}โ”‚{/} + {fg:Blue}โ”‚{/} {fg:Green}^{/} {fg:Green}end of file{/} diff --git a/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap b/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap index 84c8059..862605c 100644 --- a/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap @@ -14,10 +14,16 @@ note: end of line 1 โ”‚ Hello world! โ”‚ ^ end of line -note: end of file +note: end of line โ”Œโ”€ hello:2:11 โ”‚ 2 โ”‚ Bye world! - โ”‚ ^ end of file + โ”‚ ^ end of line + +note: end of file + โ”Œโ”€ hello:3:4 + โ”‚ +3 โ”‚ + โ”‚ ^ end of file diff --git a/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap b/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap index b3930e0..3713ee4 100644 --- a/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap +++ b/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap @@ -4,5 +4,6 @@ expression: TEST_DATA.emit_color(&config) --- hello:1:7: {fg:Green bold bright}note{bold bright}: middle{/} hello:1:13: {fg:Green bold bright}note{bold bright}: end of line{/} -hello:2:11: {fg:Green bold bright}note{bold bright}: end of file{/} +hello:2:11: {fg:Green bold bright}note{bold bright}: end of line{/} +hello:3:4: {fg:Green bold bright}note{bold bright}: end of file{/} diff --git a/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap b/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap index 5e2edfc..635a830 100644 --- a/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap @@ -4,5 +4,6 @@ expression: TEST_DATA.emit_no_color(&config) --- hello:1:7: note: middle hello:1:13: note: end of line -hello:2:11: note: end of file +hello:2:11: note: end of line +hello:3:4: note: end of file diff --git a/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap b/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap index 63bfdb9..11f9f68 100644 --- a/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap +++ b/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap @@ -6,14 +6,14 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} FizzBuzz.fun:3:15 {fg:Blue}โ”‚{/} {fg:Blue}3{/} {fg:Blue}โ”‚{/} fizzโ‚ : Nat โ†’ String - {fg:Blue}โ”‚{/} {fg:Blue}------ expected type `String` found here{/} + {fg:Blue}โ”‚{/} {fg:Blue}------{/} {fg:Blue}expected type `String` found here{/} {fg:Blue}4{/} {fg:Blue}โ”‚{/} fizzโ‚ num = case (mod num 5) (mod num 3) of {fg:Blue}โ”‚{/} {fg:Blue}โ•ญ{/}{fg:Blue}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'{/} {fg:Blue}5{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} 0 0 => "FizzBuzz" {fg:Blue}6{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} 0 _ => "Fizz" {fg:Blue}7{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} _ 0 => "Buzz" {fg:Blue}8{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} _ _ => num - {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Red}^^^ expected `String`, found `Nat`{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Red}^^^{/} {fg:Red}expected `String`, found `Nat`{/} {fg:Blue}โ”‚{/} {fg:Blue}โ•ฐ{/}{fg:Blue}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€' `case` clauses have incompatible types{/} {fg:Blue}โ”‚{/} {fg:Blue}={/} expected type `String` @@ -23,17 +23,17 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} FizzBuzz.fun:10:15 {fg:Blue}โ”‚{/} {fg:Blue}10{/} {fg:Blue}โ”‚{/} fizzโ‚‚ : Nat โ†’ String - {fg:Blue}โ”‚{/} {fg:Blue}------ expected type `String` found here{/} + {fg:Blue}โ”‚{/} {fg:Blue}------{/} {fg:Blue}expected type `String` found here{/} {fg:Blue}11{/} {fg:Blue}โ”‚{/} fizzโ‚‚ num = {fg:Blue}12{/} {fg:Blue}โ”‚{/} {fg:Blue}โ•ญ{/} case (mod num 5) (mod num 3) of {fg:Blue}13{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} 0 0 => "FizzBuzz" - {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}---------- this is found to be of type `String`{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}----------{/} {fg:Blue}this is found to be of type `String`{/} {fg:Blue}14{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} 0 _ => "Fizz" - {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}------ this is found to be of type `String`{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}------{/} {fg:Blue}this is found to be of type `String`{/} {fg:Blue}15{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} _ 0 => "Buzz" - {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}------ this is found to be of type `String`{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}------{/} {fg:Blue}this is found to be of type `String`{/} {fg:Blue}16{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} _ _ => num - {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Red}^^^ expected `String`, found `Nat`{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Red}^^^{/} {fg:Red}expected `String`, found `Nat`{/} {fg:Blue}โ”‚{/} {fg:Blue}โ•ฐ{/}{fg:Blue}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€' `case` clauses have incompatible types{/} {fg:Blue}โ”‚{/} {fg:Blue}={/} expected type `String` diff --git a/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap b/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap index 5c3be31..53ede26 100644 --- a/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap +++ b/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap @@ -6,7 +6,7 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} Data/Nat.fun:7:13 {fg:Blue}โ”‚{/} {fg:Blue}7{/} {fg:Blue}โ”‚{/} {-# BUILTIN NATRAL Nat #-} - {fg:Blue}โ”‚{/} {fg:Red}^^^^^^ unknown builtin{/} + {fg:Blue}โ”‚{/} {fg:Red}^^^^^^{/} {fg:Red}unknown builtin{/} {fg:Blue}โ”‚{/} {fg:Blue}={/} there is a builtin with a similar name: `NATURAL` @@ -14,7 +14,7 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} Data/Nat.fun:17:16 {fg:Blue}โ”‚{/} {fg:Blue}17{/} {fg:Blue}โ”‚{/} zero - succ nโ‚‚ = zero - {fg:Blue}โ”‚{/} {fg:Yellow}^^ unused parameter{/} + {fg:Blue}โ”‚{/} {fg:Yellow}^^{/} {fg:Yellow}unused parameter{/} {fg:Blue}โ”‚{/} {fg:Blue}={/} consider using a wildcard pattern: `_` @@ -22,12 +22,12 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} Test.fun:4:11 {fg:Blue}โ”‚{/} {fg:Blue} 4{/} {fg:Blue}โ”‚{/} _ = 123 + "hello" - {fg:Blue}โ”‚{/} {fg:Red}^^^^^^^ expected `Nat`, found `String`{/} + {fg:Blue}โ”‚{/} {fg:Red}^^^^^^^{/} {fg:Red}expected `Nat`, found `String`{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”Œโ”€{/} Data/Nat.fun:11:1 {fg:Blue}โ”‚{/} {fg:Blue}11{/} {fg:Blue}โ”‚{/} _+_ : Nat โ†’ Nat โ†’ Nat - {fg:Blue}โ”‚{/} {fg:Blue}--------------------- based on the definition of `_+_`{/} + {fg:Blue}โ”‚{/} {fg:Blue}---------------------{/} {fg:Blue}based on the definition of `_+_`{/} {fg:Blue}โ”‚{/} {fg:Blue}={/} expected type `Nat` found type `String` diff --git a/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap b/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap index 87f8bed..c927246 100644 --- a/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap +++ b/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap @@ -7,9 +7,9 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”‚{/} {fg:Blue}1{/} {fg:Blue}โ”‚{/} {fg:Blue}โ•ญ{/} match line_index.compare(self.last_line_index()) { {fg:Blue}2{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]), - {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}--------------------------------------------- this is found to be of type `Result`{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}---------------------------------------------{/} {fg:Blue}this is found to be of type `Result`{/} {fg:Blue}3{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} Ordering::Equal => Ok(self.source_span().end()), - {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}---------------------------- this is found to be of type `Result`{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}----------------------------{/} {fg:Blue}this is found to be of type `Result`{/} {fg:Blue}4{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} Ordering::Greater => LineIndexOutOfBoundsError { {fg:Blue}โ”‚{/} {fg:Red}โ•ญ{/}{fg:Red}โ”€{/}{fg:Blue}โ”‚{/}{fg:Red}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€^{/} {fg:Blue}5{/} {fg:Blue}โ”‚{/} {fg:Red}โ”‚{/} {fg:Blue}โ”‚{/} given: line_index, diff --git a/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap b/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap index b37e9bd..029a674 100644 --- a/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap +++ b/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap @@ -6,29 +6,35 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} nested_impl_trait.rs:5:46 {fg:Blue}โ”‚{/} {fg:Blue}5{/} {fg:Blue}โ”‚{/} fn bad_in_ret_position(x: impl Into) -> impl Into { x } - {fg:Blue}โ”‚{/} {fg:Blue}--------------------- outer `impl Trait`{/} - {fg:Blue}โ”‚{/} {fg:Red}^^^^^^^^^^ nested `impl Trait` here{/} + {fg:Blue}โ”‚{/} {fg:Blue}----------{fg:Red}^^^^^^^^^^{fg:Blue}-{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Red}โ”‚{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Red}nested `impl Trait` here{/} + {fg:Blue}โ”‚{/} {fg:Blue}outer `impl Trait`{/} {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} {fg:Blue}โ”Œโ”€{/} typeck_type_placeholder_item.rs:1:18 {fg:Blue}โ”‚{/} {fg:Blue}1{/} {fg:Blue}โ”‚{/} fn fn_test1() -> _ { 5 } - {fg:Blue}โ”‚{/} {fg:Red}^ not allowed in type signatures{/} - {fg:Blue}โ”‚{/} {fg:Blue}- help: replace with the correct return type: `i32`{/} + {fg:Blue}โ”‚{/} {fg:Red}^{/} + {fg:Blue}โ”‚{/} {fg:Red}โ”‚{/} + {fg:Blue}โ”‚{/} {fg:Red}not allowed in type signatures{/} + {fg:Blue}โ”‚{/} {fg:Blue}help: replace with the correct return type: `i32`{/} {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} {fg:Blue}โ”Œโ”€{/} typeck_type_placeholder_item.rs:2:24 {fg:Blue}โ”‚{/} {fg:Blue}2{/} {fg:Blue}โ”‚{/} fn fn_test2(x: i32) -> (_, _) { (x, x) } - {fg:Blue}โ”‚{/} {fg:Blue}------ help: replace with the correct return type: `(i32, i32)`{/} - {fg:Blue}โ”‚{/} {fg:Red}^ not allowed in type signatures{/} - {fg:Blue}โ”‚{/} {fg:Red}^ not allowed in type signatures{/} + {fg:Blue}โ”‚{/} {fg:Blue}-{fg:Red}^{fg:Blue}--{fg:Red}^{fg:Blue}-{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/}{fg:Red}โ”‚{/} {fg:Red}โ”‚{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/}{fg:Red}โ”‚{/} {fg:Red}not allowed in type signatures{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/}{fg:Red}not allowed in type signatures{/} + {fg:Blue}โ”‚{/} {fg:Blue}help: replace with the correct return type: `(i32, i32)`{/} {fg:Red bold bright}error[E0277]{bold bright}: `std::rc::Rc<()>` cannot be sent between threads safely{/} {fg:Blue}โ”Œโ”€{/} no_send_res_ports.rs:25:5 {fg:Blue}โ”‚{/} {fg:Blue}25{/} {fg:Blue}โ”‚{/} thread::spawn(move|| { - {fg:Blue}โ”‚{/} {fg:Red}^^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely{/} + {fg:Blue}โ”‚{/} {fg:Red}^^^^^^^^^^^^^{/} {fg:Red}`std::rc::Rc<()>` cannot be sent between threads safely{/} {fg:Blue}โ”‚{/} {fg:Blue}โ•ญ{/}{fg:Blue}โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'{/} {fg:Blue}26{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} let y = x; {fg:Blue}27{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} println!("{:?}", y); @@ -38,7 +44,7 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} libstd/thread/mod.rs:5:8 {fg:Blue}โ”‚{/} {fg:Blue} 5{/} {fg:Blue}โ”‚{/} F: Send + 'static, - {fg:Blue}โ”‚{/} {fg:Blue}---- required by this bound in `std::thread::spawn`{/} + {fg:Blue}โ”‚{/} {fg:Blue}----{/} {fg:Blue}required by this bound in `std::thread::spawn`{/} {fg:Blue}โ”‚{/} {fg:Blue}={/} help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` {fg:Blue}={/} note: required because it appears within the type `Port<()>` diff --git a/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap b/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap index 04b1788..e4a944d 100644 --- a/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap @@ -6,23 +6,29 @@ error[E0666]: nested `impl Trait` is not allowed โ”Œโ”€ nested_impl_trait.rs:5:46 โ”‚ 5 โ”‚ fn bad_in_ret_position(x: impl Into) -> impl Into { x } - โ”‚ --------------------- outer `impl Trait` - โ”‚ ^^^^^^^^^^ nested `impl Trait` here + โ”‚ ----------^^^^^^^^^^- + โ”‚ โ”‚ โ”‚ + โ”‚ โ”‚ nested `impl Trait` here + โ”‚ outer `impl Trait` error[E0121]: the type placeholder `_` is not allowed within types on item signatures โ”Œโ”€ typeck_type_placeholder_item.rs:1:18 โ”‚ 1 โ”‚ fn fn_test1() -> _ { 5 } - โ”‚ ^ not allowed in type signatures - โ”‚ - help: replace with the correct return type: `i32` + โ”‚ ^ + โ”‚ โ”‚ + โ”‚ not allowed in type signatures + โ”‚ help: replace with the correct return type: `i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures โ”Œโ”€ typeck_type_placeholder_item.rs:2:24 โ”‚ 2 โ”‚ fn fn_test2(x: i32) -> (_, _) { (x, x) } - โ”‚ ------ help: replace with the correct return type: `(i32, i32)` - โ”‚ ^ not allowed in type signatures - โ”‚ ^ not allowed in type signatures + โ”‚ -^--^- + โ”‚ โ”‚โ”‚ โ”‚ + โ”‚ โ”‚โ”‚ not allowed in type signatures + โ”‚ โ”‚not allowed in type signatures + โ”‚ help: replace with the correct return type: `(i32, i32)` error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely โ”Œโ”€ no_send_res_ports.rs:25:5 diff --git a/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap b/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap index 82d2eb4..ad82cdf 100644 --- a/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap +++ b/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap @@ -6,9 +6,10 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} one_line.rs:3:5 {fg:Blue}โ”‚{/} {fg:Blue}3{/} {fg:Blue}โ”‚{/} v.push(v.pop().unwrap()); - {fg:Blue}โ”‚{/} {fg:Blue}- first borrow later used by call{/} - {fg:Blue}โ”‚{/} {fg:Blue}---- first mutable borrow occurs here{/} - {fg:Blue}โ”‚{/} {fg:Red}^ second mutable borrow occurs here{/} + {fg:Blue}โ”‚{/} {fg:Blue}-{/} {fg:Blue}----{/} {fg:Red}^{/} {fg:Red}second mutable borrow occurs here{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} + {fg:Blue}โ”‚{/} {fg:Blue}โ”‚{/} {fg:Blue}first mutable borrow occurs here{/} + {fg:Blue}โ”‚{/} {fg:Blue}first borrow later used by call{/} {fg:Red bold bright}error{bold bright}: aborting due to previous error{/} {fg:Blue}={/} For more information about this error, try `rustc --explain E0499`. diff --git a/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap b/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap index c2d1c34..7342f3f 100644 --- a/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap @@ -6,9 +6,10 @@ error[E0499]: cannot borrow `v` as mutable more than once at a time โ”Œโ”€ one_line.rs:3:5 โ”‚ 3 โ”‚ v.push(v.pop().unwrap()); - โ”‚ - first borrow later used by call - โ”‚ ---- first mutable borrow occurs here - โ”‚ ^ second mutable borrow occurs here + โ”‚ - ---- ^ second mutable borrow occurs here + โ”‚ โ”‚ โ”‚ + โ”‚ โ”‚ first mutable borrow occurs here + โ”‚ first borrow later used by call error: aborting due to previous error = For more information about this error, try `rustc --explain E0499`. diff --git a/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap b/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap index cdc9ea8..49d3c86 100644 --- a/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap +++ b/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap @@ -6,7 +6,9 @@ expression: TEST_DATA.emit_color(&config) {fg:Blue}โ”Œโ”€{/} same_range:1:5 {fg:Blue}โ”‚{/} {fg:Blue}1{/} {fg:Blue}โ”‚{/} ::S { } - {fg:Blue}โ”‚{/} {fg:Red}^ Unexpected '{'{/} - {fg:Blue}โ”‚{/} {fg:Blue}- Expected '('{/} + {fg:Blue}โ”‚{/} {fg:Red}^{/} + {fg:Blue}โ”‚{/} {fg:Red}โ”‚{/} + {fg:Blue}โ”‚{/} {fg:Red}Unexpected '{'{/} + {fg:Blue}โ”‚{/} {fg:Blue}Expected '('{/} diff --git a/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap b/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap index 4dd6071..09510e6 100644 --- a/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap @@ -6,7 +6,9 @@ error: Unexpected token โ”Œโ”€ same_range:1:5 โ”‚ 1 โ”‚ ::S { } - โ”‚ ^ Unexpected '{' - โ”‚ - Expected '(' + โ”‚ ^ + โ”‚ โ”‚ + โ”‚ Unexpected '{' + โ”‚ Expected '(' diff --git a/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap b/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap index 3609f0f..653a60e 100644 --- a/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap +++ b/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap @@ -6,13 +6,13 @@ error[E01]: cow may not jump during new moon. โ”Œโ”€ moon_jump.rs:1:1 โ”‚ 1 โ”‚ ๐Ÿ„๐ŸŒ‘๐Ÿ„๐ŸŒ’๐Ÿ„๐ŸŒ“๐Ÿ„๐ŸŒ”๐Ÿ„๐ŸŒ•๐Ÿ„๐ŸŒ–๐Ÿ„๐ŸŒ—๐Ÿ„๐ŸŒ˜๐Ÿ„ - โ”‚ ^^ Invalid jump + โ”‚ ^^ Invalid jump note: invalid unicode range โ”Œโ”€ moon_jump.rs:1:1 โ”‚ 1 โ”‚ ๐Ÿ„๐ŸŒ‘๐Ÿ„๐ŸŒ’๐Ÿ„๐ŸŒ“๐Ÿ„๐ŸŒ”๐Ÿ„๐ŸŒ•๐Ÿ„๐ŸŒ–๐Ÿ„๐ŸŒ—๐Ÿ„๐ŸŒ˜๐Ÿ„ - โ”‚ -- Cow range does not start at boundary. + โ”‚ -- Cow range does not start at boundary. note: invalid unicode range โ”Œโ”€ moon_jump.rs:1:3 @@ -24,6 +24,6 @@ note: invalid unicode range โ”Œโ”€ moon_jump.rs:1:1 โ”‚ 1 โ”‚ ๐Ÿ„๐ŸŒ‘๐Ÿ„๐ŸŒ’๐Ÿ„๐ŸŒ“๐Ÿ„๐ŸŒ”๐Ÿ„๐ŸŒ•๐Ÿ„๐ŸŒ–๐Ÿ„๐ŸŒ—๐Ÿ„๐ŸŒ˜๐Ÿ„ - โ”‚ ------ Cow does not start or end at boundary. + โ”‚ ------ Cow does not start or end at boundary. diff --git a/codespan-reporting/tests/term.rs b/codespan-reporting/tests/term.rs index 5b6acef..600132a 100644 --- a/codespan-reporting/tests/term.rs +++ b/codespan-reporting/tests/term.rs @@ -330,7 +330,7 @@ mod empty_ranges { lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = { - let file = SimpleFile::new("hello", "Hello world!\nBye world!"); + let file = SimpleFile::new("hello", "Hello world!\nBye world!\n "); let eof = file.source().len(); let diagnostics = vec![ @@ -340,6 +340,9 @@ mod empty_ranges { Diagnostic::note() .with_message("end of line") .with_labels(vec![Label::primary((), 12..12).with_message("end of line")]), + Diagnostic::note() + .with_message("end of line") + .with_labels(vec![Label::primary((), 23..23).with_message("end of line")]), Diagnostic::note() .with_message("end of file") .with_labels(vec![Label::primary((), eof..eof).with_message("end of file")]),