Add test for go-to hunk and fix discovered bugs

Julia created

Change summary

crates/editor/src/editor.rs                       |  15 +
crates/editor/src/editor_tests.rs                 | 110 ++++++++++++++++
crates/editor/src/git.rs                          |  24 +++
crates/editor/src/test.rs                         |   1 
crates/editor/src/test/editor_git_test_context.rs |  56 --------
crates/editor/src/test/editor_test_context.rs     |   5 
crates/language/src/buffer.rs                     |   2 
crates/project/src/project.rs                     |   4 
8 files changed, 145 insertions(+), 72 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -5258,6 +5258,7 @@ impl Editor {
             this: &mut Editor,
             snapshot: &DisplaySnapshot,
             initial_point: Point,
+            is_wrapped: bool,
             direction: Direction,
             cx: &mut ViewContext<Editor>,
         ) -> bool {
@@ -5274,12 +5275,18 @@ impl Editor {
             let display_point = initial_point.to_display_point(snapshot);
             let mut hunks = hunks
                 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
-                .skip_while(|hunk| hunk.display_row_range().contains(&display_point.row()))
+                .skip_while(|hunk| {
+                    if is_wrapped {
+                        false
+                    } else {
+                        hunk.contains_display_row(display_point.row())
+                    }
+                })
                 .dedup();
 
             if let Some(hunk) = hunks.next() {
                 this.change_selections(Some(Autoscroll::Center), cx, |s| {
-                    let row = hunk.display_row_range().start;
+                    let row = hunk.start_display_row();
                     let point = DisplayPoint::new(row, 0);
                     s.select_display_ranges([point..point]);
                 });
@@ -5290,12 +5297,12 @@ impl Editor {
             }
         }
 
-        if !seek_in_direction(self, &snapshot, selection.head(), direction, cx) {
+        if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
             let wrapped_point = match direction {
                 Direction::Next => Point::zero(),
                 Direction::Prev => snapshot.buffer_snapshot.max_point(),
             };
-            seek_in_direction(self, &snapshot, wrapped_point, direction, cx);
+            seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
         }
     }
 

crates/editor/src/editor_tests.rs 🔗

@@ -7,11 +7,11 @@ use unindent::Unindent;
 
 use super::*;
 use crate::test::{
-    assert_text_with_selections, build_editor, editor_git_test_context::EditorGitTestContext,
-    editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
-    select_ranges,
+    assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
+    editor_test_context::EditorTestContext, select_ranges,
 };
 use gpui::{
+    executor::Deterministic,
     geometry::rect::RectF,
     platform::{WindowBounds, WindowOptions},
 };
@@ -5081,8 +5081,108 @@ fn test_combine_syntax_and_fuzzy_match_highlights() {
 }
 
 #[gpui::test]
-fn go_to_hunk(cx: &mut gpui::TestAppContext) {
-    let mut cx = EditorGitTestContext::new(cx);
+async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+    let mut cx = EditorTestContext::new(cx);
+
+    let diff_base = r#"
+        use some::mod;
+
+        const A: u32 = 42;
+
+        fn main() {
+            println!("hello");
+
+            println!("world");
+        }
+        "#
+    .unindent();
+
+    // Edits are modified, removed, modified, added
+    cx.set_state(
+        &r#"
+        use some::modified;
+
+        ˇ
+        fn main() {
+            println!("hello there");
+
+            println!("around the");
+            println!("world");
+        }
+        "#
+        .unindent(),
+    );
+
+    cx.set_diff_base(Some(&diff_base));
+    deterministic.run_until_parked();
+
+    cx.update_editor(|editor, cx| {
+        //Wrap around the bottom of the buffer
+        for _ in 0..3 {
+            editor.go_to_hunk(&GoToHunk, cx);
+        }
+    });
+
+    cx.assert_editor_state(
+        &r#"
+        ˇuse some::modified;
+    
+    
+        fn main() {
+            println!("hello there");
+    
+            println!("around the");
+            println!("world");
+        }
+        "#
+        .unindent(),
+    );
+
+    cx.update_editor(|editor, cx| {
+        //Wrap around the top of the buffer
+        for _ in 0..2 {
+            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
+        }
+    });
+
+    cx.assert_editor_state(
+        &r#"
+        use some::modified;
+
+
+        fn main() {
+        ˇ    println!("hello there");
+
+            println!("around the");
+            println!("world");
+        }
+        "#
+        .unindent(),
+    );
+
+    cx.update_editor(|editor, cx| {
+        editor.fold(&Fold, cx);
+
+        //Make sure that the fold only gets one hunk
+        for _ in 0..4 {
+            editor.go_to_hunk(&GoToHunk, cx);
+        }
+    });
+
+    cx.assert_editor_state(
+        &r#"
+        ˇuse some::modified;
+
+
+        fn main() {
+            println!("hello there");
+
+            println!("around the");
+            println!("world");
+        }
+        "#
+        .unindent(),
+    );
 }
 
 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {

crates/editor/src/git.rs 🔗

@@ -21,14 +21,32 @@ pub enum DisplayDiffHunk {
 }
 
 impl DisplayDiffHunk {
-    pub fn display_row_range(&self) -> Range<u32> {
+    pub fn start_display_row(&self) -> u32 {
         match self {
-            &DisplayDiffHunk::Folded { display_row } => display_row..display_row + 1,
+            &DisplayDiffHunk::Folded { display_row } => display_row,
             DisplayDiffHunk::Unfolded {
                 display_row_range, ..
-            } => display_row_range.clone(),
+            } => display_row_range.start,
         }
     }
+
+    pub fn contains_display_row(&self, display_row: u32) -> bool {
+        let range = match self {
+            &DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
+
+            DisplayDiffHunk::Unfolded {
+                display_row_range, ..
+            } => {
+                if display_row_range.len() == 0 {
+                    display_row_range.start..=display_row_range.end
+                } else {
+                    display_row_range.start..=display_row_range.end - 1
+                }
+            }
+        };
+
+        range.contains(&display_row)
+    }
 }
 
 pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) -> DisplayDiffHunk {

crates/editor/src/test/editor_git_test_context.rs 🔗

@@ -1,56 +0,0 @@
-use std::ops::{Deref, DerefMut};
-
-use gpui::ModelHandle;
-use language::Buffer;
-use settings::Settings;
-
-use crate::MultiBuffer;
-
-use super::{build_editor, editor_test_context::EditorTestContext};
-
-pub struct EditorGitTestContext<'a> {
-    pub cx: EditorTestContext<'a>,
-    pub buffer: ModelHandle<Buffer>,
-}
-
-impl<'a> EditorGitTestContext<'a> {
-    pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorGitTestContext<'a> {
-        let (window_id, buffer, editor) = cx.update(|cx| {
-            cx.set_global(Settings::test(cx));
-            crate::init(cx);
-
-            let buffer = cx.add_model(|cx| Buffer::new(0, "", cx));
-            let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
-
-            let (window_id, editor) =
-                cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
-
-            editor.update(cx, |_, cx| cx.focus_self());
-
-            (window_id, buffer, editor)
-        });
-
-        Self {
-            cx: EditorTestContext {
-                cx,
-                window_id,
-                editor,
-            },
-            buffer,
-        }
-    }
-}
-
-impl<'a> Deref for EditorGitTestContext<'a> {
-    type Target = EditorTestContext<'a>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cx
-    }
-}
-
-impl<'a> DerefMut for EditorGitTestContext<'a> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.cx
-    }
-}

