xswag_base/code/
filemap.rs1use super::{SrcOffset, BytePos, LineIdx, ColIdx, Loc};
2use std::cell::RefCell;
3use std::fmt;
4
5pub struct FileMap {
8 filename: String,
10 src: String,
12 lines: RefCell<Vec<BytePos>>,
20}
21
22impl FileMap {
23 pub fn new<U, V>(filename: U, src: V) -> FileMap
26 where U: Into<String>,
27 V: Into<String>
28 {
29 FileMap {
30 filename: filename.into(),
31 src: src.into(),
32 lines: RefCell::new(vec![BytePos(0)]),
33 }
34 }
35
36 pub fn filename(&self) -> &str {
37 &self.filename
38 }
39
40 pub fn src(&self) -> &str {
41 &self.src
42 }
43
44 pub fn add_line(&self, offset: BytePos) {
47 self.lines.borrow_mut().push(offset);
48 }
49
50 pub fn num_lines(&self) -> usize {
52 self.lines.borrow().len()
53 }
54
55 pub fn get_line_idx(&self, offset: BytePos) -> LineIdx {
58 LineIdx(self.lines.borrow()
61 .binary_search(&offset)
62 .unwrap_or_else(|e| e - 1) as u32)
63 }
64
65 pub fn get_loc(&self, offset: BytePos) -> Loc {
68 let line = self.get_line_idx(offset);
69 let col = offset - self.lines.borrow()[line.0 as usize];
70
71 Loc { line: line, col: ColIdx(col.0) }
72 }
73
74 pub fn get_line(&self, line: LineIdx) -> Option<&str> {
76 self.lines.borrow().get(line.0 as usize).map(|&BytePos(start)| {
77 let end = self.src[start as usize..]
78 .find("\n")
79 .unwrap_or(self.src.len() - start as usize);
80 &self.src[start as usize .. (end + start as usize)]
81 })
82 }
83
84 pub fn get_line_start(&self, line: LineIdx) -> Option<BytePos> {
86 self.lines.borrow().get(line.0 as usize).map(|&pos| pos)
87 }
88
89 pub fn find_lines(&self) {
96 let last_line_so_far = self.lines.borrow().last().unwrap().0 as usize;
98 for (pos, c) in self.src[last_line_so_far..].char_indices() {
99 if c == '\n' {
101 let line_start = (pos + c.len_utf8()) as SrcOffset;
102 self.lines.borrow_mut().push(BytePos(line_start));
103 }
104 }
105 }
106}
107
108impl fmt::Debug for FileMap {
109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 struct Dummy<'a>(&'a [BytePos]);
111 impl<'a> fmt::Debug for Dummy<'a> {
112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113 let items = self.0.iter().map(|v| v.0).enumerate();
114 f.debug_map().entries(items).finish()
115 }
116 }
117
118 f.debug_struct("FileMap")
119 .field("filename", &self.filename)
120 .field("src", &format!("<long string> (len {})", self.src.len()))
121 .field("lines", &Dummy(&self.lines.borrow()))
122 .finish()
123 }
124}
125
126#[test]
128fn location_lookup() {
129 let map = FileMap::new("<dummy>", "foo\r\nbär\nbaz");
130 map.find_lines();
131
132 assert_eq!(*map.lines.borrow(),
134 vec![BytePos(0), BytePos(5), BytePos(10)]
135 );
136
137 macro_rules! is_at {
138 ($offset:expr => [$line:expr, $col:expr]) => {
139 assert_eq!(map.get_loc(BytePos($offset)), Loc {
140 line: LineIdx($line), col: ColIdx($col)
141 });
142 }
143 }
144
145 is_at!(0 => [0, 0]);
146 is_at!(1 => [0, 1]);
147 is_at!(4 => [0, 4]);
148 is_at!(5 => [1, 0]);
149 is_at!(9 => [1, 4]);
150 is_at!(10 => [2, 0]);
151 is_at!(12 => [2, 2]);
152}