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.
13 use std::collections::VecDeque;
17 use utils::use_colored_tty;
19 #[derive(Debug, PartialEq)]
26 #[derive(Debug, PartialEq)]
28 /// The line number in the formatted version.
30 /// The line number in the original version.
31 pub line_number_orig: u32,
32 /// The set of lines (context and old/new) in the mismatch.
33 pub lines: Vec<DiffLine>,
37 fn new(line_number: u32, line_number_orig: u32) -> Mismatch {
46 // This struct handles writing output to stdout and abstracts away the logic
47 // of printing in color, if it's possible in the executing environment.
48 pub struct OutputWriter {
49 terminal: Option<Box<term::Terminal<Output = io::Stdout>>>,
53 // Create a new OutputWriter instance based on the caller's preference
54 // for colorized output and the capabilities of the terminal.
55 pub fn new(color: Color) -> Self {
56 if let Some(t) = term::stdout() {
57 if use_colored_tty(color) && t.supports_color() {
58 return OutputWriter { terminal: Some(t) };
61 OutputWriter { terminal: None }
64 // Write output in the optionally specified color. The output is written
65 // in the specified color if this OutputWriter instance contains a
66 // Terminal in its `terminal` field.
67 pub fn writeln(&mut self, msg: &str, color: Option<term::color::Color>) {
68 match &mut self.terminal {
70 if let Some(color) = color {
73 writeln!(t, "{}", msg).unwrap();
78 None => println!("{}", msg),
83 // Produces a diff between the expected output and actual output of rustfmt.
84 pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
85 let mut line_number = 1;
86 let mut line_number_orig = 1;
87 let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
88 let mut lines_since_mismatch = context_size + 1;
89 let mut results = Vec::new();
90 let mut mismatch = Mismatch::new(0, 0);
92 for result in diff::lines(expected, actual) {
94 diff::Result::Left(str) => {
95 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
96 results.push(mismatch);
97 mismatch = Mismatch::new(
98 line_number - context_queue.len() as u32,
99 line_number_orig - context_queue.len() as u32,
103 while let Some(line) = context_queue.pop_front() {
104 mismatch.lines.push(DiffLine::Context(line.to_owned()));
107 mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
108 line_number_orig += 1;
109 lines_since_mismatch = 0;
111 diff::Result::Right(str) => {
112 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
113 results.push(mismatch);
114 mismatch = Mismatch::new(
115 line_number - context_queue.len() as u32,
116 line_number_orig - context_queue.len() as u32,
120 while let Some(line) = context_queue.pop_front() {
121 mismatch.lines.push(DiffLine::Context(line.to_owned()));
124 mismatch.lines.push(DiffLine::Expected(str.to_owned()));
126 lines_since_mismatch = 0;
128 diff::Result::Both(str, _) => {
129 if context_queue.len() >= context_size {
130 let _ = context_queue.pop_front();
133 if lines_since_mismatch < context_size {
134 mismatch.lines.push(DiffLine::Context(str.to_owned()));
135 } else if context_size > 0 {
136 context_queue.push_back(str);
140 line_number_orig += 1;
141 lines_since_mismatch += 1;
146 results.push(mismatch);
152 pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F, color: Color)
154 F: Fn(u32) -> String,
156 let mut writer = OutputWriter::new(color);
158 for mismatch in diff {
159 let title = get_section_title(mismatch.line_number);
160 writer.writeln(&format!("{}", title), None);
162 for line in mismatch.lines {
164 DiffLine::Context(ref str) => writer.writeln(&format!(" {}⏎", str), None),
165 DiffLine::Expected(ref str) => {
166 writer.writeln(&format!("+{}⏎", str), Some(term::color::GREEN))
168 DiffLine::Resulting(ref str) => {
169 writer.writeln(&format!("-{}⏎", str), Some(term::color::RED))
176 /// Convert a Mismatch into a serialised form which just includes
177 /// enough information to modify the original file.
178 /// Each section starts with a line with three integers, space separated:
179 /// lineno num_removed num_added
180 /// followed by (num_added) lines of added text. The line numbers are
181 /// relative to the original file.
182 pub fn output_modified<W>(mut out: W, diff: Vec<Mismatch>)
186 for mismatch in diff {
187 let (num_removed, num_added) = mismatch.lines.iter().fold((0, 0), |(rem, add), line| {
189 DiffLine::Context(_) => panic!("No Context expected"),
190 DiffLine::Expected(_) => (rem, add + 1),
191 DiffLine::Resulting(_) => (rem + 1, add),
194 // Write a header with enough information to separate the modified lines.
198 mismatch.line_number_orig, num_removed, num_added
201 for line in mismatch.lines {
203 DiffLine::Context(_) | DiffLine::Resulting(_) => (),
204 DiffLine::Expected(ref str) => {
205 writeln!(out, "{}", str).unwrap();
214 use super::DiffLine::*;
215 use super::{make_diff, Mismatch};
219 let src = "one\ntwo\nthree\nfour\nfive\n";
220 let dest = "one\ntwo\ntrois\nfour\nfive\n";
221 let diff = make_diff(src, dest, 1);
228 Context("two".to_owned()),
229 Resulting("three".to_owned()),
230 Expected("trois".to_owned()),
231 Context("four".to_owned()),
239 let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
240 let dest = "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
241 let diff = make_diff(src, dest, 1);
249 Context("two".to_owned()),
250 Resulting("three".to_owned()),
251 Expected("trois".to_owned()),
252 Context("four".to_owned()),
259 Resulting("five".to_owned()),
260 Expected("cinq".to_owned()),
261 Context("six".to_owned()),
269 fn diff_zerocontext() {
270 let src = "one\ntwo\nthree\nfour\nfive\n";
271 let dest = "one\ntwo\ntrois\nfour\nfive\n";
272 let diff = make_diff(src, dest, 0);
278 lines: vec![Resulting("three".to_owned()), Expected("trois".to_owned())],
284 fn diff_trailing_newline() {
285 let src = "one\ntwo\nthree\nfour\nfive";
286 let dest = "one\ntwo\nthree\nfour\nfive\n";
287 let diff = make_diff(src, dest, 1);
293 lines: vec![Context("five".to_owned()), Expected("".to_owned())],