diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index a0d7120dd4f46..52c0ea931408f 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -10,14 +10,15 @@ use self::Destination::*; -use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, Span, MultiSpan, LineInfo}; +use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, LineInfo, CharPos}; use registry; use check_old_skool; use {Level, RenderSpan, CodeSuggestion, DiagnosticBuilder, CodeMapper}; use RenderSpan::*; use Level::*; -use snippet::{RenderedLineKind, SnippetData, Style, FormatMode}; +use snippet::{SnippetData, StyledString, Style, FormatMode, Annotation, Line}; +use styled_buffer::StyledBuffer; use std::{cmp, fmt}; use std::io::prelude::*; @@ -33,14 +34,13 @@ pub trait Emitter { impl Emitter for EmitterWriter { fn emit(&mut self, db: &DiagnosticBuilder) { - self.emit_message(&FullSpan(db.span.clone()), - &db.message, - db.code.as_ref().map(|s| &**s), - db.level, - true, - true); - if check_old_skool() { + self.emit_message(&FullSpan(db.span.clone()), + &db.message, + db.code.as_ref().map(|s| &**s), + db.level, + true, + true); let db_span = FullSpan(db.span.clone()); for child in &db.children { @@ -60,18 +60,7 @@ impl Emitter for EmitterWriter { show_snippet); } } else { - for child in &db.children { - let render_span = child.render_span - .clone() - .unwrap_or_else( - || FullSpan(child.span.clone())); - self.emit_message(&render_span, - &child.message, - None, - child.level, - false, - true); - } + self.emit_messages_default(db); } } } @@ -109,6 +98,12 @@ pub struct EmitterWriter { format_mode: FormatMode } +struct FileWithAnnotatedLines { + file: Rc, + lines: Vec, +} + + /// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See /// `EmitterWriter::print_maybe_styled` for details. macro_rules! print_maybe_styled { @@ -170,6 +165,560 @@ impl EmitterWriter { } } + fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec { + fn add_annotation_to_file(file_vec: &mut Vec, + file: Rc, + line_index: usize, + ann: Annotation) { + + for slot in file_vec.iter_mut() { + // Look through each of our files for the one we're adding to + if slot.file.name == file.name { + // See if we already have a line for it + for line_slot in &mut slot.lines { + if line_slot.line_index == line_index { + line_slot.annotations.push(ann); + return; + } + } + // We don't have a line yet, create one + slot.lines.push(Line { + line_index: line_index, + annotations: vec![ann], + }); + slot.lines.sort(); + return; + } + } + // This is the first time we're seeing the file + file_vec.push(FileWithAnnotatedLines { + file: file, + lines: vec![Line { + line_index: line_index, + annotations: vec![ann], + }], + }); + } + + let mut output = vec![]; + + if let Some(ref cm) = self.cm { + for span_label in msp.span_labels() { + let lo = cm.lookup_char_pos(span_label.span.lo); + let hi = cm.lookup_char_pos(span_label.span.hi); + + // If the span is multi-line, simplify down to the span of one character + let (start_col, mut end_col, is_minimized) = if lo.line != hi.line { + (lo.col, CharPos(lo.col.0 + 1), true) + } else { + (lo.col, hi.col, false) + }; + + // Watch out for "empty spans". If we get a span like 6..6, we + // want to just display a `^` at 6, so convert that to + // 6..7. This is degenerate input, but it's best to degrade + // gracefully -- and the parser likes to supply a span like + // that for EOF, in particular. + if start_col == end_col { + end_col.0 += 1; + } + + add_annotation_to_file(&mut output, + lo.file, + lo.line, + Annotation { + start_col: lo.col.0, + end_col: hi.col.0, + is_primary: span_label.is_primary, + is_minimized: is_minimized, + label: span_label.label.clone(), + }); + } + } + output + } + + fn render_source_line(&self, + buffer: &mut StyledBuffer, + file: Rc, + line: &Line, + width_offset: usize) { + let source_string = file.get_line(line.line_index - 1) + .unwrap_or(""); + + let line_offset = buffer.num_lines(); + + // First create the source line we will highlight. + buffer.puts(line_offset, width_offset, &source_string, Style::Quotation); + buffer.puts(line_offset, + 0, + &(line.line_index.to_string()), + Style::LineNumber); + + draw_col_separator(buffer, line_offset, width_offset - 2); + + if line.annotations.is_empty() { + return; + } + + // We want to display like this: + // + // vec.push(vec.pop().unwrap()); + // --- ^^^ _ previous borrow ends here + // | | + // | error occurs here + // previous borrow of `vec` occurs here + // + // But there are some weird edge cases to be aware of: + // + // vec.push(vec.pop().unwrap()); + // -------- - previous borrow ends here + // || + // |this makes no sense + // previous borrow of `vec` occurs here + // + // For this reason, we group the lines into "highlight lines" + // and "annotations lines", where the highlight lines have the `~`. + + // let mut highlight_line = Self::whitespace(&source_string); + let old_school = check_old_skool(); + + // Sort the annotations by (start, end col) + let mut annotations = line.annotations.clone(); + annotations.sort(); + + // Next, create the highlight line. + for annotation in &annotations { + if old_school { + for p in annotation.start_col..annotation.end_col { + if p == annotation.start_col { + buffer.putc(line_offset + 1, + width_offset + p, + '^', + if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::OldSchoolNote + }); + } else { + buffer.putc(line_offset + 1, + width_offset + p, + '~', + if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::OldSchoolNote + }); + } + } + } else { + for p in annotation.start_col..annotation.end_col { + if annotation.is_primary { + buffer.putc(line_offset + 1, + width_offset + p, + '^', + Style::UnderlinePrimary); + if !annotation.is_minimized { + buffer.set_style(line_offset, + width_offset + p, + Style::UnderlinePrimary); + } + } else { + buffer.putc(line_offset + 1, + width_offset + p, + '-', + Style::UnderlineSecondary); + if !annotation.is_minimized { + buffer.set_style(line_offset, + width_offset + p, + Style::UnderlineSecondary); + } + } + } + } + } + draw_col_separator(buffer, line_offset + 1, width_offset - 2); + + // Now we are going to write labels in. To start, we'll exclude + // the annotations with no labels. + let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) = annotations.into_iter() + .partition(|a| a.label.is_some()); + + // If there are no annotations that need text, we're done. + if labeled_annotations.is_empty() { + return; + } + if old_school { + return; + } + + // Now add the text labels. We try, when possible, to stick the rightmost + // annotation at the end of the highlight line: + // + // vec.push(vec.pop().unwrap()); + // --- --- - previous borrow ends here + // + // But sometimes that's not possible because one of the other + // annotations overlaps it. For example, from the test + // `span_overlap_label`, we have the following annotations + // (written on distinct lines for clarity): + // + // fn foo(x: u32) { + // -------------- + // - + // + // In this case, we can't stick the rightmost-most label on + // the highlight line, or we would get: + // + // fn foo(x: u32) { + // -------- x_span + // | + // fn_span + // + // which is totally weird. Instead we want: + // + // fn foo(x: u32) { + // -------------- + // | | + // | x_span + // fn_span + // + // which is...less weird, at least. In fact, in general, if + // the rightmost span overlaps with any other span, we should + // use the "hang below" version, so we can at least make it + // clear where the span *starts*. + let mut labeled_annotations = &labeled_annotations[..]; + match labeled_annotations.split_last().unwrap() { + (last, previous) => { + if previous.iter() + .chain(&unlabeled_annotations) + .all(|a| !overlaps(a, last)) { + // append the label afterwards; we keep it in a separate + // string + let highlight_label: String = format!(" {}", last.label.as_ref().unwrap()); + if last.is_primary { + buffer.append(line_offset + 1, &highlight_label, Style::LabelPrimary); + } else { + buffer.append(line_offset + 1, &highlight_label, Style::LabelSecondary); + } + labeled_annotations = previous; + } + } + } + + // If that's the last annotation, we're done + if labeled_annotations.is_empty() { + return; + } + + for (index, annotation) in labeled_annotations.iter().enumerate() { + // Leave: + // - 1 extra line + // - One line for each thing that comes after + let comes_after = labeled_annotations.len() - index - 1; + let blank_lines = 3 + comes_after; + + // For each blank line, draw a `|` at our column. The + // text ought to be long enough for this. + for index in 2..blank_lines { + if annotation.is_primary { + buffer.putc(line_offset + index, + width_offset + annotation.start_col, + '|', + Style::UnderlinePrimary); + } else { + buffer.putc(line_offset + index, + width_offset + annotation.start_col, + '|', + Style::UnderlineSecondary); + } + draw_col_separator(buffer, line_offset + index, width_offset - 2); + } + + if annotation.is_primary { + buffer.puts(line_offset + blank_lines, + width_offset + annotation.start_col, + annotation.label.as_ref().unwrap(), + Style::LabelPrimary); + } else { + buffer.puts(line_offset + blank_lines, + width_offset + annotation.start_col, + annotation.label.as_ref().unwrap(), + Style::LabelSecondary); + } + draw_col_separator(buffer, line_offset + blank_lines, width_offset - 2); + } + } + + fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize { + let mut max = 0; + if let Some(ref cm) = self.cm { + for primary_span in msp.primary_spans() { + let hi = cm.lookup_char_pos(primary_span.hi); + if hi.line > max { + max = hi.line; + } + } + for span_label in msp.span_labels() { + let hi = cm.lookup_char_pos(span_label.span.hi); + if hi.line > max { + max = hi.line; + } + } + } + max + } + + fn get_max_line_num(&mut self, db: &DiagnosticBuilder) -> usize { + let mut max = 0; + + let primary = self.get_multispan_max_line_num(&db.span); + max = if primary > max { primary } else { max }; + + for sub in &db.children { + let sub_result = self.get_multispan_max_line_num(&sub.span); + max = if sub_result > max { primary } else { max }; + } + max + } + + fn emit_message_default(&mut self, + msp: &MultiSpan, + msg: &str, + code: &Option, + level: &Level, + max_line_num_len: usize, + is_secondary: bool) + -> io::Result<()> { + let mut buffer = StyledBuffer::new(); + + if msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary { + // This is a secondary message with no span info + for i in 0..max_line_num_len { + buffer.prepend(0, " ", Style::NoStyle); + } + draw_note_separator(&mut buffer, 0, max_line_num_len + 1); + buffer.append(0, &level.to_string(), Style::HeaderMsg); + buffer.append(0, ": ", Style::NoStyle); + buffer.append(0, msg, Style::NoStyle); + } + else { + buffer.append(0, &level.to_string(), Style::Level(level.clone())); + match code { + &Some(ref code) => { + buffer.append(0, "[", Style::Level(level.clone())); + buffer.append(0, &code, Style::Level(level.clone())); + buffer.append(0, "]", Style::Level(level.clone())); + } + _ => {} + } + buffer.append(0, ": ", Style::HeaderMsg); + buffer.append(0, msg, Style::HeaderMsg); + } + + // Preprocess all the annotations so that they are grouped by file and by line number + // This helps us quickly iterate over the whole message (including secondary file spans) + 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)) = (self.cm.as_ref(), + msp.primary_span().as_ref()) { + cm.lookup_char_pos(primary_span.lo) + } else { + // If we don't have span information, emit and exit + emit_to_destination(&buffer.render(), level, &mut self.dst); + return Ok(()); + }; + if let Ok(pos) = + annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) { + annotated_files.swap(0, pos); + } + + // Print out the annotate source lines that correspond with the error + for annotated_file in annotated_files { + // print out the span location and spacer before we print the annotated source + // to do this, we need to know if this span will be primary + let is_primary = primary_lo.file.name == annotated_file.file.name; + if is_primary { + // remember where we are in the output buffer for easy reference + let mut buffer_msg_line_offset = buffer.num_lines(); + + buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber); + let loc = primary_lo.clone(); + buffer.append(buffer_msg_line_offset, + &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0), + Style::LineAndColumn); + for i in 0..max_line_num_len { + buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle); + } + } else { + // remember where we are in the output buffer for easy reference + let mut buffer_msg_line_offset = buffer.num_lines(); + + // Add spacing line + draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + + // Then, the secondary file indicator + buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber); + buffer.append(buffer_msg_line_offset + 1, + &annotated_file.file.name, + Style::LineAndColumn); + for i in 0..max_line_num_len { + buffer.prepend(buffer_msg_line_offset + 1, " ", Style::NoStyle); + } + } + + // Put in the spacer between the location and annotated source + let mut buffer_msg_line_offset = buffer.num_lines(); + draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + + // Next, output the annotate source for this file + for line_idx in 0..annotated_file.lines.len() { + self.render_source_line(&mut buffer, + annotated_file.file.clone(), + &annotated_file.lines[line_idx], + 3 + max_line_num_len); + + // check to see if we need to print out or elide lines that come between + // this annotated line and the next one + if line_idx < (annotated_file.lines.len() - 1) { + let line_idx_delta = annotated_file.lines[line_idx + 1].line_index - + annotated_file.lines[line_idx].line_index; + if line_idx_delta > 2 { + let last_buffer_line_num = buffer.num_lines(); + buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber); + } else if line_idx_delta == 2 { + let unannotated_line = annotated_file.file + .get_line(annotated_file.lines[line_idx].line_index) + .unwrap_or(""); + + let last_buffer_line_num = buffer.num_lines(); + + buffer.puts(last_buffer_line_num, + 0, + &(annotated_file.lines[line_idx + 1].line_index - 1) + .to_string(), + Style::LineNumber); + draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len); + buffer.puts(last_buffer_line_num, + 3 + max_line_num_len, + &unannotated_line, + Style::Quotation); + } + } + } + } + + // final step: take our styled buffer, render it, then output it + emit_to_destination(&buffer.render(), level, &mut self.dst); + + Ok(()) + } + fn emit_suggestion_default(&mut self, + suggestion: &CodeSuggestion, + level: &Level, + msg: &str, + max_line_num_len: usize) + -> io::Result<()> { + use std::borrow::Borrow; + + let primary_span = suggestion.msp.primary_span().unwrap(); + if let Some(ref cm) = self.cm { + let mut buffer = StyledBuffer::new(); + + buffer.append(0, &level.to_string(), Style::Level(level.clone())); + buffer.append(0, ": ", Style::HeaderMsg); + buffer.append(0, msg, Style::HeaderMsg); + + let lines = cm.span_to_lines(primary_span).unwrap(); + + assert!(!lines.lines.is_empty()); + + let complete = suggestion.splice_lines(cm.borrow()); + let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); + let display_lines = &lines.lines[..line_count]; + + let fm = &*lines.file; + // Calculate the widest number to format evenly + let max_digits = line_num_max_digits(display_lines.last().unwrap()); + + // 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 mut lines = complete.lines(); + let mut row_num = 1; + for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + buffer.append(row_num, line, Style::NoStyle); + row_num += 1; + } + + // if we elided some lines, add an ellipsis + if let Some(_) = lines.next() { + buffer.append(row_num, "...", Style::NoStyle); + } + emit_to_destination(&buffer.render(), level, &mut self.dst); + } + Ok(()) + } + fn emit_messages_default(&mut self, db: &DiagnosticBuilder) { + let max_line_num = self.get_max_line_num(db); + let max_line_num_len = max_line_num.to_string().len(); + + match self.emit_message_default(&db.span, + &db.message, + &db.code, + &db.level, + max_line_num_len, + false) { + Ok(()) => { + if !db.children.is_empty() { + let mut buffer = StyledBuffer::new(); + draw_col_separator(&mut buffer, 0, max_line_num_len + 1); + emit_to_destination(&buffer.render(), &db.level, &mut self.dst); + } + for child in &db.children { + match child.render_span { + Some(FullSpan(ref msp)) => { + match self.emit_message_default(msp, + &child.message, + &None, + &child.level, + max_line_num_len, + true) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + }, + Some(Suggestion(ref cs)) => { + match self.emit_suggestion_default(cs, + &child.level, + &child.message, + max_line_num_len) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + }, + None => { + match self.emit_message_default(&child.span, + &child.message, + &None, + &child.level, + max_line_num_len, + true) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + } + } + } + } + Err(e) => panic!("failed to emit error: {}", e) + } + write!(&mut self.dst, "\n"); + } + fn emit_message_(&mut self, rsp: &RenderSpan, msg: &str, @@ -363,6 +912,7 @@ impl EmitterWriter { } for snippet_data in output_vec.iter() { + /* let rendered_lines = snippet_data.render_lines(); for rendered_line in &rendered_lines { for styled_string in &rendered_line.text { @@ -372,6 +922,8 @@ impl EmitterWriter { } write!(&mut self.dst, "\n")?; } + */ + emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst); } } else { @@ -380,6 +932,8 @@ impl EmitterWriter { span_label.is_primary, span_label.label); } + emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst); + /* let rendered_lines = snippet_data.render_lines(); for rendered_line in &rendered_lines { for styled_string in &rendered_line.text { @@ -389,6 +943,7 @@ impl EmitterWriter { } write!(&mut self.dst, "\n")?; } + */ } Ok(()) } @@ -413,6 +968,33 @@ impl EmitterWriter { } } +fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { + buffer.puts(line, col, "| ", Style::LineNumber); +} + +fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { + buffer.puts(line, col, "= ", Style::LineNumber); +} + +fn overlaps(a1: &Annotation, a2: &Annotation) -> bool { + (a2.start_col..a2.end_col).contains(a1.start_col) || + (a1.start_col..a1.end_col).contains(a2.start_col) +} + +fn emit_to_destination(rendered_buffer: &Vec>, + lvl: &Level, + dst: &mut Destination) -> io::Result<()> { + for line in rendered_buffer { + for part in line { + dst.apply_style(lvl.clone(), part.style); + write!(dst, "{}", part.text); + dst.reset_attrs()?; + } + write!(dst, "\n"); + } + Ok(()) +} + fn line_num_max_digits(line: &LineInfo) -> usize { let mut max_line_num = line.line_index + 1; let mut digits = 0; @@ -480,7 +1062,7 @@ fn stderr_isatty() -> bool { } } -enum Destination { +pub enum Destination { Terminal(Box), Raw(Box), } @@ -495,35 +1077,39 @@ impl Destination { fn apply_style(&mut self, lvl: Level, - _kind: &RenderedLineKind, style: Style) -> io::Result<()> { match style { - Style::FileNameStyle | - Style::LineAndColumn => { - } + Style::FileNameStyle | Style::LineAndColumn => {} Style::LineNumber => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))); } - Style::Quotation => { + Style::ErrorCode => { + try!(self.start_attr(term::Attr::Bold)); + //try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA))); } - Style::OldSkoolNote => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))?; + Style::Quotation => {} + Style::OldSchoolNote => { + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))); } - Style::OldSkoolNoteText => { - self.start_attr(term::Attr::Bold)?; + Style::OldSchoolNoteText | Style::HeaderMsg => { + try!(self.start_attr(term::Attr::Bold)); } Style::UnderlinePrimary | Style::LabelPrimary => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(lvl.color()))?; + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(lvl.color()))); } - Style::UnderlineSecondary | Style::LabelSecondary => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + Style::UnderlineSecondary | + Style::LabelSecondary => { + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))); } - Style::NoStyle => { + Style::NoStyle => {} + Style::Level(l) => { + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(l.color()))); } } Ok(()) diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 7c14c132382e7..33781bed759f8 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -49,6 +49,7 @@ use std::thread::panicking; pub mod emitter; pub mod snippet; pub mod registry; +pub mod styled_buffer; use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION }; use syntax_pos::{MacroBacktrace}; diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs index 33f40ffc71a9f..525c83499fbe5 100644 --- a/src/librustc_errors/snippet.rs +++ b/src/librustc_errors/snippet.rs @@ -13,9 +13,11 @@ use syntax_pos::{Span, FileMap, CharPos, LineInfo}; use check_old_skool; use CodeMapper; +use styled_buffer::StyledBuffer; use std::cmp; use std::rc::Rc; use std::mem; +use {Level}; #[derive(Clone)] pub enum FormatMode { @@ -49,38 +51,40 @@ pub struct FileInfo { format_mode: FormatMode, } -#[derive(Clone, Debug)] -struct Line { - line_index: usize, - annotations: Vec, +#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +pub struct Line { + pub line_index: usize, + pub annotations: Vec, } #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] -struct Annotation { +pub struct Annotation { /// Start column, 0-based indexing -- counting *characters*, not /// utf-8 bytes. Note that it is important that this field goes /// first, so that when we sort, we sort orderings by start /// column. - start_col: usize, + pub start_col: usize, /// End column within the line (exclusive) - end_col: usize, + pub end_col: usize, /// Is this annotation derived from primary span - is_primary: bool, + pub is_primary: bool, /// Is this a large span minimized down to a smaller span - is_minimized: bool, + pub is_minimized: bool, /// Optional label to display adjacent to the annotation. - label: Option, + pub label: Option, } +/* #[derive(Debug)] pub struct RenderedLine { pub text: Vec, pub kind: RenderedLineKind, } +*/ #[derive(Debug)] pub struct StyledString { @@ -88,14 +92,9 @@ pub struct StyledString { pub style: Style, } -#[derive(Debug)] -pub struct StyledBuffer { - text: Vec>, - styles: Vec> -} - #[derive(Copy, Clone, Debug, PartialEq)] pub enum Style { + HeaderMsg, FileNameStyle, LineAndColumn, LineNumber, @@ -104,11 +103,14 @@ pub enum Style { UnderlineSecondary, LabelPrimary, LabelSecondary, - OldSkoolNoteText, - OldSkoolNote, + OldSchoolNoteText, + OldSchoolNote, NoStyle, + ErrorCode, + Level(Level), } +/* #[derive(Debug, Clone)] pub enum RenderedLineKind { PrimaryFileName, @@ -120,6 +122,7 @@ pub enum RenderedLineKind { Annotations, Elision, } +*/ impl SnippetData { pub fn new(codemap: Rc, @@ -186,15 +189,15 @@ impl SnippetData { self.files.last_mut().unwrap() } - pub fn render_lines(&self) -> Vec { + pub fn render_lines(&self) -> Vec> { debug!("SnippetData::render_lines()"); let mut rendered_lines: Vec<_> = self.files.iter() .flat_map(|f| f.render_file_lines(&self.codemap)) .collect(); - prepend_prefixes(&mut rendered_lines, &self.format_mode); - trim_lines(&mut rendered_lines); + //prepend_prefixes(&mut rendered_lines, &self.format_mode); + //trim_lines(&mut rendered_lines); rendered_lines } } @@ -215,6 +218,7 @@ impl StringSource for Vec { } } +/* impl From<(S, Style, RenderedLineKind)> for RenderedLine where S: StringSource { @@ -282,96 +286,20 @@ impl RenderedLineKind { } } } +*/ -impl StyledBuffer { - fn new() -> StyledBuffer { - StyledBuffer { text: vec![], styles: vec![] } - } - - fn render(&self, source_kind: RenderedLineKind) -> Vec { - let mut output: Vec = vec![]; - let mut styled_vec: Vec = vec![]; - - for (row, row_style) in self.text.iter().zip(&self.styles) { - let mut current_style = Style::NoStyle; - let mut current_text = String::new(); - - for (&c, &s) in row.iter().zip(row_style) { - if s != current_style { - if !current_text.is_empty() { - styled_vec.push(StyledString { text: current_text, style: current_style }); - } - current_style = s; - current_text = String::new(); - } - current_text.push(c); - } - if !current_text.is_empty() { - styled_vec.push(StyledString { text: current_text, style: current_style }); - } - - if output.is_empty() { - //We know our first output line is source and the rest are highlights and labels - output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() }); - } else { - output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations }); - } - styled_vec = vec![]; - } - - output - } - - fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) { - while line >= self.text.len() { - self.text.push(vec![]); - self.styles.push(vec![]); - } +impl FileInfo { + fn get_max_line_num(&self) -> usize { + let mut max = 0; - if col < self.text[line].len() { - self.text[line][col] = chr; - self.styles[line][col] = style; - } else { - let mut i = self.text[line].len(); - while i < col { - let s = match self.text[0].get(i) { - Some(&'\t') => '\t', - _ => ' ' - }; - self.text[line].push(s); - self.styles[line].push(Style::NoStyle); - i += 1; + for line in &self.lines { + if line.line_index > max { + max = line.line_index; } - self.text[line].push(chr); - self.styles[line].push(style); - } - } - - fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) { - let mut n = col; - for c in string.chars() { - self.putc(line, n, c, style); - n += 1; - } - } - - fn set_style(&mut self, line: usize, col: usize, style: Style) { - if self.styles.len() > line && self.styles[line].len() > col { - self.styles[line][col] = style; } + max } - fn append(&mut self, line: usize, string: &str, style: Style) { - if line >= self.text.len() { - self.puts(line, 0, string, style); - } else { - let col = self.text[line].len(); - self.puts(line, col, string, style); - } - } -} - -impl FileInfo { fn push_lines(&mut self, lines: &[LineInfo], is_primary: bool, @@ -469,16 +397,13 @@ impl FileInfo { return line_index - first_line_index; } - fn render_file_lines(&self, codemap: &Rc) -> Vec { + fn render_file_lines(&self, codemap: &Rc) -> Vec> { let old_school = match self.format_mode { FormatMode::OriginalErrorFormat => true, FormatMode::NewErrorFormat => false, FormatMode::EnvironmentSelected => check_old_skool() }; - // As a first step, we elide any instance of more than one - // continuous unannotated line. - let mut lines_iter = self.lines.iter(); let mut output = vec![]; @@ -487,39 +412,27 @@ impl FileInfo { match self.primary_span { Some(span) => { let lo = codemap.lookup_char_pos(span.lo); - output.push(RenderedLine { - text: vec![StyledString { + output.push(vec![StyledString { text: lo.file.name.clone(), style: Style::FileNameStyle, }, StyledString { text: format!(":{}:{}", lo.line, lo.col.0 + 1), style: Style::LineAndColumn, - }], - kind: RenderedLineKind::PrimaryFileName, - }); - output.push(RenderedLine { - text: vec![StyledString { + }]); + output.push(vec![StyledString { text: "".to_string(), style: Style::FileNameStyle, - }], - kind: RenderedLineKind::Annotations, - }); + }]); } None => { - output.push(RenderedLine { - text: vec![StyledString { + output.push(vec![StyledString { text: self.file.name.clone(), style: Style::FileNameStyle, - }], - kind: RenderedLineKind::OtherFileName, - }); - output.push(RenderedLine { - text: vec![StyledString { + }]); + output.push(vec![StyledString { text: "".to_string(), style: Style::FileNameStyle, - }], - kind: RenderedLineKind::Annotations, - }); + }]); } } } @@ -541,8 +454,7 @@ impl FileInfo { //as an old-style note if !line.annotations[0].is_primary { if let Some(ann) = line.annotations[0].label.clone() { - output.push(RenderedLine { - text: vec![StyledString { + output.push(vec![StyledString { text: lo.file.name.clone(), style: Style::FileNameStyle, }, StyledString { @@ -551,31 +463,29 @@ impl FileInfo { style: Style::LineAndColumn, }, StyledString { text: format!("note: "), - style: Style::OldSkoolNote, + style: Style::OldSchoolNote, }, StyledString { text: format!("{}", ann), - style: Style::OldSkoolNoteText, - }], - kind: RenderedLineKind::Annotations, - }); + style: Style::OldSchoolNoteText, + }]); } } - rendered_lines[0].text.insert(0, StyledString { + rendered_lines[0].insert(0, StyledString { text: format!(":{} ", lo.line), style: Style::LineAndColumn, }); - rendered_lines[0].text.insert(0, StyledString { + rendered_lines[0].insert(0, StyledString { text: lo.file.name.clone(), style: Style::FileNameStyle, }); let gap_amount = - rendered_lines[0].text[0].text.len() + - rendered_lines[0].text[1].text.len(); + rendered_lines[0][0].text.len() + + rendered_lines[0][1].text.len(); assert!(rendered_lines.len() >= 2, "no annotations resulted from: {:?}", line); for i in 1..rendered_lines.len() { - rendered_lines[i].text.insert(0, StyledString { + rendered_lines[i].insert(0, StyledString { text: vec![" "; gap_amount].join(""), style: Style::NoStyle }); @@ -598,9 +508,7 @@ impl FileInfo { next_line = lines_iter.next(); } if unannotated_lines > 1 { - output.push(RenderedLine::from((String::new(), - Style::NoStyle, - RenderedLineKind::Elision))); + output.push(vec![StyledString{ text: String::new(), style: Style::NoStyle}]); } else if let Some(line) = unannotated_line { output.append(&mut self.render_line(line)); } @@ -609,7 +517,7 @@ impl FileInfo { output } - fn render_line(&self, line: &Line) -> Vec { + fn render_line(&self, line: &Line) -> Vec> { let old_school = match self.format_mode { FormatMode::OriginalErrorFormat => true, FormatMode::NewErrorFormat => false, @@ -618,10 +526,12 @@ impl FileInfo { let source_string = self.file.get_line(line.line_index) .unwrap_or(""); + /* let source_kind = RenderedLineKind::SourceText { file: self.file.clone(), line_index: line.line_index, }; + */ let mut styled_buffer = StyledBuffer::new(); @@ -629,7 +539,7 @@ impl FileInfo { styled_buffer.append(0, &source_string, Style::Quotation); if line.annotations.is_empty() { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } // We want to display like this: @@ -666,7 +576,7 @@ impl FileInfo { if annotation.is_primary { Style::UnderlinePrimary } else { - Style::OldSkoolNote + Style::OldSchoolNote }); } else { @@ -674,7 +584,7 @@ impl FileInfo { if annotation.is_primary { Style::UnderlinePrimary } else { - Style::OldSkoolNote + Style::OldSchoolNote }); } } @@ -704,10 +614,10 @@ impl FileInfo { // If there are no annotations that need text, we're done. if labeled_annotations.is_empty() { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } if old_school { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } // Now add the text labels. We try, when possible, to stick the rightmost @@ -767,7 +677,7 @@ impl FileInfo { // If that's the last annotation, we're done if labeled_annotations.is_empty() { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } for (index, annotation) in labeled_annotations.iter().enumerate() { @@ -796,10 +706,11 @@ impl FileInfo { } } - styled_buffer.render(source_kind) + styled_buffer.render() } } +/* fn prepend_prefixes(rendered_lines: &mut [RenderedLine], format_mode: &FormatMode) { let old_school = match *format_mode { FormatMode::OriginalErrorFormat => true, @@ -882,6 +793,7 @@ fn trim_lines(rendered_lines: &mut [RenderedLine]) { } } } +*/ impl Line { fn new(line_index: usize) -> Line { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 0a60b7fd430c4..570c0a09bc417 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -26,7 +26,7 @@ use std::rc::Rc; use codemap::{self, CodeMap, ExpnInfo, NameAndSpan, MacroAttribute}; use errors; -use errors::snippet::{RenderedLine, SnippetData}; +use errors::snippet::{SnippetData}; use config; use entry::{self, EntryPointType}; use ext::base::{ExtCtxt, DummyMacroLoader}; diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 39bb5956312bc..7dfe19452a2a9 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -568,7 +568,7 @@ impl Sub for CharPos { // /// A source code location used for error reporting -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Loc { /// Information about the original source pub file: Rc,