Detailed changes
@@ -52,7 +52,7 @@ pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
use aho_corasick::AhoCorasick;
use anyhow::{anyhow, Context as _, Result};
use blink_manager::BlinkManager;
-use buffer_diff::DiffHunkSecondaryStatus;
+use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus};
use client::{Collaborator, ParticipantIndex};
use clock::ReplicaId;
use collections::{BTreeMap, HashMap, HashSet, VecDeque};
@@ -253,6 +253,19 @@ impl Navigated {
}
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+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,
+ },
+}
+
pub fn init_settings(cx: &mut App) {
EditorSettings::register(cx);
}
@@ -16915,6 +16928,52 @@ impl EditorSnapshot {
hunks
}
+ fn display_diff_hunks_for_rows<'a>(
+ &'a self,
+ display_rows: Range<DisplayRow>,
+ folded_buffers: &'a HashSet<BufferId>,
+ ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
+ let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
+ let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
+
+ self.buffer_snapshot
+ .diff_hunks_in_range(buffer_start..buffer_end)
+ .filter_map(|hunk| {
+ if folded_buffers.contains(&hunk.buffer_id) {
+ return None;
+ }
+
+ let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
+ let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
+
+ let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
+ let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
+
+ let display_hunk = if hunk_display_start.column() != 0 {
+ DisplayDiffHunk::Folded {
+ display_row: hunk_display_start.row(),
+ }
+ } else {
+ let mut end_row = hunk_display_end.row();
+ if hunk_display_end.column() > 0 {
+ end_row.0 += 1;
+ }
+ DisplayDiffHunk::Unfolded {
+ status: hunk.status(),
+ diff_base_byte_range: hunk.diff_base_byte_range,
+ display_row_range: hunk_display_start.row()..end_row,
+ multi_buffer_range: Anchor::range_in_buffer(
+ hunk.excerpt_id,
+ hunk.buffer_id,
+ hunk.buffer_range,
+ ),
+ }
+ };
+
+ Some(display_hunk)
+ })
+ }
+
pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
self.display_snapshot.buffer_snapshot.language_at(position)
}
@@ -24,7 +24,7 @@ use language::{
Override, Point,
};
use language_settings::{Formatter, FormatterList, IndentGuideSettings};
-use multi_buffer::IndentGuide;
+use multi_buffer::{IndentGuide, PathKey};
use parking_lot::Mutex;
use pretty_assertions::{assert_eq, assert_ne};
use project::project_settings::{LspSettings, ProjectSettings};
@@ -36,6 +36,7 @@ use std::{
sync::atomic::{self, AtomicUsize},
};
use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
+use text::ToPoint as _;
use unindent::Unindent;
use util::{
assert_set_eq, path,
@@ -14983,6 +14984,94 @@ async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestApp
);
}
+#[gpui::test]
+async fn test_display_diff_hunks(cx: &mut TestAppContext) {
+ init_test(cx, |_| {});
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(
+ path!("/test"),
+ json!({
+ ".git": {},
+ "file-1": "ONE\n",
+ "file-2": "TWO\n",
+ "file-3": "THREE\n",
+ }),
+ )
+ .await;
+
+ fs.set_head_for_repo(
+ path!("/test/.git").as_ref(),
+ &[
+ ("file-1".into(), "one\n".into()),
+ ("file-2".into(), "two\n".into()),
+ ("file-3".into(), "three\n".into()),
+ ],
+ );
+
+ let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
+ let mut buffers = vec![];
+ for i in 1..=3 {
+ let buffer = project
+ .update(cx, |project, cx| {
+ let path = format!(path!("/test/file-{}"), i);
+ project.open_local_buffer(path, cx)
+ })
+ .await
+ .unwrap();
+ buffers.push(buffer);
+ }
+
+ let multibuffer = cx.new(|cx| {
+ let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
+ multibuffer.set_all_diff_hunks_expanded(cx);
+ for buffer in &buffers {
+ let snapshot = buffer.read(cx).snapshot();
+ multibuffer.set_excerpts_for_path(
+ PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
+ buffer.clone(),
+ vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
+ DEFAULT_MULTIBUFFER_CONTEXT,
+ cx,
+ );
+ }
+ multibuffer
+ });
+
+ let editor = cx.add_window(|window, cx| {
+ Editor::new(
+ EditorMode::Full,
+ multibuffer,
+ Some(project),
+ true,
+ window,
+ cx,
+ )
+ });
+ cx.run_until_parked();
+
+ let snapshot = editor
+ .update(cx, |editor, window, cx| editor.snapshot(window, cx))
+ .unwrap();
+ let hunks = snapshot
+ .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
+ .map(|hunk| match hunk {
+ DisplayDiffHunk::Unfolded {
+ display_row_range, ..
+ } => display_row_range,
+ DisplayDiffHunk::Folded { .. } => unreachable!(),
+ })
+ .collect::<Vec<_>>();
+ assert_eq!(
+ hunks,
+ [
+ DisplayRow(3)..DisplayRow(5),
+ DisplayRow(10)..DisplayRow(12),
+ DisplayRow(17)..DisplayRow(19),
+ ]
+ );
+}
+
#[gpui::test]
async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
init_test(cx, |_| {});
@@ -15,8 +15,8 @@ use crate::{
items::BufferSearchHighlights,
mouse_context_menu::{self, MenuPosition, MouseContextMenu},
scroll::{axis_pair, scroll_amount::ScrollAmount, AxisPair},
- BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
- DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
+ BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayDiffHunk, DisplayPoint,
+ DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GoToHunk,
GoToPrevHunk, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor,
InlineCompletion, JumpData, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, RowExt,
@@ -79,19 +79,6 @@ use workspace::{item::Item, notifications::NotifyTaskExt};
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
const MIN_SCROLL_THUMB_SIZE: f32 = 25.;
-#[derive(Debug, Clone, PartialEq, Eq)]
-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,
- },
-}
-
struct SelectionLayout {
head: DisplayPoint,
cursor_shape: CursorShape,
@@ -1553,50 +1540,11 @@ impl EditorElement {
window: &mut Window,
cx: &mut App,
) -> Vec<(DisplayDiffHunk, Option<Hitbox>)> {
- let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(snapshot);
- let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(snapshot);
-
- let mut display_hunks = Vec::<(DisplayDiffHunk, Option<Hitbox>)>::new();
let folded_buffers = self.editor.read(cx).folded_buffers(cx);
-
- for hunk in snapshot
- .buffer_snapshot
- .diff_hunks_in_range(buffer_start..buffer_end)
- {
- if folded_buffers.contains(&hunk.buffer_id) {
- continue;
- }
-
- let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
- let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
-
- let hunk_display_start = snapshot.point_to_display_point(hunk_start_point, Bias::Left);
- let hunk_display_end = snapshot.point_to_display_point(hunk_end_point, Bias::Right);
-
- let display_hunk = if hunk_display_start.column() != 0 {
- DisplayDiffHunk::Folded {
- display_row: hunk_display_start.row(),
- }
- } else {
- let mut end_row = hunk_display_end.row();
- if hunk_display_end.column() > 0 {
- end_row.0 += 1;
- }
- DisplayDiffHunk::Unfolded {
- status: hunk.status(),
- diff_base_byte_range: hunk.diff_base_byte_range,
- display_row_range: hunk_display_start.row()..end_row,
- multi_buffer_range: Anchor::range_in_buffer(
- hunk.excerpt_id,
- hunk.buffer_id,
- hunk.buffer_range,
- ),
- }
- };
-
- display_hunks.push((display_hunk, None));
- }
-
+ let mut display_hunks = snapshot
+ .display_diff_hunks_for_rows(display_rows, folded_buffers)
+ .map(|hunk| (hunk, None))
+ .collect::<Vec<_>>();
let git_gutter_setting = ProjectSettings::get_global(cx)
.git
.git_gutter
@@ -6146,6 +6146,16 @@ where
|| self.diff_transforms.item().is_none()
{
self.excerpts.next(&());
+ } else if let Some(DiffTransform::DeletedHunk { hunk_info, .. }) =
+ self.diff_transforms.item()
+ {
+ if self
+ .excerpts
+ .item()
+ .map_or(false, |excerpt| excerpt.id != hunk_info.excerpt_id)
+ {
+ self.excerpts.next(&());
+ }
}
}
}
@@ -6183,12 +6193,13 @@ where
return true;
}
- self.diff_transforms.next(&());
- let next_transform = self.diff_transforms.item();
- self.diff_transforms.prev(&());
-
- next_transform.map_or(true, |next_transform| {
- matches!(next_transform, DiffTransform::BufferContent { .. })
+ let next_transform = self.diff_transforms.next_item();
+ next_transform.map_or(true, |next_transform| match next_transform {
+ DiffTransform::BufferContent { .. } => true,
+ DiffTransform::DeletedHunk { hunk_info, .. } => self
+ .excerpts
+ .item()
+ .map_or(false, |excerpt| excerpt.id != hunk_info.excerpt_id),
})
}