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 if self.readonly {
139 return Err(EditError::ReadOnly);
140 }
141 if base != self.revision() {
142 return Err(EditError::StaleRevision {
143 expected: base,
144 found: self.revision(),
145 });
146 }
147 replacements.retain(|edit| !edit.range.is_empty() || !edit.text.is_empty());
148 for edit in &replacements {
149 self.validate_range(edit.range)?;
150 }
151 replacements.sort_unstable_by_key(|edit| (edit.range.start.get(), edit.range.end.get()));
152 let pairs: Vec<(usize, usize)> = replacements
156 .iter()
157 .map(|edit| (edit.range.start.get(), edit.range.end.get()))
158 .collect();
159 let sorted = crate::editmap::check_batch(self.len_bytes(), pairs)
160 .map_err(|()| EditError::Overlap)?;
161 debug_assert!(replacements
164 .iter()
165 .zip(sorted)
166 .all(|(edit, pair)| (edit.range.start.get(), edit.range.end.get()) == pair));
167 self.epoch
168 .checked_add(replacements.len() as u64)
169 .ok_or(EditError::RevisionExhausted)?;
170 Ok(PreparedReplacements {
171 buffer: self.trace_id(),
172 revision: base,
173 edits: replacements,
174 })
175 }
176
177 pub fn apply_prepared(
178 &mut self,
179 prepared: PreparedReplacements,
180 undo_open: bool,
181 ) -> Result<BufferRevision, EditError> {
182 if prepared.buffer != self.trace_id() {
183 return Err(EditError::WrongBuffer);
184 }
185 if self.readonly {
186 return Err(EditError::ReadOnly);
187 }
188 if prepared.revision != self.revision() {
189 return Err(EditError::StaleRevision {
190 expected: prepared.revision,
191 found: self.revision(),
192 });
193 }
194 if prepared.is_empty() {
195 return Ok(self.revision());
196 }
197 self.history.begin();
198 for replacement in prepared.edits.into_iter().rev() {
199 self.replace_validated(replacement.range, &replacement.text, ChangeOrigin::User);
200 }
201 if !undo_open {
202 self.history.commit();
203 }
204 Ok(self.revision())
205 }
206
207 fn replace_validated(&mut self, range: Range, text: &str, origin: ChangeOrigin) {
208 debug_assert!(self.validate_range(range).is_ok());
209 debug_assert!(self.epoch < u64::MAX);
210 let (start_byte, end_byte) = (range.start.get(), range.end.get());
211 let start_point = self.point_of(start_byte);
212 let old_end_point = self.point_of(end_byte);
213 let extent = Self::point_extent(text);
214 let new_end_point = if extent.0 == 0 {
215 (start_point.0, start_point.1 + extent.1)
216 } else {
217 (start_point.0 + extent.0, extent.1)
218 };
219 let edit = InputEdit {
220 start_byte,
221 old_end_byte: end_byte,
222 new_end_byte: start_byte + text.len(),
223 start_point,
224 old_end_point,
225 new_end_point,
226 };
227 let mut history = None;
228 if origin == ChangeOrigin::User {
229 if !range.is_empty() {
230 let removed = self.rope.byte_slice(start_byte..end_byte).to_string();
231 history = Some(self.history.record(
232 Edit {
233 at: start_byte,
234 text: removed.clone(),
235 kind: EditKind::Insert,
236 },
237 Edit {
238 at: start_byte,
239 text: removed,
240 kind: EditKind::Delete,
241 },
242 ));
243 }
244 if !text.is_empty() {
245 history = Some(self.history.record(
246 Edit {
247 at: start_byte,
248 text: text.into(),
249 kind: EditKind::Delete,
250 },
251 Edit {
252 at: start_byte,
253 text: text.into(),
254 kind: EditKind::Insert,
255 },
256 ));
257 }
258 }
259 let start = self.rope.byte_to_char(start_byte);
260 let end = self.rope.byte_to_char(end_byte);
261 if start != end {
262 self.rope.remove(start..end);
263 }
264 if !text.is_empty() {
265 self.rope.insert(start, text);
266 }
267 self.publish_change(edit, origin, history);
268 self.trace_edit(origin, start_byte, range.len(), text);
269 }
270
271 fn publish_change(
272 &mut self,
273 edit: InputEdit,
274 origin: ChangeOrigin,
275 history: Option<crate::history::EditRef>,
276 ) {
277 debug_assert!(self.epoch < u64::MAX);
278 self.epoch += 1;
279 self.invalidate_line_layouts(&edit);
280 self.dirty |= origin != ChangeOrigin::System;
281 self.changes.push(Change {
282 revision: self.revision(),
283 origin,
284 edit,
285 history,
286 });
287 }
288}
289
290impl UserEdit<'_> {
291 pub fn insert(&mut self, at: impl Into<ByteOffset>, text: &str) -> Result<(), EditError> {
292 let at = at.into();
293 self.replace(Range::charwise(at, at), text)
294 }
295 pub fn delete(&mut self, range: Range) -> Result<String, EditError> {
296 if self.buffer.readonly {
297 return Err(EditError::ReadOnly);
298 }
299 self.buffer.validate_range(range)?;
300 let removed = self.buffer.slice_string(range);
301 self.replace(range, "")?;
302 Ok(removed)
303 }
304 pub fn replace(&mut self, range: Range, text: &str) -> Result<(), EditError> {
305 if self.buffer.readonly {
306 return Err(EditError::ReadOnly);
307 }
308 self.buffer.validate_range(range)?;
309 if range.is_empty() && text.is_empty() {
310 return Ok(());
311 }
312 self.buffer
313 .epoch
314 .checked_add(1)
315 .ok_or(EditError::RevisionExhausted)?;
316 self.buffer
317 .replace_validated(range, text, ChangeOrigin::User);
318 Ok(())
319 }
320}
321
322impl SystemEdit<'_> {
323 pub fn replace(&mut self, range: Range, text: &str) -> Result<(), EditError> {
327 self.buffer.validate_range(range)?;
328 if range.is_empty() && text.is_empty() {
329 return Ok(());
330 }
331 self.buffer
332 .epoch
333 .checked_add(1)
334 .ok_or(EditError::RevisionExhausted)?;
335 self.buffer
336 .replace_validated(range, text, ChangeOrigin::System);
337 Ok(())
338 }
339
340 pub fn replace_all(&mut self, text: &str) -> Result<(), EditError> {
342 self.buffer
343 .epoch
344 .checked_add(1)
345 .ok_or(EditError::RevisionExhausted)?;
346 self.buffer.replace_validated(
347 Range::charwise(0, self.buffer.len_bytes()),
348 text,
349 ChangeOrigin::System,
350 );
351 self.buffer.adopt_history(Default::default());
352 Ok(())
353 }
354
355 pub fn replace_rope(&mut self, rope: ropey::Rope) -> Result<(), EditError> {
358 self.buffer
359 .epoch
360 .checked_add(1)
361 .ok_or(EditError::RevisionExhausted)?;
362 let old_end_byte = self.buffer.len_bytes();
363 let old_end_point = self.buffer.point_of(old_end_byte);
364 let new_end_byte = rope.len_bytes();
365 let line = rope.byte_to_line(new_end_byte);
366 let new_end_point = (line, new_end_byte - rope.line_to_byte(line));
367 self.buffer.rope = rope;
368 self.buffer.publish_change(
369 InputEdit {
370 start_byte: 0,
371 old_end_byte,
372 new_end_byte,
373 start_point: (0, 0),
374 old_end_point,
375 new_end_point,
376 },
377 ChangeOrigin::System,
378 None,
379 );
380 self.buffer.adopt_history(Default::default());
381 self.buffer.trace_snapshot(old_end_byte);
382 Ok(())
383 }
384}
385
386#[derive(Debug, Clone, Copy)]
387pub struct HistoryMove {
388 pub start: ByteOffset,
389 pub end: ByteOffset,
390}
391
392impl Buffer {
393 pub fn undo(&mut self) -> Result<Option<HistoryMove>, EditError> {
394 self.move_history(crate::history::HistoryAction::Undo)
395 }
396 pub fn redo(&mut self) -> Result<Option<HistoryMove>, EditError> {
397 self.move_history(crate::history::HistoryAction::Redo)
398 }
399 pub fn restore_revision(&mut self, revision: usize) -> Result<Option<HistoryMove>, EditError> {
400 self.move_history(crate::history::HistoryAction::Jump(revision))
401 }
402 pub fn check_undo(&self) -> Result<bool, EditError> {
404 self.check_history_move(crate::history::HistoryAction::Undo)
405 .map(|cost| cost.is_some())
406 }
407
408 pub fn check_restore_revision(&self, revision: usize) -> Result<bool, EditError> {
410 self.check_history_move(crate::history::HistoryAction::Jump(revision))
411 .map(|cost| cost.is_some())
412 }
413
414 fn check_history_move(
415 &self,
416 action: crate::history::HistoryAction,
417 ) -> Result<Option<usize>, EditError> {
418 if self.readonly {
419 return Err(EditError::ReadOnly);
420 }
421 let cost = self.history.movement_cost(action);
422 if let Some(cost) = cost {
423 self.epoch
424 .checked_add(cost as u64)
425 .ok_or(EditError::RevisionExhausted)?;
426 }
427 Ok(cost)
428 }
429 fn move_history(
430 &mut self,
431 action: crate::history::HistoryAction,
432 ) -> Result<Option<HistoryMove>, EditError> {
433 let Some(cost) = self.check_history_move(action)? else {
434 return Ok(None);
435 };
436 let Some(ops) = self.history.navigate(action) else {
438 return Ok(None);
439 };
440 debug_assert_eq!(cost, ops.len());
441 let start = ops.iter().map(|edit| edit.at).min().unwrap_or(0);
442 let end = ops
443 .last()
444 .map(|edit| {
445 edit.at
446 + if edit.kind == EditKind::Insert {
447 edit.text.len()
448 } else {
449 0
450 }
451 })
452 .unwrap_or(start);
453 for op in &ops {
454 let (range, text) = match op.kind {
455 EditKind::Insert => (Range::charwise(op.at, op.at), op.text.as_str()),
456 EditKind::Delete => (Range::charwise(op.at, op.at + op.text.len()), ""),
457 };
458 debug_assert!(self.validate_range(range).is_ok(), "sealed history range");
459 debug_assert!(
460 op.kind != EditKind::Delete
461 || self.rope.byte_slice(range.start.get()..range.end.get()) == op.text,
462 "sealed history content"
463 );
464 self.replace_validated(range, text, ChangeOrigin::History);
465 }
466 self.trace_history(&ops);
467 Ok(Some(HistoryMove {
468 start: start.into(),
469 end: end.into(),
470 }))
471 }
472}