crates/editor/src/test/editor_test_context.rs 🔗

@@ -151,6 +151,11 @@ impl<'a> EditorTestContext<'a> {
         snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
     }
 
+    pub fn set_diff_base(&mut self, diff_base: Option<&str>) {
+        let diff_base = diff_base.map(String::from);
+        self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
+    }
+
     /// Change the editor's text and selections using a string containing
     /// embedded range markers that represent the ranges and directions of
     /// each selection.

crates/language/src/buffer.rs 🔗

@@ -681,7 +681,7 @@ impl Buffer {
         self.diff_base.as_deref()
     }
 
-    pub fn update_diff_base(&mut self, diff_base: Option<String>, cx: &mut ModelContext<Self>) {
+    pub fn set_diff_base(&mut self, diff_base: Option<String>, cx: &mut ModelContext<Self>) {
         self.diff_base = diff_base;
         self.git_diff_recalc(cx);
     }

crates/project/src/project.rs 🔗

@@ -4410,7 +4410,7 @@ impl Project {
                         .await;
 
                     let buffer_id = buffer.update(&mut cx, |buffer, cx| {
-                        buffer.update_diff_base(diff_base.clone(), cx);
+                        buffer.set_diff_base(diff_base.clone(), cx);
                         buffer.remote_id()
                     });
 
@@ -4968,7 +4968,7 @@ impl Project {
                 .and_then(|b| b.upgrade(cx))
                 .ok_or_else(|| anyhow!("No such buffer {}", buffer_id))?;
 
-            buffer.update(cx, |buffer, cx| buffer.update_diff_base(diff_base, cx));
+            buffer.update(cx, |buffer, cx| buffer.set_diff_base(diff_base, cx));
 
             Ok(())
         })