Detailed changes
@@ -402,7 +402,9 @@
{
"context": "Editor",
"bindings": {
- "alt-enter": "editor::OpenExcerpts"
+ "alt-enter": "editor::OpenExcerpts",
+ "cmd-f8": "editor::GoToHunk",
+ "cmd-shift-f8": "editor::GoToPrevHunk"
}
},
{
@@ -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::<Vec<_>>()
);
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")],
@@ -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,72 @@ impl Editor {
}
}
+ fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
+ self.go_to_hunk_impl(Direction::Next, cx)
+ }
+
+ fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
+ self.go_to_hunk_impl(Direction::Prev, cx)
+ }
+
+ pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
+ let snapshot = self
+ .display_map
+ .update(cx, |display_map, cx| display_map.snapshot(cx));
+ let selection = self.selections.newest::<Point>(cx);
+
+ fn seek_in_direction(
+ this: &mut Editor,
+ snapshot: &DisplaySnapshot,
+ initial_point: Point,
+ is_wrapped: bool,
+ direction: Direction,
+ cx: &mut ViewContext<Editor>,
+ ) -> 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| {
+ 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.start_display_row();
+ let point = DisplayPoint::new(row, 0);
+ s.select_display_ranges([point..point]);
+ });
+
+ true
+ } else {
+ false
+ }
+ }
+
+ 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, true, direction, cx);
+ }
+ }
+
pub fn go_to_definition(
workspace: &mut Workspace,
_: &GoToDefinition,
@@ -11,6 +11,7 @@ use crate::test::{
editor_test_context::EditorTestContext, select_ranges,
};
use gpui::{
+ executor::Deterministic,
geometry::rect::RectF,
platform::{WindowBounds, WindowOptions},
};
@@ -5116,6 +5117,111 @@ fn test_combine_syntax_and_fuzzy_match_highlights() {
);
}
+#[gpui::test]
+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> {
let point = DisplayPoint::new(row as u32, column as u32);
point..point
@@ -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<u32>,
- 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,11 +627,11 @@ 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;
+ let end_y = end_row as f32 * line_height - scroll_top + line_height;
let width = diff_style.width_em * line_height;
let highlight_origin = bounds.origin() + vec2f(-width, start_y);
@@ -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<u32>,
+ display_rows: Range<u32>,
snapshot: &EditorSnapshot,
- ) -> Vec<DiffHunkLayout> {
+ ) -> Vec<DisplayDiffHunk> {
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::<DiffHunkLayout>::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<u32, bool>,
highlighted_rows: Option<Range<u32>>,
line_number_layouts: Vec<Option<text_layout::Line>>,
- hunk_layouts: Vec<DiffHunkLayout>,
+ display_hunks: Vec<DisplayDiffHunk>,
blocks: Vec<BlockLayout>,
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
@@ -0,0 +1,93 @@
+use std::ops::RangeInclusive;
+
+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: RangeInclusive<u32>,
+ status: DiffHunkStatus,
+ },
+}
+
+impl DisplayDiffHunk {
+ pub fn start_display_row(&self) -> u32 {
+ match self {
+ &DisplayDiffHunk::Folded { display_row } => display_row,
+ DisplayDiffHunk::Unfolded {
+ display_row_range, ..
+ } => *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, ..
+ } => display_row_range.clone(),
+ };
+
+ range.contains(&display_row)
+ }
+}
+
+pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) -> DisplayDiffHunk {
+ let hunk_start_point = Point::new(hunk.buffer_range.start, 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(2), 0);
+ let folds_end = Point::new(hunk.buffer_range.end + 2, 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 hunk_end_row_inclusive = hunk
+ .buffer_range
+ .end
+ .saturating_sub(1)
+ .max(hunk.buffer_range.start);
+ let hunk_end_point = Point::new(hunk_end_row_inclusive, 0);
+ let end = hunk_end_point.to_display_point(snapshot).row();
+
+ DisplayDiffHunk::Unfolded {
+ display_row_range: start..=end,
+ status: hunk.status(),
+ }
+ }
+}
@@ -2592,10 +2592,13 @@ impl MultiBufferSnapshot {
pub fn git_diff_hunks_in_range<'a>(
&'a self,
row_range: Range<u32>,
+ reversed: bool,
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
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<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
@@ -594,7 +594,6 @@ impl<'a> MutableSelectionsCollection<'a> {
self.select_anchors(selections)
}
- #[cfg(any(test, feature = "test-support"))]
pub fn select_display_ranges<T>(&mut self, ranges: T)
where
T: IntoIterator<Item = Range<DisplayPoint>>,
@@ -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.
@@ -15,12 +15,12 @@ pub enum DiffHunkStatus {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiffHunk<T> {
pub buffer_range: Range<T>,
- pub head_byte_range: Range<usize>,
+ pub diff_base_byte_range: Range<usize>,
}
impl DiffHunk<u32> {
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<u32>,
buffer: &'a BufferSnapshot,
+ reversed: bool,
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
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<Item = DiffHunk<u32>> {
- 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<GitPatch<'a>> {
@@ -171,7 +177,7 @@ impl BufferDiff {
let mut first_deletion_buffer_row: Option<u32> = None;
let mut buffer_row_range: Option<Range<u32>> = None;
- let mut head_byte_range: Option<Range<usize>> = None;
+ let mut diff_base_byte_range: Option<Range<usize>> = None;
for line_index in 0..line_item_count {
let line = patch.line_in_hunk(hunk_index, line_index).unwrap();
@@ -192,9 +198,9 @@ impl BufferDiff {
if kind == GitDiffLineType::Deletion {
let end = content_offset + content_len;
- match &mut head_byte_range {
+ match &mut diff_base_byte_range {
Some(head_byte_range) => head_byte_range.end = end as usize,
- None => head_byte_range = Some(content_offset as usize..end as usize),
+ None => diff_base_byte_range = Some(content_offset as usize..end as usize),
}
if first_deletion_buffer_row.is_none() {
@@ -215,14 +221,14 @@ impl BufferDiff {
});
//unwrap_or addition without deletion
- let head_byte_range = head_byte_range.unwrap_or(0..0);
+ let diff_base_byte_range = diff_base_byte_range.unwrap_or(0..0);
let start = Point::new(buffer_row_range.start, 0);
let end = Point::new(buffer_row_range.end, 0);
let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end);
DiffHunk {
buffer_range,
- head_byte_range,
+ diff_base_byte_range,
}
}
}
@@ -242,7 +248,7 @@ pub fn assert_hunks<Iter>(
.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,
&[
@@ -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);
}
@@ -2294,8 +2294,10 @@ impl BufferSnapshot {
pub fn git_diff_hunks_in_range<'a>(
&'a self,
query_row_range: Range<u32>,
+ reversed: bool,
) -> impl 'a + Iterator<Item = git::diff::DiffHunk<u32>> {
- 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>(
@@ -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(())
})
@@ -3257,7 +3257,6 @@ mod tests {
}
},
"c.txt": "",
-
}));
let http_client = FakeHttpClient::with_404_response();