use syntax::codemap::{CodeMap, Span, BytePos};
use std::fmt;
use std::fs::File;
-use std::io::Write;
+use std::io::{Write, stdout};
use WriteMode;
+use NewlineStyle;
+use config::Config;
+use utils::round_up_to_power_of_two;
// This is basically a wrapper around a bunch of Ropes which makes it convenient
// to work with libsyntax. It is badly named.
impl<'a> ChangeSet<'a> {
// Create a new ChangeSet for a given libsyntax CodeMap.
pub fn from_codemap(codemap: &'a CodeMap) -> ChangeSet<'a> {
- let mut result = ChangeSet {
- file_map: HashMap::new(),
- codemap: codemap,
- file_spans: Vec::with_capacity(codemap.files.borrow().len()),
- };
+ let mut result = ChangeSet { file_map: HashMap::new(),
+ codemap: codemap,
+ file_spans: Vec::with_capacity(codemap.files.borrow().len()), };
for f in codemap.files.borrow().iter() {
// Use the length of the file as a heuristic for how much space we
- // need. I hope that at some stage someone rounds this up to the next
- // power of two. TODO check that or do it here.
- result.file_map.insert(f.name.clone(),
- StringBuffer::with_capacity(f.src.as_ref().unwrap().len()));
+ // need. Round to the next power of two.
+ let buffer_cap = round_up_to_power_of_two(f.src.as_ref().unwrap().len());
+ result.file_map.insert(f.name.clone(), StringBuffer::with_capacity(buffer_cap));
result.file_spans.push((f.start_pos.0, f.end_pos.0));
}
self.push_str(&file_name, text)
}
+ // Fetch the output buffer for the given file name.
+ // Panics on unknown files.
+ pub fn get(&mut self, file_name: &str) -> &StringBuffer {
+ self.file_map.get(file_name).unwrap()
+ }
+
+ // Fetch a mutable reference to the output buffer for the given file name.
+ // Panics on unknown files.
pub fn get_mut(&mut self, file_name: &str) -> &mut StringBuffer {
self.file_map.get_mut(file_name).unwrap()
}
// Return an iterator over the entire changed text.
pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> {
- FileIterator {
- change_set: self,
- keys: self.file_map.keys().collect(),
- cur_key: 0,
- }
+ FileIterator { change_set: self, keys: self.file_map.keys().collect(), cur_key: 0 }
}
// Append a newline to the end of each file.
}
pub fn write_all_files(&self,
- mode: WriteMode)
+ mode: WriteMode,
+ config: &Config)
-> Result<(HashMap<String, String>), ::std::io::Error> {
let mut result = HashMap::new();
for filename in self.file_map.keys() {
- let one_result = try!(self.write_file(filename, mode));
+ let one_result = try!(self.write_file(filename, mode, config));
if let Some(r) = one_result {
result.insert(filename.clone(), r);
}
pub fn write_file(&self,
filename: &str,
- mode: WriteMode)
+ mode: WriteMode,
+ config: &Config)
-> Result<Option<String>, ::std::io::Error> {
let text = &self.file_map[filename];
+ // prints all newlines either as `\n` or as `\r\n`
+ fn write_system_newlines<T>(mut writer: T,
+ text: &StringBuffer,
+ config: &Config)
+ -> Result<(), ::std::io::Error>
+ where T: Write
+ {
+ match config.newline_style {
+ NewlineStyle::Unix => write!(writer, "{}", text),
+ NewlineStyle::Windows => {
+ for (c, _) in text.chars() {
+ match c {
+ '\n' => try!(write!(writer, "\r\n")),
+ '\r' => continue,
+ c => try!(write!(writer, "{}", c)),
+ }
+ }
+ Ok(())
+ },
+ }
+ }
+
match mode {
WriteMode::Overwrite => {
// Do a little dance to make writing safer - write to a temp file
let bk_name = filename.to_owned() + ".bk";
{
// Write text to temp file
- let mut tmp_file = try!(File::create(&tmp_name));
- try!(write!(tmp_file, "{}", text));
+ let tmp_file = try!(File::create(&tmp_name));
+ try!(write_system_newlines(tmp_file, text, config));
}
try!(::std::fs::rename(filename, bk_name));
}
WriteMode::NewFile(extn) => {
let filename = filename.to_owned() + "." + extn;
- let mut file = try!(File::create(&filename));
- try!(write!(file, "{}", text));
+ let file = try!(File::create(&filename));
+ try!(write_system_newlines(file, text, config));
}
WriteMode::Display => {
println!("{}:\n", filename);
- println!("{}", text);
+ let stdout = stdout();
+ let stdout_lock = stdout.lock();
+ try!(write_system_newlines(stdout_lock, text, config));
}
WriteMode::Return(_) => {
- return Ok(Some(text.to_string()));
+ // io::Write is not implemented for String, working around with Vec<u8>
+ let mut v = Vec::new();
+ try!(write_system_newlines(&mut v, text, config));
+ // won't panic, we are writing correct utf8
+ return Ok(Some(String::from_utf8(v).unwrap()));
}
}
Ok(None)
}
+
+ pub fn is_changed(&self, filename: &str) -> bool {
+ self.file_map.get(filename).expect("Unknown filename").len != 0
+ }
}
// Iterates over each file in the ChangSet. Yields the filename and the changed