1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
16 use strings::string_buffer::StringBuffer;
17 use std::collections::HashMap;
18 use syntax::codemap::{CodeMap, Span, BytePos};
21 use std::io::{Write, stdout};
25 use utils::round_up_to_power_of_two;
27 // This is basically a wrapper around a bunch of Ropes which makes it convenient
28 // to work with libsyntax. It is badly named.
29 pub struct ChangeSet<'a> {
30 file_map: HashMap<String, StringBuffer>,
32 file_spans: Vec<(u32, u32)>,
35 impl<'a> ChangeSet<'a> {
36 // Create a new ChangeSet for a given libsyntax CodeMap.
37 pub fn from_codemap(codemap: &'a CodeMap) -> ChangeSet<'a> {
38 let mut result = ChangeSet {
39 file_map: HashMap::new(),
41 file_spans: Vec::with_capacity(codemap.files.borrow().len()),
44 for f in codemap.files.borrow().iter() {
45 // Use the length of the file as a heuristic for how much space we
46 // need. Round to the next power of two.
47 let buffer_cap = round_up_to_power_of_two(f.src.as_ref().unwrap().len());
49 result.file_map.insert(f.name.clone(), StringBuffer::with_capacity(buffer_cap));
50 result.file_spans.push((f.start_pos.0, f.end_pos.0));
53 result.file_spans.sort();
58 pub fn filespans_for_span(&self, start: BytePos, end: BytePos) -> Vec<(u32, u32)> {
59 assert!(start.0 <= end.0);
61 if self.file_spans.len() == 0 {
65 // idx is the index into file_spans which indicates the current file, we
66 // with the file start denotes.
67 let mut idx = match self.file_spans.binary_search(&(start.0, ::std::u32::MAX)) {
73 let mut result = Vec::new();
74 let mut start = start.0;
76 let cur_file = &self.file_spans[idx];
79 if idx >= self.file_spans.len() || start >= end.0 {
81 result.push((start, end.0));
86 let end = ::std::cmp::min(cur_file.1 - 1, end.0);
88 result.push((start, end));
90 start = self.file_spans[idx].0;
94 pub fn push_str(&mut self, filename: &str, text: &str) {
95 let buf = self.file_map.get_mut(&*filename).unwrap();
99 pub fn push_str_span(&mut self, span: Span, text: &str) {
100 let file_name = self.codemap.span_to_filename(span);
101 self.push_str(&file_name, text)
104 pub fn get_mut(&mut self, file_name: &str) -> &mut StringBuffer {
105 self.file_map.get_mut(file_name).unwrap()
108 pub fn cur_offset(&mut self, filename: &str) -> usize {
109 self.file_map[&*filename].cur_offset()
112 pub fn cur_offset_span(&mut self, span: Span) -> usize {
113 let filename = self.codemap.span_to_filename(span);
114 self.cur_offset(&filename)
117 // Return an iterator over the entire changed text.
118 pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> {
121 keys: self.file_map.keys().collect(),
126 // Append a newline to the end of each file.
127 pub fn append_newlines(&mut self) {
128 for (_, s) in self.file_map.iter_mut() {
133 pub fn write_all_files(&self,
136 -> Result<(HashMap<String, String>), ::std::io::Error> {
137 let mut result = HashMap::new();
138 for filename in self.file_map.keys() {
139 let one_result = try!(self.write_file(filename, mode, config));
140 if let Some(r) = one_result {
141 result.insert(filename.clone(), r);
148 pub fn write_file(&self,
152 -> Result<Option<String>, ::std::io::Error> {
153 let text = &self.file_map[filename];
155 // prints all newlines either as `\n` or as `\r\n`
156 fn write_system_newlines<T>(
160 -> Result<(), ::std::io::Error>
163 match config.newline_style {
164 NewlineStyle::Unix => write!(writer, "{}", text),
165 NewlineStyle::Windows => {
166 for (c, _) in text.chars() {
168 '\n' => try!(write!(writer, "\r\n")),
170 c => try!(write!(writer, "{}", c)),
179 WriteMode::Overwrite => {
180 // Do a little dance to make writing safer - write to a temp file
181 // rename the original to a .bk, then rename the temp file to the
183 let tmp_name = filename.to_owned() + ".tmp";
184 let bk_name = filename.to_owned() + ".bk";
186 // Write text to temp file
187 let tmp_file = try!(File::create(&tmp_name));
188 try!(write_system_newlines(tmp_file, text, config));
191 try!(::std::fs::rename(filename, bk_name));
192 try!(::std::fs::rename(tmp_name, filename));
194 WriteMode::NewFile(extn) => {
195 let filename = filename.to_owned() + "." + extn;
196 let file = try!(File::create(&filename));
197 try!(write_system_newlines(file, text, config));
199 WriteMode::Display => {
200 println!("{}:\n", filename);
201 let stdout = stdout();
202 let stdout_lock = stdout.lock();
203 try!(write_system_newlines(stdout_lock, text, config));
205 WriteMode::Return(_) => {
206 // io::Write is not implemented for String, working around with Vec<u8>
207 let mut v = Vec::new();
208 try!(write_system_newlines(&mut v, text, config));
209 // won't panic, we are writing correct utf8
210 return Ok(Some(String::from_utf8(v).unwrap()));
218 // Iterates over each file in the ChangSet. Yields the filename and the changed
219 // text for that file.
220 pub struct FileIterator<'c, 'a: 'c> {
221 change_set: &'c ChangeSet<'a>,
222 keys: Vec<&'c String>,
226 impl<'c, 'a> Iterator for FileIterator<'c, 'a> {
227 type Item = (&'c str, &'c StringBuffer);
229 fn next(&mut self) -> Option<(&'c str, &'c StringBuffer)> {
230 if self.cur_key >= self.keys.len() {
234 let key = self.keys[self.cur_key];
236 return Some((&key, &self.change_set.file_map[&*key]))
240 impl<'a> fmt::Display for ChangeSet<'a> {
241 // Prints the entire changed text.
242 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
243 for (f, r) in self.text() {
244 try!(write!(fmt, "{}:\n", f));
245 try!(write!(fmt, "{}\n\n", r));