1use {
2 crate::xkb::{
3 code::Code,
4 span::{Span, SpanUnit},
5 },
6 hashbrown::HashMap,
7 std::{path::PathBuf, sync::Arc},
8};
9
10#[derive(Default)]
11pub(crate) struct CodeMap {
12 ranges: Vec<CodeRange>,
13 canonical_idx: HashMap<*const u8, usize>,
14}
15
16struct CodeRange {
17 span: Span,
18 code: Code,
19 file: Option<Arc<PathBuf>>,
20 canonical_idx: Option<usize>,
21 lines: Option<Vec<SpanUnit>>,
22 include_span: Option<Span>,
23}
24
25pub(crate) struct CodeInfo<'a> {
26 pub(crate) code: &'a Code,
27 pub(crate) span: Span,
28 pub(crate) file: Option<&'a Arc<PathBuf>>,
29 pub(crate) lines: &'a [SpanUnit],
30 pub(crate) lines_offset: SpanUnit,
31 pub(crate) include_span: Option<Span>,
32}
33
34impl CodeRange {
35 fn create_lines(&mut self) {
36 if self.lines.is_some() {
37 return;
38 }
39 let mut lines = vec![];
40 let mut lo = self.span.lo;
41 lines.push(lo);
42 let mut code = self.code.as_bytes();
43 while code.len() > 0 {
44 let Some(pos) = code.iter().position(|&c| c == b'\n') else {
45 break;
46 };
47 code = &code[pos + 1..];
48 lo = lo.saturating_add((pos + 1) as SpanUnit);
49 lines.push(lo);
50 }
51 self.lines = Some(lines);
52 }
53}
54
55impl CodeMap {
56 pub(crate) fn add(
57 &mut self,
58 file: Option<&Arc<PathBuf>>,
59 include_span: Option<Span>,
60 code: &Code,
61 ) -> Span {
62 let lo = self
63 .ranges
64 .last()
65 .map(|l| l.span.hi.saturating_add(1))
66 .unwrap_or_default();
67 let span = Span {
68 lo,
69 hi: lo.saturating_add(code.len() as SpanUnit),
70 };
71 let canonical_idx = self
72 .canonical_idx
73 .try_insert(code.as_ptr(), self.ranges.len())
74 .err()
75 .map(|e| *e.entry.get());
76 self.ranges.push(CodeRange {
77 span,
78 code: code.clone(),
79 file: file.cloned(),
80 canonical_idx,
81 lines: None,
82 include_span,
83 });
84 span
85 }
86
87 pub(crate) fn get(&mut self, span: Span) -> CodeInfo<'_> {
88 let range = self.ranges.binary_search_by_key(&span.lo, |r| r.span.hi);
89 let mut idx = range.unwrap_or_else(|i| i);
90 if idx == self.ranges.len() {
91 idx -= 1;
92 }
93 let (lo, hi) = self.ranges.split_at_mut(idx);
94 let range = &mut hi[0];
95 let (lines, lines_offset) = match range.canonical_idx {
96 Some(idx) => {
97 let crange = &mut lo[idx];
98 crange.create_lines();
99 (
100 crange.lines.as_ref().unwrap(),
101 range.span.lo - crange.span.lo,
102 )
103 }
104 _ => {
105 range.create_lines();
106 (range.lines.as_ref().unwrap(), 0)
107 }
108 };
109 CodeInfo {
110 code: &range.code,
111 span: range.span,
112 file: range.file.as_ref(),
113 lines,
114 lines_offset,
115 include_span: range.include_span,
116 }
117 }
118}