|
1 | 1 | pub mod blame;
|
2 |
| - |
3 |
| -use std::ops::Range; |
4 |
| - |
5 |
| -use git::diff::DiffHunkStatus; |
6 |
| -use language::Point; |
7 |
| -use multi_buffer::{Anchor, MultiBufferDiffHunk}; |
8 |
| - |
9 |
| -use crate::{ |
10 |
| - display_map::{DisplaySnapshot, ToDisplayPoint}, |
11 |
| - hunk_status, AnchorRangeExt, DisplayRow, |
12 |
| -}; |
13 |
| - |
14 |
| -#[derive(Debug, Clone, PartialEq, Eq)] |
15 |
| -pub enum DisplayDiffHunk { |
16 |
| - Folded { |
17 |
| - display_row: DisplayRow, |
18 |
| - }, |
19 |
| - |
20 |
| - Unfolded { |
21 |
| - diff_base_byte_range: Range<usize>, |
22 |
| - display_row_range: Range<DisplayRow>, |
23 |
| - multi_buffer_range: Range<Anchor>, |
24 |
| - status: DiffHunkStatus, |
25 |
| - }, |
26 |
| -} |
27 |
| - |
28 |
| -impl DisplayDiffHunk { |
29 |
| - pub fn start_display_row(&self) -> DisplayRow { |
30 |
| - match self { |
31 |
| - &DisplayDiffHunk::Folded { display_row } => display_row, |
32 |
| - DisplayDiffHunk::Unfolded { |
33 |
| - display_row_range, .. |
34 |
| - } => display_row_range.start, |
35 |
| - } |
36 |
| - } |
37 |
| - |
38 |
| - pub fn contains_display_row(&self, display_row: DisplayRow) -> bool { |
39 |
| - let range = match self { |
40 |
| - &DisplayDiffHunk::Folded { display_row } => display_row..=display_row, |
41 |
| - |
42 |
| - DisplayDiffHunk::Unfolded { |
43 |
| - display_row_range, .. |
44 |
| - } => display_row_range.start..=display_row_range.end, |
45 |
| - }; |
46 |
| - |
47 |
| - range.contains(&display_row) |
48 |
| - } |
49 |
| -} |
50 |
| - |
51 |
| -pub fn diff_hunk_to_display( |
52 |
| - hunk: &MultiBufferDiffHunk, |
53 |
| - snapshot: &DisplaySnapshot, |
54 |
| -) -> DisplayDiffHunk { |
55 |
| - let hunk_start_point = Point::new(hunk.row_range.start.0, 0); |
56 |
| - let hunk_start_point_sub = Point::new(hunk.row_range.start.0.saturating_sub(1), 0); |
57 |
| - let hunk_end_point_sub = Point::new( |
58 |
| - hunk.row_range |
59 |
| - .end |
60 |
| - .0 |
61 |
| - .saturating_sub(1) |
62 |
| - .max(hunk.row_range.start.0), |
63 |
| - 0, |
64 |
| - ); |
65 |
| - |
66 |
| - let status = hunk_status(hunk); |
67 |
| - let is_removal = status == DiffHunkStatus::Removed; |
68 |
| - |
69 |
| - let folds_start = Point::new(hunk.row_range.start.0.saturating_sub(2), 0); |
70 |
| - let folds_end = Point::new(hunk.row_range.end.0 + 2, 0); |
71 |
| - let folds_range = folds_start..folds_end; |
72 |
| - |
73 |
| - let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| { |
74 |
| - let fold_point_range = fold.range.to_point(&snapshot.buffer_snapshot); |
75 |
| - let fold_point_range = fold_point_range.start..=fold_point_range.end; |
76 |
| - |
77 |
| - let folded_start = fold_point_range.contains(&hunk_start_point); |
78 |
| - let folded_end = fold_point_range.contains(&hunk_end_point_sub); |
79 |
| - let folded_start_sub = fold_point_range.contains(&hunk_start_point_sub); |
80 |
| - |
81 |
| - (folded_start && folded_end) || (is_removal && folded_start_sub) |
82 |
| - }); |
83 |
| - |
84 |
| - if let Some(fold) = containing_fold { |
85 |
| - let row = fold.range.start.to_display_point(snapshot).row(); |
86 |
| - DisplayDiffHunk::Folded { display_row: row } |
87 |
| - } else { |
88 |
| - let start = hunk_start_point.to_display_point(snapshot).row(); |
89 |
| - |
90 |
| - let hunk_end_row = hunk.row_range.end.max(hunk.row_range.start); |
91 |
| - let hunk_end_point = Point::new(hunk_end_row.0, 0); |
92 |
| - |
93 |
| - let multi_buffer_start = snapshot.buffer_snapshot.anchor_before(hunk_start_point); |
94 |
| - let multi_buffer_end = snapshot.buffer_snapshot.anchor_after(hunk_end_point); |
95 |
| - let end = hunk_end_point.to_display_point(snapshot).row(); |
96 |
| - |
97 |
| - DisplayDiffHunk::Unfolded { |
98 |
| - display_row_range: start..end, |
99 |
| - multi_buffer_range: multi_buffer_start..multi_buffer_end, |
100 |
| - status, |
101 |
| - diff_base_byte_range: hunk.diff_base_byte_range.clone(), |
102 |
| - } |
103 |
| - } |
104 |
| -} |
105 |
| - |
106 |
| -#[cfg(test)] |
107 |
| -mod tests { |
108 |
| - use crate::Point; |
109 |
| - use crate::{editor_tests::init_test, hunk_status}; |
110 |
| - use gpui::{Context, TestAppContext}; |
111 |
| - use language::Capability::ReadWrite; |
112 |
| - use multi_buffer::{ExcerptRange, MultiBuffer, MultiBufferRow}; |
113 |
| - use project::{FakeFs, Project}; |
114 |
| - use unindent::Unindent; |
115 |
| - #[gpui::test] |
116 |
| - async fn test_diff_hunks_in_range(cx: &mut TestAppContext) { |
117 |
| - use git::diff::DiffHunkStatus; |
118 |
| - init_test(cx, |_| {}); |
119 |
| - |
120 |
| - let fs = FakeFs::new(cx.background_executor.clone()); |
121 |
| - let project = Project::test(fs, [], cx).await; |
122 |
| - |
123 |
| - // buffer has two modified hunks with two rows each |
124 |
| - let buffer_1 = project.update(cx, |project, cx| { |
125 |
| - project.create_local_buffer( |
126 |
| - " |
127 |
| - 1.zero |
128 |
| - 1.ONE |
129 |
| - 1.TWO |
130 |
| - 1.three |
131 |
| - 1.FOUR |
132 |
| - 1.FIVE |
133 |
| - 1.six |
134 |
| - " |
135 |
| - .unindent() |
136 |
| - .as_str(), |
137 |
| - None, |
138 |
| - cx, |
139 |
| - ) |
140 |
| - }); |
141 |
| - buffer_1.update(cx, |buffer, cx| { |
142 |
| - buffer.set_diff_base( |
143 |
| - Some( |
144 |
| - " |
145 |
| - 1.zero |
146 |
| - 1.one |
147 |
| - 1.two |
148 |
| - 1.three |
149 |
| - 1.four |
150 |
| - 1.five |
151 |
| - 1.six |
152 |
| - " |
153 |
| - .unindent(), |
154 |
| - ), |
155 |
| - cx, |
156 |
| - ); |
157 |
| - }); |
158 |
| - |
159 |
| - // buffer has a deletion hunk and an insertion hunk |
160 |
| - let buffer_2 = project.update(cx, |project, cx| { |
161 |
| - project.create_local_buffer( |
162 |
| - " |
163 |
| - 2.zero |
164 |
| - 2.one |
165 |
| - 2.two |
166 |
| - 2.three |
167 |
| - 2.four |
168 |
| - 2.five |
169 |
| - 2.six |
170 |
| - " |
171 |
| - .unindent() |
172 |
| - .as_str(), |
173 |
| - None, |
174 |
| - cx, |
175 |
| - ) |
176 |
| - }); |
177 |
| - buffer_2.update(cx, |buffer, cx| { |
178 |
| - buffer.set_diff_base( |
179 |
| - Some( |
180 |
| - " |
181 |
| - 2.zero |
182 |
| - 2.one |
183 |
| - 2.one-and-a-half |
184 |
| - 2.two |
185 |
| - 2.three |
186 |
| - 2.four |
187 |
| - 2.six |
188 |
| - " |
189 |
| - .unindent(), |
190 |
| - ), |
191 |
| - cx, |
192 |
| - ); |
193 |
| - }); |
194 |
| - |
195 |
| - cx.background_executor.run_until_parked(); |
196 |
| - |
197 |
| - let multibuffer = cx.new_model(|cx| { |
198 |
| - let mut multibuffer = MultiBuffer::new(ReadWrite); |
199 |
| - multibuffer.push_excerpts( |
200 |
| - buffer_1.clone(), |
201 |
| - [ |
202 |
| - // excerpt ends in the middle of a modified hunk |
203 |
| - ExcerptRange { |
204 |
| - context: Point::new(0, 0)..Point::new(1, 5), |
205 |
| - primary: Default::default(), |
206 |
| - }, |
207 |
| - // excerpt begins in the middle of a modified hunk |
208 |
| - ExcerptRange { |
209 |
| - context: Point::new(5, 0)..Point::new(6, 5), |
210 |
| - primary: Default::default(), |
211 |
| - }, |
212 |
| - ], |
213 |
| - cx, |
214 |
| - ); |
215 |
| - multibuffer.push_excerpts( |
216 |
| - buffer_2.clone(), |
217 |
| - [ |
218 |
| - // excerpt ends at a deletion |
219 |
| - ExcerptRange { |
220 |
| - context: Point::new(0, 0)..Point::new(1, 5), |
221 |
| - primary: Default::default(), |
222 |
| - }, |
223 |
| - // excerpt starts at a deletion |
224 |
| - ExcerptRange { |
225 |
| - context: Point::new(2, 0)..Point::new(2, 5), |
226 |
| - primary: Default::default(), |
227 |
| - }, |
228 |
| - // excerpt fully contains a deletion hunk |
229 |
| - ExcerptRange { |
230 |
| - context: Point::new(1, 0)..Point::new(2, 5), |
231 |
| - primary: Default::default(), |
232 |
| - }, |
233 |
| - // excerpt fully contains an insertion hunk |
234 |
| - ExcerptRange { |
235 |
| - context: Point::new(4, 0)..Point::new(6, 5), |
236 |
| - primary: Default::default(), |
237 |
| - }, |
238 |
| - ], |
239 |
| - cx, |
240 |
| - ); |
241 |
| - multibuffer |
242 |
| - }); |
243 |
| - |
244 |
| - let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx)); |
245 |
| - |
246 |
| - assert_eq!( |
247 |
| - snapshot.text(), |
248 |
| - " |
249 |
| - 1.zero |
250 |
| - 1.ONE |
251 |
| - 1.FIVE |
252 |
| - 1.six |
253 |
| - 2.zero |
254 |
| - 2.one |
255 |
| - 2.two |
256 |
| - 2.one |
257 |
| - 2.two |
258 |
| - 2.four |
259 |
| - 2.five |
260 |
| - 2.six" |
261 |
| - .unindent() |
262 |
| - ); |
263 |
| - |
264 |
| - let expected = [ |
265 |
| - ( |
266 |
| - DiffHunkStatus::Modified, |
267 |
| - MultiBufferRow(1)..MultiBufferRow(2), |
268 |
| - ), |
269 |
| - ( |
270 |
| - DiffHunkStatus::Modified, |
271 |
| - MultiBufferRow(2)..MultiBufferRow(3), |
272 |
| - ), |
273 |
| - //TODO: Define better when and where removed hunks show up at range extremities |
274 |
| - ( |
275 |
| - DiffHunkStatus::Removed, |
276 |
| - MultiBufferRow(6)..MultiBufferRow(6), |
277 |
| - ), |
278 |
| - ( |
279 |
| - DiffHunkStatus::Removed, |
280 |
| - MultiBufferRow(8)..MultiBufferRow(8), |
281 |
| - ), |
282 |
| - ( |
283 |
| - DiffHunkStatus::Added, |
284 |
| - MultiBufferRow(10)..MultiBufferRow(11), |
285 |
| - ), |
286 |
| - ]; |
287 |
| - |
288 |
| - assert_eq!( |
289 |
| - snapshot |
290 |
| - .git_diff_hunks_in_range(MultiBufferRow(0)..MultiBufferRow(12)) |
291 |
| - .map(|hunk| (hunk_status(&hunk), hunk.row_range)) |
292 |
| - .collect::<Vec<_>>(), |
293 |
| - &expected, |
294 |
| - ); |
295 |
| - |
296 |
| - assert_eq!( |
297 |
| - snapshot |
298 |
| - .git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(12)) |
299 |
| - .map(|hunk| (hunk_status(&hunk), hunk.row_range)) |
300 |
| - .collect::<Vec<_>>(), |
301 |
| - expected |
302 |
| - .iter() |
303 |
| - .rev() |
304 |
| - .cloned() |
305 |
| - .collect::<Vec<_>>() |
306 |
| - .as_slice(), |
307 |
| - ); |
308 |
| - } |
309 |
| -} |
0 commit comments