]> git.lizzy.rs Git - rust.git/blob - src/changes.rs
Work across multiple files
[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
21 // This is basically a wrapper around a bunch of Ropes which makes it convenient
22 // to work with libsyntax. It is badly named.
23 pub struct ChangeSet<'a> {
24     file_map: HashMap<String, StringBuffer>,
25     codemap: &'a CodeMap,
26     file_spans: Vec<(u32, u32)>,
27 }
28
29 impl<'a> ChangeSet<'a> {
30     // Create a new ChangeSet for a given libsyntax CodeMap.
31     pub fn from_codemap(codemap: &'a CodeMap) -> ChangeSet<'a> {
32         let mut result = ChangeSet {
33             file_map: HashMap::new(),
34             codemap: codemap,
35             file_spans: Vec::with_capacity(codemap.files.borrow().len()),
36         };
37
38         for f in codemap.files.borrow().iter() {
39             // Use the length of the file as a heuristic for how much space we
40             // need. I hope that at some stage someone rounds this up to the next
41             // power of two. TODO check that or do it here.
42             result.file_map.insert(f.name.clone(),
43                                    StringBuffer::with_capacity(f.src.as_ref().unwrap().len()));
44
45             result.file_spans.push((f.start_pos.0, f.end_pos.0));
46         }
47
48         result.file_spans.sort();
49
50         result
51     }
52
53     pub fn filespans_for_span(&self, start: BytePos, end: BytePos) -> Vec<(u32, u32)> {
54         assert!(start.0 <= end.0);
55
56         if self.file_spans.len() == 0 {
57             return Vec::new();
58         }
59
60         let mut idx = match self.file_spans.binary_search(&(start.0, ::std::u32::MAX)) {
61             Ok(i) => i,
62             Err(0) => 0,
63             Err(i) => i - 1,
64         };
65
66         let mut result = Vec::new();
67         let mut start = start.0;
68         loop {
69             let cur_file = &self.file_spans[idx];
70             idx += 1;
71
72             if idx >= self.file_spans.len() || start >= end.0 {
73                 if start < end.0 {
74                     result.push((start, end.0));
75                 }
76                 return result;
77             }
78
79             let end = ::std::cmp::min(cur_file.1 - 1, end.0);
80             if start < end {
81                 result.push((start, end));
82             }
83             start = self.file_spans[idx].0;
84         }
85     }
86
87     pub fn push_str(&mut self, file_name: &str, text: &str) {
88         let buf = self.file_map.get_mut(&*file_name).unwrap();
89         buf.push_str(text)
90     }
91
92     pub fn push_str_span(&mut self, span: Span, text: &str) {
93         let file_name = self.codemap.span_to_filename(span);
94         self.push_str(&file_name, text)
95     }
96
97     pub fn cur_offset(&mut self, file_name: &str) -> usize {
98         self.file_map[&*file_name].cur_offset()
99     }
100
101     pub fn cur_offset_span(&mut self, span: Span) -> usize {
102         let file_name = self.codemap.span_to_filename(span);
103         self.cur_offset(&file_name)
104     }
105
106     // Return an iterator over the entire changed text.
107     pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> {
108         FileIterator {
109             change_set: self,
110             keys: self.file_map.keys().collect(),
111             cur_key: 0,
112         }
113     }
114
115 }
116
117 // Iterates over each file in the ChangSet. Yields the filename and the changed
118 // text for that file.
119 pub struct FileIterator<'c, 'a: 'c> {
120     change_set: &'c ChangeSet<'a>,
121     keys: Vec<&'c String>,
122     cur_key: usize,
123 }
124
125 impl<'c, 'a> Iterator for FileIterator<'c, 'a> {
126     type Item = (&'c str, &'c StringBuffer);
127
128     fn next(&mut self) -> Option<(&'c str, &'c StringBuffer)> {
129         if self.cur_key >= self.keys.len() {
130             return None;
131         }
132
133         let key = self.keys[self.cur_key];
134         self.cur_key += 1;
135         return Some((&key, &self.change_set.file_map[&*key]))
136     }
137 }
138
139 impl<'a> fmt::Display for ChangeSet<'a> {
140     // Prints the entire changed text.
141     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
142         for (f, r) in self.text() {
143             try!(write!(fmt, "{}:\n", f));
144             try!(write!(fmt, "{}\n\n", r));
145         }
146         Ok(())
147     }    
148 }