Skip to content

Commit 5794d43

Browse files
committed
Allow for stdin input in EmitMode::ModifiedLines
1 parent 693d2d9 commit 5794d43

File tree

4 files changed

+80
-27
lines changed

4 files changed

+80
-27
lines changed

src/formatting.rs

+11-5
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,

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::make_diff;
37+
3638
#[macro_use]
3739
mod utils;
3840

src/source_file.rs

+42-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use std::fs;
22
use std::io::{self, Write};
3+
use std::path::Path;
4+
5+
use syntax::source_map::SourceMap;
36

47
use crate::checkstyle::output_checkstyle_file;
58
use crate::config::{Config, EmitMode, FileName, Verbosity};
@@ -26,7 +29,7 @@ where
2629
write!(out, "{}", crate::checkstyle::header())?;
2730
}
2831
for &(ref filename, ref text) in source_file {
29-
write_file(text, filename, out, config)?;
32+
write_file(None, filename, text, out, config)?;
3033
}
3134
if config.emit_mode() == EmitMode::Checkstyle {
3235
write!(out, "{}", crate::checkstyle::footer())?;
@@ -36,24 +39,46 @@ where
3639
}
3740

3841
pub fn write_file<T>(
39-
formatted_text: &str,
42+
source_map: Option<&SourceMap>,
4043
filename: &FileName,
44+
formatted_text: &str,
4145
out: &mut T,
4246
config: &Config,
4347
) -> Result<bool, io::Error>
4448
where
4549
T: Write,
4650
{
47-
let filename_to_path = || match *filename {
48-
FileName::Real(ref path) => path,
49-
_ => panic!("cannot format `{}` and emit to files", filename),
51+
fn ensure_real_path(filename: &FileName) -> &Path {
52+
match *filename {
53+
FileName::Real(ref path) => path,
54+
_ => panic!("cannot format `{}` and emit to files", filename),
55+
}
56+
}
57+
58+
impl From<&FileName> for syntax_pos::FileName {
59+
fn from(filename: &FileName) -> syntax_pos::FileName {
60+
match filename {
61+
FileName::Real(path) => syntax_pos::FileName::Real(path.to_owned()),
62+
FileName::Stdin => syntax_pos::FileName::Custom("stdin".to_owned()),
63+
}
64+
}
65+
}
66+
67+
// If parse session is around (cfg(not(test))) then try getting source from
68+
// there instead of hitting the file system. This also supports getting
69+
// original text for `FileName::Stdin`.
70+
let original_text = source_map
71+
.and_then(|x| x.get_source_file(&filename.into()))
72+
.and_then(|x| x.src.as_ref().map(|x| x.to_string()));
73+
let original_text = match original_text {
74+
Some(ori) => ori,
75+
None => fs::read_to_string(ensure_real_path(filename))?,
5076
};
5177

5278
match config.emit_mode() {
5379
EmitMode::Files if config.make_backup() => {
54-
let filename = filename_to_path();
55-
let ori = fs::read_to_string(filename)?;
56-
if ori != formatted_text {
80+
let filename = ensure_real_path(filename);
81+
if original_text != formatted_text {
5782
// Do a little dance to make writing safer - write to a temp file
5883
// rename the original to a .bk, then rename the temp file to the
5984
// original.
@@ -67,9 +92,9 @@ where
6792
}
6893
EmitMode::Files => {
6994
// Write text directly over original file if there is a diff.
70-
let filename = filename_to_path();
71-
let ori = fs::read_to_string(filename)?;
72-
if ori != formatted_text {
95+
let filename = ensure_real_path(filename);
96+
97+
if original_text != formatted_text {
7398
fs::write(filename, formatted_text)?;
7499
}
75100
}
@@ -80,27 +105,23 @@ where
80105
write!(out, "{}", formatted_text)?;
81106
}
82107
EmitMode::ModifiedLines => {
83-
let filename = filename_to_path();
84-
let ori = fs::read_to_string(filename)?;
85-
let mismatch = make_diff(&ori, formatted_text, 0);
108+
let mismatch = make_diff(&original_text, formatted_text, 0);
86109
let has_diff = !mismatch.is_empty();
87110
output_modified(out, mismatch);
88111
return Ok(has_diff);
89112
}
90113
EmitMode::Checkstyle => {
91-
let filename = filename_to_path();
92-
let ori = fs::read_to_string(filename)?;
93-
let diff = make_diff(&ori, formatted_text, 3);
114+
let filename = ensure_real_path(filename);
115+
116+
let diff = make_diff(&original_text, formatted_text, 3);
94117
output_checkstyle_file(out, filename, diff)?;
95118
}
96119
EmitMode::Diff => {
97-
let filename = filename_to_path();
98-
let ori = fs::read_to_string(filename)?;
99-
let mismatch = make_diff(&ori, formatted_text, 3);
120+
let mismatch = make_diff(&original_text, formatted_text, 3);
100121
let has_diff = !mismatch.is_empty();
101122
print_diff(
102123
mismatch,
103-
|line_num| format!("Diff in {} at line {}:", filename.display(), line_num),
124+
|line_num| format!("Diff in {} at line {}:", filename, line_num),
104125
config,
105126
);
106127
return Ok(has_diff);

src/test/mod.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::process::{Command, Stdio};
99
use std::str::Chars;
1010

1111
use crate::config::{Color, Config, EmitMode, FileName, ReportTactic};
12-
use crate::formatting::{ModifiedChunk, SourceFile};
12+
use crate::formatting::{ModifiedChunk, ReportedErrors, SourceFile};
1313
use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, OutputWriter};
1414
use crate::source_file;
1515
use crate::{FormatReport, Input, Session};
@@ -290,6 +290,30 @@ fn stdin_parser_panic_caught() {
290290
}
291291
}
292292

293+
/// Ensures that `EmitMode::ModifiedLines` works with input from `stdin`. Useful
294+
/// when embedding Rustfmt (e.g. inside RLS).
295+
#[test]
296+
fn stdin_works_with_modified_lines() {
297+
let input = "\nfn\n some( )\n{\n}\nfn main () {}\n";
298+
let output = "1 6 2\nfn some() {}\nfn main() {}\n";
299+
300+
let input = Input::Text(input.to_owned());
301+
let mut config = Config::default();
302+
config.set().emit_mode(EmitMode::ModifiedLines);
303+
let mut buf: Vec<u8> = vec![];
304+
{
305+
let mut session = Session::new(config, Some(&mut buf));
306+
session.format(input).unwrap();
307+
let errors = ReportedErrors {
308+
has_diff: true,
309+
..Default::default()
310+
};
311+
assert_eq!(session.errors, errors);
312+
}
313+
let newline = if cfg!(windows) { "\r\n" } else { "\n" };
314+
assert_eq!(buf, output.replace('\n', newline).as_bytes());
315+
}
316+
293317
#[test]
294318
fn stdin_disable_all_formatting_test() {
295319
match option_env!("CFG_RELEASE_CHANNEL") {

0 commit comments

Comments
 (0)