From c4b21a0ab54a2c99e0354e4324b0db9ae18ba222 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 24 Oct 2022 16:33:43 -0400 Subject: [PATCH] Add action to go to next/previous git diff in editor Co-Authored-By: Kay Simmons --- assets/keymaps/default.json | 4 +- crates/collab/src/integration_tests.rs | 18 +-- crates/editor/src/editor.rs | 67 +++++++++++ crates/editor/src/element.rs | 134 +++++++-------------- crates/editor/src/git.rs | 74 ++++++++++++ crates/editor/src/multi_buffer.rs | 5 +- crates/editor/src/selections_collection.rs | 1 - crates/git/src/diff.rs | 22 ++-- crates/language/src/buffer.rs | 4 +- 9 files changed, 219 insertions(+), 110 deletions(-) create mode 100644 crates/editor/src/git.rs diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index b4ce8c1b92ceb9eb29e92a2d6bb44d05202ed279..7ca49a414d3aba99d6c908a86abd613ff8be967b 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -402,7 +402,9 @@ { "context": "Editor", "bindings": { - "alt-enter": "editor::OpenExcerpts" + "alt-enter": "editor::OpenExcerpts", + "cmd-f8": "editor::GoToHunk", + "cmd-shift-f8": "editor::GoToPrevHunk" } }, { diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 41c9052ebeb454bdba83c6f73e446c4a3b3cea17..166de2b4ac2b848353fe28389106f6cbd2fc4844 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -1483,7 +1483,7 @@ async fn test_git_diff_base_change( buffer_local_a.read_with(cx_a, |buffer, _| { assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(1..2, "", "two\n")], @@ -1503,7 +1503,7 @@ async fn test_git_diff_base_change( buffer_remote_a.read_with(cx_b, |buffer, _| { assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(1..2, "", "two\n")], @@ -1527,7 +1527,7 @@ async fn test_git_diff_base_change( assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(2..3, "", "three\n")], @@ -1538,7 +1538,7 @@ async fn test_git_diff_base_change( buffer_remote_a.read_with(cx_b, |buffer, _| { assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(2..3, "", "three\n")], @@ -1581,7 +1581,7 @@ async fn test_git_diff_base_change( buffer_local_b.read_with(cx_a, |buffer, _| { assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(1..2, "", "two\n")], @@ -1601,7 +1601,7 @@ async fn test_git_diff_base_change( buffer_remote_b.read_with(cx_b, |buffer, _| { assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(1..2, "", "two\n")], @@ -1629,12 +1629,12 @@ async fn test_git_diff_base_change( "{:?}", buffer .snapshot() - .git_diff_hunks_in_range(0..4) + .git_diff_hunks_in_range(0..4, false) .collect::>() ); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(2..3, "", "three\n")], @@ -1645,7 +1645,7 @@ async fn test_git_diff_base_change( buffer_remote_b.read_with(cx_b, |buffer, _| { assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); git::diff::assert_hunks( - buffer.snapshot().git_diff_hunks_in_range(0..4), + buffer.snapshot().git_diff_hunks_in_range(0..4, false), &buffer, &diff_base, &[(2..3, "", "three\n")], diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 327f143db57e725f138b15e12f9a90e289973417..42f26b2cf11fc7ac1512b3887b5f1c2139f0ac6f 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1,6 +1,7 @@ mod blink_manager; pub mod display_map; mod element; +mod git; mod highlight_matching_bracket; mod hover_popover; pub mod items; @@ -42,6 +43,7 @@ use gpui::{ use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; pub use items::MAX_TAB_TITLE_LEN; +use itertools::Itertools; pub use language::{char_kind, CharKind}; use language::{ AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape, @@ -79,6 +81,8 @@ use theme::{DiagnosticStyle, Theme}; use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ItemNavHistory, Workspace}; +use crate::git::diff_hunk_to_display; + const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1); const MAX_LINE_LEN: usize = 1024; @@ -162,6 +166,8 @@ actions!( NewlineBelow, GoToDiagnostic, GoToPrevDiagnostic, + GoToHunk, + GoToPrevHunk, Indent, Outdent, DeleteLine, @@ -338,6 +344,8 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Editor::redo_selection); cx.add_action(Editor::go_to_diagnostic); cx.add_action(Editor::go_to_prev_diagnostic); + cx.add_action(Editor::go_to_hunk); + cx.add_action(Editor::go_to_prev_hunk); cx.add_action(Editor::go_to_definition); cx.add_action(Editor::go_to_type_definition); cx.add_action(Editor::fold); @@ -5232,6 +5240,65 @@ impl Editor { } } + fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext) { + self.go_to_hunk_impl(Direction::Next, cx) + } + + fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext) { + self.go_to_hunk_impl(Direction::Prev, cx) + } + + pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext) { + let snapshot = self + .display_map + .update(cx, |display_map, cx| display_map.snapshot(cx)); + let selection = self.selections.newest::(cx); + + fn seek_in_direction( + this: &mut Editor, + snapshot: &DisplaySnapshot, + initial_point: Point, + direction: Direction, + cx: &mut ViewContext, + ) -> bool { + let hunks = if direction == Direction::Next { + snapshot + .buffer_snapshot + .git_diff_hunks_in_range(initial_point.row..u32::MAX, false) + } else { + snapshot + .buffer_snapshot + .git_diff_hunks_in_range(0..initial_point.row, true) + }; + + 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())) + .dedup(); + + if let Some(hunk) = hunks.next() { + this.change_selections(Some(Autoscroll::Center), cx, |s| { + let row = hunk.display_row_range().start; + let point = DisplayPoint::new(row, 0); + s.select_display_ranges([point..point]); + }); + + true + } else { + false + } + } + + if !seek_in_direction(self, &snapshot, selection.head(), 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); + } + } + pub fn go_to_definition( workspace: &mut Workspace, _: &GoToDefinition, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d80b4ea844433b9fd75e0b13f3395899304ed85b..21ad1aacbe4a9568ce563f947958149ac34430ad 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -5,6 +5,7 @@ use super::{ }; use crate::{ display_map::{BlockStyle, DisplaySnapshot, TransformBlock}, + git::{diff_hunk_to_display, DisplayDiffHunk}, hover_popover::{ HoverAt, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT, }, @@ -12,7 +13,7 @@ use crate::{ GoToFetchedDefinition, GoToFetchedTypeDefinition, UpdateGoToDefinitionLink, }, mouse_context_menu::DeployMouseContextMenu, - AnchorRangeExt, EditorStyle, + EditorStyle, }; use clock::ReplicaId; use collections::{BTreeMap, HashMap}; @@ -33,8 +34,9 @@ use gpui::{ Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent, MouseRegion, MutableAppContext, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WeakViewHandle, }; +use itertools::Itertools; use json::json; -use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Point, Selection}; +use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection}; use project::ProjectPath; use settings::{GitGutter, Settings}; use smallvec::SmallVec; @@ -46,13 +48,6 @@ use std::{ sync::Arc, }; -#[derive(Debug)] -struct DiffHunkLayout { - visual_range: Range, - status: DiffHunkStatus, - is_folded: bool, -} - struct SelectionLayout { head: DisplayPoint, cursor_shape: CursorShape, @@ -576,17 +571,11 @@ impl EditorElement { let scroll_position = layout.position_map.snapshot.scroll_position(); let scroll_top = scroll_position.y() * line_height; - for hunk in &layout.hunk_layouts { - let color = match (hunk.status, hunk.is_folded) { - (DiffHunkStatus::Added, false) => diff_style.inserted, - (DiffHunkStatus::Modified, false) => diff_style.modified, - + for hunk in &layout.display_hunks { + let (display_row_range, status) = match hunk { //TODO: This rendering is entirely a horrible hack - (DiffHunkStatus::Removed, false) => { - let row = hunk.visual_range.start; - - let offset = line_height / 2.; - let start_y = row as f32 * line_height - offset - scroll_top; + &DisplayDiffHunk::Folded { display_row: row } => { + let start_y = row as f32 * line_height - scroll_top; let end_y = start_y + line_height; let width = diff_style.removed_width_em * line_height; @@ -596,7 +585,7 @@ impl EditorElement { cx.scene.push_quad(Quad { bounds: highlight_bounds, - background: Some(diff_style.deleted), + background: Some(diff_style.modified), border: Border::new(0., Color::transparent_black()), corner_radius: 1. * line_height, }); @@ -604,9 +593,22 @@ impl EditorElement { continue; } - (_, true) => { - let row = hunk.visual_range.start; - let start_y = row as f32 * line_height - scroll_top; + DisplayDiffHunk::Unfolded { + display_row_range, + status, + } => (display_row_range, status), + }; + + let color = match status { + DiffHunkStatus::Added => diff_style.inserted, + DiffHunkStatus::Modified => diff_style.modified, + + //TODO: This rendering is entirely a horrible hack + DiffHunkStatus::Removed => { + let row = display_row_range.start; + + let offset = line_height / 2.; + let start_y = row as f32 * line_height - offset - scroll_top; let end_y = start_y + line_height; let width = diff_style.removed_width_em * line_height; @@ -616,7 +618,7 @@ impl EditorElement { cx.scene.push_quad(Quad { bounds: highlight_bounds, - background: Some(diff_style.modified), + background: Some(diff_style.deleted), border: Border::new(0., Color::transparent_black()), corner_radius: 1. * line_height, }); @@ -625,8 +627,8 @@ impl EditorElement { } }; - let start_row = hunk.visual_range.start; - let end_row = hunk.visual_range.end; + let start_row = display_row_range.start; + let end_row = display_row_range.end; let start_y = start_row as f32 * line_height - scroll_top; let end_y = end_row as f32 * line_height - scroll_top; @@ -1107,69 +1109,23 @@ impl EditorElement { //If a fold contains any hunks then that fold line is marked as modified fn layout_git_gutters( &self, - rows: Range, + display_rows: Range, snapshot: &EditorSnapshot, - ) -> Vec { + ) -> Vec { let buffer_snapshot = &snapshot.buffer_snapshot; - let visual_start = DisplayPoint::new(rows.start, 0).to_point(snapshot).row; - let visual_end = DisplayPoint::new(rows.end, 0).to_point(snapshot).row; - let hunks = buffer_snapshot.git_diff_hunks_in_range(visual_start..visual_end); - - let mut layouts = Vec::::new(); - - for hunk in hunks { - let hunk_start_point = Point::new(hunk.buffer_range.start, 0); - let hunk_end_point = Point::new(hunk.buffer_range.end, 0); - let hunk_start_point_sub = Point::new(hunk.buffer_range.start.saturating_sub(1), 0); - let hunk_end_point_sub = Point::new( - hunk.buffer_range - .end - .saturating_sub(1) - .max(hunk.buffer_range.start), - 0, - ); - - let is_removal = hunk.status() == DiffHunkStatus::Removed; - - let folds_start = Point::new(hunk.buffer_range.start.saturating_sub(1), 0); - let folds_end = Point::new(hunk.buffer_range.end + 1, 0); - let folds_range = folds_start..folds_end; - - let containing_fold = snapshot.folds_in_range(folds_range).find(|fold_range| { - let fold_point_range = fold_range.to_point(buffer_snapshot); - let fold_point_range = fold_point_range.start..=fold_point_range.end; - - let folded_start = fold_point_range.contains(&hunk_start_point); - let folded_end = fold_point_range.contains(&hunk_end_point_sub); - let folded_start_sub = fold_point_range.contains(&hunk_start_point_sub); - - (folded_start && folded_end) || (is_removal && folded_start_sub) - }); - - let visual_range = if let Some(fold) = containing_fold { - let row = fold.start.to_display_point(snapshot).row(); - row..row - } else { - let start = hunk_start_point.to_display_point(snapshot).row(); - let end = hunk_end_point.to_display_point(snapshot).row(); - start..end - }; - - let has_existing_layout = match layouts.last() { - Some(e) => visual_range == e.visual_range && e.status == hunk.status(), - None => false, - }; - - if !has_existing_layout { - layouts.push(DiffHunkLayout { - visual_range, - status: hunk.status(), - is_folded: containing_fold.is_some(), - }); - } - } - layouts + let buffer_start_row = DisplayPoint::new(display_rows.start, 0) + .to_point(snapshot) + .row; + let buffer_end_row = DisplayPoint::new(display_rows.end, 0) + .to_point(snapshot) + .row; + + buffer_snapshot + .git_diff_hunks_in_range(buffer_start_row..buffer_end_row, false) + .map(|hunk| diff_hunk_to_display(hunk, snapshot)) + .dedup() + .collect() } fn layout_line_numbers( @@ -1721,7 +1677,7 @@ impl Element for EditorElement { let line_number_layouts = self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx); - let hunk_layouts = self.layout_git_gutters(start_row..end_row, &snapshot); + let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot); let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines); @@ -1880,7 +1836,7 @@ impl Element for EditorElement { highlighted_rows, highlighted_ranges, line_number_layouts, - hunk_layouts, + display_hunks, blocks, selections, context_menu, @@ -2002,7 +1958,7 @@ pub struct LayoutState { active_rows: BTreeMap, highlighted_rows: Option>, line_number_layouts: Vec>, - hunk_layouts: Vec, + display_hunks: Vec, blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, diff --git a/crates/editor/src/git.rs b/crates/editor/src/git.rs new file mode 100644 index 0000000000000000000000000000000000000000..0c3302bfba4ae55756cd70b121806259c953588c --- /dev/null +++ b/crates/editor/src/git.rs @@ -0,0 +1,74 @@ +use std::ops::Range; + +use git::diff::{DiffHunk, DiffHunkStatus}; +use language::Point; + +use crate::{ + display_map::{DisplaySnapshot, ToDisplayPoint}, + AnchorRangeExt, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DisplayDiffHunk { + Folded { + display_row: u32, + }, + + Unfolded { + display_row_range: Range, + status: DiffHunkStatus, + }, +} + +impl DisplayDiffHunk { + pub fn display_row_range(&self) -> Range { + match self { + &DisplayDiffHunk::Folded { display_row } => display_row..display_row + 1, + DisplayDiffHunk::Unfolded { + display_row_range, .. + } => display_row_range.clone(), + } + } +} + +pub fn diff_hunk_to_display(hunk: DiffHunk, snapshot: &DisplaySnapshot) -> DisplayDiffHunk { + let hunk_start_point = Point::new(hunk.buffer_range.start, 0); + let hunk_end_point = Point::new(hunk.buffer_range.end, 0); + let hunk_start_point_sub = Point::new(hunk.buffer_range.start.saturating_sub(1), 0); + let hunk_end_point_sub = Point::new( + hunk.buffer_range + .end + .saturating_sub(1) + .max(hunk.buffer_range.start), + 0, + ); + + let is_removal = hunk.status() == DiffHunkStatus::Removed; + + let folds_start = Point::new(hunk.buffer_range.start.saturating_sub(1), 0); + let folds_end = Point::new(hunk.buffer_range.end + 1, 0); + let folds_range = folds_start..folds_end; + + let containing_fold = snapshot.folds_in_range(folds_range).find(|fold_range| { + let fold_point_range = fold_range.to_point(&snapshot.buffer_snapshot); + let fold_point_range = fold_point_range.start..=fold_point_range.end; + + let folded_start = fold_point_range.contains(&hunk_start_point); + let folded_end = fold_point_range.contains(&hunk_end_point_sub); + let folded_start_sub = fold_point_range.contains(&hunk_start_point_sub); + + (folded_start && folded_end) || (is_removal && folded_start_sub) + }); + + if let Some(fold) = containing_fold { + let row = fold.start.to_display_point(snapshot).row(); + DisplayDiffHunk::Folded { display_row: row } + } else { + let start = hunk_start_point.to_display_point(snapshot).row(); + let end = hunk_end_point.to_display_point(snapshot).row(); + DisplayDiffHunk::Unfolded { + display_row_range: start..end, + status: hunk.status(), + } + } +} diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 1cff0038686f88c27816124d850eece8427ae864..38475daf28f75a74278d36f5e4d2c16e006a1b05 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -2592,10 +2592,13 @@ impl MultiBufferSnapshot { pub fn git_diff_hunks_in_range<'a>( &'a self, row_range: Range, + reversed: bool, ) -> impl 'a + Iterator> { self.as_singleton() .into_iter() - .flat_map(move |(_, _, buffer)| buffer.git_diff_hunks_in_range(row_range.clone())) + .flat_map(move |(_, _, buffer)| { + buffer.git_diff_hunks_in_range(row_range.clone(), reversed) + }) } pub fn range_for_syntax_ancestor(&self, range: Range) -> Option> { diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 999f410db5f81abee6e5739dfebf593fcd6e6c64..fdb1fc44e4caa79d61874a890c951eb644eecef6 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -594,7 +594,6 @@ impl<'a> MutableSelectionsCollection<'a> { self.select_anchors(selections) } - #[cfg(any(test, feature = "test-support"))] pub fn select_display_ranges(&mut self, ranges: T) where T: IntoIterator>, diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 900f8967d700c207080fc7ec435d5bcac2039e76..bdcb796e5b2014e8a535239c8ca17549070f5d8b 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -15,12 +15,12 @@ pub enum DiffHunkStatus { #[derive(Debug, Clone, PartialEq, Eq)] pub struct DiffHunk { pub buffer_range: Range, - pub head_byte_range: Range, + pub diff_base_byte_range: Range, } impl DiffHunk { pub fn status(&self) -> DiffHunkStatus { - if self.head_byte_range.is_empty() { + if self.diff_base_byte_range.is_empty() { DiffHunkStatus::Added } else if self.buffer_range.is_empty() { DiffHunkStatus::Removed @@ -75,6 +75,7 @@ impl BufferDiff { &'a self, query_row_range: Range, buffer: &'a BufferSnapshot, + reversed: bool, ) -> impl 'a + Iterator> { let start = buffer.anchor_before(Point::new(query_row_range.start, 0)); let end = buffer.anchor_after(Point::new(query_row_range.end, 0)); @@ -86,7 +87,12 @@ impl BufferDiff { }); std::iter::from_fn(move || { - cursor.next(buffer); + if reversed { + cursor.prev(buffer); + } else { + cursor.next(buffer); + } + let hunk = cursor.item()?; let range = hunk.buffer_range.to_point(buffer); @@ -98,7 +104,7 @@ impl BufferDiff { Some(DiffHunk { buffer_range: range.start.row..end_row, - head_byte_range: hunk.head_byte_range.clone(), + diff_base_byte_range: hunk.diff_base_byte_range.clone(), }) }) } @@ -135,7 +141,7 @@ impl BufferDiff { #[cfg(test)] fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator> { - self.hunks_in_range(0..u32::MAX, text) + self.hunks_in_range(0..u32::MAX, text, false) } fn diff<'a>(head: &'a str, current: &'a str) -> Option> { @@ -222,7 +228,7 @@ impl BufferDiff { let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end); DiffHunk { buffer_range, - head_byte_range, + diff_base_byte_range: head_byte_range, } } } @@ -242,7 +248,7 @@ pub fn assert_hunks( .map(|hunk| { ( hunk.buffer_range.clone(), - &diff_base[hunk.head_byte_range], + &diff_base[hunk.diff_base_byte_range], buffer .text_for_range( Point::new(hunk.buffer_range.start, 0) @@ -349,7 +355,7 @@ mod tests { assert_eq!(diff.hunks(&buffer).count(), 8); assert_hunks( - diff.hunks_in_range(7..12, &buffer), + diff.hunks_in_range(7..12, &buffer, false), &buffer, &diff_base, &[ diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 5a75f1a56f53c4a621679649ee37fb7fafdb7611..3ac4ea9667850a3bcf9377d29d7ba495a59b8591 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2286,8 +2286,10 @@ impl BufferSnapshot { pub fn git_diff_hunks_in_range<'a>( &'a self, query_row_range: Range, + reversed: bool, ) -> impl 'a + Iterator> { - self.git_diff.hunks_in_range(query_row_range, self) + self.git_diff + .hunks_in_range(query_row_range, self, reversed) } pub fn diagnostics_in_range<'a, T, O>(