aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor/src/edit.rs
blob: 163ecf6dee862e8badeba42acd87399f3b278aee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use {TextRange, TextUnit};

#[derive(Debug)]
pub struct Edit {
    pub atoms: Vec<AtomEdit>,
}

#[derive(Debug)]
pub struct AtomEdit {
    pub delete: TextRange,
    pub insert: String,
}

#[derive(Debug)]
pub struct EditBuilder {
    atoms: Vec<AtomEdit>
}

impl EditBuilder {
    pub fn new() -> EditBuilder {
        EditBuilder { atoms: Vec::new() }
    }

    pub fn replace(&mut self, range: TextRange, replacement: String) {
        let range = self.translate(range);
        self.atoms.push(AtomEdit { delete: range, insert: replacement })
    }

    pub fn delete(&mut self, range: TextRange) {
        self.replace(range, String::new());
    }

    pub fn insert(&mut self, offset: TextUnit, text: String) {
        self.replace(TextRange::offset_len(offset, 0.into()), text)
    }

    pub fn finish(self) -> Edit {
        Edit { atoms: self.atoms }
    }

    fn translate(&self, range: TextRange) -> TextRange {
        let mut range = range;
        for atom in self.atoms.iter() {
            range = atom.apply_to_range(range)
                .expect("conflicting edits");
        }
        range
    }
}

impl Edit {
    pub fn apply(&self, text: &str) -> String {
        let mut text = text.to_owned();
        for atom in self.atoms.iter() {
            text = atom.apply(&text);
        }
        text
    }
}

impl AtomEdit {
    fn apply(&self, text: &str) -> String {
        let prefix = &text[
            TextRange::from_to(0.into(), self.delete.start())
        ];
        let suffix = &text[
            TextRange::from_to(self.delete.end(), TextUnit::of_str(text))
        ];
        let mut res = String::with_capacity(prefix.len() + self.insert.len() + suffix.len());
        res.push_str(prefix);
        res.push_str(&self.insert);
        res.push_str(suffix);
        res
    }

    fn apply_to_position(&self, pos: TextUnit) -> Option<TextUnit> {
        if pos <= self.delete.start() {
            return Some(pos);
        }
        if pos < self.delete.end() {
            return None;
        }
        Some(pos - self.delete.len() + TextUnit::of_str(&self.insert))
    }

    fn apply_to_range(&self, range: TextRange) -> Option<TextRange> {
        Some(TextRange::from_to(
            self.apply_to_position(range.start())?,
            self.apply_to_position(range.end())?,
        ))
    }
}