diff --git a/crates/editor/src/split.rs b/crates/editor/src/split.rs index 98ce8bf8b56222990379e3a33be4902e5a384acd..ca3aae839194deec1164b23b297068f4efc1aed2 100644 --- a/crates/editor/src/split.rs +++ b/crates/editor/src/split.rs @@ -1,16 +1,13 @@ -use std::ops::Range; +use std::{ops::Range, sync::Arc}; -use buffer_diff::{BufferDiff, BufferDiffSnapshot}; -use collections::HashMap; use feature_flags::{FeatureFlag, FeatureFlagAppExt as _}; use gpui::{ Action, AppContext as _, Entity, EventEmitter, Focusable, NoAction, Subscription, WeakEntity, }; -use language::{Buffer, BufferSnapshot, Capability}; -use multi_buffer::{Anchor, ExcerptRange, MultiBuffer, MultiBufferSnapshot, PathKey}; +use language::{Buffer, Capability, LanguageRegistry}; +use multi_buffer::{Anchor, ExcerptRange, MultiBuffer, PathKey}; use project::Project; use rope::Point; -use text::{BufferId, OffsetRangeExt as _}; use ui::{ App, Context, InteractiveElement as _, IntoElement as _, ParentElement as _, Render, Styled as _, Window, div, @@ -140,86 +137,18 @@ impl SplittableEditor { return; }; let project = workspace.read(cx).project().clone(); - let language_registry = project.read(cx).languages().clone(); - - let primary_multibuffer = self.primary_editor.read(cx).buffer().read(cx); - - let base_text_buffers_by_main_buffer_id: HashMap< - BufferId, - (Entity, BufferDiffSnapshot), - > = primary_multibuffer - .all_buffer_ids_iter() - .filter_map(|main_buffer_id| { - let diff = primary_multibuffer.diff_for(main_buffer_id)?; - let base_text_buffer = cx.new(|cx| { - let base_text = diff.read(cx).base_text(); - let mut buffer = Buffer::local_normalized( - base_text.as_rope().clone(), - base_text.line_ending(), - cx, - ); - buffer.set_language(base_text.language().cloned(), cx); - buffer.set_language_registry(language_registry.clone()); - buffer - }); - Some(( - main_buffer_id, - (base_text_buffer, diff.read(cx).snapshot(cx)), - )) - }) - .collect(); - let snapshot = primary_multibuffer.snapshot(cx); - let mut excerpt_ranges_by_base_buffer: HashMap< - Entity, - (PathKey, Vec>), - > = HashMap::default(); - for (path_key, excerpt_id) in primary_multibuffer.excerpts_with_paths() { - let main_buffer = snapshot.buffer_for_excerpt(*excerpt_id).unwrap(); - let excerpt_range = snapshot.excerpt_range_for_excerpt(*excerpt_id).unwrap(); - let (base_text_buffer, diff) = base_text_buffers_by_main_buffer_id - .get(&main_buffer.remote_id()) - .unwrap(); - let point_to_base_text_point = |point: Point| { - let row = diff.row_to_base_text_row(point.row, &main_buffer); - let column = diff.base_text().line_len(row); - Point::new(row, column) - }; - let primary = excerpt_range.primary.to_point(&main_buffer); - let context = excerpt_range.context.to_point(&main_buffer); - let translated_range = ExcerptRange { - primary: point_to_base_text_point(primary.start) - ..point_to_base_text_point(primary.end), - context: point_to_base_text_point(context.start) - ..point_to_base_text_point(context.end), - }; - excerpt_ranges_by_base_buffer - .entry(base_text_buffer.clone()) - .or_insert((path_key.clone(), Vec::new())) - .1 - .push(translated_range); - } - - let secondary_multibuffer = cx.new(|cx| { - let mut multibuffer = MultiBuffer::new(Capability::ReadOnly); - for (base_text_buffer, (path_key, ranges)) in excerpt_ranges_by_base_buffer { - let base_text_buffer_snapshot = base_text_buffer.read(cx).snapshot(); - multibuffer.update_path_excerpts( - path_key, - base_text_buffer, - &base_text_buffer_snapshot, - ranges, - cx, - ); - } - multibuffer - }); - let secondary_editor = - cx.new(|cx| Editor::for_multibuffer(secondary_multibuffer, Some(project), window, cx)); // FIXME // - have to subscribe to the diffs to update the base text buffers (and handle language changed I think?) - // - implement SplittableEditor::set_excerpts_for_path + let secondary_editor = cx.new(|cx| { + let multibuffer = cx.new(|cx| { + let mut multibuffer = MultiBuffer::new(Capability::ReadOnly); + multibuffer.set_all_diff_hunks_expanded(cx); + multibuffer + }); + Editor::for_multibuffer(multibuffer, Some(project.clone()), window, cx) + }); let secondary_pane = cx.new(|cx| { let mut pane = Pane::new( workspace.downgrade(), @@ -260,15 +189,20 @@ impl SplittableEditor { has_latest_selection: false, _subscriptions: subscriptions, }; - for (path_key, diff, original_range, original_buffer) in whatever { - secondary.sync_path_excerpts_for_buffer( - path_key, - diff, - original_range, - original_buffer, - cx, - ); - } + self.primary_editor.update(cx, |editor, cx| { + editor.buffer().update(cx, |primary_multibuffer, cx| { + primary_multibuffer.set_show_deleted_hunks(false, cx); + let paths = primary_multibuffer.paths().collect::>(); + for path in paths { + secondary.sync_path_excerpts( + path, + primary_multibuffer, + project.read(cx).languages().clone(), + cx, + ); + } + }) + }); self.secondary = Some(secondary); let primary_pane = self.panes.first_pane(); @@ -284,8 +218,8 @@ impl SplittableEditor { }; self.panes.remove(&secondary.pane).unwrap(); self.primary_editor.update(cx, |primary, cx| { - primary.buffer().update(cx, |buffer, _| { - buffer.set_filter_mode(None); + primary.buffer().update(cx, |buffer, cx| { + buffer.set_show_deleted_hunks(true, cx); }); }); cx.notify(); @@ -316,17 +250,28 @@ impl SplittableEditor { context_line_count: u32, cx: &mut Context, ) -> (Vec>, bool) { - let (anchors, added_a_new_excerpt) = - self.primary_editor - .read(cx) - .buffer() - .update(cx, |multibuffer, cx| { - multibuffer.set_excerpts_for_path(path, buffer, ranges, context_line_count, cx) - }); - if let Some(secondary) = &mut self.secondary { - secondary.sync_path_excerpts_for_buffer(cx); - } - (anchors, added_a_new_excerpt) + self.primary_editor.update(cx, |editor, cx| { + editor.buffer().update(cx, |primary_multibuffer, cx| { + let (anchors, added_a_new_excerpt) = primary_multibuffer.set_excerpts_for_path( + path.clone(), + buffer, + ranges, + context_line_count, + cx, + ); + if let Some(secondary) = &mut self.secondary + && let Some(languages) = self + .workspace + .update(cx, |workspace, cx| { + workspace.project().read(cx).languages().clone() + }) + .ok() + { + secondary.sync_path_excerpts(path, primary_multibuffer, languages, cx); + } + (anchors, added_a_new_excerpt) + }) + }) } } @@ -367,35 +312,62 @@ impl Render for SplittableEditor { } impl SecondaryEditor { - fn sync_path_excerpts_for_buffer( + fn sync_path_excerpts( &mut self, path_key: PathKey, - main_buffer: &BufferSnapshot, - primary_multibuffer: &MultiBuffer, + primary_multibuffer: &mut MultiBuffer, + languages: Arc, cx: &mut App, ) { + let excerpt_id = primary_multibuffer + .excerpts_for_path(&path_key) + .next() + .unwrap(); + let primary_multibuffer_snapshot = primary_multibuffer.snapshot(cx); + let main_buffer = primary_multibuffer_snapshot + .buffer_for_excerpt(excerpt_id) + .unwrap(); let diff = primary_multibuffer .diff_for(main_buffer.remote_id()) .unwrap(); let diff = diff.read(cx).snapshot(cx); - // option 1: hold onto the base text buffers in splittable editor so that we can check whether they exist yet - // option 2: have the multibuffer continue to be fully responsible for holding the base text buffers; then need to be able to get a buffer out of the multibuffer based on a pathkey - let base_text_buffer = self.editor.update(cx, |editor, cx| { - editor - .buffer() - .update(cx, |buffer, cx| buffer.buffer_for_path_key) - }); + let base_text_buffer = self + .editor + .update(cx, |editor, cx| { + editor.buffer().update(cx, |secondary_multibuffer, cx| { + let excerpt_id = secondary_multibuffer.excerpts_for_path(&path_key).next()?; + let secondary_buffer_snapshot = secondary_multibuffer.snapshot(cx); + let buffer = secondary_buffer_snapshot + .buffer_for_excerpt(excerpt_id) + .unwrap(); + Some(secondary_multibuffer.buffer(buffer.remote_id()).unwrap()) + }) + }) + .unwrap_or_else(|| { + cx.new(|cx| { + let base_text = diff.base_text(); + let mut buffer = Buffer::local_normalized( + base_text.as_rope().clone(), + base_text.line_ending(), + cx, + ); + buffer.set_language(base_text.language().cloned(), cx); + buffer.set_language_registry(languages); + buffer + }) + }); + let base_text_buffer_snapshot = base_text_buffer.read(cx).snapshot(); let new = primary_multibuffer .excerpts_for_buffer(main_buffer.remote_id(), cx) .into_iter() - .map(|(excerpt_id, excerpt_range)| { + .map(|(_, excerpt_range)| { let point_to_base_text_point = |point: Point| { - let row = diff.row_to_base_text_row(point.row, &main_buffer); + let row = diff.row_to_base_text_row(point.row, main_buffer); let column = diff.base_text().line_len(row); Point::new(row, column) }; - let primary = excerpt_range.primary.to_point(&main_buffer); - let context = excerpt_range.context.to_point(&main_buffer); + let primary = excerpt_range.primary.to_point(main_buffer); + let context = excerpt_range.context.to_point(main_buffer); ExcerptRange { primary: point_to_base_text_point(primary.start) ..point_to_base_text_point(primary.end), @@ -410,7 +382,7 @@ impl SecondaryEditor { buffer.update_path_excerpts( path_key, base_text_buffer, - base_text_buffer_snapshot, + &base_text_buffer_snapshot, new, cx, ) diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index f8e2a76f6704459ea998587f2144900eaf4e8eb6..a153c1830e8ad1576a5b98546b0a10283a7ac1e7 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -610,6 +610,7 @@ pub struct MultiBufferSnapshot { replaced_excerpts: TreeMap, trailing_excerpt_update_count: usize, all_diff_hunks_expanded: bool, + show_deleted_hunks: bool, show_headers: bool, } @@ -1089,6 +1090,7 @@ impl MultiBuffer { capability, MultiBufferSnapshot { show_headers: true, + show_deleted_hunks: true, ..MultiBufferSnapshot::default() }, ) @@ -1862,6 +1864,7 @@ impl MultiBuffer { replaced_excerpts, trailing_excerpt_update_count, all_diff_hunks_expanded: _, + show_deleted_hunks: _, show_headers: _, } = self.snapshot.get_mut(); let start = ExcerptDimension(MultiBufferOffset::ZERO); @@ -2652,6 +2655,11 @@ impl MultiBuffer { self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], false, cx); } + pub fn set_show_deleted_hunks(&mut self, show: bool, cx: &mut Context) { + self.snapshot.get_mut().show_deleted_hunks = show; + self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], true, cx); + } + pub fn has_multiple_hunks(&self, cx: &App) -> bool { self.read(cx) .diff_hunks_in_range(Anchor::min()..Anchor::max()) @@ -2989,6 +2997,7 @@ impl MultiBuffer { replaced_excerpts: _, trailing_excerpt_update_count: _, all_diff_hunks_expanded: _, + show_deleted_hunks: _, show_headers: _, } = snapshot; *is_dirty = false; @@ -3382,10 +3391,10 @@ impl MultiBuffer { excerpt.id ); - // FIXME don't push the deleted region if this is the RHS of a split if !hunk.diff_base_byte_range.is_empty() && hunk_buffer_range.start >= edit_buffer_start && hunk_buffer_range.start <= excerpt_buffer_end + && snapshot.show_deleted_hunks { let base_text = diff.base_text(); let mut text_cursor = diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index ac7aeba6b17cf43554963945e16f9aac1a49a783..28aee772c88cf818ffc1cdaa12d5916712223b08 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -69,6 +69,14 @@ impl MultiBuffer { .flat_map(|(key, ex_ids)| ex_ids.iter().map(move |id| (key, id))) } + pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator { + self.excerpts_by_path + .get(path) + .into_iter() + .flatten() + .copied() + } + /// Sets excerpts, returns `true` if at least one new excerpt was added. pub fn set_excerpts_for_path( &mut self,