]> git.lizzy.rs Git - rust.git/blobdiff - src/changes.rs
Merge pull request #128 from marcusklaas/subexpr
[rust.git] / src / changes.rs
index 562aada3103fc306d697f80da9f1386336be3295..4cce45e5eb3af31a4a0f4a2198f108244fc200da 100644 (file)
 
 
 // TODO
-// print to files 
+// print to files
 // tests
 
-use string_buffer::StringBuffer;
+use strings::string_buffer::StringBuffer;
 use std::collections::HashMap;
-use syntax::codemap::{CodeMap, Span};
+use syntax::codemap::{CodeMap, Span, BytePos};
 use std::fmt;
+use std::fs::File;
+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.
 pub struct ChangeSet<'a> {
     file_map: HashMap<String, StringBuffer>,
     codemap: &'a CodeMap,
+    file_spans: Vec<(u32, u32)>,
 }
 
 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,
-        };
+        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));
         }
 
+        result.file_spans.sort();
+
         result
     }
 
-    pub fn push_str(&mut self, file_name: &str, text: &str) {
-        let buf = self.file_map.get_mut(&*file_name).unwrap();
+    pub fn filespans_for_span(&self, start: BytePos, end: BytePos) -> Vec<(u32, u32)> {
+        assert!(start.0 <= end.0);
+
+        if self.file_spans.len() == 0 {
+            return Vec::new();
+        }
+
+        // idx is the index into file_spans which indicates the current file, we
+        // with the file start denotes.
+        let mut idx = match self.file_spans.binary_search(&(start.0, ::std::u32::MAX)) {
+            Ok(i) => i,
+            Err(0) => 0,
+            Err(i) => i - 1,
+        };
+
+        let mut result = Vec::new();
+        let mut start = start.0;
+        loop {
+            let cur_file = &self.file_spans[idx];
+            idx += 1;
+
+            if idx >= self.file_spans.len() || start >= end.0 {
+                if start < end.0 {
+                    result.push((start, end.0));
+                }
+                return result;
+            }
+
+            let end = ::std::cmp::min(cur_file.1 - 1, end.0);
+            if start < end {
+                result.push((start, end));
+            }
+            start = self.file_spans[idx].0;
+        }
+    }
+
+    pub fn push_str(&mut self, filename: &str, text: &str) {
+        let buf = self.file_map.get_mut(&*filename).unwrap();
         buf.push_str(text)
     }
 
@@ -54,24 +99,125 @@ pub fn push_str_span(&mut self, span: Span, text: &str) {
         self.push_str(&file_name, text)
     }
 
-    pub fn cur_offset(&mut self, file_name: &str) -> usize {
-        self.file_map[&*file_name].cur_offset()
+    // 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()
+    }
+
+    pub fn cur_offset(&mut self, filename: &str) -> usize {
+        self.file_map[&*filename].cur_offset()
     }
 
     pub fn cur_offset_span(&mut self, span: Span) -> usize {
-        let file_name = self.codemap.span_to_filename(span);
-        self.cur_offset(&file_name)
+        let filename = self.codemap.span_to_filename(span);
+        self.cur_offset(&filename)
     }
 
     // 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 append_newlines(&mut self) {
+        for (_, s) in self.file_map.iter_mut() {
+            s.push_str("\n");
+        }
+    }
+
+    pub fn write_all_files(&self,
+                           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, config));
+            if let Some(r) = one_result {
+                result.insert(filename.clone(), r);
+            }
         }
+
+        Ok(result)
     }
 
+    pub fn write_file(&self,
+                      filename: &str,
+                      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
+                // rename the original to a .bk, then rename the temp file to the
+                // original.
+                let tmp_name = filename.to_owned() + ".tmp";
+                let bk_name = filename.to_owned() + ".bk";
+                {
+                    // Write text to temp file
+                    let tmp_file = try!(File::create(&tmp_name));
+                    try!(write_system_newlines(tmp_file, text, config));
+                }
+
+                try!(::std::fs::rename(filename, bk_name));
+                try!(::std::fs::rename(tmp_name, filename));
+            }
+            WriteMode::NewFile(extn) => {
+                let filename = filename.to_owned() + "." + extn;
+                let file = try!(File::create(&filename));
+                try!(write_system_newlines(file, text, config));
+            }
+            WriteMode::Display => {
+                println!("{}:\n", filename);
+                let stdout = stdout();
+                let stdout_lock = stdout.lock();
+                try!(write_system_newlines(stdout_lock, text, config));
+            }
+            WriteMode::Return(_) => {
+                // 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
@@ -104,5 +250,5 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
             try!(write!(fmt, "{}\n\n", r));
         }
         Ok(())
-    }    
+    }
 }