kbvm/xkb/
code_map.rs

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}