Skip to main content

surql_parser/upstream/syn/error/
location.rs

1use crate::upstream::syn::token::Span;
2use std::ops::Range;
3/// A human readable location inside a string.
4///
5/// Locations are 1 indexed, the first character on the first line being on line
6/// 1 column 1.
7#[derive(Clone, Copy, Debug)]
8pub struct Location {
9	pub line: usize,
10	/// In chars.
11	pub column: usize,
12}
13/// Safety: b must be a substring of a.
14unsafe fn str_offset(a: &str, b: &str) -> usize {
15	unsafe { b.as_ptr().offset_from(a.as_ptr()) as usize }
16}
17impl Location {
18	fn range_of_source_end(source: &str) -> Range<Self> {
19		let (line, column) = source
20			.lines()
21			.enumerate()
22			.last()
23			.map(|(idx, line)| {
24				let idx = idx + 1;
25				let line_idx = line.chars().count().max(1);
26				(idx, line_idx)
27			})
28			.unwrap_or((1, 1));
29		Self { line, column }..Self {
30			line,
31			column: column + 1,
32		}
33	}
34	pub fn range_of_span(source: &str, span: Span) -> Range<Self> {
35		if source.len() <= span.offset as usize {
36			return Self::range_of_source_end(source);
37		}
38		let mut prev_line = "";
39		let mut lines = source.lines().enumerate().peekable();
40		let start_offset = span.offset as usize;
41		let start = loop {
42			let Some((line_idx, line)) = lines.peek().copied() else {
43				return Self::range_of_source_end(source);
44			};
45			let line_offset = unsafe { str_offset(source, line) };
46			if start_offset < line_offset {
47				let len = prev_line.chars().count();
48				break Self {
49					line: line_idx,
50					column: len + 1,
51				};
52			}
53			if (line_offset..(line_offset + line.len())).contains(&start_offset) {
54				let column_offset = start_offset - line_offset;
55				let column = line
56					.char_indices()
57					.enumerate()
58					.find(|(_, (char_idx, _))| *char_idx >= column_offset)
59					.map(|(l, _)| l)
60					.unwrap_or_else(|| line.chars().count());
61				break Self {
62					line: line_idx + 1,
63					column: column + 1,
64				};
65			}
66			lines.next();
67			prev_line = line;
68		};
69		let end_offset = span.offset as usize + span.len as usize;
70		let end = loop {
71			let Some((line_idx, line)) = lines.peek().copied() else {
72				break Self::range_of_source_end(source).end;
73			};
74			let line_offset = unsafe { str_offset(source, line) };
75			if end_offset < line_offset {
76				let len = prev_line.chars().count();
77				break Self {
78					line: line_idx,
79					column: len + 1,
80				};
81			}
82			if (line_offset..(line_offset + line.len())).contains(&end_offset) {
83				let column_offset = end_offset - line_offset;
84				let column = line
85					.char_indices()
86					.enumerate()
87					.find(|(_, (char_idx, _))| *char_idx >= column_offset)
88					.map(|(l, _)| l)
89					.unwrap_or_else(|| line.chars().count());
90				break Self {
91					line: line_idx + 1,
92					column: column + 1,
93				};
94			}
95			lines.next();
96			prev_line = line;
97		};
98		start..end
99	}
100}