1use super::Buffer;
3use crate::history::{Edit, EditKind};
4use crate::id::{BufferRevision, ByteOffset};
5use crate::{InputEdit, Range};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
8pub enum ChangeOrigin {
9 User,
10 History,
11 System,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct Change {
16 pub revision: BufferRevision,
17 pub origin: ChangeOrigin,
18 pub edit: InputEdit,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
22pub enum EditError {
23 #[error("readonly buffer")]
24 ReadOnly,
25 #[error("invalid edit range")]
26 InvalidRange,
27 #[error("overlapping replacements in pre-edit coordinates")]
28 Overlap,
29 #[error("prepared replacement belongs to another buffer")]
30 WrongBuffer,
31 #[error("stale revision: expected {expected}, found {found}")]
32 StaleRevision {
33 expected: BufferRevision,
34 found: BufferRevision,
35 },
36 #[error("buffer revision exhausted")]
37 RevisionExhausted,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Replacement {
42 pub range: Range,
43 pub text: String,
44}
45impl Replacement {
46 pub fn new(range: Range, text: impl Into<String>) -> Self {
47 Self {
48 range,
49 text: text.into(),
50 }
51 }
52}
53
54pub struct PreparedReplacements {
57 buffer: crate::diagnostics::BufferTraceId,
58 revision: BufferRevision,
59 edits: Vec<Replacement>,
60}
61impl PreparedReplacements {
62 pub fn is_empty(&self) -> bool {
63 self.edits.is_empty()
64 }
65 pub fn edits(&self) -> &[Replacement] {
67 &self.edits
68 }
69}
70
71pub struct UserEdit<'a> {
72 buffer: &'a mut Buffer,
73}
74pub struct SystemEdit<'a> {
75 buffer: &'a mut Buffer,
76}
77
78impl Buffer {
79 pub fn edit(&mut self) -> UserEdit<'_> {
80 UserEdit { buffer: self }
81 }
82 pub fn system_edit(&mut self) -> SystemEdit<'_> {
83 SystemEdit { buffer: self }
84 }
85 pub fn changes(&self) -> &[Change] {
86 &self.changes
87 }
88 pub fn clear_changes(&mut self) {
89 self.changes.clear();
90 }
91 pub fn begin_undo_group(&mut self) {
92 self.history.begin();
93 }
94 pub fn commit_undo_group(&mut self) {
95 self.history.commit();
96 }
97
98 fn validate_range(&self, range: Range) -> Result<(), EditError> {
99 if range.start > range.end || !self.is_boundary(range.start) || !self.is_boundary(range.end)
100 {
101 return Err(EditError::InvalidRange);
102 }
103 Ok(())
104 }
105
106 pub fn prepare_replacements(
109 &self,
110 base: BufferRevision,
111 mut replacements: Vec<Replacement>,
112 ) -> Result<PreparedReplacements, EditError> {
113 if self.readonly {
114 return Err(EditError::ReadOnly);
115 }
116 if base != self.revision() {
117 return Err(EditError::StaleRevision {
118 expected: base,
119 found: self.revision(),
120 });
121 }
122 replacements.retain(|edit| !edit.range.is_empty() || !edit.text.is_empty());
123 for edit in &replacements {
124 self.validate_range(edit.range)?;
125 }
126 replacements.sort_unstable_by_key(|edit| (edit.range.start.get(), edit.range.end.get()));
127 let pairs: Vec<(usize, usize)> = replacements
131 .iter()
132 .map(|edit| (edit.range.start.get(), edit.range.end.get()))
133 .collect();
134 let sorted = crate::editmap::check_batch(self.len_bytes(), pairs)
135 .map_err(|()| EditError::Overlap)?;
136 debug_assert!(replacements
139 .iter()
140 .zip(sorted)
141 .all(|(edit, pair)| (edit.range.start.get(), edit.range.end.get()) == pair));
142 self.epoch
143 .checked_add(replacements.len() as u64)
144 .ok_or(EditError::RevisionExhausted)?;
145 Ok(PreparedReplacements {
146 buffer: self.trace_id(),
147 revision: base,
148 edits: replacements,
149 })
150 }
151
152 pub fn apply_prepared(
153 &mut self,
154 prepared: PreparedReplacements,
155 undo_open: bool,
156 ) -> Result<BufferRevision, EditError> {
157 if prepared.buffer != self.trace_id() {
158 return Err(EditError::WrongBuffer);
159 }
160 if self.readonly {
161 return Err(EditError::ReadOnly);
162 }
163 if prepared.revision != self.revision() {
164 return Err(EditError::StaleRevision {
165 expected: prepared.revision,
166 found: self.revision(),
167 });
168 }
169 if prepared.is_empty() {
170 return Ok(self.revision());
171 }
172 self.history.begin();
173 for replacement in prepared.edits.into_iter().rev() {
174 self.replace_validated(replacement.range, &replacement.text, ChangeOrigin::User);
175 }
176 if !undo_open {
177 self.history.commit();
178 }
179 Ok(self.revision())
180 }
181
182 fn replace_validated(&mut self, range: Range, text: &str, origin: ChangeOrigin) {
183 debug_assert!(self.validate_range(range).is_ok());
184 debug_assert!(self.epoch < u64::MAX);
185 let (start_byte, end_byte) = (range.start.get(), range.end.get());
186 let start_point = self.point_of(start_byte);
187 let old_end_point = self.point_of(end_byte);
188 let extent = Self::point_extent(text);
189 let new_end_point = if extent.0 == 0 {
190 (start_point.0, start_point.1 + extent.1)
191 } else {
192 (start_point.0 + extent.0, extent.1)
193 };
194 let edit = InputEdit {
195 start_byte,
196 old_end_byte: end_byte,
197 new_end_byte: start_byte + text.len(),
198 start_point,
199 old_end_point,
200 new_end_point,
201 };
202 if origin == ChangeOrigin::User {
203 if !range.is_empty() {
204 let removed = self.rope.byte_slice(start_byte..end_byte).to_string();
205 self.history.record(
206 Edit {
207 at: start_byte,
208 text: removed.clone(),
209 kind: EditKind::Insert,
210 },
211 Edit {
212 at: start_byte,
213 text: removed,
214 kind: EditKind::Delete,
215 },
216 );
217 }
218 if !text.is_empty() {
219 self.history.record(
220 Edit {
221 at: start_byte,
222 text: text.into(),
223 kind: EditKind::Delete,
224 },
225 Edit {
226 at: start_byte,
227 text: text.into(),
228 kind: EditKind::Insert,
229 },
230 );
231 }
232 }
233 let start = self.rope.byte_to_char(start_byte);
234 let end = self.rope.byte_to_char(end_byte);
235 if start != end {
236 self.rope.remove(start..end);
237 }
238 if !text.is_empty() {
239 self.rope.insert(start, text);
240 }
241 self.publish_change(edit, origin);
242 self.trace_edit(origin, start_byte, range.len(), text);
243 }
244
245 fn publish_change(&mut self, edit: InputEdit, origin: ChangeOrigin) {
246 debug_assert!(self.epoch < u64::MAX);
247 self.epoch += 1;
248 self.invalidate_line_layouts(&edit);
249 self.dirty |= origin != ChangeOrigin::System;
250 self.changes.push(Change {
251 revision: self.revision(),
252 origin,
253 edit,
254 });
255 }
256}
257
258impl UserEdit<'_> {
259 pub fn insert(&mut self, at: impl Into<ByteOffset>, text: &str) -> Result<(), EditError> {
260 let at = at.into();
261 self.replace(Range::charwise(at, at), text)
262 }
263 pub fn delete(&mut self, range: Range) -> Result<String, EditError> {
264 if self.buffer.readonly {
265 return Err(EditError::ReadOnly);
266 }
267 self.buffer.validate_range(range)?;
268 let removed = self.buffer.slice_string(range);
269 self.replace(range, "")?;
270 Ok(removed)
271 }
272 pub fn replace(&mut self, range: Range, text: &str) -> Result<(), EditError> {
273 if self.buffer.readonly {
274 return Err(EditError::ReadOnly);
275 }
276 self.buffer.validate_range(range)?;
277 if range.is_empty() && text.is_empty() {
278 return Ok(());
279 }
280 self.buffer
281 .epoch
282 .checked_add(1)
283 .ok_or(EditError::RevisionExhausted)?;
284 self.buffer
285 .replace_validated(range, text, ChangeOrigin::User);
286 Ok(())
287 }
288}
289
290impl SystemEdit<'_> {
291 pub fn replace(&mut self, range: Range, text: &str) -> Result<(), EditError> {
295 self.buffer.validate_range(range)?;
296 if range.is_empty() && text.is_empty() {
297 return Ok(());
298 }
299 self.buffer
300 .epoch
301 .checked_add(1)
302 .ok_or(EditError::RevisionExhausted)?;
303 self.buffer
304 .replace_validated(range, text, ChangeOrigin::System);
305 Ok(())
306 }
307
308 pub fn replace_all(&mut self, text: &str) -> Result<(), EditError> {
310 self.buffer
311 .epoch
312 .checked_add(1)
313 .ok_or(EditError::RevisionExhausted)?;
314 self.buffer.replace_validated(
315 Range::charwise(0, self.buffer.len_bytes()),
316 text,
317 ChangeOrigin::System,
318 );
319 self.buffer.history = Default::default();
320 Ok(())
321 }
322
323 pub fn replace_rope(&mut self, rope: ropey::Rope) -> Result<(), EditError> {
326 self.buffer
327 .epoch
328 .checked_add(1)
329 .ok_or(EditError::RevisionExhausted)?;
330 let old_end_byte = self.buffer.len_bytes();
331 let old_end_point = self.buffer.point_of(old_end_byte);
332 let new_end_byte = rope.len_bytes();
333 let line = rope.byte_to_line(new_end_byte);
334 let new_end_point = (line, new_end_byte - rope.line_to_byte(line));
335 self.buffer.rope = rope;
336 self.buffer.publish_change(
337 InputEdit {
338 start_byte: 0,
339 old_end_byte,
340 new_end_byte,
341 start_point: (0, 0),
342 old_end_point,
343 new_end_point,
344 },
345 ChangeOrigin::System,
346 );
347 self.buffer.history = Default::default();
348 self.buffer.trace_snapshot(old_end_byte);
349 Ok(())
350 }
351}
352
353#[derive(Debug, Clone, Copy)]
354pub struct HistoryMove {
355 pub start: ByteOffset,
356 pub end: ByteOffset,
357}
358
359impl Buffer {
360 pub fn undo(&mut self) -> Result<Option<HistoryMove>, EditError> {
361 self.move_history(crate::history::HistoryAction::Undo)
362 }
363 pub fn redo(&mut self) -> Result<Option<HistoryMove>, EditError> {
364 self.move_history(crate::history::HistoryAction::Redo)
365 }
366 pub fn restore_revision(&mut self, revision: usize) -> Result<Option<HistoryMove>, EditError> {
367 self.move_history(crate::history::HistoryAction::Jump(revision))
368 }
369 pub fn check_undo(&self) -> Result<bool, EditError> {
371 self.check_history_move(crate::history::HistoryAction::Undo)
372 .map(|cost| cost.is_some())
373 }
374
375 pub fn check_restore_revision(&self, revision: usize) -> Result<bool, EditError> {
377 self.check_history_move(crate::history::HistoryAction::Jump(revision))
378 .map(|cost| cost.is_some())
379 }
380
381 fn check_history_move(
382 &self,
383 action: crate::history::HistoryAction,
384 ) -> Result<Option<usize>, EditError> {
385 if self.readonly {
386 return Err(EditError::ReadOnly);
387 }
388 let cost = self.history.movement_cost(action);
389 if let Some(cost) = cost {
390 self.epoch
391 .checked_add(cost as u64)
392 .ok_or(EditError::RevisionExhausted)?;
393 }
394 Ok(cost)
395 }
396 fn move_history(
397 &mut self,
398 action: crate::history::HistoryAction,
399 ) -> Result<Option<HistoryMove>, EditError> {
400 let Some(cost) = self.check_history_move(action)? else {
401 return Ok(None);
402 };
403 let Some(ops) = self.history.navigate(action) else {
405 return Ok(None);
406 };
407 debug_assert_eq!(cost, ops.len());
408 let start = ops.iter().map(|edit| edit.at).min().unwrap_or(0);
409 let end = ops
410 .last()
411 .map(|edit| {
412 edit.at
413 + if edit.kind == EditKind::Insert {
414 edit.text.len()
415 } else {
416 0
417 }
418 })
419 .unwrap_or(start);
420 for op in &ops {
421 let (range, text) = match op.kind {
422 EditKind::Insert => (Range::charwise(op.at, op.at), op.text.as_str()),
423 EditKind::Delete => (Range::charwise(op.at, op.at + op.text.len()), ""),
424 };
425 debug_assert!(self.validate_range(range).is_ok(), "sealed history range");
426 debug_assert!(
427 op.kind != EditKind::Delete
428 || self.rope.byte_slice(range.start.get()..range.end.get()) == op.text,
429 "sealed history content"
430 );
431 self.replace_validated(range, text, ChangeOrigin::History);
432 }
433 self.trace_history(&ops);
434 Ok(Some(HistoryMove {
435 start: start.into(),
436 end: end.into(),
437 }))
438 }
439}