git.rs

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