use collections::{HashMap, HashSet};use git::diff::DiffHunkStatus;use gpui::{ Action, AnchorCorner, AppContext, CursorStyle, Hsla, Model, MouseButton, Subscription, Task, View,};use language::{Buffer, BufferId, Point};use multi_buffer::{ Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow, MultiBufferSnapshot, ToOffset, ToPoint,};use project::buffer_store::BufferChangeSet;use std::{ops::Range, sync::Arc};use sum_tree::TreeMap;use text::OffsetRangeExt;use ui::{ prelude::*, ActiveTheme, ContextMenu, IconButtonShape, InteractiveElement, IntoElement, ParentElement, PopoverMenu, Styled, Tooltip, ViewContext, VisualContext,};use util::RangeExt;use workspace::Item; use crate::{ editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyAllDiffHunks, ApplyDiffHunk, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow, DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk, RevertFile, RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,}; #[derive(Debug, Clone)]pub(super) struct HoveredHunk { pub multi_buffer_range: Range<Anchor>, pub status: DiffHunkStatus, pub diff_base_byte_range: Range<usize>,} #[derive(Default)]pub(super) struct DiffMap { pub(crate) hunks: Vec<ExpandedHunk>, pub(crate) diff_bases: HashMap<BufferId, DiffBaseState>, pub(crate) snapshot: DiffMapSnapshot, hunk_update_tasks: HashMap<Option<BufferId>, Task<()>>, expand_all: bool,} #[derive(Debug, Clone)]pub(super) struct ExpandedHunk { pub blocks: Vec<CustomBlockId>, pub hunk_range: Range<Anchor>, pub diff_base_byte_range: Range<usize>, pub status: DiffHunkStatus, pub folded: bool,} #[derive(Clone, Debug, Default)]pub(crate) struct DiffMapSnapshot(TreeMap<BufferId, git::diff::BufferDiff>); pub(crate) struct DiffBaseState { pub(crate) change_set: Model<BufferChangeSet>, pub(crate) last_version: Option<usize>, _subscription: Subscription,} #[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 DiffMap { pub fn snapshot(&self) -> DiffMapSnapshot { self.snapshot.clone() } pub fn add_change_set( &mut self, change_set: Model<BufferChangeSet>, cx: &mut ViewContext<Editor>, ) { let buffer_id = change_set.read(cx).buffer_id; self.snapshot .0 .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone()); Editor::sync_expanded_diff_hunks(self, buffer_id, cx); self.diff_bases.insert( buffer_id, DiffBaseState { last_version: None, _subscription: cx.observe(&change_set, move |editor, change_set, cx| { editor .diff_map .snapshot .0 .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone()); Editor::sync_expanded_diff_hunks(&mut editor.diff_map, buffer_id, cx); }), change_set, }, ); } pub fn hunks(&self, include_folded: bool) -> impl Iterator<Item = &ExpandedHunk> { self.hunks .iter() .filter(move |hunk| include_folded || !hunk.folded) }} impl DiffMapSnapshot { pub fn is_empty(&self) -> bool { self.0.values().all(|diff| diff.is_empty()) } pub fn diff_hunks<'a>( &'a self, buffer_snapshot: &'a MultiBufferSnapshot, ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a { self.diff_hunks_in_range(0..buffer_snapshot.len(), buffer_snapshot) } pub fn diff_hunks_in_range<'a, T: ToOffset>( &'a self, range: Range<T>, buffer_snapshot: &'a MultiBufferSnapshot, ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a { let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot); buffer_snapshot .excerpts_for_range(range.clone()) .filter_map(move |excerpt| { let buffer = excerpt.buffer(); let buffer_id = buffer.remote_id(); let diff = self.0.get(&buffer_id)?; let buffer_range = excerpt.map_range_to_buffer(range.clone()); let buffer_range = buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end); Some( diff.hunks_intersecting_range(buffer_range, excerpt.buffer()) .map(move |hunk| { let start = excerpt.map_point_from_buffer(Point::new(hunk.row_range.start, 0)); let end = excerpt.map_point_from_buffer(Point::new(hunk.row_range.end, 0)); MultiBufferDiffHunk { row_range: MultiBufferRow(start.row)..MultiBufferRow(end.row), buffer_id, buffer_range: hunk.buffer_range.clone(), diff_base_byte_range: hunk.diff_base_byte_range.clone(), } }), ) }) .flatten() } pub fn diff_hunks_in_range_rev<'a, T: ToOffset>( &'a self, range: Range<T>, buffer_snapshot: &'a MultiBufferSnapshot, ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a { let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot); buffer_snapshot .excerpts_for_range_rev(range.clone()) .filter_map(move |excerpt| { let buffer = excerpt.buffer(); let buffer_id = buffer.remote_id(); let diff = self.0.get(&buffer_id)?; let buffer_range = excerpt.map_range_to_buffer(range.clone()); let buffer_range = buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end); Some( diff.hunks_intersecting_range_rev(buffer_range, excerpt.buffer()) .map(move |hunk| { let start_row = excerpt .map_point_from_buffer(Point::new(hunk.row_range.start, 0)) .row; let end_row = excerpt .map_point_from_buffer(Point::new(hunk.row_range.end, 0)) .row; MultiBufferDiffHunk { row_range: MultiBufferRow(start_row)..MultiBufferRow(end_row), buffer_id, buffer_range: hunk.buffer_range.clone(), diff_base_byte_range: hunk.diff_base_byte_range.clone(), } }), ) }) .flatten() }} impl Editor { pub fn set_expand_all_diff_hunks(&mut self) { self.diff_map.expand_all = true; } pub(super) fn toggle_hovered_hunk( &mut self, hovered_hunk: &HoveredHunk, cx: &mut ViewContext<Editor>, ) { let editor_snapshot = self.snapshot(cx); if let Some(diff_hunk) = to_diff_hunk(hovered_hunk, &editor_snapshot.buffer_snapshot) { self.toggle_hunks_expanded(vec![diff_hunk], cx); self.change_selections(None, cx, |selections| selections.refresh()); } } pub fn toggle_hunk_diff(&mut self, _: &ToggleHunkDiff, cx: &mut ViewContext<Self>) { let snapshot = self.snapshot(cx); let selections = self.selections.all(cx); self.toggle_hunks_expanded(hunks_for_selections(&snapshot, &selections), cx); } pub fn expand_all_hunk_diffs(&mut self, _: &ExpandAllHunkDiffs, cx: &mut ViewContext<Self>) { let snapshot = self.snapshot(cx); let display_rows_with_expanded_hunks = self .diff_map .hunks(false) .map(|hunk| &hunk.hunk_range) .map(|anchor_range| { ( anchor_range .start .to_display_point(&snapshot.display_snapshot) .row(), anchor_range .end .to_display_point(&snapshot.display_snapshot) .row(), ) }) .collect::<HashMap<_, _>>(); let hunks = self .diff_map .snapshot .diff_hunks(&snapshot.display_snapshot.buffer_snapshot) .filter(|hunk| { let hunk_display_row_range = Point::new(hunk.row_range.start.0, 0) .to_display_point(&snapshot.display_snapshot) ..Point::new(hunk.row_range.end.0, 0) .to_display_point(&snapshot.display_snapshot); let row_range_end = display_rows_with_expanded_hunks.get(&hunk_display_row_range.start.row()); row_range_end.is_none() || row_range_end != Some(&hunk_display_row_range.end.row()) }); self.toggle_hunks_expanded(hunks.collect(), cx); } fn toggle_hunks_expanded( &mut self, hunks_to_toggle: Vec<MultiBufferDiffHunk>, cx: &mut ViewContext<Self>, ) { if self.diff_map.expand_all { return; } let previous_toggle_task = self.diff_map.hunk_update_tasks.remove(&None); let new_toggle_task = cx.spawn(move |editor, mut cx| async move { if let Some(task) = previous_toggle_task { task.await; } editor .update(&mut cx, |editor, cx| { let snapshot = editor.snapshot(cx); let mut hunks_to_toggle = hunks_to_toggle.into_iter().fuse().peekable(); let mut highlights_to_remove = Vec::with_capacity(editor.diff_map.hunks.len()); let mut blocks_to_remove = HashSet::default(); let mut hunks_to_expand = Vec::new(); editor.diff_map.hunks.retain(|expanded_hunk| { if expanded_hunk.folded { return true; } let expanded_hunk_row_range = expanded_hunk .hunk_range .start .to_display_point(&snapshot) .row() ..expanded_hunk .hunk_range .end .to_display_point(&snapshot) .row(); let mut retain = true; while let Some(hunk_to_toggle) = hunks_to_toggle.peek() { match diff_hunk_to_display(hunk_to_toggle, &snapshot) { DisplayDiffHunk::Folded { .. } => { hunks_to_toggle.next(); continue; } DisplayDiffHunk::Unfolded { diff_base_byte_range, display_row_range, multi_buffer_range, status, } => { let hunk_to_toggle_row_range = display_row_range; if hunk_to_toggle_row_range.start > expanded_hunk_row_range.end { break; } else if expanded_hunk_row_range == hunk_to_toggle_row_range { highlights_to_remove.push(expanded_hunk.hunk_range.clone()); blocks_to_remove .extend(expanded_hunk.blocks.iter().copied()); hunks_to_toggle.next(); retain = false; break; } else { hunks_to_expand.push(HoveredHunk { status, multi_buffer_range, diff_base_byte_range, }); hunks_to_toggle.next(); continue; } } } } retain }); for hunk in hunks_to_toggle { let remaining_hunk_point_range = Point::new(hunk.row_range.start.0, 0) ..Point::new(hunk.row_range.end.0, 0); let hunk_start = snapshot .buffer_snapshot .anchor_before(remaining_hunk_point_range.start); let hunk_end = snapshot .buffer_snapshot .anchor_in_excerpt(hunk_start.excerpt_id, hunk.buffer_range.end) .unwrap(); hunks_to_expand.push(HoveredHunk { status: hunk_status(&hunk), multi_buffer_range: hunk_start..hunk_end, diff_base_byte_range: hunk.diff_base_byte_range.clone(), }); } editor.remove_highlighted_rows::<DiffRowHighlight>(highlights_to_remove, cx); editor.remove_blocks(blocks_to_remove, None, cx); for hunk in hunks_to_expand { editor.expand_diff_hunk(None, &hunk, cx); } cx.notify(); }) .ok(); }); self.diff_map .hunk_update_tasks .insert(None, cx.background_executor().spawn(new_toggle_task)); } pub(super) fn expand_diff_hunk( &mut self, diff_base_buffer: Option<Model<Buffer>>, hunk: &HoveredHunk, cx: &mut ViewContext<'_, Editor>, ) -> Option<()> { let buffer = self.buffer.clone(); let multi_buffer_snapshot = buffer.read(cx).snapshot(cx); let hunk_range = hunk.multi_buffer_range.clone(); let buffer_id = hunk_range.start.buffer_id?; let diff_base_buffer = diff_base_buffer.or_else(|| { self.diff_map .diff_bases .get(&buffer_id)? .change_set .read(cx) .base_text .clone() })?; let diff_base = diff_base_buffer.read(cx); let diff_start_row = diff_base .offset_to_point(hunk.diff_base_byte_range.start) .row; let diff_end_row = diff_base.offset_to_point(hunk.diff_base_byte_range.end).row; let deleted_text_lines = diff_end_row - diff_start_row; let block_insert_index = self .diff_map .hunks .binary_search_by(|probe| { probe .hunk_range .start .cmp(&hunk_range.start, &multi_buffer_snapshot) }) .err()?; let blocks; match hunk.status { DiffHunkStatus::Removed => { blocks = self.insert_blocks( [ self.hunk_header_block(&hunk, cx), Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx), ], None, cx, ); } DiffHunkStatus::Added => { self.highlight_rows::<DiffRowHighlight>( hunk_range.clone(), added_hunk_color(cx), false, cx, ); blocks = self.insert_blocks([self.hunk_header_block(&hunk, cx)], None, cx); } DiffHunkStatus::Modified => { self.highlight_rows::<DiffRowHighlight>( hunk_range.clone(), added_hunk_color(cx), false, cx, ); blocks = self.insert_blocks( [ self.hunk_header_block(&hunk, cx), Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx), ], None, cx, ); } }; self.diff_map.hunks.insert( block_insert_index, ExpandedHunk { blocks, hunk_range, status: hunk.status, folded: false, diff_base_byte_range: hunk.diff_base_byte_range.clone(), }, ); Some(()) } fn apply_diff_hunks_in_range( &mut self, range: Range<Anchor>, cx: &mut ViewContext<'_, Editor>, ) -> Option<()> { let (buffer, range, _) = self .buffer .read(cx) .range_to_buffer_ranges(range, cx) .into_iter() .next()?; buffer.update(cx, |branch_buffer, cx| { branch_buffer.merge_into_base(vec![range], cx); }); if let Some(project) = self.project.clone() { self.save(true, project, cx).detach_and_log_err(cx); } None } pub(crate) fn apply_all_diff_hunks( &mut self, _: &ApplyAllDiffHunks, cx: &mut ViewContext<Self>, ) { let buffers = self.buffer.read(cx).all_buffers(); for branch_buffer in buffers { branch_buffer.update(cx, |branch_buffer, cx| { branch_buffer.merge_into_base(Vec::new(), cx); }); } if let Some(project) = self.project.clone() { self.save(true, project, cx).detach_and_log_err(cx); } } pub(crate) fn apply_selected_diff_hunks( &mut self, _: &ApplyDiffHunk, cx: &mut ViewContext<Self>, ) { let snapshot = self.snapshot(cx);Lines 1–500 of 1498. Load more
use collections::{HashMap, HashSet}; use git::diff::DiffHunkStatus; use gpui::{ Action, AnchorCorner, AppContext, CursorStyle, Hsla, Model, MouseButton, Subscription, Task, View, }; use language::{Buffer, BufferId, Point}; use multi_buffer::{ Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow, MultiBufferSnapshot, ToOffset, ToPoint, }; use project::buffer_store::BufferChangeSet; use std::{ops::Range, sync::Arc}; use sum_tree::TreeMap; use text::OffsetRangeExt; use ui::{ prelude::*, ActiveTheme, ContextMenu, IconButtonShape, InteractiveElement, IntoElement, ParentElement, PopoverMenu, Styled, Tooltip, ViewContext, VisualContext, }; use util::RangeExt; use workspace::Item; use crate::{ editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyAllDiffHunks, ApplyDiffHunk, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow, DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk, RevertFile, RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff, }; #[derive(Debug, Clone)] pub(super) struct HoveredHunk { pub multi_buffer_range: Range<Anchor>, pub status: DiffHunkStatus, pub diff_base_byte_range: Range<usize>, } #[derive(Default)] pub(super) struct DiffMap { pub(crate) hunks: Vec<ExpandedHunk>, pub(crate) diff_bases: HashMap<BufferId, DiffBaseState>, pub(crate) snapshot: DiffMapSnapshot, hunk_update_tasks: HashMap<Option<BufferId>, Task<()>>, expand_all: bool, } #[derive(Debug, Clone)] pub(super) struct ExpandedHunk { pub blocks: Vec<CustomBlockId>, pub hunk_range: Range<Anchor>, pub diff_base_byte_range: Range<usize>, pub status: DiffHunkStatus, pub folded: bool, } #[derive(Clone, Debug, Default)] pub(crate) struct DiffMapSnapshot(TreeMap<BufferId, git::diff::BufferDiff>); pub(crate) struct DiffBaseState { pub(crate) change_set: Model<BufferChangeSet>, pub(crate) last_version: Option<usize>, _subscription: Subscription, } #[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 DiffMap { pub fn snapshot(&self) -> DiffMapSnapshot { self.snapshot.clone() } pub fn add_change_set( &mut self, change_set: Model<BufferChangeSet>, cx: &mut ViewContext<Editor>, ) { let buffer_id = change_set.read(cx).buffer_id; self.snapshot .0 .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone()); Editor::sync_expanded_diff_hunks(self, buffer_id, cx); self.diff_bases.insert( buffer_id, DiffBaseState { last_version: None, _subscription: cx.observe(&change_set, move |editor, change_set, cx| { editor .diff_map .snapshot .0 .insert(buffer_id, change_set.read(cx).diff_to_buffer.clone()); Editor::sync_expanded_diff_hunks(&mut editor.diff_map, buffer_id, cx); }), change_set, }, ); } pub fn hunks(&self, include_folded: bool) -> impl Iterator<Item = &ExpandedHunk> { self.hunks .iter() .filter(move |hunk| include_folded || !hunk.folded) } } impl DiffMapSnapshot { pub fn is_empty(&self) -> bool { self.0.values().all(|diff| diff.is_empty()) } pub fn diff_hunks<'a>( &'a self, buffer_snapshot: &'a MultiBufferSnapshot, ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a { self.diff_hunks_in_range(0..buffer_snapshot.len(), buffer_snapshot) } pub fn diff_hunks_in_range<'a, T: ToOffset>( &'a self, range: Range<T>, buffer_snapshot: &'a MultiBufferSnapshot, ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a { let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot); buffer_snapshot .excerpts_for_range(range.clone()) .filter_map(move |excerpt| { let buffer = excerpt.buffer(); let buffer_id = buffer.remote_id(); let diff = self.0.get(&buffer_id)?; let buffer_range = excerpt.map_range_to_buffer(range.clone()); let buffer_range = buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end); Some( diff.hunks_intersecting_range(buffer_range, excerpt.buffer()) .map(move |hunk| { let start = excerpt.map_point_from_buffer(Point::new(hunk.row_range.start, 0)); let end = excerpt.map_point_from_buffer(Point::new(hunk.row_range.end, 0)); MultiBufferDiffHunk { row_range: MultiBufferRow(start.row)..MultiBufferRow(end.row), buffer_id, buffer_range: hunk.buffer_range.clone(), diff_base_byte_range: hunk.diff_base_byte_range.clone(), } }), ) }) .flatten() } pub fn diff_hunks_in_range_rev<'a, T: ToOffset>( &'a self, range: Range<T>, buffer_snapshot: &'a MultiBufferSnapshot, ) -> impl Iterator<Item = MultiBufferDiffHunk> + 'a { let range = range.start.to_offset(buffer_snapshot)..range.end.to_offset(buffer_snapshot); buffer_snapshot .excerpts_for_range_rev(range.clone()) .filter_map(move |excerpt| { let buffer = excerpt.buffer(); let buffer_id = buffer.remote_id(); let diff = self.0.get(&buffer_id)?; let buffer_range = excerpt.map_range_to_buffer(range.clone()); let buffer_range = buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end); Some( diff.hunks_intersecting_range_rev(buffer_range, excerpt.buffer()) .map(move |hunk| { let start_row = excerpt .map_point_from_buffer(Point::new(hunk.row_range.start, 0)) .row; let end_row = excerpt .map_point_from_buffer(Point::new(hunk.row_range.end, 0)) .row; MultiBufferDiffHunk { row_range: MultiBufferRow(start_row)..MultiBufferRow(end_row), buffer_id, buffer_range: hunk.buffer_range.clone(), diff_base_byte_range: hunk.diff_base_byte_range.clone(), } }), ) }) .flatten() } } impl Editor { pub fn set_expand_all_diff_hunks(&mut self) { self.diff_map.expand_all = true; } pub(super) fn toggle_hovered_hunk( &mut self, hovered_hunk: &HoveredHunk, cx: &mut ViewContext<Editor>, ) { let editor_snapshot = self.snapshot(cx); if let Some(diff_hunk) = to_diff_hunk(hovered_hunk, &editor_snapshot.buffer_snapshot) { self.toggle_hunks_expanded(vec![diff_hunk], cx); self.change_selections(None, cx, |selections| selections.refresh()); } } pub fn toggle_hunk_diff(&mut self, _: &ToggleHunkDiff, cx: &mut ViewContext<Self>) { let snapshot = self.snapshot(cx); let selections = self.selections.all(cx); self.toggle_hunks_expanded(hunks_for_selections(&snapshot, &selections), cx); } pub fn expand_all_hunk_diffs(&mut self, _: &ExpandAllHunkDiffs, cx: &mut ViewContext<Self>) { let snapshot = self.snapshot(cx); let display_rows_with_expanded_hunks = self .diff_map .hunks(false) .map(|hunk| &hunk.hunk_range) .map(|anchor_range| { ( anchor_range .start .to_display_point(&snapshot.display_snapshot) .row(), anchor_range .end .to_display_point(&snapshot.display_snapshot) .row(), ) }) .collect::<HashMap<_, _>>(); let hunks = self .diff_map .snapshot .diff_hunks(&snapshot.display_snapshot.buffer_snapshot) .filter(|hunk| { let hunk_display_row_range = Point::new(hunk.row_range.start.0, 0) .to_display_point(&snapshot.display_snapshot) ..Point::new(hunk.row_range.end.0, 0) .to_display_point(&snapshot.display_snapshot); let row_range_end = display_rows_with_expanded_hunks.get(&hunk_display_row_range.start.row()); row_range_end.is_none() || row_range_end != Some(&hunk_display_row_range.end.row()) }); self.toggle_hunks_expanded(hunks.collect(), cx); } fn toggle_hunks_expanded( &mut self, hunks_to_toggle: Vec<MultiBufferDiffHunk>, cx: &mut ViewContext<Self>, ) { if self.diff_map.expand_all { return; } let previous_toggle_task = self.diff_map.hunk_update_tasks.remove(&None); let new_toggle_task = cx.spawn(move |editor, mut cx| async move { if let Some(task) = previous_toggle_task { task.await; } editor .update(&mut cx, |editor, cx| { let snapshot = editor.snapshot(cx); let mut hunks_to_toggle = hunks_to_toggle.into_iter().fuse().peekable(); let mut highlights_to_remove = Vec::with_capacity(editor.diff_map.hunks.len()); let mut blocks_to_remove = HashSet::default(); let mut hunks_to_expand = Vec::new(); editor.diff_map.hunks.retain(|expanded_hunk| { if expanded_hunk.folded { return true; } let expanded_hunk_row_range = expanded_hunk .hunk_range .start .to_display_point(&snapshot) .row() ..expanded_hunk .hunk_range .end .to_display_point(&snapshot) .row(); let mut retain = true; while let Some(hunk_to_toggle) = hunks_to_toggle.peek() { match diff_hunk_to_display(hunk_to_toggle, &snapshot) { DisplayDiffHunk::Folded { .. } => { hunks_to_toggle.next(); continue; } DisplayDiffHunk::Unfolded { diff_base_byte_range, display_row_range, multi_buffer_range, status, } => { let hunk_to_toggle_row_range = display_row_range; if hunk_to_toggle_row_range.start > expanded_hunk_row_range.end { break; } else if expanded_hunk_row_range == hunk_to_toggle_row_range { highlights_to_remove.push(expanded_hunk.hunk_range.clone()); blocks_to_remove .extend(expanded_hunk.blocks.iter().copied()); hunks_to_toggle.next(); retain = false; break; } else { hunks_to_expand.push(HoveredHunk { status, multi_buffer_range, diff_base_byte_range, }); hunks_to_toggle.next(); continue; } } } } retain }); for hunk in hunks_to_toggle { let remaining_hunk_point_range = Point::new(hunk.row_range.start.0, 0) ..Point::new(hunk.row_range.end.0, 0); let hunk_start = snapshot .buffer_snapshot .anchor_before(remaining_hunk_point_range.start); let hunk_end = snapshot .buffer_snapshot .anchor_in_excerpt(hunk_start.excerpt_id, hunk.buffer_range.end) .unwrap(); hunks_to_expand.push(HoveredHunk { status: hunk_status(&hunk), multi_buffer_range: hunk_start..hunk_end, diff_base_byte_range: hunk.diff_base_byte_range.clone(), }); } editor.remove_highlighted_rows::<DiffRowHighlight>(highlights_to_remove, cx); editor.remove_blocks(blocks_to_remove, None, cx); for hunk in hunks_to_expand { editor.expand_diff_hunk(None, &hunk, cx); } cx.notify(); }) .ok(); }); self.diff_map .hunk_update_tasks .insert(None, cx.background_executor().spawn(new_toggle_task)); } pub(super) fn expand_diff_hunk( &mut self, diff_base_buffer: Option<Model<Buffer>>, hunk: &HoveredHunk, cx: &mut ViewContext<'_, Editor>, ) -> Option<()> { let buffer = self.buffer.clone(); let multi_buffer_snapshot = buffer.read(cx).snapshot(cx); let hunk_range = hunk.multi_buffer_range.clone(); let buffer_id = hunk_range.start.buffer_id?; let diff_base_buffer = diff_base_buffer.or_else(|| { self.diff_map .diff_bases .get(&buffer_id)? .change_set .read(cx) .base_text .clone() })?; let diff_base = diff_base_buffer.read(cx); let diff_start_row = diff_base .offset_to_point(hunk.diff_base_byte_range.start) .row; let diff_end_row = diff_base.offset_to_point(hunk.diff_base_byte_range.end).row; let deleted_text_lines = diff_end_row - diff_start_row; let block_insert_index = self .diff_map .hunks .binary_search_by(|probe| { probe .hunk_range .start .cmp(&hunk_range.start, &multi_buffer_snapshot) }) .err()?; let blocks; match hunk.status { DiffHunkStatus::Removed => { blocks = self.insert_blocks( [ self.hunk_header_block(&hunk, cx), Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx), ], None, cx, ); } DiffHunkStatus::Added => { self.highlight_rows::<DiffRowHighlight>( hunk_range.clone(), added_hunk_color(cx), false, cx, ); blocks = self.insert_blocks([self.hunk_header_block(&hunk, cx)], None, cx); } DiffHunkStatus::Modified => { self.highlight_rows::<DiffRowHighlight>( hunk_range.clone(), added_hunk_color(cx), false, cx, ); blocks = self.insert_blocks( [ self.hunk_header_block(&hunk, cx), Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx), ], None, cx, ); } }; self.diff_map.hunks.insert( block_insert_index, ExpandedHunk { blocks, hunk_range, status: hunk.status, folded: false, diff_base_byte_range: hunk.diff_base_byte_range.clone(), }, ); Some(()) } fn apply_diff_hunks_in_range( &mut self, range: Range<Anchor>, cx: &mut ViewContext<'_, Editor>, ) -> Option<()> { let (buffer, range, _) = self .buffer .read(cx) .range_to_buffer_ranges(range, cx) .into_iter() .next()?; buffer.update(cx, |branch_buffer, cx| { branch_buffer.merge_into_base(vec![range], cx); }); if let Some(project) = self.project.clone() { self.save(true, project, cx).detach_and_log_err(cx); } None } pub(crate) fn apply_all_diff_hunks( &mut self, _: &ApplyAllDiffHunks, cx: &mut ViewContext<Self>, ) { let buffers = self.buffer.read(cx).all_buffers(); for branch_buffer in buffers { branch_buffer.update(cx, |branch_buffer, cx| { branch_buffer.merge_into_base(Vec::new(), cx); }); } if let Some(project) = self.project.clone() { self.save(true, project, cx).detach_and_log_err(cx); } } pub(crate) fn apply_selected_diff_hunks( &mut self, _: &ApplyDiffHunk, cx: &mut ViewContext<Self>, ) { let snapshot = self.snapshot(cx);