1use crate::span::HasSpan;
2use ecow::{EcoString, EcoVec};
3use std::ops::Range;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, PartialEq, Hash, Eq)]
7pub enum Patch {
8 Insert {
9 at: usize,
11 text: EcoString,
12 },
13 Replace {
14 range: Range<usize>,
16 text: EcoString,
17 },
18 Delete {
19 range: Range<usize>,
21 },
22}
23
24impl Patch {
25 pub fn apply(&self, source: &mut String) {
26 match self {
27 Patch::Insert { at, text } => source.insert_str(*at, text),
28 Patch::Replace { range, text } => source.replace_range(range.clone(), text),
29 Patch::Delete { range } => source.replace_range(range.clone(), ""),
30 }
31 }
32
33 pub fn shifted(self, delta: isize) -> Result<Patch, PatchError> {
36 fn shift_pos(pos: usize, delta: isize) -> Result<usize, PatchError> {
37 let new = pos as isize + delta;
38 if new < 0 { Err(PatchError::OutOfBounds) } else { Ok(new as usize) }
39 }
40
41 Ok(match self {
42 Patch::Insert { at, text } => Patch::Insert {
43 at: shift_pos(at, delta)?,
44 text,
45 },
46 Patch::Replace { range, text } => Patch::Replace {
47 range: shift_pos(range.start, delta)?..shift_pos(range.end, delta)?,
48 text,
49 },
50 Patch::Delete { range } => Patch::Delete {
51 range: shift_pos(range.start, delta)?..range.end,
52 },
53 })
54 }
55
56 fn range(&self) -> Range<usize> {
57 match self {
58 Patch::Insert { at, .. } => *at..*at,
59 Patch::Replace { range, .. } => range.clone(),
60 Patch::Delete { range } => range.clone(),
61 }
62 }
63
64 fn start_pos(&self) -> usize {
65 match self {
66 Patch::Insert { at, .. } => *at,
67 Patch::Replace { range, .. } => range.start,
68 Patch::Delete { range } => range.start,
69 }
70 }
71
72 fn conflicts_with(&self, other: &Patch) -> bool {
73 let r1 = self.range();
74 let r2 = other.range();
75
76 if r1.end > r2.start && r2.end > r1.start {
77 return true;
78 }
79
80 if let Patch::Insert { at, .. } = self {
81 if *at > r2.start && *at < r2.end {
82 return true;
83 }
84 }
85 if let Patch::Insert { at, .. } = other {
86 if *at > r1.start && *at < r1.end {
87 return true;
88 }
89 }
90
91 false
92 }
93}
94
95pub struct PatchEngine {
96 patches: Vec<Patch>,
97}
98
99impl PatchEngine {
100}
101
102#[derive(Debug, Clone)]
103pub enum PatchError {
104 SpanWithoutRange,
105 OutOfBounds,
106 Conflict {
107 a: Arc<Patch>,
108 b: Arc<Patch>,
109 }
110}
111
112impl PatchEngine {
113 pub fn new() -> Self {
114 Self { patches: vec![] }
115 }
116
117 pub fn add_patch(&mut self, patch: Patch) {
118 self.patches.push(patch);
119 }
120
121 pub fn add_patches(&mut self, patches: impl IntoIterator<Item = Patch>) {
122 self.patches.extend(patches);
123 }
124
125 pub fn insert_before(
126 &mut self,
127 node: &impl HasSpan,
128 text: impl Into<EcoString>,
129 ) -> Result<(), PatchError> {
130 let span = node.span();
131 self.add_patch(Patch::Insert {
132 at: span.range().ok_or(PatchError::SpanWithoutRange)?.start,
133 text: text.into(),
134 });
135
136 Ok(())
137 }
138
139 pub fn insert_after(
140 &mut self,
141 node: &impl HasSpan,
142 text: impl Into<EcoString>,
143 ) -> Result<(), PatchError> {
144 let span = node.span();
145 self.add_patch(Patch::Insert {
146 at: span.range().ok_or(PatchError::SpanWithoutRange)?.end,
147 text: text.into(),
148 });
149
150 Ok(())
151 }
152
153 pub fn replace_node(
154 &mut self,
155 node: &impl HasSpan,
156 text: impl Into<EcoString>,
157 ) -> Result<(), PatchError> {
158 let span = node.span();
159 self.add_patch(Patch::Replace {
160 range: span.range().ok_or(PatchError::SpanWithoutRange)?,
161 text: text.into(),
162 });
163
164 Ok(())
165 }
166
167 pub fn delete_node(&mut self, node: &impl HasSpan) -> Result<(), PatchError> {
168 self.add_patch(Patch::Delete {
169 range: node.span().range().ok_or(PatchError::SpanWithoutRange)?,
170 });
171
172 Ok(())
173 }
174
175 fn check_conflicts(&self) -> Result<(), PatchError> {
176 for (i, p1) in self.patches.iter().enumerate() {
177 for p2 in self.patches.iter().skip(i + 1) {
178 if p1.conflicts_with(p2) {
179 return Err(PatchError::Conflict {
180 a: Arc::new(p1.clone()),
181 b: Arc::new(p2.clone()),
182 });
183 }
184 }
185 }
186 Ok(())
187 }
188
189 pub fn apply_all(self, source: &str) -> Result<String, PatchError> {
190 self.apply_all_with_offset(source, 0)
191 }
192
193 pub fn apply_all_with_offset(mut self, snippet: &str, base_offset: usize) -> Result<String, PatchError> {
194 self.check_conflicts()?;
195
196 let mut result = snippet.to_string();
197 self.patches.sort_by_key(|p| p.start_pos());
198 self.patches.reverse();
199
200 let offset = -(base_offset as isize);
201 for patch in self.patches.iter().filter(|p| {
202 p.range().start >= base_offset && p.range().end <= base_offset + snippet.len()
203 }) {
204 patch.clone().shifted(offset)?.apply(&mut result);
205 }
206
207 Ok(result)
208 }
209
210 pub(crate) fn get_patches(&self) -> EcoVec<Patch> {
211 self.patches.iter().cloned().collect()
212 }
213}