use snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style};
use styled_buffer::StyledBuffer;
+use std::borrow::Cow;
use std::io::prelude::*;
use std::io;
use std::rc::Rc;
// don't display multiline suggestions as labels
sugg.substitution_parts[0].substitutions[0].find('\n').is_none() {
let substitution = &sugg.substitution_parts[0].substitutions[0];
- let msg = format!("help: {} `{}`", sugg.msg, substitution);
+ let msg = if substitution.len() == 0 {
+ // This substitution is only removal, don't show it
+ format!("help: {}", sugg.msg)
+ } else {
+ format!("help: {}: `{}`", sugg.msg, substitution)
+ };
primary_span.push_span_label(sugg.substitution_spans().next().unwrap(), msg);
} else {
// if there are multiple suggestions, print them all in full
}
}
- fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
+ fn preprocess_annotations(&mut self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
file: Rc<FileMap>,
line_index: usize,
if span_label.span == DUMMY_SP {
continue;
}
+
let lo = cm.lookup_char_pos(span_label.span.lo);
let mut hi = cm.lookup_char_pos(span_label.span.hi);
// and "annotations lines", where the highlight lines have the `^`.
// Sort the annotations by (start, end col)
+ // The labels are reversed, sort and then reversed again.
+ // Consider a list of annotations (A1, A2, C1, C2, B1, B2) where
+ // the letter signifies the span. Here we are only sorting by the
+ // span and hence, the order of the elements with the same span will
+ // not change. On reversing the ordering (|a, b| but b.cmp(a)), you get
+ // (C1, C2, B1, B2, A1, A2). All the elements with the same span are
+ // still ordered first to last, but all the elements with different
+ // spans are ordered by their spans in last to first order. Last to
+ // first order is important, because the jiggly lines and | are on
+ // the left, so the rightmost span needs to be rendered first,
+ // otherwise the lines would end up needing to go over a message.
+
let mut annotations = line.annotations.clone();
- annotations.sort();
- annotations.reverse();
+ annotations.sort_by(|a,b| b.start_col.cmp(&a.start_col));
// First, figure out where each label will be positioned.
//
&& next.has_label()) // multiline start/end, move it to a new line
|| (annotation.has_label() // so as not to overlap the orizontal lines.
&& next.takes_space())
- || (annotation.takes_space()
- && next.takes_space())
+ || (annotation.takes_space() && next.takes_space())
+ || (overlaps(next, annotation, l)
+ && next.end_col <= annotation.end_col
+ && next.has_label()
+ && p == 0) // Avoid #42595.
{
// This annotation needs a new line in the output.
p += 1;
if *sp == DUMMY_SP {
continue;
}
- if cm.span_to_filename(sp.clone()).contains("macros>") {
- let v = sp.macro_backtrace();
- if let Some(use_site) = v.last() {
- before_after.push((sp.clone(), use_site.call_site.clone()));
- }
+ let call_sp = cm.call_span_if_macro(*sp);
+ if call_sp != *sp {
+ before_after.push((sp.clone(), call_sp));
}
for trace in sp.macro_backtrace().iter().rev() {
// Only show macro locations that are local
let mut annotated_files = self.preprocess_annotations(msp);
// Make sure our primary file comes first
- let primary_lo = if let (Some(ref cm), Some(ref primary_span)) =
+ let (primary_lo, cm) = if let (Some(cm), Some(ref primary_span)) =
(self.cm.as_ref(), msp.primary_span().as_ref()) {
if primary_span != &&DUMMY_SP {
- cm.lookup_char_pos(primary_span.lo)
+ (cm.lookup_char_pos(primary_span.lo), cm)
} else {
emit_to_destination(&buffer.render(), level, &mut self.dst)?;
return Ok(());
// Print out the annotate source lines that correspond with the error
for annotated_file in annotated_files {
// we can't annotate anything if the source is unavailable.
- if annotated_file.file.src.is_none() {
+ if !cm.ensure_filemap_source_present(annotated_file.file.clone()) {
continue;
}
} else if line_idx_delta == 2 {
let unannotated_line = annotated_file.file
.get_line(annotated_file.lines[line_idx].line_index)
- .unwrap_or("");
+ .unwrap_or_else(|| Cow::from(""));
let last_buffer_line_num = buffer.num_lines();
-> io::Result<()> {
use std::borrow::Borrow;
- let primary_span = suggestion.substitution_spans().next().unwrap();
+ let primary_sub = &suggestion.substitution_parts[0];
if let Some(ref cm) = self.cm {
let mut buffer = StyledBuffer::new();
- let lines = cm.span_to_lines(primary_span).unwrap();
+ let lines = cm.span_to_lines(primary_sub.span).unwrap();
assert!(!lines.lines.is_empty());
buffer.append(0, &level.to_string(), Style::Level(level.clone()));
buffer.append(0, ": ", Style::HeaderMsg);
self.msg_to_buffer(&mut buffer,
- &[(suggestion.msg.to_owned(), Style::NoStyle)],
- max_line_num_len,
- "suggestion",
- Some(Style::HeaderMsg));
+ &[(suggestion.msg.to_owned(), Style::NoStyle)],
+ max_line_num_len,
+ "suggestion",
+ Some(Style::HeaderMsg));
let suggestions = suggestion.splice_lines(cm.borrow());
- let mut row_num = 1;
- for complete in suggestions.iter().take(MAX_SUGGESTIONS) {
-
- // print the suggestion without any line numbers, but leave
- // space for them. This helps with lining up with previous
- // snippets from the actual error being reported.
+ let span_start_pos = cm.lookup_char_pos(primary_sub.span.lo);
+ let line_start = span_start_pos.line;
+ draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1);
+ let mut row_num = 2;
+ for (&(ref complete, show_underline), ref sub) in suggestions
+ .iter().zip(primary_sub.substitutions.iter()).take(MAX_SUGGESTIONS)
+ {
+ let mut line_pos = 0;
+ // Only show underline if there's a single suggestion and it is a single line
let mut lines = complete.lines();
for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
+ // Print the span column to avoid confusion
+ buffer.puts(row_num,
+ 0,
+ &((line_start + line_pos).to_string()),
+ Style::LineNumber);
+ // print the suggestion
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
buffer.append(row_num, line, Style::NoStyle);
+ line_pos += 1;
row_num += 1;
+ // Only show an underline in the suggestions if the suggestion is not the
+ // entirety of the code being shown and the displayed code is not multiline.
+ if show_underline {
+ draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
+ let sub_len = sub.trim_right().len();
+ let underline_start = span_start_pos.col.0;
+ let underline_end = span_start_pos.col.0 + sub_len;
+ for p in underline_start..underline_end {
+ buffer.putc(row_num,
+ max_line_num_len + 3 + p,
+ '^',
+ Style::UnderlinePrimary);
+ }
+ row_num += 1;
+ }
}
// if we elided some lines, add an ellipsis
if let Some(_) = lines.next() {
- buffer.append(row_num, "...", Style::NoStyle);
+ buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber);
+ } else if !show_underline {
+ draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1);
+ row_num += 1;
}
}
if suggestions.len() > MAX_SUGGESTIONS {
let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS);
- buffer.append(row_num, &msg, Style::NoStyle);
+ buffer.puts(row_num, 0, &msg, Style::NoStyle);
}
emit_to_destination(&buffer.render(), level, &mut self.dst)?;
}
fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> {
match style {
- Style::FileNameStyle | Style::LineAndColumn => {}
+ Style::LineAndColumn => {}
Style::LineNumber => {
self.start_attr(term::Attr::Bold)?;
if cfg!(windows) {
self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
}
}
- Style::ErrorCode => {
- self.start_attr(term::Attr::Bold)?;
- self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA))?;
- }
Style::Quotation => {}
- Style::OldSchoolNote => {
- self.start_attr(term::Attr::Bold)?;
- self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))?;
- }
- Style::OldSchoolNoteText | Style::HeaderMsg => {
+ Style::HeaderMsg => {
self.start_attr(term::Attr::Bold)?;
if cfg!(windows) {
self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_WHITE))?;