Skip to main content

squawk_syntax/
decoded_text.rs

1use rowan::{TextRange, TextSize};
2
3#[derive(Debug)]
4pub struct DecodedText {
5    text: String,
6    marks: Vec<Mark>,
7}
8
9#[derive(Debug)]
10struct Mark {
11    decoded: u32,
12    pos: u32,
13}
14
15impl DecodedText {
16    pub fn new(pos: TextSize) -> Self {
17        Self {
18            text: String::new(),
19            marks: vec![Mark {
20                decoded: 0,
21                pos: pos.into(),
22            }],
23        }
24    }
25
26    pub fn text(&self) -> &str {
27        &self.text
28    }
29
30    pub fn into_text(self) -> String {
31        self.text
32    }
33
34    pub fn push_str(&mut self, text: &str, pos: TextSize) {
35        self.sync(pos);
36        self.text.push_str(text);
37    }
38
39    pub fn push_char(&mut self, c: char, pos: TextSize) {
40        self.sync(pos);
41        self.text.push(c);
42    }
43
44    pub fn mark_end(&mut self, pos: TextSize) {
45        self.sync(pos);
46    }
47
48    fn sync(&mut self, pos: TextSize) {
49        let decoded = self.text.len() as u32;
50        let pos = pos.into();
51        match self.marks.last_mut() {
52            Some(mark) if mark.decoded == decoded => mark.pos = pos,
53            Some(mark) if mark.pos + (decoded - mark.decoded) == pos => (),
54            _ => self.marks.push(Mark { decoded, pos }),
55        }
56    }
57
58    pub fn source_pos(&self, offset: TextSize) -> TextSize {
59        let offset = u32::from(offset);
60        let idx = self.marks.partition_point(|mark| mark.decoded <= offset);
61        let mark = &self.marks[idx.saturating_sub(1)];
62        TextSize::new(mark.pos + offset.saturating_sub(mark.decoded))
63    }
64
65    pub fn source_range(&self, range: TextRange) -> TextRange {
66        TextRange::new(self.source_pos(range.start()), self.source_pos(range.end()))
67    }
68}