Move DisplayDiffHunk into hunk_diff module (#18307)
Release Notes: - N/A Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
parent
c4e0f5e0ee
commit
fdb03d3058
4 changed files with 320 additions and 338 deletions
|
@ -71,7 +71,6 @@ pub use element::{
|
||||||
use futures::{future, FutureExt};
|
use futures::{future, FutureExt};
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use git::blame::GitBlame;
|
use git::blame::GitBlame;
|
||||||
use git::diff_hunk_to_display;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
|
div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
|
||||||
AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
|
AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
|
||||||
|
@ -84,8 +83,8 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
use hover_popover::{hide_hover, HoverState};
|
||||||
use hunk_diff::ExpandedHunks;
|
|
||||||
pub(crate) use hunk_diff::HoveredHunk;
|
pub(crate) use hunk_diff::HoveredHunk;
|
||||||
|
use hunk_diff::{diff_hunk_to_display, ExpandedHunks};
|
||||||
use indent_guides::ActiveIndentGuidesState;
|
use indent_guides::ActiveIndentGuidesState;
|
||||||
use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
|
use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
|
||||||
pub use inline_completion_provider::*;
|
pub use inline_completion_provider::*;
|
||||||
|
|
|
@ -7,14 +7,11 @@ use crate::{
|
||||||
CurrentLineHighlight, DoubleClickInMultibuffer, MultiCursorModifier, ScrollBeyondLastLine,
|
CurrentLineHighlight, DoubleClickInMultibuffer, MultiCursorModifier, ScrollBeyondLastLine,
|
||||||
ShowScrollbar,
|
ShowScrollbar,
|
||||||
},
|
},
|
||||||
git::{
|
git::blame::{CommitDetails, GitBlame},
|
||||||
blame::{CommitDetails, GitBlame},
|
|
||||||
diff_hunk_to_display, DisplayDiffHunk,
|
|
||||||
},
|
|
||||||
hover_popover::{
|
hover_popover::{
|
||||||
self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
|
self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
hunk_diff::ExpandedHunk,
|
hunk_diff::{diff_hunk_to_display, DisplayDiffHunk, ExpandedHunk},
|
||||||
hunk_status,
|
hunk_status,
|
||||||
items::BufferSearchHighlights,
|
items::BufferSearchHighlights,
|
||||||
mouse_context_menu::{self, MenuPosition, MouseContextMenu},
|
mouse_context_menu::{self, MenuPosition, MouseContextMenu},
|
||||||
|
|
|
@ -1,309 +1 @@
|
||||||
pub mod blame;
|
pub mod blame;
|
||||||
|
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use git::diff::DiffHunkStatus;
|
|
||||||
use language::Point;
|
|
||||||
use multi_buffer::{Anchor, MultiBufferDiffHunk};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
|
||||||
hunk_status, AnchorRangeExt, DisplayRow,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum DisplayDiffHunk {
|
|
||||||
Folded {
|
|
||||||
display_row: DisplayRow,
|
|
||||||
},
|
|
||||||
|
|
||||||
Unfolded {
|
|
||||||
diff_base_byte_range: Range<usize>,
|
|
||||||
display_row_range: Range<DisplayRow>,
|
|
||||||
multi_buffer_range: Range<Anchor>,
|
|
||||||
status: DiffHunkStatus,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DisplayDiffHunk {
|
|
||||||
pub fn start_display_row(&self) -> DisplayRow {
|
|
||||||
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: DisplayRow) -> bool {
|
|
||||||
let range = match self {
|
|
||||||
&DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
|
|
||||||
|
|
||||||
DisplayDiffHunk::Unfolded {
|
|
||||||
display_row_range, ..
|
|
||||||
} => display_row_range.start..=display_row_range.end,
|
|
||||||
};
|
|
||||||
|
|
||||||
range.contains(&display_row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn diff_hunk_to_display(
|
|
||||||
hunk: &MultiBufferDiffHunk,
|
|
||||||
snapshot: &DisplaySnapshot,
|
|
||||||
) -> DisplayDiffHunk {
|
|
||||||
let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
|
|
||||||
let hunk_start_point_sub = Point::new(hunk.row_range.start.0.saturating_sub(1), 0);
|
|
||||||
let hunk_end_point_sub = Point::new(
|
|
||||||
hunk.row_range
|
|
||||||
.end
|
|
||||||
.0
|
|
||||||
.saturating_sub(1)
|
|
||||||
.max(hunk.row_range.start.0),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
let status = hunk_status(hunk);
|
|
||||||
let is_removal = status == DiffHunkStatus::Removed;
|
|
||||||
|
|
||||||
let folds_start = Point::new(hunk.row_range.start.0.saturating_sub(2), 0);
|
|
||||||
let folds_end = Point::new(hunk.row_range.end.0 + 2, 0);
|
|
||||||
let folds_range = folds_start..folds_end;
|
|
||||||
|
|
||||||
let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
|
|
||||||
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.range.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 = hunk.row_range.end.max(hunk.row_range.start);
|
|
||||||
let hunk_end_point = Point::new(hunk_end_row.0, 0);
|
|
||||||
|
|
||||||
let multi_buffer_start = snapshot.buffer_snapshot.anchor_before(hunk_start_point);
|
|
||||||
let multi_buffer_end = snapshot.buffer_snapshot.anchor_after(hunk_end_point);
|
|
||||||
let end = hunk_end_point.to_display_point(snapshot).row();
|
|
||||||
|
|
||||||
DisplayDiffHunk::Unfolded {
|
|
||||||
display_row_range: start..end,
|
|
||||||
multi_buffer_range: multi_buffer_start..multi_buffer_end,
|
|
||||||
status,
|
|
||||||
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::Point;
|
|
||||||
use crate::{editor_tests::init_test, hunk_status};
|
|
||||||
use gpui::{Context, TestAppContext};
|
|
||||||
use language::Capability::ReadWrite;
|
|
||||||
use multi_buffer::{ExcerptRange, MultiBuffer, MultiBufferRow};
|
|
||||||
use project::{FakeFs, Project};
|
|
||||||
use unindent::Unindent;
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
|
|
||||||
use git::diff::DiffHunkStatus;
|
|
||||||
init_test(cx, |_| {});
|
|
||||||
|
|
||||||
let fs = FakeFs::new(cx.background_executor.clone());
|
|
||||||
let project = Project::test(fs, [], cx).await;
|
|
||||||
|
|
||||||
// buffer has two modified hunks with two rows each
|
|
||||||
let buffer_1 = project.update(cx, |project, cx| {
|
|
||||||
project.create_local_buffer(
|
|
||||||
"
|
|
||||||
1.zero
|
|
||||||
1.ONE
|
|
||||||
1.TWO
|
|
||||||
1.three
|
|
||||||
1.FOUR
|
|
||||||
1.FIVE
|
|
||||||
1.six
|
|
||||||
"
|
|
||||||
.unindent()
|
|
||||||
.as_str(),
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
buffer_1.update(cx, |buffer, cx| {
|
|
||||||
buffer.set_diff_base(
|
|
||||||
Some(
|
|
||||||
"
|
|
||||||
1.zero
|
|
||||||
1.one
|
|
||||||
1.two
|
|
||||||
1.three
|
|
||||||
1.four
|
|
||||||
1.five
|
|
||||||
1.six
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// buffer has a deletion hunk and an insertion hunk
|
|
||||||
let buffer_2 = project.update(cx, |project, cx| {
|
|
||||||
project.create_local_buffer(
|
|
||||||
"
|
|
||||||
2.zero
|
|
||||||
2.one
|
|
||||||
2.two
|
|
||||||
2.three
|
|
||||||
2.four
|
|
||||||
2.five
|
|
||||||
2.six
|
|
||||||
"
|
|
||||||
.unindent()
|
|
||||||
.as_str(),
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
buffer_2.update(cx, |buffer, cx| {
|
|
||||||
buffer.set_diff_base(
|
|
||||||
Some(
|
|
||||||
"
|
|
||||||
2.zero
|
|
||||||
2.one
|
|
||||||
2.one-and-a-half
|
|
||||||
2.two
|
|
||||||
2.three
|
|
||||||
2.four
|
|
||||||
2.six
|
|
||||||
"
|
|
||||||
.unindent(),
|
|
||||||
),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.background_executor.run_until_parked();
|
|
||||||
|
|
||||||
let multibuffer = cx.new_model(|cx| {
|
|
||||||
let mut multibuffer = MultiBuffer::new(ReadWrite);
|
|
||||||
multibuffer.push_excerpts(
|
|
||||||
buffer_1.clone(),
|
|
||||||
[
|
|
||||||
// excerpt ends in the middle of a modified hunk
|
|
||||||
ExcerptRange {
|
|
||||||
context: Point::new(0, 0)..Point::new(1, 5),
|
|
||||||
primary: Default::default(),
|
|
||||||
},
|
|
||||||
// excerpt begins in the middle of a modified hunk
|
|
||||||
ExcerptRange {
|
|
||||||
context: Point::new(5, 0)..Point::new(6, 5),
|
|
||||||
primary: Default::default(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
multibuffer.push_excerpts(
|
|
||||||
buffer_2.clone(),
|
|
||||||
[
|
|
||||||
// excerpt ends at a deletion
|
|
||||||
ExcerptRange {
|
|
||||||
context: Point::new(0, 0)..Point::new(1, 5),
|
|
||||||
primary: Default::default(),
|
|
||||||
},
|
|
||||||
// excerpt starts at a deletion
|
|
||||||
ExcerptRange {
|
|
||||||
context: Point::new(2, 0)..Point::new(2, 5),
|
|
||||||
primary: Default::default(),
|
|
||||||
},
|
|
||||||
// excerpt fully contains a deletion hunk
|
|
||||||
ExcerptRange {
|
|
||||||
context: Point::new(1, 0)..Point::new(2, 5),
|
|
||||||
primary: Default::default(),
|
|
||||||
},
|
|
||||||
// excerpt fully contains an insertion hunk
|
|
||||||
ExcerptRange {
|
|
||||||
context: Point::new(4, 0)..Point::new(6, 5),
|
|
||||||
primary: Default::default(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
multibuffer
|
|
||||||
});
|
|
||||||
|
|
||||||
let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
snapshot.text(),
|
|
||||||
"
|
|
||||||
1.zero
|
|
||||||
1.ONE
|
|
||||||
1.FIVE
|
|
||||||
1.six
|
|
||||||
2.zero
|
|
||||||
2.one
|
|
||||||
2.two
|
|
||||||
2.one
|
|
||||||
2.two
|
|
||||||
2.four
|
|
||||||
2.five
|
|
||||||
2.six"
|
|
||||||
.unindent()
|
|
||||||
);
|
|
||||||
|
|
||||||
let expected = [
|
|
||||||
(
|
|
||||||
DiffHunkStatus::Modified,
|
|
||||||
MultiBufferRow(1)..MultiBufferRow(2),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DiffHunkStatus::Modified,
|
|
||||||
MultiBufferRow(2)..MultiBufferRow(3),
|
|
||||||
),
|
|
||||||
//TODO: Define better when and where removed hunks show up at range extremities
|
|
||||||
(
|
|
||||||
DiffHunkStatus::Removed,
|
|
||||||
MultiBufferRow(6)..MultiBufferRow(6),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DiffHunkStatus::Removed,
|
|
||||||
MultiBufferRow(8)..MultiBufferRow(8),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DiffHunkStatus::Added,
|
|
||||||
MultiBufferRow(10)..MultiBufferRow(11),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
snapshot
|
|
||||||
.git_diff_hunks_in_range(MultiBufferRow(0)..MultiBufferRow(12))
|
|
||||||
.map(|hunk| (hunk_status(&hunk), hunk.row_range))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
&expected,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
snapshot
|
|
||||||
.git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(12))
|
|
||||||
.map(|hunk| (hunk_status(&hunk), hunk.row_range))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
expected
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.as_slice(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
use std::{
|
|
||||||
ops::{Range, RangeInclusive},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use collections::{hash_map, HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet};
|
||||||
use git::diff::DiffHunkStatus;
|
use git::diff::DiffHunkStatus;
|
||||||
use gpui::{Action, AppContext, CursorStyle, Hsla, Model, MouseButton, Subscription, Task, View};
|
use gpui::{Action, AppContext, CursorStyle, Hsla, Model, MouseButton, Subscription, Task, View};
|
||||||
use language::Buffer;
|
use language::{Buffer, BufferId, Point};
|
||||||
use multi_buffer::{
|
use multi_buffer::{
|
||||||
Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow,
|
Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow,
|
||||||
MultiBufferSnapshot, ToPoint,
|
MultiBufferSnapshot, ToPoint,
|
||||||
};
|
};
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use text::{BufferId, Point};
|
use std::{
|
||||||
|
ops::{Range, RangeInclusive},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use ui::{
|
use ui::{
|
||||||
prelude::*, ActiveTheme, ContextMenu, InteractiveElement, IntoElement, ParentElement, Pixels,
|
prelude::*, ActiveTheme, ContextMenu, InteractiveElement, IntoElement, ParentElement, Pixels,
|
||||||
Styled, ViewContext, VisualContext,
|
Styled, ViewContext, VisualContext,
|
||||||
|
@ -20,13 +18,11 @@ use ui::{
|
||||||
use util::{debug_panic, RangeExt};
|
use util::{debug_panic, RangeExt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
editor_settings::CurrentLineHighlight,
|
editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections,
|
||||||
git::{diff_hunk_to_display, DisplayDiffHunk},
|
mouse_context_menu::MouseContextMenu, BlockDisposition, BlockProperties, BlockStyle,
|
||||||
hunk_status, hunks_for_selections,
|
CustomBlockId, DiffRowHighlight, DisplayRow, DisplaySnapshot, Editor, EditorElement,
|
||||||
mouse_context_menu::MouseContextMenu,
|
EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt, RevertFile, RevertSelectedHunks,
|
||||||
BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, Editor,
|
ToDisplayPoint, ToggleHunkDiff,
|
||||||
EditorElement, EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt, RevertFile,
|
|
||||||
RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -43,12 +39,35 @@ pub(super) struct ExpandedHunks {
|
||||||
hunk_update_tasks: HashMap<Option<BufferId>, Task<()>>,
|
hunk_update_tasks: HashMap<Option<BufferId>, Task<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(super) struct ExpandedHunk {
|
||||||
|
pub block: Option<CustomBlockId>,
|
||||||
|
pub hunk_range: Range<Anchor>,
|
||||||
|
pub diff_base_byte_range: Range<usize>,
|
||||||
|
pub status: DiffHunkStatus,
|
||||||
|
pub folded: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DiffBaseBuffer {
|
struct DiffBaseBuffer {
|
||||||
buffer: Model<Buffer>,
|
buffer: Model<Buffer>,
|
||||||
diff_base_version: usize,
|
diff_base_version: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum DisplayDiffHunk {
|
||||||
|
Folded {
|
||||||
|
display_row: DisplayRow,
|
||||||
|
},
|
||||||
|
|
||||||
|
Unfolded {
|
||||||
|
diff_base_byte_range: Range<usize>,
|
||||||
|
display_row_range: Range<DisplayRow>,
|
||||||
|
multi_buffer_range: Range<Anchor>,
|
||||||
|
status: DiffHunkStatus,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
impl ExpandedHunks {
|
impl ExpandedHunks {
|
||||||
pub fn hunks(&self, include_folded: bool) -> impl Iterator<Item = &ExpandedHunk> {
|
pub fn hunks(&self, include_folded: bool) -> impl Iterator<Item = &ExpandedHunk> {
|
||||||
self.hunks
|
self.hunks
|
||||||
|
@ -57,15 +76,6 @@ impl ExpandedHunks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(super) struct ExpandedHunk {
|
|
||||||
pub block: Option<CustomBlockId>,
|
|
||||||
pub hunk_range: Range<Anchor>,
|
|
||||||
pub diff_base_byte_range: Range<usize>,
|
|
||||||
pub status: DiffHunkStatus,
|
|
||||||
pub folded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
pub(super) fn open_hunk_context_menu(
|
pub(super) fn open_hunk_context_menu(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -883,3 +893,287 @@ fn to_inclusive_row_range(
|
||||||
let new_range = point_range.to_anchors(&snapshot.buffer_snapshot);
|
let new_range = point_range.to_anchors(&snapshot.buffer_snapshot);
|
||||||
new_range.start..=new_range.end
|
new_range.start..=new_range.end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DisplayDiffHunk {
|
||||||
|
pub fn start_display_row(&self) -> DisplayRow {
|
||||||
|
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: DisplayRow) -> bool {
|
||||||
|
let range = match self {
|
||||||
|
&DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
|
||||||
|
|
||||||
|
DisplayDiffHunk::Unfolded {
|
||||||
|
display_row_range, ..
|
||||||
|
} => display_row_range.start..=display_row_range.end,
|
||||||
|
};
|
||||||
|
|
||||||
|
range.contains(&display_row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diff_hunk_to_display(
|
||||||
|
hunk: &MultiBufferDiffHunk,
|
||||||
|
snapshot: &DisplaySnapshot,
|
||||||
|
) -> DisplayDiffHunk {
|
||||||
|
let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
|
||||||
|
let hunk_start_point_sub = Point::new(hunk.row_range.start.0.saturating_sub(1), 0);
|
||||||
|
let hunk_end_point_sub = Point::new(
|
||||||
|
hunk.row_range
|
||||||
|
.end
|
||||||
|
.0
|
||||||
|
.saturating_sub(1)
|
||||||
|
.max(hunk.row_range.start.0),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let status = hunk_status(hunk);
|
||||||
|
let is_removal = status == DiffHunkStatus::Removed;
|
||||||
|
|
||||||
|
let folds_start = Point::new(hunk.row_range.start.0.saturating_sub(2), 0);
|
||||||
|
let folds_end = Point::new(hunk.row_range.end.0 + 2, 0);
|
||||||
|
let folds_range = folds_start..folds_end;
|
||||||
|
|
||||||
|
let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
|
||||||
|
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.range.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 = hunk.row_range.end.max(hunk.row_range.start);
|
||||||
|
let hunk_end_point = Point::new(hunk_end_row.0, 0);
|
||||||
|
|
||||||
|
let multi_buffer_start = snapshot.buffer_snapshot.anchor_before(hunk_start_point);
|
||||||
|
let multi_buffer_end = snapshot.buffer_snapshot.anchor_after(hunk_end_point);
|
||||||
|
let end = hunk_end_point.to_display_point(snapshot).row();
|
||||||
|
|
||||||
|
DisplayDiffHunk::Unfolded {
|
||||||
|
display_row_range: start..end,
|
||||||
|
multi_buffer_range: multi_buffer_start..multi_buffer_end,
|
||||||
|
status,
|
||||||
|
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{editor_tests::init_test, hunk_status};
|
||||||
|
use gpui::{Context, TestAppContext};
|
||||||
|
use language::Capability::ReadWrite;
|
||||||
|
use multi_buffer::{ExcerptRange, MultiBuffer, MultiBufferRow};
|
||||||
|
use project::{FakeFs, Project};
|
||||||
|
use unindent::Unindent as _;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
|
||||||
|
use git::diff::DiffHunkStatus;
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.background_executor.clone());
|
||||||
|
let project = Project::test(fs, [], cx).await;
|
||||||
|
|
||||||
|
// buffer has two modified hunks with two rows each
|
||||||
|
let buffer_1 = project.update(cx, |project, cx| {
|
||||||
|
project.create_local_buffer(
|
||||||
|
"
|
||||||
|
1.zero
|
||||||
|
1.ONE
|
||||||
|
1.TWO
|
||||||
|
1.three
|
||||||
|
1.FOUR
|
||||||
|
1.FIVE
|
||||||
|
1.six
|
||||||
|
"
|
||||||
|
.unindent()
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
buffer_1.update(cx, |buffer, cx| {
|
||||||
|
buffer.set_diff_base(
|
||||||
|
Some(
|
||||||
|
"
|
||||||
|
1.zero
|
||||||
|
1.one
|
||||||
|
1.two
|
||||||
|
1.three
|
||||||
|
1.four
|
||||||
|
1.five
|
||||||
|
1.six
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// buffer has a deletion hunk and an insertion hunk
|
||||||
|
let buffer_2 = project.update(cx, |project, cx| {
|
||||||
|
project.create_local_buffer(
|
||||||
|
"
|
||||||
|
2.zero
|
||||||
|
2.one
|
||||||
|
2.two
|
||||||
|
2.three
|
||||||
|
2.four
|
||||||
|
2.five
|
||||||
|
2.six
|
||||||
|
"
|
||||||
|
.unindent()
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
buffer_2.update(cx, |buffer, cx| {
|
||||||
|
buffer.set_diff_base(
|
||||||
|
Some(
|
||||||
|
"
|
||||||
|
2.zero
|
||||||
|
2.one
|
||||||
|
2.one-and-a-half
|
||||||
|
2.two
|
||||||
|
2.three
|
||||||
|
2.four
|
||||||
|
2.six
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.background_executor.run_until_parked();
|
||||||
|
|
||||||
|
let multibuffer = cx.new_model(|cx| {
|
||||||
|
let mut multibuffer = MultiBuffer::new(ReadWrite);
|
||||||
|
multibuffer.push_excerpts(
|
||||||
|
buffer_1.clone(),
|
||||||
|
[
|
||||||
|
// excerpt ends in the middle of a modified hunk
|
||||||
|
ExcerptRange {
|
||||||
|
context: Point::new(0, 0)..Point::new(1, 5),
|
||||||
|
primary: Default::default(),
|
||||||
|
},
|
||||||
|
// excerpt begins in the middle of a modified hunk
|
||||||
|
ExcerptRange {
|
||||||
|
context: Point::new(5, 0)..Point::new(6, 5),
|
||||||
|
primary: Default::default(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
multibuffer.push_excerpts(
|
||||||
|
buffer_2.clone(),
|
||||||
|
[
|
||||||
|
// excerpt ends at a deletion
|
||||||
|
ExcerptRange {
|
||||||
|
context: Point::new(0, 0)..Point::new(1, 5),
|
||||||
|
primary: Default::default(),
|
||||||
|
},
|
||||||
|
// excerpt starts at a deletion
|
||||||
|
ExcerptRange {
|
||||||
|
context: Point::new(2, 0)..Point::new(2, 5),
|
||||||
|
primary: Default::default(),
|
||||||
|
},
|
||||||
|
// excerpt fully contains a deletion hunk
|
||||||
|
ExcerptRange {
|
||||||
|
context: Point::new(1, 0)..Point::new(2, 5),
|
||||||
|
primary: Default::default(),
|
||||||
|
},
|
||||||
|
// excerpt fully contains an insertion hunk
|
||||||
|
ExcerptRange {
|
||||||
|
context: Point::new(4, 0)..Point::new(6, 5),
|
||||||
|
primary: Default::default(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
multibuffer
|
||||||
|
});
|
||||||
|
|
||||||
|
let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
snapshot.text(),
|
||||||
|
"
|
||||||
|
1.zero
|
||||||
|
1.ONE
|
||||||
|
1.FIVE
|
||||||
|
1.six
|
||||||
|
2.zero
|
||||||
|
2.one
|
||||||
|
2.two
|
||||||
|
2.one
|
||||||
|
2.two
|
||||||
|
2.four
|
||||||
|
2.five
|
||||||
|
2.six"
|
||||||
|
.unindent()
|
||||||
|
);
|
||||||
|
|
||||||
|
let expected = [
|
||||||
|
(
|
||||||
|
DiffHunkStatus::Modified,
|
||||||
|
MultiBufferRow(1)..MultiBufferRow(2),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DiffHunkStatus::Modified,
|
||||||
|
MultiBufferRow(2)..MultiBufferRow(3),
|
||||||
|
),
|
||||||
|
//TODO: Define better when and where removed hunks show up at range extremities
|
||||||
|
(
|
||||||
|
DiffHunkStatus::Removed,
|
||||||
|
MultiBufferRow(6)..MultiBufferRow(6),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DiffHunkStatus::Removed,
|
||||||
|
MultiBufferRow(8)..MultiBufferRow(8),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DiffHunkStatus::Added,
|
||||||
|
MultiBufferRow(10)..MultiBufferRow(11),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
snapshot
|
||||||
|
.git_diff_hunks_in_range(MultiBufferRow(0)..MultiBufferRow(12))
|
||||||
|
.map(|hunk| (hunk_status(&hunk), hunk.row_range))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
&expected,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
snapshot
|
||||||
|
.git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(12))
|
||||||
|
.map(|hunk| (hunk_status(&hunk), hunk.row_range))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
expected
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.as_slice(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue