]> git.lizzy.rs Git - rust.git/blob - src/changes.rs
Working prototype
[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 (maybe that shouldn't be here, but in mod)
14 // tests
15
16 use rope::{Rope, RopeSlice};
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, Rope>,
25     codemap: &'a CodeMap,
26     pub count: u64,
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             count: 0,
36         };
37
38         for f in codemap.files.borrow().iter() {
39             let contents = Rope::from_string((&**f.src.as_ref().unwrap()).clone());
40             result.file_map.insert(f.name.clone(), contents);
41         }
42
43         result
44     }
45
46     // Change a span of text in our stored text into the new text (`text`).
47     // The span of text to change is given in the coordinates of the original
48     // source text, not the current text,
49     pub fn change(&mut self, file_name: &str, start: usize, end: usize, text: String) {
50         println!("change: {}:{}-{} \"{}\"", file_name, start, end, text);
51
52         self.count += 1;
53
54         let file = &mut self.file_map[*file_name];
55
56         if end - start == text.len() {
57             // TODO src_replace_str would be much more efficient
58             //file.src_replace_str(start, &text);
59             file.src_remove(start, end);
60             file.src_insert(start, text);
61         } else {
62             // TODO if we do this in one op, could we get better change info?
63             file.src_remove(start, end);
64             file.src_insert(start, text);
65         }
66     }
67
68     // As for `change()`, but use a Span to indicate the text to change.
69     pub fn change_span(&mut self, span: Span, text: String) {
70         let l_loc = self.codemap.lookup_char_pos(span.lo);
71         let file_offset = l_loc.file.start_pos.0;
72         self.change(&l_loc.file.name,
73                     (span.lo.0 - file_offset) as usize,
74                     (span.hi.0 - file_offset) as usize,
75                     text)
76     }
77
78     // Get a slice of the current text. Coordinates are relative to the source
79     // text. I.e., this method returns the text which has been changed from the
80     // indicated span.
81     pub fn slice(&self, file_name: &str, start: usize, end: usize) -> RopeSlice {
82         let file = &self.file_map[*file_name];
83         file.src_slice(start..end)
84     }
85
86     // As for `slice()`, but use a Span to indicate the text to return.
87     pub fn slice_span(&self, span:Span) -> RopeSlice {
88         let l_loc = self.codemap.lookup_char_pos(span.lo);
89         let file_offset = l_loc.file.start_pos.0;
90         self.slice(&l_loc.file.name,
91                    (span.lo.0 - file_offset) as usize,
92                    (span.hi.0 - file_offset) as usize)
93     }
94
95     // Return an iterator over the entire changed text.
96     pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> {
97         FileIterator {
98             change_set: self,
99             keys: self.file_map.keys().collect(),
100             cur_key: 0,
101         }
102     }
103
104     // Get the current line-relative position of a position in the source text.
105     pub fn col(&self, loc: BytePos) -> usize {
106         let l_loc = self.codemap.lookup_char_pos(loc);
107         let file_offset = l_loc.file.start_pos.0;
108         let file = &self.file_map[l_loc.file.name[..]];
109         file.col_for_src_loc(loc.0 as usize - file_offset as usize)
110     }
111 }
112
113 // Iterates over each file in the ChangSet. Yields the filename and the changed
114 // text for that file.
115 pub struct FileIterator<'c, 'a: 'c> {
116     change_set: &'c ChangeSet<'a>,
117     keys: Vec<&'c String>,
118     cur_key: usize,
119 }
120
121 impl<'c, 'a> Iterator for FileIterator<'c, 'a> {
122     type Item = (&'c str, &'c Rope);
123
124     fn next(&mut self) -> Option<(&'c str, &'c Rope)> {
125         if self.cur_key >= self.keys.len() {
126             return None;
127         }
128
129         let key = self.keys[self.cur_key];
130         self.cur_key += 1;
131         return Some((&key, &self.change_set.file_map[*key]))
132     }
133 }
134
135 impl<'a> fmt::Display for ChangeSet<'a> {
136     // Prints the entire changed text.
137     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
138         for (f, r) in self.text() {
139             try!(write!(fmt, "{}:\n", f));
140             try!(write!(fmt, "{}\n\n", r));
141         }
142         Ok(())
143     }    
144 }