floem_editor_core/buffer/
diff.rs1use std::{
2 borrow::Cow,
3 ops::Range,
4 sync::{
5 atomic::{self, AtomicU64},
6 Arc,
7 },
8};
9
10use lapce_xi_rope::Rope;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum DiffResult<T> {
14 Left(T),
15 Both(T, T),
16 Right(T),
17}
18
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct DiffBothInfo {
21 pub left: Range<usize>,
22 pub right: Range<usize>,
23 pub skip: Option<Range<usize>>,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub enum DiffLines {
28 Left(Range<usize>),
29 Both(DiffBothInfo),
30 Right(Range<usize>),
31}
32
33pub enum DiffExpand {
34 Up(usize),
35 Down(usize),
36 All,
37}
38
39pub fn expand_diff_lines(
40 diff_lines: &mut [DiffLines],
41 line: usize,
42 expand: DiffExpand,
43 is_right: bool,
44) {
45 for diff_line in diff_lines.iter_mut() {
46 if let DiffLines::Both(info) = diff_line {
47 if (is_right && info.right.start == line) || (!is_right && info.left.start == line) {
48 match expand {
49 DiffExpand::All => {
50 info.skip = None;
51 }
52 DiffExpand::Up(n) => {
53 if let Some(skip) = &mut info.skip {
54 if n >= skip.len() {
55 info.skip = None;
56 } else {
57 skip.start += n;
58 }
59 }
60 }
61 DiffExpand::Down(n) => {
62 if let Some(skip) = &mut info.skip {
63 if n >= skip.len() {
64 info.skip = None;
65 } else {
66 skip.end -= n;
67 }
68 }
69 }
70 }
71 break;
72 }
73 }
74 }
75}
76
77pub fn rope_diff(
78 left_rope: Rope,
79 right_rope: Rope,
80 rev: u64,
81 atomic_rev: Arc<AtomicU64>,
82 context_lines: Option<usize>,
83) -> Option<Vec<DiffLines>> {
84 let left_lines = left_rope.lines(..).collect::<Vec<Cow<str>>>();
85 let right_lines = right_rope.lines(..).collect::<Vec<Cow<str>>>();
86
87 let left_count = left_lines.len();
88 let right_count = right_lines.len();
89 let min_count = std::cmp::min(left_count, right_count);
90
91 let leading_equals = left_lines
92 .iter()
93 .zip(right_lines.iter())
94 .take_while(|p| p.0 == p.1)
95 .count();
96 let trailing_equals = left_lines
97 .iter()
98 .rev()
99 .zip(right_lines.iter().rev())
100 .take(min_count - leading_equals)
101 .take_while(|p| p.0 == p.1)
102 .count();
103
104 let left_diff_size = left_count - leading_equals - trailing_equals;
105 let right_diff_size = right_count - leading_equals - trailing_equals;
106
107 let table: Vec<Vec<u32>> = {
108 let mut table = vec![vec![0; right_diff_size + 1]; left_diff_size + 1];
109 let left_skip = left_lines.iter().skip(leading_equals).take(left_diff_size);
110 let right_skip = right_lines
111 .iter()
112 .skip(leading_equals)
113 .take(right_diff_size);
114
115 for (i, l) in left_skip.enumerate() {
116 for (j, r) in right_skip.clone().enumerate() {
117 if atomic_rev.load(atomic::Ordering::Acquire) != rev {
118 return None;
119 }
120 table[i + 1][j + 1] = if l == r {
121 table[i][j] + 1
122 } else {
123 std::cmp::max(table[i][j + 1], table[i + 1][j])
124 };
125 }
126 }
127
128 table
129 };
130
131 let diff = {
132 let mut diff = Vec::with_capacity(left_diff_size + right_diff_size);
133 let mut i = left_diff_size;
134 let mut j = right_diff_size;
135 let mut li = left_lines.iter().rev().skip(trailing_equals);
136 let mut ri = right_lines.iter().skip(trailing_equals);
137
138 loop {
139 if atomic_rev.load(atomic::Ordering::Acquire) != rev {
140 return None;
141 }
142 if j > 0 && (i == 0 || table[i][j] == table[i][j - 1]) {
143 j -= 1;
144 diff.push(DiffResult::Right(ri.next().unwrap()));
145 } else if i > 0 && (j == 0 || table[i][j] == table[i - 1][j]) {
146 i -= 1;
147 diff.push(DiffResult::Left(li.next().unwrap()));
148 } else if i > 0 && j > 0 {
149 i -= 1;
150 j -= 1;
151 diff.push(DiffResult::Both(li.next().unwrap(), ri.next().unwrap()));
152 } else {
153 break;
154 }
155 }
156
157 diff
158 };
159
160 let mut changes = Vec::new();
161 let mut left_line = 0;
162 let mut right_line = 0;
163 if leading_equals > 0 {
164 changes.push(DiffLines::Both(DiffBothInfo {
165 left: 0..leading_equals,
166 right: 0..leading_equals,
167 skip: None,
168 }))
169 }
170 left_line += leading_equals;
171 right_line += leading_equals;
172
173 for diff in diff.iter().rev() {
174 if atomic_rev.load(atomic::Ordering::Acquire) != rev {
175 return None;
176 }
177 match diff {
178 DiffResult::Left(_) => {
179 match changes.last_mut() {
180 Some(DiffLines::Left(r)) => r.end = left_line + 1,
181 _ => changes.push(DiffLines::Left(left_line..left_line + 1)),
182 }
183 left_line += 1;
184 }
185 DiffResult::Both(_, _) => {
186 match changes.last_mut() {
187 Some(DiffLines::Both(info)) => {
188 info.left.end = left_line + 1;
189 info.right.end = right_line + 1;
190 }
191 _ => changes.push(DiffLines::Both(DiffBothInfo {
192 left: left_line..left_line + 1,
193 right: right_line..right_line + 1,
194 skip: None,
195 })),
196 }
197 left_line += 1;
198 right_line += 1;
199 }
200 DiffResult::Right(_) => {
201 match changes.last_mut() {
202 Some(DiffLines::Right(r)) => r.end = right_line + 1,
203 _ => changes.push(DiffLines::Right(right_line..right_line + 1)),
204 }
205 right_line += 1;
206 }
207 }
208 }
209
210 if trailing_equals > 0 {
211 changes.push(DiffLines::Both(DiffBothInfo {
212 left: left_count - trailing_equals..left_count,
213 right: right_count - trailing_equals..right_count,
214 skip: None,
215 }));
216 }
217 if let Some(context_lines) = context_lines {
218 if !changes.is_empty() {
219 let changes_last = changes.len() - 1;
220 for (i, change) in changes.iter_mut().enumerate() {
221 if atomic_rev.load(atomic::Ordering::Acquire) != rev {
222 return None;
223 }
224 if let DiffLines::Both(info) = change {
225 if i == 0 || i == changes_last {
226 if info.right.len() > context_lines {
227 if i == 0 {
228 info.skip = Some(0..info.right.len() - context_lines);
229 } else {
230 info.skip = Some(context_lines..info.right.len());
231 }
232 }
233 } else if info.right.len() > context_lines * 2 {
234 info.skip = Some(context_lines..info.right.len() - context_lines);
235 }
236 }
237 }
238 }
239 }
240
241 Some(changes)
242}