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::*;
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 {
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);
}
}
}
format_mode: FormatMode
}
+struct FileWithAnnotatedLines {
+ file: Rc<FileMap>,
+ lines: Vec<Line>,
+}
+
+
/// 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 {
}
}
+ fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
+ fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
+ file: Rc<FileMap>,
+ 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<FileMap>,
+ 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<String>,
+ 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,
}
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 {
}
write!(&mut self.dst, "\n")?;
}
+ */
+ emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst);
}
}
else {
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 {
}
write!(&mut self.dst, "\n")?;
}
+ */
}
Ok(())
}
}
}
+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<Vec<StyledString>>,
+ 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;
}
}
-enum Destination {
+pub enum Destination {
Terminal(Box<term::StderrTerminal>),
Raw(Box<Write + Send>),
}
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(())
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 {
format_mode: FormatMode,
}
-#[derive(Clone, Debug)]
-struct Line {
- line_index: usize,
- annotations: Vec<Annotation>,
+#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
+pub struct Line {
+ pub line_index: usize,
+ pub annotations: Vec<Annotation>,
}
#[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<String>,
+ pub label: Option<String>,
}
+/*
#[derive(Debug)]
pub struct RenderedLine {
pub text: Vec<StyledString>,
pub kind: RenderedLineKind,
}
+*/
#[derive(Debug)]
pub struct StyledString {
pub style: Style,
}
-#[derive(Debug)]
-pub struct StyledBuffer {
- text: Vec<Vec<char>>,
- styles: Vec<Vec<Style>>
-}
-
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Style {
+ HeaderMsg,
FileNameStyle,
LineAndColumn,
LineNumber,
UnderlineSecondary,
LabelPrimary,
LabelSecondary,
- OldSkoolNoteText,
- OldSkoolNote,
+ OldSchoolNoteText,
+ OldSchoolNote,
NoStyle,
+ ErrorCode,
+ Level(Level),
}
+/*
#[derive(Debug, Clone)]
pub enum RenderedLineKind {
PrimaryFileName,
Annotations,
Elision,
}
+*/
impl SnippetData {
pub fn new(codemap: Rc<CodeMapper>,
self.files.last_mut().unwrap()
}
- pub fn render_lines(&self) -> Vec<RenderedLine> {
+ pub fn render_lines(&self) -> Vec<Vec<StyledString>> {
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
}
}
}
}
+/*
impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine
where S: StringSource
{
}
}
}
+*/
-impl StyledBuffer {
- fn new() -> StyledBuffer {
- StyledBuffer { text: vec![], styles: vec![] }
- }
-
- fn render(&self, source_kind: RenderedLineKind) -> Vec<RenderedLine> {
- let mut output: Vec<RenderedLine> = vec![];
- let mut styled_vec: Vec<StyledString> = 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,
return line_index - first_line_index;
}
- fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
+ fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<Vec<StyledString>> {
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![];
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,
- });
+ }]);
}
}
}
//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 {
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
});
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));
}
output
}
- fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
+ fn render_line(&self, line: &Line) -> Vec<Vec<StyledString>> {
let old_school = match self.format_mode {
FormatMode::OriginalErrorFormat => true,
FormatMode::NewErrorFormat => false,
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();
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:
if annotation.is_primary {
Style::UnderlinePrimary
} else {
- Style::OldSkoolNote
+ Style::OldSchoolNote
});
}
else {
if annotation.is_primary {
Style::UnderlinePrimary
} else {
- Style::OldSkoolNote
+ Style::OldSchoolNote
});
}
}
// 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
// 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() {
}
}
- 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,
}
}
}
+*/
impl Line {
fn new(line_index: usize) -> Line {