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