]> git.lizzy.rs Git - rust.git/blob - src/changes.rs
Fix formatting in changes.rs
[rust.git] / src / changes.rs
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.
4 //
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.
10
11
12 // TODO
13 // print to files
14 // tests
15
16 use strings::string_buffer::StringBuffer;
17 use std::collections::HashMap;
18 use syntax::codemap::{CodeMap, Span, BytePos};
19 use std::fmt;
20 use std::fs::File;
21 use std::io::Write;
22 use WriteMode;
23
24 // This is basically a wrapper around a bunch of Ropes which makes it convenient
25 // to work with libsyntax. It is badly named.
26 pub struct ChangeSet<'a> {
27     file_map: HashMap<String, StringBuffer>,
28     codemap: &'a CodeMap,
29     file_spans: Vec<(u32, u32)>,
30 }
31
32 impl<'a> ChangeSet<'a> {
33     // Create a new ChangeSet for a given libsyntax CodeMap.
34     pub fn from_codemap(codemap: &'a CodeMap) -> ChangeSet<'a> {
35         let mut result = ChangeSet {
36             file_map: HashMap::new(),
37             codemap: codemap,
38             file_spans: Vec::with_capacity(codemap.files.borrow().len()),
39         };
40
41         for f in codemap.files.borrow().iter() {
42             // Use the length of the file as a heuristic for how much space we
43             // need. I hope that at some stage someone rounds this up to the next
44             // power of two. TODO check that or do it here.
45             result.file_map.insert(f.name.clone(),
46                                    StringBuffer::with_capacity(f.src.as_ref().unwrap().len()));
47
48             result.file_spans.push((f.start_pos.0, f.end_pos.0));
49         }
50
51         result.file_spans.sort();
52
53         result
54     }
55
56     pub fn filespans_for_span(&self, start: BytePos, end: BytePos) -> Vec<(u32, u32)> {
57         assert!(start.0 <= end.0);
58
59         if self.file_spans.len() == 0 {
60             return Vec::new();
61         }
62
63         let mut idx = match self.file_spans.binary_search(&(start.0, ::std::u32::MAX)) {
64             Ok(i) => i,
65             Err(0) => 0,
66             Err(i) => i - 1,
67         };
68
69         let mut result = Vec::new();
70         let mut start = start.0;
71         loop {
72             let cur_file = &self.file_spans[idx];
73             idx += 1;
74
75             if idx >= self.file_spans.len() || start >= end.0 {
76                 if start < end.0 {
77                     result.push((start, end.0));
78                 }
79                 return result;
80             }
81
82             let end = ::std::cmp::min(cur_file.1 - 1, end.0);
83             if start < end {
84                 result.push((start, end));
85             }
86             start = self.file_spans[idx].0;
87         }
88     }
89
90     pub fn push_str(&mut self, filename: &str, text: &str) {
91         let buf = self.file_map.get_mut(&*filename).unwrap();
92         buf.push_str(text)
93     }
94
95     pub fn push_str_span(&mut self, span: Span, text: &str) {
96         let file_name = self.codemap.span_to_filename(span);
97         self.push_str(&file_name, text)
98     }
99
100     pub fn cur_offset(&mut self, filename: &str) -> usize {
101         self.file_map[&*filename].cur_offset()
102     }
103
104     pub fn cur_offset_span(&mut self, span: Span) -> usize {
105         let filename = self.codemap.span_to_filename(span);
106         self.cur_offset(&filename)
107     }
108
109     // Return an iterator over the entire changed text.
110     pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> {
111         FileIterator {
112             change_set: self,
113             keys: self.file_map.keys().collect(),
114             cur_key: 0,
115         }
116     }
117
118     pub fn write_all_files(&self,
119                            mode: WriteMode)
120                            -> Result<(HashMap<String, String>), ::std::io::Error> {
121         let mut result = HashMap::new();
122         for filename in self.file_map.keys() {
123             let one_result = try!(self.write_file(filename, mode));
124             if let Some(r) = one_result {
125                 result.insert(filename.clone(), r);
126             }
127         }
128
129         Ok(result)
130     }
131
132     pub fn write_file(&self,
133                       filename: &str,
134                       mode: WriteMode)
135                       -> Result<Option<String>, ::std::io::Error> {
136         let text = &self.file_map[filename];
137
138         match mode {
139             WriteMode::Overwrite => {
140                 // Do a little dance to make writing safer - write to a temp file
141                 // rename the original to a .bk, then rename the temp file to the
142                 // original.
143                 let tmp_name = filename.to_string() + ".tmp";
144                 let bk_name = filename.to_string() + ".bk";
145                 {
146                     // Write text to temp file
147                     let mut tmp_file = try!(File::create(&tmp_name));
148                     try!(write!(tmp_file, "{}", text));
149                 }
150
151                 try!(::std::fs::rename(filename, bk_name));
152                 try!(::std::fs::rename(tmp_name, filename));
153             }
154             WriteMode::NewFile(extn) => {
155                 let filename = filename.to_string() + "." + extn;
156                 let mut file = try!(File::create(&filename));
157                 try!(write!(file, "{}", text));
158             }
159             WriteMode::Display => {
160                 println!("{}:\n", filename);
161                 println!("{}", text);
162             }
163             WriteMode::Return(_) => {
164                 return Ok(Some(text.to_string()));
165             }
166         }
167
168         Ok(None)
169     }
170 }
171
172 // Iterates over each file in the ChangSet. Yields the filename and the changed
173 // text for that file.
174 pub struct FileIterator<'c, 'a: 'c> {
175     change_set: &'c ChangeSet<'a>,
176     keys: Vec<&'c String>,
177     cur_key: usize,
178 }
179
180 impl<'c, 'a> Iterator for FileIterator<'c, 'a> {
181     type Item = (&'c str, &'c StringBuffer);
182
183     fn next(&mut self) -> Option<(&'c str, &'c StringBuffer)> {
184         if self.cur_key >= self.keys.len() {
185             return None;
186         }
187
188         let key = self.keys[self.cur_key];
189         self.cur_key += 1;
190         return Some((&key, &self.change_set.file_map[&*key]))
191     }
192 }
193
194 impl<'a> fmt::Display for ChangeSet<'a> {
195     // Prints the entire changed text.
196     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
197         for (f, r) in self.text() {
198             try!(write!(fmt, "{}:\n", f));
199             try!(write!(fmt, "{}\n\n", r));
200         }
201         Ok(())
202     }
203 }