Merge pull request #2086 from jugglerchris/difflines_mode
Add a new "modified lines" write mode.
This commit is contained in:
commit
091ed2f0a4
@ -107,7 +107,7 @@ pub fn to_toml(&self) -> Result<String, String> {
|
||||
|
||||
// Macro hygiene won't allow us to make `set_$i()` methods on Config
|
||||
// for each item, so this struct is used to give the API to set values:
|
||||
// `config.get().option(false)`. It's pretty ugly. Consider replacing
|
||||
// `config.set().option(false)`. It's pretty ugly. Consider replacing
|
||||
// with `config.set_option(false)` if we ever get a stable/usable
|
||||
// `concat_idents!()`.
|
||||
pub struct ConfigSetter<'a>(&'a mut Config);
|
||||
|
@ -176,6 +176,8 @@ pub fn to_list_tactic(self) -> ListTactic {
|
||||
Plain,
|
||||
// Outputs a checkstyle XML file.
|
||||
Checkstyle,
|
||||
// Output the changed lines (for internal value only)
|
||||
Modified,
|
||||
}
|
||||
|
||||
configuration_option_enum! { Color:
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use checkstyle::{output_checkstyle_file, output_footer, output_header};
|
||||
use config::{Config, NewlineStyle, WriteMode};
|
||||
use rustfmt_diff::{make_diff, print_diff, Mismatch};
|
||||
use rustfmt_diff::{make_diff, output_modified, print_diff, Mismatch};
|
||||
use syntax::codemap::FileName;
|
||||
|
||||
use FileRecord;
|
||||
@ -164,6 +164,15 @@ fn create_diff(
|
||||
return Ok(has_diff);
|
||||
}
|
||||
}
|
||||
WriteMode::Modified => {
|
||||
let filename = filename_to_path();
|
||||
if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
|
||||
let mismatch = make_diff(&ori, &fmt, 0);
|
||||
let has_diff = !mismatch.is_empty();
|
||||
output_modified(out, mismatch);
|
||||
return Ok(has_diff);
|
||||
}
|
||||
}
|
||||
WriteMode::Checkstyle => {
|
||||
let filename = filename_to_path();
|
||||
let diff = create_diff(filename, text, config)?;
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::io::{self, stdout, Write};
|
||||
use std::io::{self, stdout, BufRead, Write};
|
||||
use std::iter::repeat;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
@ -723,6 +723,79 @@ fn duration_to_f32(d: Duration) -> f32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// A single span of changed lines, with 0 or more removed lines
|
||||
/// and a vector of 0 or more inserted lines.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ModifiedChunk {
|
||||
/// The first to be removed from the original text
|
||||
pub line_number_orig: u32,
|
||||
/// The number of lines which have been replaced
|
||||
pub lines_removed: u32,
|
||||
/// The new lines
|
||||
pub lines: Vec<String>,
|
||||
}
|
||||
|
||||
/// Set of changed sections of a file.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ModifiedLines {
|
||||
/// The set of changed chunks.
|
||||
pub chunks: Vec<ModifiedChunk>,
|
||||
}
|
||||
|
||||
/// The successful result of formatting via get_modified_lines().
|
||||
pub struct ModifiedLinesResult {
|
||||
/// The high level summary details
|
||||
pub summary: Summary,
|
||||
/// The result Filemap
|
||||
pub filemap: FileMap,
|
||||
/// Map of formatting errors
|
||||
pub report: FormatReport,
|
||||
/// The sets of updated lines.
|
||||
pub modified_lines: ModifiedLines,
|
||||
}
|
||||
|
||||
/// Format a file and return a `ModifiedLines` data structure describing
|
||||
/// the changed ranges of lines.
|
||||
pub fn get_modified_lines(
|
||||
input: Input,
|
||||
config: &Config,
|
||||
) -> Result<ModifiedLinesResult, (io::Error, Summary)> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
let mut config = config.clone();
|
||||
config.set().write_mode(config::WriteMode::Modified);
|
||||
let (summary, filemap, formatreport) = format_input(input, &config, Some(&mut data))?;
|
||||
|
||||
let mut lines = data.lines();
|
||||
let mut chunks = Vec::new();
|
||||
while let Some(Ok(header)) = lines.next() {
|
||||
// Parse the header line
|
||||
let values: Vec<_> = header
|
||||
.split(' ')
|
||||
.map(|s| s.parse::<u32>().unwrap())
|
||||
.collect();
|
||||
assert_eq!(values.len(), 3);
|
||||
let line_number_orig = values[0];
|
||||
let lines_removed = values[1];
|
||||
let num_added = values[2];
|
||||
let mut added_lines = Vec::new();
|
||||
for _ in 0..num_added {
|
||||
added_lines.push(lines.next().unwrap().unwrap());
|
||||
}
|
||||
chunks.push(ModifiedChunk {
|
||||
line_number_orig,
|
||||
lines_removed,
|
||||
lines: added_lines,
|
||||
});
|
||||
}
|
||||
Ok(ModifiedLinesResult {
|
||||
summary: summary,
|
||||
filemap: filemap,
|
||||
report: formatreport,
|
||||
modified_lines: ModifiedLines { chunks },
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Input {
|
||||
File(PathBuf),
|
||||
|
@ -13,6 +13,7 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use term;
|
||||
use std::io::Write;
|
||||
use utils::use_colored_tty;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -24,14 +25,19 @@ pub enum DiffLine {
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Mismatch {
|
||||
/// The line number in the formatted version.
|
||||
pub line_number: u32,
|
||||
/// The line number in the original version.
|
||||
pub line_number_orig: u32,
|
||||
/// The set of lines (context and old/new) in the mismatch.
|
||||
pub lines: Vec<DiffLine>,
|
||||
}
|
||||
|
||||
impl Mismatch {
|
||||
fn new(line_number: u32) -> Mismatch {
|
||||
fn new(line_number: u32, line_number_orig: u32) -> Mismatch {
|
||||
Mismatch {
|
||||
line_number,
|
||||
line_number_orig,
|
||||
lines: Vec::new(),
|
||||
}
|
||||
}
|
||||
@ -77,17 +83,21 @@ pub fn writeln(&mut self, msg: &str, color: Option<term::color::Color>) {
|
||||
// Produces a diff between the expected output and actual output of rustfmt.
|
||||
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
|
||||
let mut line_number = 1;
|
||||
let mut line_number_orig = 1;
|
||||
let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
|
||||
let mut lines_since_mismatch = context_size + 1;
|
||||
let mut results = Vec::new();
|
||||
let mut mismatch = Mismatch::new(0);
|
||||
let mut mismatch = Mismatch::new(0, 0);
|
||||
|
||||
for result in diff::lines(expected, actual) {
|
||||
match result {
|
||||
diff::Result::Left(str) => {
|
||||
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
|
||||
results.push(mismatch);
|
||||
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
|
||||
mismatch = Mismatch::new(
|
||||
line_number - context_queue.len() as u32,
|
||||
line_number_orig - context_queue.len() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
while let Some(line) = context_queue.pop_front() {
|
||||
@ -95,12 +105,16 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
|
||||
}
|
||||
|
||||
mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
|
||||
line_number_orig += 1;
|
||||
lines_since_mismatch = 0;
|
||||
}
|
||||
diff::Result::Right(str) => {
|
||||
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
|
||||
results.push(mismatch);
|
||||
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
|
||||
mismatch = Mismatch::new(
|
||||
line_number - context_queue.len() as u32,
|
||||
line_number_orig - context_queue.len() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
while let Some(line) = context_queue.pop_front() {
|
||||
@ -123,6 +137,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
|
||||
}
|
||||
|
||||
line_number += 1;
|
||||
line_number_orig += 1;
|
||||
lines_since_mismatch += 1;
|
||||
}
|
||||
}
|
||||
@ -158,6 +173,42 @@ pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F, color: Color)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a Mismatch into a serialised form which just includes
|
||||
/// enough information to modify the original file.
|
||||
/// Each section starts with a line with three integers, space separated:
|
||||
/// lineno num_removed num_added
|
||||
/// followed by (num_added) lines of added text. The line numbers are
|
||||
/// relative to the original file.
|
||||
pub fn output_modified<W>(mut out: W, diff: Vec<Mismatch>)
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
for mismatch in diff {
|
||||
let (num_removed, num_added) = mismatch.lines.iter().fold((0, 0), |(rem, add), line| {
|
||||
match *line {
|
||||
DiffLine::Context(_) => panic!("No Context expected"),
|
||||
DiffLine::Expected(_) => (rem, add + 1),
|
||||
DiffLine::Resulting(_) => (rem + 1, add),
|
||||
}
|
||||
});
|
||||
// Write a header with enough information to separate the modified lines.
|
||||
writeln!(
|
||||
out,
|
||||
"{} {} {}",
|
||||
mismatch.line_number_orig, num_removed, num_added
|
||||
).unwrap();
|
||||
|
||||
for line in mismatch.lines {
|
||||
match line {
|
||||
DiffLine::Context(_) | DiffLine::Resulting(_) => (),
|
||||
DiffLine::Expected(ref str) => {
|
||||
writeln!(out, "{}", str).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{make_diff, Mismatch};
|
||||
@ -173,6 +224,7 @@ fn diff_simple() {
|
||||
vec![
|
||||
Mismatch {
|
||||
line_number: 2,
|
||||
line_number_orig: 2,
|
||||
lines: vec![
|
||||
Context("two".to_owned()),
|
||||
Resulting("three".to_owned()),
|
||||
@ -194,6 +246,7 @@ fn diff_simple2() {
|
||||
vec![
|
||||
Mismatch {
|
||||
line_number: 2,
|
||||
line_number_orig: 2,
|
||||
lines: vec![
|
||||
Context("two".to_owned()),
|
||||
Resulting("three".to_owned()),
|
||||
@ -203,6 +256,7 @@ fn diff_simple2() {
|
||||
},
|
||||
Mismatch {
|
||||
line_number: 5,
|
||||
line_number_orig: 5,
|
||||
lines: vec![
|
||||
Resulting("five".to_owned()),
|
||||
Expected("cinq".to_owned()),
|
||||
@ -223,6 +277,7 @@ fn diff_zerocontext() {
|
||||
vec![
|
||||
Mismatch {
|
||||
line_number: 3,
|
||||
line_number_orig: 3,
|
||||
lines: vec![Resulting("three".to_owned()), Expected("trois".to_owned())],
|
||||
},
|
||||
]
|
||||
@ -239,6 +294,7 @@ fn diff_trailing_newline() {
|
||||
vec![
|
||||
Mismatch {
|
||||
line_number: 5,
|
||||
line_number_orig: 5,
|
||||
lines: vec![Context("five".to_owned()), Expected("".to_owned())],
|
||||
},
|
||||
]
|
||||
|
@ -143,6 +143,30 @@ fn checkstyle_test() {
|
||||
assert_output(Path::new(filename), Path::new(expected_filename));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modified_test() {
|
||||
// Test "modified" output
|
||||
let filename = "tests/writemode/source/modified.rs";
|
||||
let result = get_modified_lines(Input::File(filename.into()), &Config::default()).unwrap();
|
||||
assert_eq!(
|
||||
result.modified_lines,
|
||||
ModifiedLines {
|
||||
chunks: vec![
|
||||
ModifiedChunk {
|
||||
line_number_orig: 4,
|
||||
lines_removed: 4,
|
||||
lines: vec!["fn blah() {}".into()],
|
||||
},
|
||||
ModifiedChunk {
|
||||
line_number_orig: 9,
|
||||
lines_removed: 6,
|
||||
lines: vec!["#[cfg(a, b)]".into(), "fn main() {}".into()],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Helper function for comparing the results of rustfmt
|
||||
// to a known output file generated by one of the write modes.
|
||||
fn assert_output(source: &Path, expected_filename: &Path) {
|
||||
@ -529,6 +553,7 @@ fn rustfmt_diff_make_diff_tests() {
|
||||
vec![
|
||||
Mismatch {
|
||||
line_number: 1,
|
||||
line_number_orig: 1,
|
||||
lines: vec![
|
||||
DiffLine::Context("a".into()),
|
||||
DiffLine::Resulting("b".into()),
|
||||
|
14
rustfmt-core/tests/writemode/source/modified.rs
Normal file
14
rustfmt-core/tests/writemode/source/modified.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// rustfmt-write_mode: modified
|
||||
// Test "modified" output
|
||||
|
||||
fn
|
||||
blah
|
||||
()
|
||||
{ }
|
||||
|
||||
|
||||
#[cfg
|
||||
( a , b
|
||||
)]
|
||||
fn
|
||||
main() {}
|
5
rustfmt-core/tests/writemode/target/modified.txt
Normal file
5
rustfmt-core/tests/writemode/target/modified.txt
Normal file
@ -0,0 +1,5 @@
|
||||
4 4 1
|
||||
fn blah() {}
|
||||
10 5 2
|
||||
#[cfg(a, b)]
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user