squawk_syntax/
decoded_text.rs1use 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}