]> git.lizzy.rs Git - rust.git/commitdiff
Implement latest rfc style using simpler rendering
authorJonathan Turner <jturner@mozilla.com>
Mon, 11 Jul 2016 20:02:03 +0000 (16:02 -0400)
committerJonathan Turner <jturner@mozilla.com>
Thu, 14 Jul 2016 11:57:46 +0000 (07:57 -0400)
src/librustc_errors/emitter.rs
src/librustc_errors/lib.rs
src/librustc_errors/snippet.rs
src/libsyntax/test.rs
src/libsyntax_pos/lib.rs

index a0d7120dd4f46ea6183da9331f3d3d9eaf8a6b13..52c0ea931408f733db7a907ebd17e8d9da254923 100644 (file)
 
 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 @@ fn emit(&mut self, db: &DiagnosticBuilder) {
                                     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<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 {
@@ -170,6 +165,560 @@ fn emit_message(&mut self,
         }
     }
 
+    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,
@@ -363,6 +912,7 @@ pub fn highlight_lines(&mut self,
             }
 
             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 @@ pub fn highlight_lines(&mut self,
                     }
                     write!(&mut self.dst, "\n")?;
                 }
+                */
+                emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst);
             }
         }
         else {
@@ -380,6 +932,8 @@ pub fn highlight_lines(&mut self,
                                   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 @@ pub fn highlight_lines(&mut self,
                 }
                 write!(&mut self.dst, "\n")?;
             }
+            */
         }
         Ok(())
     }
@@ -413,6 +968,33 @@ fn print_macro_backtrace(&mut self,
     }
 }
 
+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;
@@ -480,7 +1062,7 @@ fn GetConsoleMode(hConsoleHandle: HANDLE,
     }
 }
 
-enum Destination {
+pub enum Destination {
     Terminal(Box<term::StderrTerminal>),
     Raw(Box<Write + Send>),
 }
@@ -495,35 +1077,39 @@ fn from_stderr() -> 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(())
index 7c14c132382e770984035967307987422445b3c9..33781bed759f852d8dfeb06caccaad11a7d005d9 100644 (file)
@@ -49,6 +49,7 @@
 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};
index 33f40ffc71a9f69b78b965db5c2f7cb066503e4c..525c83499fbe5cacf3b5df6c29f1808dbda47592 100644 (file)
 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<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 {
@@ -88,14 +92,9 @@ 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,
@@ -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<CodeMapper>,
@@ -186,15 +189,15 @@ fn file(&mut self, file_map: &Rc<FileMap>) -> &mut FileInfo {
         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
     }
 }
@@ -215,6 +218,7 @@ fn make_string(self) -> String {
     }
 }
 
+/*
 impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine
     where S: StringSource
 {
@@ -282,96 +286,20 @@ fn prefix(&self) -> StyledString {
         }
     }
 }
+*/
 
-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,
@@ -469,16 +397,13 @@ fn ensure_source_line(&mut self, line_index: usize) -> usize {
         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![];
 
@@ -487,39 +412,27 @@ fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
             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 @@ fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
                             //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 @@ fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
                                             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 @@ fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
                 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 @@ fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
         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,
@@ -618,10 +526,12 @@ fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
 
         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 @@ fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
         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 @@ fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
                             if annotation.is_primary {
                                 Style::UnderlinePrimary
                             } else {
-                                Style::OldSkoolNote
+                                Style::OldSchoolNote
                             });
                     }
                     else {
@@ -674,7 +584,7 @@ fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
                             if annotation.is_primary {
                                 Style::UnderlinePrimary
                             } else {
-                                Style::OldSkoolNote
+                                Style::OldSchoolNote
                             });
                     }
                 }
@@ -704,10 +614,10 @@ fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
 
         // 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 @@ fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
 
         // 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 @@ fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
             }
         }
 
-        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 {
index 0a60b7fd430c427277fcbe33cf3d7668388c9ae5..570c0a09bc417e418c4d0e6f1c0034035b9d068f 100644 (file)
@@ -26,7 +26,7 @@
 
 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};
index 39bb5956312bce7b752388892d7e33c00dea7625..7dfe19452a2a933a64cdd849e2a21ce535880f50 100644 (file)
@@ -568,7 +568,7 @@ fn sub(self, rhs: CharPos) -> 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<FileMap>,