Add a new get_modified_lines() API to get only the new

changed lines from rustfmting.

Squashed commit of the following:

commit e90f9da64bbdb640b8c9ee61c3ad395617d8b4da
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Sat Jan 20 20:10:16 2018 +0000

    Fix tests after merging with master.

commit c3af0042769fe459b0c9c94a0934605ea4b40e40
Merge: 03868583 e0e3e222
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Sat Jan 20 17:45:05 2018 +0000

    Merge remote-tracking branch 'origin/master' into HEAD

commit 03868583f8555aae30bdfb5839a82afd3704f4cb
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Mon Nov 20 01:57:56 2017 +0000

    Fix some warnings.

commit 162b13463e44c782394d418db5ca5710931beb7a
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Mon Nov 20 01:48:02 2017 +0000

    Remove unneeded import.

commit 20cce3cbfd0f386d92b80bf4c7b83ab4d78a73e7
Merge: 81e98147 fa794f58
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Mon Nov 20 01:07:17 2017 +0000

    Merge branch 'master' into difflines_mode

commit 81e981472ceb3a0938d6f050edf8dcd5ebff8e33
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Mon Nov 20 01:02:50 2017 +0000

    Add a simple "modified lines" test.

commit 018390ced3523ca9fdd5384a6c1004cdb99174a9
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Thu Nov 2 23:06:21 2017 +0000

    Update test output.

commit 7909f4986ed21999aff7b3d075332e686ac464ff
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Thu Nov 2 23:03:22 2017 +0000

    Rerun rustfmt.

commit 6275f1a8da52db1df36c4b7432996cdbb94ca463
Merge: 7a66d286 175c0c6f
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Thu Nov 2 21:40:29 2017 +0000

    Merge remote-tracking branch 'origin/master' into difflines_mode

commit 7a66d2866432c430b046938bb37bf5efc03fa9da
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Thu Nov 2 21:36:40 2017 +0000

    WIP: Add a separate API to get changed lines.
    Currently calls format_input() and adjusts the output.

commit c8163a923c7d9ae42fd8078cd9b2b51c6f73e36e
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Fri Oct 27 22:53:33 2017 +0100

    Remove "modified" from the documentation again.

commit 94041fa115a6f428afe40e01d41bf2fe603f70bb
Merge: acaa3c7c 2adf7eec
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Fri Oct 27 22:47:05 2017 +0100

    Merge branch 'master' into difflines_mode

commit acaa3c7ce446297cd3fe5c9610763629a2d8537c
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Tue Oct 24 23:34:14 2017 +0100

    Update the Modified write mode to use `out` instead of just prinln!().

    This means we can test it more easily, so do so.

commit 9f1bbca1f3c12d933ea823918cc548e69b438b1e
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Tue Oct 24 23:11:55 2017 +0100

    Add "Modified" to the various lists of modes.

commit e12f023756cf3daf202dcaa02bd6492b0d2a0455
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Tue Oct 24 22:57:33 2017 +0100

    Rerun cargo fmt.

commit 0f8a43630fa1548e95dcb1c0933708f9c11ae135
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Tue Oct 24 22:46:26 2017 +0100

    Add `line_number_orig` to instances of `Mismatch` in tests.

commit d432a7061f74dbc159584f08470c64985a4b41d9
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Tue Oct 24 22:41:40 2017 +0100

    Add a `line_number_orig` field to `Mismatch` to track the pre-format line number.
    Use that for the write-mode=modified output.

commit bdb7d1d23f02f7b8f18e7073a65be88ff94cdbb3
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Tue Oct 24 22:35:50 2017 +0100

    First basic --write-mode=modified implementation.

commit ea1433dae0c32879a31182c11be08b1bf53fbf31
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Fri Oct 20 00:04:16 2017 +0100

    WIP on new "modified" mode.

commit 27ee9483cf937a11a0e115f54de0afcc3f9ceb44
Merge: e48dd81a 2a84352d
Author: Chris Emerson <github@mail.nosreme.org>
Date:   Tue Oct 24 21:56:44 2017 +0100

    Merge remote-tracking branch 'jc/diff_zero_context' into difflines_mode
This commit is contained in:
Chris Emerson 2018-01-20 20:23:25 +00:00
parent e0e3e22248
commit fc377f3fb0
8 changed files with 182 additions and 9 deletions

View File

@ -1766,7 +1766,7 @@ See also: [`match_block_trailing_comma`](#match_block_trailing_comma).
What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage
- **Default value**: `"Overwrite"`
- **Possible values**: `"Checkstyle"`, `"Coverage"`, `"Diff"`, `"Display"`, `"Overwrite"`, `"Plain"`, `"Replace"`
- **Possible values**: `"Checkstyle"`, `"Coverage"`, `"Diff"`, `"Display"`, `"Overwrite"`, `"Plain"`, `"Replace"`, `"Modified"`
- **Stable**: No
## `blank_lines_upper_bound`

View File

@ -128,6 +128,8 @@ pub fn to_list_tactic(self) -> ListTactic {
Plain,
// Outputs a checkstyle XML file.
Checkstyle,
// Output the changed lines
Modified,
}
configuration_option_enum! { Color:
@ -298,7 +300,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);
@ -677,7 +679,7 @@ pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
// Control options (changes the operation of rustfmt, rather than the formatting)
write_mode: WriteMode, WriteMode::Overwrite, false,
"What Write Mode to use when none is supplied: \
Replace, Overwrite, Display, Plain, Diff, Coverage";
Replace, Overwrite, Display, Plain, Diff, Coverage, Modified";
color: Color, Color::Auto, false,
"What Color option to use when none is supplied: Always, Never, Auto";
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,

View File

@ -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;
// A map of the files of a crate, with their new content
@ -167,6 +167,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)?;

View File

@ -29,7 +29,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;
@ -693,6 +693,67 @@ 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 affected line before formatting.
pub line_number: 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>,
}
/// 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<(Summary, FileMap, FormatReport, ModifiedLines), (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 = values[0];
let num_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: line_number,
lines_removed: num_removed,
lines: added_lines,
});
}
Ok((
summary,
filemap,
formatreport,
ModifiedLines { chunks: chunks },
))
}
#[derive(Debug)]
pub enum Input {
File(PathBuf),

View File

@ -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,
line_number_orig: 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
/// followd 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())],
},
]

View File

@ -143,6 +143,31 @@ 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 (_summary, _filemap, _report, modified) =
get_modified_lines(Input::File(filename.into()), &Config::default()).unwrap();
assert_eq!(
modified,
ModifiedLines {
chunks: vec![
ModifiedChunk {
line_number: 4,
lines_removed: 4,
lines: vec!["fn blah() {}".into()],
},
ModifiedChunk {
line_number: 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) {
@ -503,6 +528,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()),

View File

@ -0,0 +1,14 @@
// rustfmt-write_mode: modified
// Test "modified" output
fn
blah
()
{ }
#[cfg
( a , b
)]
fn
main() {}

View File

@ -0,0 +1,5 @@
4 4 1
fn blah() {}
10 5 2
#[cfg(a, b)]
fn main() {}