1use crate::Span;
2use std::cell::RefCell;
3use std::rc::Rc;
4
5#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
10pub struct SourceLocation {
11 location: (usize, usize),
14}
15
16impl SourceLocation {
17 pub(crate) fn new(row: usize, column: usize) -> SourceLocation {
21 if row == 0 || column == 0 {
22 return Self::empty();
23 }
24 Self {
25 location: (row, column),
26 }
27 }
28
29 pub(crate) fn empty() -> SourceLocation {
31 Self { location: (0, 0) }
32 }
33
34 pub fn row_column(&self) -> Option<(usize, usize)> {
37 match self.location {
38 (0, 0) => None,
39 other => Some(other),
40 }
41 }
42
43 pub fn row(&self) -> Option<usize> {
46 match self.location {
47 (0, 0) => None,
48 (row, _) => Some(row),
49 }
50 }
51
52 pub fn column(&self) -> Option<usize> {
55 match self.location {
56 (0, 0) => None,
57 (_, col) => Some(col),
58 }
59 }
60}
61
62#[cfg(test)]
63mod source_location_tests {
64 use crate::location::SourceLocation;
65 #[test]
66 fn empty_source_location() {
67 let location = SourceLocation::empty();
68 assert_eq!(None, location.row_column());
69 assert_eq!(None, location.row());
70 assert_eq!(None, location.column());
71 }
72
73 #[test]
74 fn non_empty_source_location() {
75 let location = SourceLocation::new(2, 3);
76 assert_eq!(Some((2, 3)), location.row_column());
77 assert_eq!(Some(2), location.row());
78 assert_eq!(Some(3), location.column());
79 }
80}
81
82#[derive(Debug, Clone)]
86pub(crate) struct SourceLocationState {
87 row_start_offsets: Rc<RefCell<Vec<usize>>>,
90}
91impl SourceLocationState {
92 pub fn new() -> Self {
93 Self {
94 row_start_offsets: Rc::from(RefCell::new(vec![0])),
95 }
96 }
97
98 pub fn update_from_source<T: AsRef<[u8]>>(&mut self, stream_offset: usize, data: T) {
100 let data = data.as_ref();
101 if !data.is_empty() {
102 let newlines = memchr::memchr_iter(b'\n', data);
103 self.row_start_offsets
104 .borrow_mut()
105 .extend(newlines.map(|it| it + stream_offset + 1));
106 }
107 }
108
109 pub fn calculate_location_for_span(&self, span: Span<'_>) -> SourceLocation {
110 let range = span.range();
111 let (row, row_start_offset) = self
112 .row_start_offsets
113 .borrow()
114 .iter()
115 .copied()
116 .enumerate()
117 .rfind(|&(_, newline_offset)| newline_offset <= range.start)
118 .unwrap();
120 let column = range.start - row_start_offset;
121 SourceLocation::new(row + 1, column + 1)
123 }
124}