kimun_notes/ropetext/change.rs
1//! What an edit did, told rather than diffed.
2
3use std::ops::Range;
4
5use crate::ropetext::position::Revision;
6
7/// One replaced region, in the coordinates of the text *after* the change.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Edit {
10 inserted: Range<usize>,
11 removed_bytes: usize,
12}
13
14impl Edit {
15 pub(crate) fn new(inserted: Range<usize>, removed_bytes: usize) -> Self {
16 Self {
17 inserted,
18 removed_bytes,
19 }
20 }
21
22 /// Byte range the new text occupies, in the new text.
23 pub fn inserted(&self) -> Range<usize> {
24 self.inserted.clone()
25 }
26
27 /// How many bytes stood there before.
28 pub fn removed_bytes(&self) -> usize {
29 self.removed_bytes
30 }
31}
32
33/// The outcome of a committed transaction, an undo, or a redo.
34///
35/// A consumer holding a derived cache — a parse, a layout — is *told* what moved
36/// and re-does that much. Nothing has to compare two buffers to find out, which
37/// is the cost this type exists to remove, and nothing has to guess how far the
38/// damage spread, which is the correctness problem underneath that cost.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct Change {
41 revision: Revision,
42 edits: Option<Vec<Edit>>,
43 rows: Range<usize>,
44 line_delta: isize,
45}
46
47impl Change {
48 pub(crate) fn new(
49 revision: Revision,
50 edits: Option<Vec<Edit>>,
51 rows: Range<usize>,
52 line_delta: isize,
53 ) -> Self {
54 Self {
55 revision,
56 edits,
57 rows,
58 line_delta,
59 }
60 }
61
62 /// Which state of the text this change produced.
63 pub fn revision(&self) -> Revision {
64 self.revision
65 }
66
67 /// The replaced regions, in the new text's coordinates.
68 ///
69 /// `None` when the change cannot be described that precisely: undoing or
70 /// redoing a group that several transactions were folded into means several
71 /// coordinate spaces, and a list of ranges that were each correct at a
72 /// different moment is worse than no list at all. A committed transaction
73 /// always reports its edits; only a coalesced group's undo declines to.
74 /// [`Self::rows`] is always populated.
75 pub fn edits(&self) -> Option<&[Edit]> {
76 self.edits.as_deref()
77 }
78
79 /// Rows of the new text whose content is not what it was, so a consumer
80 /// holding a derived cache re-derives that much. Rows *after* it are
81 /// unchanged in content but have moved by [`Self::line_delta`].
82 ///
83 /// Exact for a single transaction. Widened, never narrowed, for a coalesced
84 /// group — over-reporting costs work, under-reporting costs correctness.
85 pub fn rows(&self) -> Range<usize> {
86 self.rows.clone()
87 }
88
89 /// How the text's row count changed. Positive when rows were added.
90 pub fn line_delta(&self) -> isize {
91 self.line_delta
92 }
93
94 /// Whether the damage reaches past a single row.
95 ///
96 /// The named form of `rows().len() > 1 || line_delta() != 0`, for the caches
97 /// whose cheap path is only valid for a change confined to one row.
98 pub fn is_bulk(&self) -> bool {
99 self.line_delta != 0 || self.rows.end.saturating_sub(self.rows.start) > 1
100 }
101}