1use serde::Serialize;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
7pub struct Span {
8 pub start: usize,
9 pub end: usize,
10}
11
12impl Span {
13 pub fn new(start: usize, end: usize) -> Self {
14 Span { start, end }
15 }
16
17 pub const DUMMY: Span = Span { start: 0, end: 0 };
18
19 pub fn to(self, other: Span) -> Span {
21 Span::new(self.start.min(other.start), self.end.max(other.end))
22 }
23
24 pub fn len(&self) -> usize {
25 self.end.saturating_sub(self.start)
26 }
27
28 pub fn is_empty(&self) -> bool {
29 self.len() == 0
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
35pub struct LineCol {
36 pub line: usize,
37 pub col: usize,
38}
39
40#[derive(Debug, Clone)]
42pub struct SourceFile {
43 pub name: String,
44 pub text: String,
45 line_starts: Vec<usize>,
46}
47
48impl SourceFile {
49 pub fn new(name: impl Into<String>, text: impl Into<String>) -> Self {
50 let text = text.into();
51 let mut line_starts = vec![0];
52 for (i, b) in text.bytes().enumerate() {
53 if b == b'\n' {
54 line_starts.push(i + 1);
55 }
56 }
57 SourceFile {
58 name: name.into(),
59 text,
60 line_starts,
61 }
62 }
63
64 pub fn line_col(&self, offset: usize) -> LineCol {
67 let offset = offset.min(self.text.len());
68 let line_idx = match self.line_starts.binary_search(&offset) {
69 Ok(i) => i,
70 Err(i) => i - 1,
71 };
72 let line_start = self.line_starts[line_idx];
73 let col = self.text[line_start..offset].chars().count() + 1;
74 LineCol {
75 line: line_idx + 1,
76 col,
77 }
78 }
79
80 pub fn line_text(&self, line: usize) -> &str {
82 let idx = line - 1;
83 let start = self.line_starts[idx];
84 let end = self
85 .line_starts
86 .get(idx + 1)
87 .copied()
88 .unwrap_or(self.text.len());
89 self.text[start..end].trim_end_matches(['\n', '\r'])
90 }
91
92 pub fn line_count(&self) -> usize {
93 self.line_starts.len()
94 }
95
96 pub fn snippet(&self, span: Span) -> &str {
97 &self.text[span.start.min(self.text.len())..span.end.min(self.text.len())]
98 }
99}