1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use config::{Color, Config, Verbosity};
13 use std::collections::VecDeque;
17 #[derive(Debug, PartialEq)]
24 #[derive(Debug, PartialEq)]
26 /// The line number in the formatted version.
28 /// The line number in the original version.
29 pub line_number_orig: u32,
30 /// The set of lines (context and old/new) in the mismatch.
31 pub lines: Vec<DiffLine>,
35 fn new(line_number: u32, line_number_orig: u32) -> Mismatch {
44 // This struct handles writing output to stdout and abstracts away the logic
45 // of printing in color, if it's possible in the executing environment.
46 pub struct OutputWriter {
47 terminal: Option<Box<term::Terminal<Output = io::Stdout>>>,
51 // Create a new OutputWriter instance based on the caller's preference
52 // for colorized output and the capabilities of the terminal.
53 pub fn new(color: Color) -> Self {
54 if let Some(t) = term::stdout() {
55 if color.use_colored_tty() && t.supports_color() {
56 return OutputWriter { terminal: Some(t) };
59 OutputWriter { terminal: None }
62 // Write output in the optionally specified color. The output is written
63 // in the specified color if this OutputWriter instance contains a
64 // Terminal in its `terminal` field.
65 pub fn writeln(&mut self, msg: &str, color: Option<term::color::Color>) {
66 match &mut self.terminal {
68 if let Some(color) = color {
71 writeln!(t, "{}", msg).unwrap();
76 None => println!("{}", msg),
81 // Produces a diff between the expected output and actual output of rustfmt.
82 pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
83 let mut line_number = 1;
84 let mut line_number_orig = 1;
85 let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
86 let mut lines_since_mismatch = context_size + 1;
87 let mut results = Vec::new();
88 let mut mismatch = Mismatch::new(0, 0);
90 for result in diff::lines(expected, actual) {
92 diff::Result::Left(str) => {
93 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
94 results.push(mismatch);
95 mismatch = Mismatch::new(
96 line_number - context_queue.len() as u32,
97 line_number_orig - context_queue.len() as u32,
101 while let Some(line) = context_queue.pop_front() {
102 mismatch.lines.push(DiffLine::Context(line.to_owned()));
105 mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
106 line_number_orig += 1;
107 lines_since_mismatch = 0;
109 diff::Result::Right(str) => {
110 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
111 results.push(mismatch);
112 mismatch = Mismatch::new(
113 line_number - context_queue.len() as u32,
114 line_number_orig - context_queue.len() as u32,
118 while let Some(line) = context_queue.pop_front() {
119 mismatch.lines.push(DiffLine::Context(line.to_owned()));
122 mismatch.lines.push(DiffLine::Expected(str.to_owned()));
124 lines_since_mismatch = 0;
126 diff::Result::Both(str, _) => {
127 if context_queue.len() >= context_size {
128 let _ = context_queue.pop_front();
131 if lines_since_mismatch < context_size {
132 mismatch.lines.push(DiffLine::Context(str.to_owned()));
133 } else if context_size > 0 {
134 context_queue.push_back(str);
138 line_number_orig += 1;
139 lines_since_mismatch += 1;
144 results.push(mismatch);
150 pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F, config: &Config)
152 F: Fn(u32) -> String,
154 let color = config.color();
155 let line_terminator = if config.verbose() == Verbosity::Verbose {
161 let mut writer = OutputWriter::new(color);
163 for mismatch in diff {
164 let title = get_section_title(mismatch.line_number);
165 writer.writeln(&title, None);
167 for line in mismatch.lines {
169 DiffLine::Context(ref str) => {
170 writer.writeln(&format!(" {}{}", str, line_terminator), None)
172 DiffLine::Expected(ref str) => writer.writeln(
173 &format!("+{}{}", str, line_terminator),
174 Some(term::color::GREEN),
176 DiffLine::Resulting(ref str) => writer.writeln(
177 &format!("-{}{}", str, line_terminator),
178 Some(term::color::RED),
185 /// Convert a Mismatch into a serialised form which just includes
186 /// enough information to modify the original file.
187 /// Each section starts with a line with three integers, space separated:
188 /// lineno num_removed num_added
189 /// followed by (num_added) lines of added text. The line numbers are
190 /// relative to the original file.
191 pub fn output_modified<W>(mut out: W, diff: Vec<Mismatch>)
195 for mismatch in diff {
196 let (num_removed, num_added) =
200 .fold((0, 0), |(rem, add), line| match *line {
201 DiffLine::Context(_) => panic!("No Context expected"),
202 DiffLine::Expected(_) => (rem, add + 1),
203 DiffLine::Resulting(_) => (rem + 1, add),
205 // Write a header with enough information to separate the modified lines.
209 mismatch.line_number_orig, num_removed, num_added
212 for line in mismatch.lines {
214 DiffLine::Context(_) | DiffLine::Resulting(_) => (),
215 DiffLine::Expected(ref str) => {
216 writeln!(out, "{}", str).unwrap();
225 use super::DiffLine::*;
226 use super::{make_diff, Mismatch};
230 let src = "one\ntwo\nthree\nfour\nfive\n";
231 let dest = "one\ntwo\ntrois\nfour\nfive\n";
232 let diff = make_diff(src, dest, 1);
239 Context("two".to_owned()),
240 Resulting("three".to_owned()),
241 Expected("trois".to_owned()),
242 Context("four".to_owned()),
250 let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
251 let dest = "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
252 let diff = make_diff(src, dest, 1);
260 Context("two".to_owned()),
261 Resulting("three".to_owned()),
262 Expected("trois".to_owned()),
263 Context("four".to_owned()),
270 Resulting("five".to_owned()),
271 Expected("cinq".to_owned()),
272 Context("six".to_owned()),
280 fn diff_zerocontext() {
281 let src = "one\ntwo\nthree\nfour\nfive\n";
282 let dest = "one\ntwo\ntrois\nfour\nfive\n";
283 let diff = make_diff(src, dest, 0);
289 lines: vec![Resulting("three".to_owned()), Expected("trois".to_owned())],
295 fn diff_trailing_newline() {
296 let src = "one\ntwo\nthree\nfour\nfive";
297 let dest = "one\ntwo\nthree\nfour\nfive\n";
298 let diff = make_diff(src, dest, 1);
304 lines: vec![Context("five".to_owned()), Expected("".to_owned())],