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.
13 // print to files (maybe that shouldn't be here, but in mod)
16 use rope::{Rope, RopeSlice};
17 use std::collections::HashMap;
18 use syntax::codemap::{CodeMap, Span, BytePos};
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>,
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(),
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);
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);
54 let file = &mut self.file_map[*file_name];
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);
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);
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,
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
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)
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)
95 // Return an iterator over the entire changed text.
96 pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> {
99 keys: self.file_map.keys().collect(),
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)
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>,
121 impl<'c, 'a> Iterator for FileIterator<'c, 'a> {
122 type Item = (&'c str, &'c Rope);
124 fn next(&mut self) -> Option<(&'c str, &'c Rope)> {
125 if self.cur_key >= self.keys.len() {
129 let key = self.keys[self.cur_key];
131 return Some((&key, &self.change_set.file_map[*key]))
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));