Skip to content

Commit 06fc39f

Browse files
authored
Merge pull request #3424 from Xanewok/modified-lines-for-stdin
Support EmitMode::ModifiedLines with stdin input
2 parents a4da1e9 + fbfda61 commit 06fc39f

File tree

5 files changed

+227
-89
lines changed

5 files changed

+227
-89
lines changed

src/formatting.rs

+11-24
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,20 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
181181
self.report
182182
.add_non_formatted_ranges(visitor.skipped_range.clone());
183183

184-
self.handler
185-
.handle_formatted_file(path, visitor.buffer.to_owned(), &mut self.report)
184+
self.handler.handle_formatted_file(
185+
self.parse_session.source_map(),
186+
path,
187+
visitor.buffer.to_owned(),
188+
&mut self.report,
189+
)
186190
}
187191
}
188192

189193
// Handle the results of formatting.
190194
trait FormatHandler {
191195
fn handle_formatted_file(
192196
&mut self,
197+
source_map: &SourceMap,
193198
path: FileName,
194199
result: String,
195200
report: &mut FormatReport,
@@ -200,13 +205,14 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> {
200205
// Called for each formatted file.
201206
fn handle_formatted_file(
202207
&mut self,
208+
source_map: &SourceMap,
203209
path: FileName,
204210
result: String,
205211
report: &mut FormatReport,
206212
) -> Result<(), ErrorKind> {
207213
if let Some(ref mut out) = self.out {
208-
match source_file::write_file(&result, &path, out, &self.config) {
209-
Ok(b) if b => report.add_diff(),
214+
match source_file::write_file(Some(source_map), &path, &result, out, &self.config) {
215+
Ok(has_diff) if has_diff => report.add_diff(),
210216
Err(e) => {
211217
// Create a new error with path_str to help users see which files failed
212218
let err_msg = format!("{}: {}", path, e);
@@ -299,7 +305,7 @@ impl FormattingError {
299305

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

302-
#[derive(Default, Debug)]
308+
#[derive(Default, Debug, PartialEq)]
303309
pub(crate) struct ReportedErrors {
304310
// Encountered e.g., an IO error.
305311
pub(crate) has_operational_errors: bool,
@@ -332,25 +338,6 @@ impl ReportedErrors {
332338
}
333339
}
334340

335-
/// A single span of changed lines, with 0 or more removed lines
336-
/// and a vector of 0 or more inserted lines.
337-
#[derive(Debug, PartialEq, Eq)]
338-
pub(crate) struct ModifiedChunk {
339-
/// The first to be removed from the original text
340-
pub line_number_orig: u32,
341-
/// The number of lines which have been replaced
342-
pub lines_removed: u32,
343-
/// The new lines
344-
pub lines: Vec<String>,
345-
}
346-
347-
/// Set of changed sections of a file.
348-
#[derive(Debug, PartialEq, Eq)]
349-
pub(crate) struct ModifiedLines {
350-
/// The set of changed chunks.
351-
pub chunks: Vec<ModifiedChunk>,
352-
}
353-
354341
#[derive(Clone, Copy, Debug)]
355342
enum Timer {
356343
Disabled,

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ pub use crate::config::{
3333
Range, Verbosity,
3434
};
3535

36+
pub use crate::rustfmt_diff::{ModifiedChunk, ModifiedLines};
37+
3638
#[macro_use]
3739
mod utils;
3840

src/rustfmt_diff.rs

+143-39
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::VecDeque;
2+
use std::fmt;
23
use std::io;
34
use std::io::Write;
45

@@ -33,6 +34,116 @@ impl Mismatch {
3334
}
3435
}
3536

37+
/// A single span of changed lines, with 0 or more removed lines
38+
/// and a vector of 0 or more inserted lines.
39+
#[derive(Debug, PartialEq, Eq)]
40+
pub struct ModifiedChunk {
41+
/// The first to be removed from the original text
42+
pub line_number_orig: u32,
43+
/// The number of lines which have been replaced
44+
pub lines_removed: u32,
45+
/// The new lines
46+
pub lines: Vec<String>,
47+
}
48+
49+
/// Set of changed sections of a file.
50+
#[derive(Debug, PartialEq, Eq)]
51+
pub struct ModifiedLines {
52+
/// The set of changed chunks.
53+
pub chunks: Vec<ModifiedChunk>,
54+
}
55+
56+
impl From<Vec<Mismatch>> for ModifiedLines {
57+
fn from(mismatches: Vec<Mismatch>) -> ModifiedLines {
58+
let chunks = mismatches.into_iter().map(|mismatch| {
59+
let lines = mismatch.lines.iter();
60+
let num_removed = lines
61+
.filter(|line| match line {
62+
DiffLine::Resulting(_) => true,
63+
_ => false,
64+
})
65+
.count();
66+
67+
let new_lines = mismatch.lines.into_iter().filter_map(|line| match line {
68+
DiffLine::Context(_) | DiffLine::Resulting(_) => None,
69+
DiffLine::Expected(str) => Some(str),
70+
});
71+
72+
ModifiedChunk {
73+
line_number_orig: mismatch.line_number_orig,
74+
lines_removed: num_removed as u32,
75+
lines: new_lines.collect(),
76+
}
77+
});
78+
79+
ModifiedLines {
80+
chunks: chunks.collect(),
81+
}
82+
}
83+
}
84+
85+
// Converts a `Mismatch` into a serialized form, which just includes
86+
// enough information to modify the original file.
87+
// Each section starts with a line with three integers, space separated:
88+
// lineno num_removed num_added
89+
// followed by (`num_added`) lines of added text. The line numbers are
90+
// relative to the original file.
91+
impl fmt::Display for ModifiedLines {
92+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93+
for chunk in &self.chunks {
94+
writeln!(
95+
f,
96+
"{} {} {}",
97+
chunk.line_number_orig,
98+
chunk.lines_removed,
99+
chunk.lines.iter().count()
100+
)?;
101+
102+
for line in &chunk.lines {
103+
writeln!(f, "{}", line)?;
104+
}
105+
}
106+
107+
Ok(())
108+
}
109+
}
110+
111+
// Allows to convert `Display`ed `ModifiedLines` back to the structural data.
112+
impl std::str::FromStr for ModifiedLines {
113+
type Err = ();
114+
115+
fn from_str(s: &str) -> Result<ModifiedLines, ()> {
116+
let mut chunks = vec![];
117+
118+
let mut lines = s.lines();
119+
while let Some(header) = lines.next() {
120+
let mut header = header.split_whitespace();
121+
let (orig, rem, new_lines) = match (header.next(), header.next(), header.next()) {
122+
(Some(orig), Some(removed), Some(added)) => (orig, removed, added),
123+
_ => return Err(()),
124+
};
125+
let (orig, rem, new_lines): (u32, u32, usize) =
126+
match (orig.parse(), rem.parse(), new_lines.parse()) {
127+
(Ok(a), Ok(b), Ok(c)) => (a, b, c),
128+
_ => return Err(()),
129+
};
130+
let lines = lines.by_ref().take(new_lines);
131+
let lines: Vec<_> = lines.map(ToOwned::to_owned).collect();
132+
if lines.len() != new_lines {
133+
return Err(());
134+
}
135+
136+
chunks.push(ModifiedChunk {
137+
line_number_orig: orig,
138+
lines_removed: rem,
139+
lines,
140+
});
141+
}
142+
143+
Ok(ModifiedLines { chunks })
144+
}
145+
}
146+
36147
// This struct handles writing output to stdout and abstracts away the logic
37148
// of printing in color, if it's possible in the executing environment.
38149
pub struct OutputWriter {
@@ -174,49 +285,11 @@ where
174285
}
175286
}
176287

177-
/// Converts a `Mismatch` into a serialized form, which just includes
178-
/// enough information to modify the original file.
179-
/// Each section starts with a line with three integers, space separated:
180-
/// lineno num_removed num_added
181-
/// followed by (`num_added`) lines of added text. The line numbers are
182-
/// relative to the original file.
183-
pub fn output_modified<W>(mut out: W, diff: Vec<Mismatch>)
184-
where
185-
W: Write,
186-
{
187-
for mismatch in diff {
188-
let (num_removed, num_added) =
189-
mismatch
190-
.lines
191-
.iter()
192-
.fold((0, 0), |(rem, add), line| match *line {
193-
DiffLine::Context(_) => panic!("No Context expected"),
194-
DiffLine::Expected(_) => (rem, add + 1),
195-
DiffLine::Resulting(_) => (rem + 1, add),
196-
});
197-
// Write a header with enough information to separate the modified lines.
198-
writeln!(
199-
out,
200-
"{} {} {}",
201-
mismatch.line_number_orig, num_removed, num_added
202-
)
203-
.unwrap();
204-
205-
for line in mismatch.lines {
206-
match line {
207-
DiffLine::Context(_) | DiffLine::Resulting(_) => (),
208-
DiffLine::Expected(ref str) => {
209-
writeln!(out, "{}", str).unwrap();
210-
}
211-
}
212-
}
213-
}
214-
}
215-
216288
#[cfg(test)]
217289
mod test {
218290
use super::DiffLine::*;
219291
use super::{make_diff, Mismatch};
292+
use super::{ModifiedChunk, ModifiedLines};
220293

221294
#[test]
222295
fn diff_simple() {
@@ -298,4 +371,35 @@ mod test {
298371
}]
299372
);
300373
}
374+
375+
#[test]
376+
fn modified_lines_from_str() {
377+
use std::str::FromStr;
378+
379+
let src = "1 6 2\nfn some() {}\nfn main() {}\n25 3 1\n struct Test {}";
380+
let lines = ModifiedLines::from_str(src).unwrap();
381+
assert_eq!(
382+
lines,
383+
ModifiedLines {
384+
chunks: vec![
385+
ModifiedChunk {
386+
line_number_orig: 1,
387+
lines_removed: 6,
388+
lines: vec!["fn some() {}".to_owned(), "fn main() {}".to_owned(),]
389+
},
390+
ModifiedChunk {
391+
line_number_orig: 25,
392+
lines_removed: 3,
393+
lines: vec![" struct Test {}".to_owned()]
394+
}
395+
]
396+
}
397+
);
398+
399+
let src = "1 5 3";
400+
assert_eq!(ModifiedLines::from_str(src), Err(()));
401+
402+
let src = "1 5 3\na\nb";
403+
assert_eq!(ModifiedLines::from_str(src), Err(()));
404+
}
301405
}

0 commit comments

Comments
 (0)