Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support EmitMode::ModifiedLines with stdin input #3424

Merged
merged 5 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 11 additions & 24 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,20 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
self.report
.add_non_formatted_ranges(visitor.skipped_range.clone());

self.handler
.handle_formatted_file(path, visitor.buffer.to_owned(), &mut self.report)
self.handler.handle_formatted_file(
self.parse_session.source_map(),
path,
visitor.buffer.to_owned(),
&mut self.report,
)
}
}

// Handle the results of formatting.
trait FormatHandler {
fn handle_formatted_file(
&mut self,
source_map: &SourceMap,
path: FileName,
result: String,
report: &mut FormatReport,
Expand All @@ -200,13 +205,14 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> {
// Called for each formatted file.
fn handle_formatted_file(
&mut self,
source_map: &SourceMap,
path: FileName,
result: String,
report: &mut FormatReport,
) -> Result<(), ErrorKind> {
if let Some(ref mut out) = self.out {
match source_file::write_file(&result, &path, out, &self.config) {
Ok(b) if b => report.add_diff(),
match source_file::write_file(Some(source_map), &path, &result, out, &self.config) {
Ok(has_diff) if has_diff => report.add_diff(),
Err(e) => {
// Create a new error with path_str to help users see which files failed
let err_msg = format!("{}: {}", path, e);
Expand Down Expand Up @@ -299,7 +305,7 @@ impl FormattingError {

pub(crate) type FormatErrorMap = HashMap<FileName, Vec<FormattingError>>;

#[derive(Default, Debug)]
#[derive(Default, Debug, PartialEq)]
pub(crate) struct ReportedErrors {
// Encountered e.g., an IO error.
pub(crate) has_operational_errors: bool,
Expand Down Expand Up @@ -332,25 +338,6 @@ impl ReportedErrors {
}
}

/// 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(crate) 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(crate) struct ModifiedLines {
/// The set of changed chunks.
pub chunks: Vec<ModifiedChunk>,
}

#[derive(Clone, Copy, Debug)]
enum Timer {
Disabled,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub use crate::config::{
Range, Verbosity,
};

pub use crate::rustfmt_diff::{ModifiedChunk, ModifiedLines};

#[macro_use]
mod utils;

Expand Down
182 changes: 143 additions & 39 deletions src/rustfmt_diff.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::VecDeque;
use std::fmt;
use std::io;
use std::io::Write;

Expand Down Expand Up @@ -33,6 +34,116 @@ impl Mismatch {
}
}

/// 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>,
}

impl From<Vec<Mismatch>> for ModifiedLines {
fn from(mismatches: Vec<Mismatch>) -> ModifiedLines {
let chunks = mismatches.into_iter().map(|mismatch| {
let lines = mismatch.lines.iter();
let num_removed = lines
.filter(|line| match line {
DiffLine::Resulting(_) => true,
_ => false,
})
.count();

let new_lines = mismatch.lines.into_iter().filter_map(|line| match line {
DiffLine::Context(_) | DiffLine::Resulting(_) => None,
DiffLine::Expected(str) => Some(str),
});

ModifiedChunk {
line_number_orig: mismatch.line_number_orig,
lines_removed: num_removed as u32,
lines: new_lines.collect(),
}
});

ModifiedLines {
chunks: chunks.collect(),
}
}
}

// Converts a `Mismatch` into a serialized 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.
impl fmt::Display for ModifiedLines {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for chunk in &self.chunks {
writeln!(
f,
"{} {} {}",
chunk.line_number_orig,
chunk.lines_removed,
chunk.lines.iter().count()
)?;

for line in &chunk.lines {
writeln!(f, "{}", line)?;
}
}

Ok(())
}
}

// Allows to convert `Display`ed `ModifiedLines` back to the structural data.
impl std::str::FromStr for ModifiedLines {
type Err = ();

fn from_str(s: &str) -> Result<ModifiedLines, ()> {
let mut chunks = vec![];

let mut lines = s.lines();
while let Some(header) = lines.next() {
let mut header = header.split_whitespace();
let (orig, rem, new_lines) = match (header.next(), header.next(), header.next()) {
(Some(orig), Some(removed), Some(added)) => (orig, removed, added),
_ => return Err(()),
};
let (orig, rem, new_lines): (u32, u32, usize) =
match (orig.parse(), rem.parse(), new_lines.parse()) {
(Ok(a), Ok(b), Ok(c)) => (a, b, c),
_ => return Err(()),
};
let lines = lines.by_ref().take(new_lines);
let lines: Vec<_> = lines.map(ToOwned::to_owned).collect();
if lines.len() != new_lines {
return Err(());
}

chunks.push(ModifiedChunk {
line_number_orig: orig,
lines_removed: rem,
lines,
});
}

Ok(ModifiedLines { chunks })
}
}

// This struct handles writing output to stdout and abstracts away the logic
// of printing in color, if it's possible in the executing environment.
pub struct OutputWriter {
Expand Down Expand Up @@ -174,49 +285,11 @@ where
}
}

/// Converts a `Mismatch` into a serialized 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::DiffLine::*;
use super::{make_diff, Mismatch};
use super::{ModifiedChunk, ModifiedLines};

#[test]
fn diff_simple() {
Expand Down Expand Up @@ -298,4 +371,35 @@ mod test {
}]
);
}

#[test]
fn modified_lines_from_str() {
use std::str::FromStr;

let src = "1 6 2\nfn some() {}\nfn main() {}\n25 3 1\n struct Test {}";
let lines = ModifiedLines::from_str(src).unwrap();
assert_eq!(
lines,
ModifiedLines {
chunks: vec![
ModifiedChunk {
line_number_orig: 1,
lines_removed: 6,
lines: vec!["fn some() {}".to_owned(), "fn main() {}".to_owned(),]
},
ModifiedChunk {
line_number_orig: 25,
lines_removed: 3,
lines: vec![" struct Test {}".to_owned()]
}
]
}
);

let src = "1 5 3";
assert_eq!(ModifiedLines::from_str(src), Err(()));

let src = "1 5 3\na\nb";
assert_eq!(ModifiedLines::from_str(src), Err(()));
}
}
Loading