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.
16 use strings::string_buffer::StringBuffer;
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, StringBuffer>,
26 file_spans: Vec<(u32, u32)>,
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(),
35 file_spans: Vec::with_capacity(codemap.files.borrow().len()),
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()));
45 result.file_spans.push((f.start_pos.0, f.end_pos.0));
48 result.file_spans.sort();
53 pub fn filespans_for_span(&self, start: BytePos, end: BytePos) -> Vec<(u32, u32)> {
54 assert!(start.0 <= end.0);
56 if self.file_spans.len() == 0 {
60 let mut idx = match self.file_spans.binary_search(&(start.0, ::std::u32::MAX)) {
66 let mut result = Vec::new();
67 let mut start = start.0;
69 let cur_file = &self.file_spans[idx];
72 if idx >= self.file_spans.len() || start >= end.0 {
74 result.push((start, end.0));
79 let end = ::std::cmp::min(cur_file.1 - 1, end.0);
81 result.push((start, end));
83 start = self.file_spans[idx].0;
87 pub fn push_str(&mut self, file_name: &str, text: &str) {
88 let buf = self.file_map.get_mut(&*file_name).unwrap();
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)
97 pub fn cur_offset(&mut self, file_name: &str) -> usize {
98 self.file_map[&*file_name].cur_offset()
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)
106 // Return an iterator over the entire changed text.
107 pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> {
110 keys: self.file_map.keys().collect(),
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>,
125 impl<'c, 'a> Iterator for FileIterator<'c, 'a> {
126 type Item = (&'c str, &'c StringBuffer);
128 fn next(&mut self) -> Option<(&'c str, &'c StringBuffer)> {
129 if self.cur_key >= self.keys.len() {
133 let key = self.keys[self.cur_key];
135 return Some((&key, &self.change_set.file_map[&*key]))
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));