Detailed changes
@@ -81,8 +81,8 @@ mod wrap_map;
pub use crate::display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap};
pub use block_map::{
Block, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap, BlockPlacement,
- BlockPoint, BlockProperties, BlockRows, BlockStyle, CompanionView, CustomBlockId,
- EditorMargins, RenderBlock, StickyHeaderExcerpt,
+ BlockPoint, BlockProperties, BlockRows, BlockStyle, CompanionView, CompanionViewMut,
+ CustomBlockId, EditorMargins, RenderBlock, StickyHeaderExcerpt,
};
pub use crease_map::*;
pub use fold_map::{
@@ -238,6 +238,8 @@ pub(crate) struct Companion {
lhs_excerpt_to_rhs_excerpt: HashMap<ExcerptId, ExcerptId>,
rhs_rows_to_lhs_rows: ConvertMultiBufferRows,
lhs_rows_to_rhs_rows: ConvertMultiBufferRows,
+ rhs_custom_blocks_to_lhs_custom_blocks: HashMap<CustomBlockId, CustomBlockId>,
+ lhs_custom_blocks_to_rhs_custom_blocks: HashMap<CustomBlockId, CustomBlockId>,
}
impl Companion {
@@ -256,9 +258,46 @@ impl Companion {
lhs_excerpt_to_rhs_excerpt: Default::default(),
rhs_rows_to_lhs_rows,
lhs_rows_to_rhs_rows,
+ rhs_custom_blocks_to_lhs_custom_blocks: Default::default(),
+ lhs_custom_blocks_to_rhs_custom_blocks: Default::default(),
}
}
+ pub(crate) fn is_rhs(&self, display_map_id: EntityId) -> bool {
+ self.rhs_display_map_id == display_map_id
+ }
+
+ pub(crate) fn companion_custom_block_to_custom_block(
+ &self,
+ display_map_id: EntityId,
+ ) -> &HashMap<CustomBlockId, CustomBlockId> {
+ if self.is_rhs(display_map_id) {
+ &self.lhs_custom_blocks_to_rhs_custom_blocks
+ } else {
+ &self.rhs_custom_blocks_to_lhs_custom_blocks
+ }
+ }
+
+ pub(crate) fn add_custom_block_mapping(
+ &mut self,
+ lhs_id: CustomBlockId,
+ rhs_id: CustomBlockId,
+ ) {
+ self.lhs_custom_blocks_to_rhs_custom_blocks
+ .insert(lhs_id, rhs_id);
+ self.rhs_custom_blocks_to_lhs_custom_blocks
+ .insert(rhs_id, lhs_id);
+ }
+
+ pub(crate) fn remove_custom_block_mapping(
+ &mut self,
+ lhs_id: &CustomBlockId,
+ rhs_id: &CustomBlockId,
+ ) {
+ self.lhs_custom_blocks_to_rhs_custom_blocks.remove(lhs_id);
+ self.rhs_custom_blocks_to_lhs_custom_blocks.remove(rhs_id);
+ }
+
pub(crate) fn convert_rows_to_companion(
&self,
display_map_id: EntityId,
@@ -266,7 +305,7 @@ impl Companion {
our_snapshot: &MultiBufferSnapshot,
bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),
) -> Vec<CompanionExcerptPatch> {
- let (excerpt_map, convert_fn) = if display_map_id == self.rhs_display_map_id {
+ let (excerpt_map, convert_fn) = if self.is_rhs(display_map_id) {
(&self.rhs_excerpt_to_lhs_excerpt, self.rhs_rows_to_lhs_rows)
} else {
(&self.lhs_excerpt_to_rhs_excerpt, self.lhs_rows_to_rhs_rows)
@@ -281,7 +320,7 @@ impl Companion {
companion_snapshot: &MultiBufferSnapshot,
point: MultiBufferPoint,
) -> Range<MultiBufferPoint> {
- let (excerpt_map, convert_fn) = if display_map_id == self.rhs_display_map_id {
+ let (excerpt_map, convert_fn) = if self.is_rhs(display_map_id) {
(&self.lhs_excerpt_to_rhs_excerpt, self.lhs_rows_to_rhs_rows)
} else {
(&self.rhs_excerpt_to_lhs_excerpt, self.rhs_rows_to_lhs_rows)
@@ -306,7 +345,7 @@ impl Companion {
&self,
display_map_id: EntityId,
) -> &HashMap<ExcerptId, ExcerptId> {
- if display_map_id == self.rhs_display_map_id {
+ if self.is_rhs(display_map_id) {
&self.lhs_excerpt_to_rhs_excerpt
} else {
&self.rhs_excerpt_to_lhs_excerpt
@@ -314,7 +353,7 @@ impl Companion {
}
fn buffer_to_companion_buffer(&self, display_map_id: EntityId) -> &HashMap<BufferId, BufferId> {
- if display_map_id == self.rhs_display_map_id {
+ if self.is_rhs(display_map_id) {
&self.rhs_buffer_to_lhs_buffer
} else {
&self.lhs_buffer_to_rhs_buffer
@@ -493,20 +532,56 @@ impl DisplayMap {
.read(snapshot.clone(), edits.clone(), companion_view);
if let Some((companion_dm, _)) = &self.companion {
- let _ = companion_dm.update(cx, |dm, _cx| {
+ let _ = companion_dm.update(cx, |dm, cx| {
if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
- let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(_cx));
+ let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c);
+
dm.block_map.read(
companion_snapshot,
companion_edits,
- their_companion_ref
- .map(|c| CompanionView::new(dm.entity_id, &snapshot, &edits, c)),
+ their_companion_ref.map(|c| {
+ CompanionView::new(dm.entity_id, &snapshot, &edits, c.read(cx))
+ }),
);
}
});
}
}
+ pub(crate) fn sync_custom_blocks_into_companion(&mut self, cx: &mut Context<Self>) {
+ if self.companion.is_none() {
+ return;
+ }
+
+ let (self_wrap_snapshot, _) = self.sync_through_wrap(cx);
+ let (companion_dm, companion) = self
+ .companion
+ .as_ref()
+ .expect("companion must exist at this point");
+
+ companion
+ .update(cx, |companion, cx| {
+ companion_dm.update(cx, |dm, cx| {
+ let (companion_snapshot, _) = dm.sync_through_wrap(cx);
+ // Sync existing custom blocks to the companion
+ for block in self
+ .block_map
+ .read(self_wrap_snapshot.clone(), Patch::default(), None)
+ .blocks
+ {
+ dm.block_map.insert_custom_block_into_companion(
+ self.entity_id,
+ &companion_snapshot,
+ block,
+ self_wrap_snapshot.buffer_snapshot(),
+ companion,
+ )
+ }
+ })
+ })
+ .ok();
+ }
+
pub(crate) fn companion(&self) -> Option<&Entity<Companion>> {
self.companion.as_ref().map(|(_, c)| c)
}
@@ -693,52 +768,68 @@ impl DisplayMap {
let (self_wrap_snapshot, self_wrap_edits) =
(self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
- let mut block_map = {
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
- self.block_map
- .write(self_new_wrap_snapshot, self_new_wrap_edits, companion_view)
- };
- let blocks = creases.into_iter().filter_map(|crease| {
- if let Crease::Block {
- range,
- block_height,
- render_block,
- block_style,
- block_priority,
- ..
- } = crease
- {
- Some((
+ let blocks = creases
+ .into_iter()
+ .filter_map(|crease| {
+ if let Crease::Block {
range,
- render_block,
block_height,
+ render_block,
block_style,
block_priority,
- ))
- } else {
- None
- }
- });
- block_map.insert(
- blocks
- .into_iter()
- .map(|(range, render, height, style, priority)| {
- let start = buffer_snapshot.anchor_before(range.start);
- let end = buffer_snapshot.anchor_after(range.end);
- BlockProperties {
- placement: BlockPlacement::Replace(start..=end),
- render,
- height: Some(height),
- style,
- priority,
- }
- }),
- );
+ ..
+ } = crease
+ {
+ Some((
+ range,
+ render_block,
+ block_height,
+ block_style,
+ block_priority,
+ ))
+ } else {
+ None
+ }
+ })
+ .map(|(range, render, height, style, priority)| {
+ let start = buffer_snapshot.anchor_before(range.start);
+ let end = buffer_snapshot.anchor_after(range.end);
+ BlockProperties {
+ placement: BlockPlacement::Replace(start..=end),
+ render,
+ height: Some(height),
+ style,
+ priority,
+ }
+ });
+
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, _| {
+ self.block_map
+ .write(
+ self_new_wrap_snapshot,
+ self_new_wrap_edits,
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .insert(blocks);
+ })
+ })
+ .ok();
+ } else {
+ self.block_map
+ .write(self_new_wrap_snapshot, self_new_wrap_edits, None)
+ .insert(blocks);
+ };
if let Some((companion_dm, _)) = &self.companion {
let _ = companion_dm.update(cx, |dm, cx| {
@@ -805,15 +896,29 @@ impl DisplayMap {
let (self_wrap_snapshot, self_wrap_edits) =
(self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
{
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, _| {
+ self.block_map.write(
+ self_new_wrap_snapshot,
+ self_new_wrap_edits,
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ );
+ })
+ })
+ .ok();
+ } else {
self.block_map
- .write(self_new_wrap_snapshot, self_new_wrap_edits, companion_view);
+ .write(self_new_wrap_snapshot, self_new_wrap_edits, None);
}
if let Some((companion_dm, _)) = &self.companion {
@@ -886,20 +991,33 @@ impl DisplayMap {
let (self_wrap_snapshot, self_wrap_edits) =
(self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
- let mut block_map = {
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
- self.block_map.write(
- self_new_wrap_snapshot.clone(),
- self_new_wrap_edits,
- companion_view,
- )
- };
- block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, _| {
+ self.block_map
+ .write(
+ self_new_wrap_snapshot.clone(),
+ self_new_wrap_edits,
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .remove_intersecting_replace_blocks(offset_ranges, inclusive);
+ })
+ })
+ .ok();
+ } else {
+ self.block_map
+ .write(self_new_wrap_snapshot.clone(), self_new_wrap_edits, None)
+ .remove_intersecting_replace_blocks(offset_ranges, inclusive);
+ }
if let Some((companion_dm, _)) = &self.companion {
let _ = companion_dm.update(cx, |dm, cx| {
@@ -934,19 +1052,33 @@ impl DisplayMap {
.ok()
});
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
-
- let mut block_map = self.block_map.write(
- self_wrap_snapshot.clone(),
- self_wrap_edits.clone(),
- companion_view,
- );
- block_map.disable_header_for_buffer(buffer_id);
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, _| {
+ self.block_map
+ .write(
+ self_wrap_snapshot.clone(),
+ self_wrap_edits.clone(),
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .disable_header_for_buffer(buffer_id);
+ })
+ })
+ .ok();
+ } else {
+ self.block_map
+ .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
+ .disable_header_for_buffer(buffer_id);
+ }
if let Some((companion_dm, _)) = &self.companion {
let _ = companion_dm.update(cx, |dm, cx| {
@@ -1000,19 +1132,33 @@ impl DisplayMap {
.ok()
});
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
-
- let mut block_map = self.block_map.write(
- self_wrap_snapshot.clone(),
- self_wrap_edits.clone(),
- companion_view,
- );
- block_map.fold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, cx| {
+ self.block_map
+ .write(
+ self_wrap_snapshot.clone(),
+ self_wrap_edits.clone(),
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .fold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
+ })
+ })
+ .ok();
+ } else {
+ self.block_map
+ .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
+ .fold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
+ }
if let Some((companion_dm, companion_entity)) = &self.companion {
let buffer_mapping = companion_entity
@@ -1025,21 +1171,30 @@ impl DisplayMap {
let _ = companion_dm.update(cx, |dm, cx| {
if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
- let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
- let mut block_map = dm.block_map.write(
- companion_snapshot,
- companion_edits,
- their_companion_ref.map(|c| {
- CompanionView::new(
- dm.entity_id,
- &self_wrap_snapshot,
- &self_wrap_edits,
- c,
- )
- }),
- );
- if !their_buffer_ids.is_empty() {
- block_map.fold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
+ if let Some((_, their_companion)) = dm.companion.as_ref() {
+ their_companion.update(cx, |their_companion, cx| {
+ let mut block_map = dm.block_map.write(
+ companion_snapshot,
+ companion_edits,
+ Some(CompanionViewMut::new(
+ dm.entity_id,
+ &self_wrap_snapshot,
+ &self_wrap_edits,
+ their_companion,
+ &mut self.block_map,
+ )),
+ );
+ if !their_buffer_ids.is_empty() {
+ block_map.fold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
+ }
+ })
+ } else {
+ let mut block_map =
+ dm.block_map
+ .write(companion_snapshot, companion_edits, None);
+ if !their_buffer_ids.is_empty() {
+ block_map.fold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
+ }
}
}
});
@@ -1078,19 +1233,33 @@ impl DisplayMap {
.ok()
});
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
-
- let mut block_map = self.block_map.write(
- self_wrap_snapshot.clone(),
- self_wrap_edits.clone(),
- companion_view,
- );
- block_map.unfold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, cx| {
+ self.block_map
+ .write(
+ self_wrap_snapshot.clone(),
+ self_wrap_edits.clone(),
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .unfold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
+ })
+ })
+ .ok();
+ } else {
+ self.block_map
+ .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
+ .unfold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
+ }
if let Some((companion_dm, companion_entity)) = &self.companion {
let buffer_mapping = companion_entity
@@ -1103,21 +1272,30 @@ impl DisplayMap {
let _ = companion_dm.update(cx, |dm, cx| {
if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
- let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
- let mut block_map = dm.block_map.write(
- companion_snapshot,
- companion_edits,
- their_companion_ref.map(|c| {
- CompanionView::new(
- dm.entity_id,
- &self_wrap_snapshot,
- &self_wrap_edits,
- c,
- )
- }),
- );
- if !their_buffer_ids.is_empty() {
- block_map.unfold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
+ if let Some((_, their_companion)) = dm.companion.as_ref() {
+ their_companion.update(cx, |their_companion, cx| {
+ let mut block_map = dm.block_map.write(
+ companion_snapshot,
+ companion_edits,
+ Some(CompanionViewMut::new(
+ dm.entity_id,
+ &self_wrap_snapshot,
+ &self_wrap_edits,
+ their_companion,
+ &mut self.block_map,
+ )),
+ );
+ if !their_buffer_ids.is_empty() {
+ block_map.unfold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
+ }
+ })
+ } else {
+ let mut block_map =
+ dm.block_map
+ .write(companion_snapshot, companion_edits, None);
+ if !their_buffer_ids.is_empty() {
+ block_map.unfold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
+ }
}
}
});
@@ -1168,19 +1346,34 @@ impl DisplayMap {
.ok()
});
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
-
- let mut block_map = self.block_map.write(
- self_wrap_snapshot.clone(),
- self_wrap_edits.clone(),
- companion_view,
- );
- let result = block_map.insert(blocks);
+ let result = if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, _| {
+ self.block_map
+ .write(
+ self_wrap_snapshot.clone(),
+ self_wrap_edits.clone(),
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .insert(blocks)
+ })
+ })
+ .ok()
+ .expect("success inserting blocks with companion")
+ } else {
+ self.block_map
+ .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
+ .insert(blocks)
+ };
if let Some((companion_dm, _)) = &self.companion {
let _ = companion_dm.update(cx, |dm, cx| {
@@ -1215,19 +1408,33 @@ impl DisplayMap {
.ok()
});
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
-
- let mut block_map = self.block_map.write(
- self_wrap_snapshot.clone(),
- self_wrap_edits.clone(),
- companion_view,
- );
- block_map.resize(heights);
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, _| {
+ self.block_map
+ .write(
+ self_wrap_snapshot.clone(),
+ self_wrap_edits.clone(),
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .resize(heights);
+ })
+ })
+ .ok();
+ } else {
+ self.block_map
+ .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
+ .resize(heights);
+ }
if let Some((companion_dm, _)) = &self.companion {
let _ = companion_dm.update(cx, |dm, cx| {
@@ -1265,19 +1472,33 @@ impl DisplayMap {
.ok()
});
- let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
- let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
- |((snapshot, edits), companion)| {
- CompanionView::new(self.entity_id, snapshot, edits, companion)
- },
- );
-
- let mut block_map = self.block_map.write(
- self_wrap_snapshot.clone(),
- self_wrap_edits.clone(),
- companion_view,
- );
- block_map.remove(ids);
+ if let Some((companion_dm, companion)) = self.companion.as_ref()
+ && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
+ {
+ companion_dm
+ .update(cx, |dm, cx| {
+ companion.update(cx, |companion, _| {
+ self.block_map
+ .write(
+ self_wrap_snapshot.clone(),
+ self_wrap_edits.clone(),
+ Some(CompanionViewMut::new(
+ self.entity_id,
+ snapshot,
+ edits,
+ companion,
+ &mut dm.block_map,
+ )),
+ )
+ .remove(ids);
+ })
+ })
+ .ok();
+ } else {
+ self.block_map
+ .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
+ .remove(ids);
+ }
if let Some((companion_dm, _)) = &self.companion {
let _ = companion_dm.update(cx, |dm, cx| {
@@ -48,7 +48,7 @@ pub struct BlockMap {
}
pub struct BlockMapReader<'a> {
- blocks: &'a Vec<Arc<CustomBlock>>,
+ pub blocks: &'a Vec<Arc<CustomBlock>>,
pub snapshot: BlockSnapshot,
}
@@ -57,16 +57,22 @@ pub struct BlockMapWriter<'a> {
companion: Option<BlockMapWriterCompanion<'a>>,
}
-struct BlockMapWriterCompanion<'a>(CompanionView<'a>);
+struct BlockMapWriterCompanion<'a>(CompanionViewMut<'a>);
impl<'a> Deref for BlockMapWriterCompanion<'a> {
- type Target = CompanionView<'a>;
+ type Target = CompanionViewMut<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
+impl<'a> DerefMut for BlockMapWriterCompanion<'a> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
#[derive(Clone)]
pub struct BlockSnapshot {
pub(super) wrap_snapshot: WrapSnapshot,
@@ -282,6 +288,7 @@ pub struct BlockContext<'a, 'b> {
pub em_width: Pixels,
pub line_height: Pixels,
pub block_id: BlockId,
+ pub height: u32,
pub selected: bool,
pub editor_style: &'b EditorStyle,
}
@@ -542,6 +549,54 @@ impl<'a> CompanionView<'a> {
}
}
+impl<'a> From<CompanionViewMut<'a>> for CompanionView<'a> {
+ fn from(view_mut: CompanionViewMut<'a>) -> Self {
+ Self {
+ entity_id: view_mut.entity_id,
+ wrap_snapshot: view_mut.wrap_snapshot,
+ wrap_edits: view_mut.wrap_edits,
+ companion: view_mut.companion,
+ }
+ }
+}
+
+impl<'a> From<&'a CompanionViewMut<'a>> for CompanionView<'a> {
+ fn from(view_mut: &'a CompanionViewMut<'a>) -> Self {
+ Self {
+ entity_id: view_mut.entity_id,
+ wrap_snapshot: view_mut.wrap_snapshot,
+ wrap_edits: view_mut.wrap_edits,
+ companion: view_mut.companion,
+ }
+ }
+}
+
+pub struct CompanionViewMut<'a> {
+ entity_id: EntityId,
+ wrap_snapshot: &'a WrapSnapshot,
+ wrap_edits: &'a WrapPatch,
+ companion: &'a mut Companion,
+ block_map: &'a mut BlockMap,
+}
+
+impl<'a> CompanionViewMut<'a> {
+ pub(crate) fn new(
+ entity_id: EntityId,
+ wrap_snapshot: &'a WrapSnapshot,
+ wrap_edits: &'a WrapPatch,
+ companion: &'a mut Companion,
+ block_map: &'a mut BlockMap,
+ ) -> Self {
+ Self {
+ entity_id,
+ wrap_snapshot,
+ wrap_edits,
+ companion,
+ block_map,
+ }
+ }
+}
+
impl BlockMap {
#[ztracing::instrument(skip_all)]
pub fn new(
@@ -600,9 +655,13 @@ impl BlockMap {
&'a mut self,
wrap_snapshot: WrapSnapshot,
edits: WrapPatch,
- companion_view: Option<CompanionView<'a>>,
+ companion_view: Option<CompanionViewMut<'a>>,
) -> BlockMapWriter<'a> {
- self.sync(&wrap_snapshot, edits, companion_view);
+ self.sync(
+ &wrap_snapshot,
+ edits,
+ companion_view.as_ref().map(CompanionView::from),
+ );
*self.wrap_snapshot.borrow_mut() = wrap_snapshot;
BlockMapWriter {
block_map: self,
@@ -1402,6 +1461,55 @@ impl BlockMap {
_ => false,
});
}
+
+ pub(crate) fn insert_custom_block_into_companion(
+ &mut self,
+ entity_id: EntityId,
+ snapshot: &WrapSnapshot,
+ block: &CustomBlock,
+ companion_snapshot: &MultiBufferSnapshot,
+ companion: &mut Companion,
+ ) {
+ let their_anchor = block.placement.start();
+ let their_point = their_anchor.to_point(companion_snapshot);
+ let my_patches = companion.convert_rows_to_companion(
+ entity_id,
+ snapshot.buffer_snapshot(),
+ companion_snapshot,
+ (Bound::Included(their_point), Bound::Included(their_point)),
+ );
+ let my_excerpt = my_patches
+ .first()
+ .expect("at least one companion excerpt exists");
+ let my_range = my_excerpt.patch.edit_for_old_position(their_point).new;
+ let my_point = my_range.start;
+ let anchor = snapshot.buffer_snapshot().anchor_before(my_point);
+ let height = block.height.unwrap_or(1);
+ let new_block = BlockProperties {
+ placement: BlockPlacement::Above(anchor),
+ height: Some(height),
+ style: BlockStyle::Sticky,
+ render: Arc::new(move |cx| {
+ crate::EditorElement::render_spacer_block(
+ cx.block_id,
+ cx.height,
+ cx.line_height,
+ cx.window,
+ cx.app,
+ )
+ }),
+ priority: 0,
+ };
+ log::debug!("Inserting matching companion custom block: {block:#?} => {new_block:#?}");
+ let new_block_id = self
+ .write(snapshot.clone(), Patch::default(), None)
+ .insert([new_block])[0];
+ if companion.is_rhs(entity_id) {
+ companion.add_custom_block_mapping(block.id, new_block_id);
+ } else {
+ companion.add_custom_block_mapping(new_block_id, block.id);
+ }
+ }
}
#[ztracing::instrument(skip(tree, wrap_snapshot))]
@@ -1562,7 +1670,7 @@ impl BlockMapWriter<'_> {
};
let new_block = Arc::new(CustomBlock {
id,
- placement: block.placement,
+ placement: block.placement.clone(),
height: block.height,
render: Arc::new(Mutex::new(block.render)),
style: block.style,
@@ -1571,7 +1679,27 @@ impl BlockMapWriter<'_> {
self.block_map
.custom_blocks
.insert(block_ix, new_block.clone());
- self.block_map.custom_blocks_by_id.insert(id, new_block);
+ self.block_map
+ .custom_blocks_by_id
+ .insert(id, new_block.clone());
+
+ // Insert a matching custom block in the companion (if any)
+ if let Some(CompanionViewMut {
+ entity_id: their_entity_id,
+ wrap_snapshot: their_snapshot,
+ block_map: their_block_map,
+ companion,
+ ..
+ }) = self.companion.as_deref_mut()
+ {
+ their_block_map.insert_custom_block_into_companion(
+ *their_entity_id,
+ their_snapshot,
+ &new_block,
+ buffer,
+ companion,
+ );
+ }
edits = edits.compose([Edit {
old: start_row..end_row,
@@ -1584,13 +1712,13 @@ impl BlockMapWriter<'_> {
wrap_snapshot,
edits,
self.companion.as_deref().map(
- |&CompanionView {
+ |CompanionViewMut {
entity_id,
wrap_snapshot,
companion,
..
}| {
- CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
+ CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
},
),
);
@@ -1654,13 +1782,13 @@ impl BlockMapWriter<'_> {
wrap_snapshot,
edits,
self.companion.as_deref().map(
- |&CompanionView {
+ |CompanionViewMut {
entity_id,
wrap_snapshot,
companion,
..
}| {
- CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
+ CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
},
),
);
@@ -1709,18 +1837,49 @@ impl BlockMapWriter<'_> {
self.block_map
.custom_blocks_by_id
.retain(|id, _| !block_ids.contains(id));
+
+ if let Some(CompanionViewMut {
+ entity_id: their_entity_id,
+ wrap_snapshot: their_snapshot,
+ companion,
+ block_map: their_block_map,
+ ..
+ }) = self.companion.as_deref_mut()
+ {
+ let their_block_ids: HashSet<_> = block_ids
+ .iter()
+ .filter_map(|my_block_id| {
+ let mapping = companion.companion_custom_block_to_custom_block(*their_entity_id);
+ let their_block_id =
+ mapping.get(my_block_id)?;
+ log::debug!("Removing custom block in the companion with id {their_block_id:?} for mine {my_block_id:?}");
+ Some(*their_block_id)
+ })
+ .collect();
+ for (lhs_id, rhs_id) in block_ids.iter().zip(their_block_ids.iter()) {
+ if !companion.is_rhs(*their_entity_id) {
+ companion.remove_custom_block_mapping(lhs_id, rhs_id);
+ } else {
+ companion.remove_custom_block_mapping(rhs_id, lhs_id);
+ }
+ }
+ their_block_map
+ .write(their_snapshot.clone(), Patch::default(), None)
+ .remove(their_block_ids);
+ }
+
let default_patch = Patch::default();
self.block_map.sync(
wrap_snapshot,
edits,
self.companion.as_deref().map(
- |&CompanionView {
+ |CompanionViewMut {
entity_id,
wrap_snapshot,
companion,
..
}| {
- CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
+ CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
},
),
);
@@ -1809,13 +1968,13 @@ impl BlockMapWriter<'_> {
&wrap_snapshot,
edits,
self.companion.as_deref().map(
- |&CompanionView {
+ |CompanionViewMut {
entity_id,
wrap_snapshot,
companion,
..
}| {
- CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
+ CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
},
),
);
@@ -3914,6 +3914,7 @@ impl EditorElement {
line_height,
em_width,
block_id,
+ height: custom.height.unwrap_or(1),
selected,
max_width: text_hitbox.size.width.max(*scroll_width),
editor_style: &self.style,
@@ -4004,26 +4005,9 @@ impl EditorElement {
result.into_any()
}
- Block::Spacer { height, .. } => div()
- .id(block_id)
- .w_full()
- .h((*height as f32) * line_height)
- // the checkerboard pattern is semi-transparent, so we render a
- // solid background to prevent indent guides peeking through
- .bg(cx.theme().colors().editor_background)
- .child(
- div()
- .size_full()
- .bg(checkerboard(cx.theme().colors().panel_background, {
- let target_size = 16.0;
- let scale = window.scale_factor();
- Self::checkerboard_size(
- f32::from(line_height) * scale,
- target_size * scale,
- )
- })),
- )
- .into_any(),
+ Block::Spacer { height, .. } => {
+ Self::render_spacer_block(block_id, *height, line_height, window, cx)
+ }
};
// Discover the element's content height, then round up to the nearest multiple of line height.
@@ -4102,6 +4086,32 @@ impl EditorElement {
}
}
+ pub fn render_spacer_block(
+ block_id: BlockId,
+ block_height: u32,
+ line_height: Pixels,
+ window: &mut Window,
+ cx: &App,
+ ) -> AnyElement {
+ div()
+ .id(block_id)
+ .w_full()
+ .h((block_height as f32) * line_height)
+ // the checkerboard pattern is semi-transparent, so we render a
+ // solid background to prevent indent guides peeking through
+ .bg(cx.theme().colors().editor_background)
+ .child(
+ div()
+ .size_full()
+ .bg(checkerboard(cx.theme().colors().panel_background, {
+ let target_size = 16.0;
+ let scale = window.scale_factor();
+ Self::checkerboard_size(f32::from(line_height) * scale, target_size * scale)
+ })),
+ )
+ .into_any()
+ }
+
fn render_buffer_header(
&self,
for_excerpt: &ExcerptInfo,
@@ -574,6 +574,9 @@ impl SplittableEditor {
lhs_display_map.update(cx, |dm, cx| {
dm.set_companion(Some((rhs_display_map.downgrade(), companion)), cx);
});
+ rhs_display_map.update(cx, |dm, cx| {
+ dm.sync_custom_blocks_into_companion(cx);
+ });
let shared_scroll_anchor = self
.rhs_editor
@@ -1922,7 +1925,9 @@ impl LhsEditor {
#[cfg(test)]
mod tests {
use buffer_diff::BufferDiff;
+ use collections::HashSet;
use fs::FakeFs;
+ use gpui::Element as _;
use gpui::{AppContext as _, Entity, Pixels, VisualTestContext};
use language::language_settings::SoftWrap;
use language::{Buffer, Capability};
@@ -1931,11 +1936,14 @@ mod tests {
use project::Project;
use rand::rngs::StdRng;
use settings::SettingsStore;
- use ui::{VisualContext as _, px};
+ use std::sync::Arc;
+ use ui::{VisualContext as _, div, px};
use workspace::Workspace;
use crate::SplittableEditor;
- use crate::test::editor_content_with_blocks_and_width;
+ use crate::display_map::{BlockPlacement, BlockProperties, BlockStyle};
+ use crate::split::{SplitDiff, UnsplitDiff};
+ use crate::test::{editor_content_with_blocks_and_width, set_block_content_for_tests};
async fn init_test(
cx: &mut gpui::TestAppContext,
@@ -3847,4 +3855,704 @@ mod tests {
"LHS should have same horizontal scroll position as RHS after autoscroll"
);
}
+
+ #[gpui::test]
+ async fn test_custom_block_sync_between_split_views(cx: &mut gpui::TestAppContext) {
+ use rope::Point;
+ use unindent::Unindent as _;
+
+ let (editor, mut cx) = init_test(cx, SoftWrap::None).await;
+
+ let base_text = "
+ bbb
+ ccc
+ "
+ .unindent();
+ let current_text = "
+ aaa
+ bbb
+ ccc
+ "
+ .unindent();
+
+ let (buffer, diff) = buffer_with_diff(&base_text, ¤t_text, &mut cx);
+
+ editor.update(cx, |editor, cx| {
+ let path = PathKey::for_buffer(&buffer, cx);
+ editor.set_excerpts_for_path(
+ path,
+ buffer.clone(),
+ vec![Point::new(0, 0)..buffer.read(cx).max_point()],
+ 0,
+ diff.clone(),
+ cx,
+ );
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc"
+ .unindent(),
+ &mut cx,
+ );
+
+ let block_ids = editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ let snapshot = rhs_editor.buffer().read(cx).snapshot(cx);
+ let anchor = snapshot.anchor_before(Point::new(2, 0));
+ rhs_editor.insert_blocks(
+ [BlockProperties {
+ placement: BlockPlacement::Above(anchor),
+ height: Some(1),
+ style: BlockStyle::Fixed,
+ render: Arc::new(|_| div().into_any()),
+ priority: 0,
+ }],
+ None,
+ cx,
+ )
+ })
+ });
+
+ let rhs_editor = editor.read_with(cx, |editor, _| editor.rhs_editor.clone());
+ let lhs_editor =
+ editor.read_with(cx, |editor, _| editor.lhs.as_ref().unwrap().editor.clone());
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&rhs_editor, block_ids[0], cx, |_| {
+ "custom block".to_string()
+ });
+ });
+
+ let lhs_block_id = lhs_editor.read_with(cx, |lhs_editor, cx| {
+ let display_map = lhs_editor.display_map.read(cx);
+ let companion = display_map.companion().unwrap().read(cx);
+ let mapping = companion.companion_custom_block_to_custom_block(
+ rhs_editor.read(cx).display_map.entity_id(),
+ );
+ *mapping.get(&block_ids[0]).unwrap()
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&lhs_editor, lhs_block_id, cx, |_| {
+ "custom block".to_string()
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ยง custom block
+ ccc"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ยง custom block
+ ccc"
+ .unindent(),
+ &mut cx,
+ );
+
+ editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ rhs_editor.remove_blocks(HashSet::from_iter(block_ids), None, cx);
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc"
+ .unindent(),
+ &mut cx,
+ );
+ }
+
+ #[gpui::test]
+ async fn test_custom_block_deletion_and_resplit_sync(cx: &mut gpui::TestAppContext) {
+ use rope::Point;
+ use unindent::Unindent as _;
+
+ let (editor, mut cx) = init_test(cx, SoftWrap::None).await;
+
+ let base_text = "
+ bbb
+ ccc
+ "
+ .unindent();
+ let current_text = "
+ aaa
+ bbb
+ ccc
+ "
+ .unindent();
+
+ let (buffer, diff) = buffer_with_diff(&base_text, ¤t_text, &mut cx);
+
+ editor.update(cx, |editor, cx| {
+ let path = PathKey::for_buffer(&buffer, cx);
+ editor.set_excerpts_for_path(
+ path,
+ buffer.clone(),
+ vec![Point::new(0, 0)..buffer.read(cx).max_point()],
+ 0,
+ diff.clone(),
+ cx,
+ );
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc"
+ .unindent(),
+ &mut cx,
+ );
+
+ let block_ids = editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ let snapshot = rhs_editor.buffer().read(cx).snapshot(cx);
+ let anchor1 = snapshot.anchor_before(Point::new(2, 0));
+ let anchor2 = snapshot.anchor_before(Point::new(3, 0));
+ rhs_editor.insert_blocks(
+ [
+ BlockProperties {
+ placement: BlockPlacement::Above(anchor1),
+ height: Some(1),
+ style: BlockStyle::Fixed,
+ render: Arc::new(|_| div().into_any()),
+ priority: 0,
+ },
+ BlockProperties {
+ placement: BlockPlacement::Above(anchor2),
+ height: Some(1),
+ style: BlockStyle::Fixed,
+ render: Arc::new(|_| div().into_any()),
+ priority: 0,
+ },
+ ],
+ None,
+ cx,
+ )
+ })
+ });
+
+ let rhs_editor = editor.read_with(cx, |editor, _| editor.rhs_editor.clone());
+ let lhs_editor =
+ editor.read_with(cx, |editor, _| editor.lhs.as_ref().unwrap().editor.clone());
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&rhs_editor, block_ids[0], cx, |_| {
+ "custom block 1".to_string()
+ });
+ set_block_content_for_tests(&rhs_editor, block_ids[1], cx, |_| {
+ "custom block 2".to_string()
+ });
+ });
+
+ let (lhs_block_id_1, lhs_block_id_2) = lhs_editor.read_with(cx, |lhs_editor, cx| {
+ let display_map = lhs_editor.display_map.read(cx);
+ let companion = display_map.companion().unwrap().read(cx);
+ let mapping = companion.companion_custom_block_to_custom_block(
+ rhs_editor.read(cx).display_map.entity_id(),
+ );
+ (
+ *mapping.get(&block_ids[0]).unwrap(),
+ *mapping.get(&block_ids[1]).unwrap(),
+ )
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&lhs_editor, lhs_block_id_1, cx, |_| {
+ "custom block 1".to_string()
+ });
+ set_block_content_for_tests(&lhs_editor, lhs_block_id_2, cx, |_| {
+ "custom block 2".to_string()
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ยง custom block 1
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ยง custom block 1
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+
+ editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ rhs_editor.remove_blocks(HashSet::from_iter([block_ids[0]]), None, cx);
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+
+ editor.update_in(cx, |splittable_editor, window, cx| {
+ splittable_editor.unsplit(&UnsplitDiff, window, cx);
+ });
+
+ cx.run_until_parked();
+
+ editor.update_in(cx, |splittable_editor, window, cx| {
+ splittable_editor.split(&SplitDiff, window, cx);
+ });
+
+ cx.run_until_parked();
+
+ let lhs_editor =
+ editor.read_with(cx, |editor, _| editor.lhs.as_ref().unwrap().editor.clone());
+
+ let lhs_block_id_2 = lhs_editor.read_with(cx, |lhs_editor, cx| {
+ let display_map = lhs_editor.display_map.read(cx);
+ let companion = display_map.companion().unwrap().read(cx);
+ let mapping = companion.companion_custom_block_to_custom_block(
+ rhs_editor.read(cx).display_map.entity_id(),
+ );
+ *mapping.get(&block_ids[1]).unwrap()
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&lhs_editor, lhs_block_id_2, cx, |_| {
+ "custom block 2".to_string()
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+ }
+
+ #[gpui::test]
+ async fn test_custom_block_sync_with_unsplit_start(cx: &mut gpui::TestAppContext) {
+ use rope::Point;
+ use unindent::Unindent as _;
+
+ let (editor, mut cx) = init_test(cx, SoftWrap::None).await;
+
+ let base_text = "
+ bbb
+ ccc
+ "
+ .unindent();
+ let current_text = "
+ aaa
+ bbb
+ ccc
+ "
+ .unindent();
+
+ let (buffer, diff) = buffer_with_diff(&base_text, ¤t_text, &mut cx);
+
+ editor.update(cx, |editor, cx| {
+ let path = PathKey::for_buffer(&buffer, cx);
+ editor.set_excerpts_for_path(
+ path,
+ buffer.clone(),
+ vec![Point::new(0, 0)..buffer.read(cx).max_point()],
+ 0,
+ diff.clone(),
+ cx,
+ );
+ });
+
+ cx.run_until_parked();
+
+ editor.update_in(cx, |splittable_editor, window, cx| {
+ splittable_editor.unsplit(&UnsplitDiff, window, cx);
+ });
+
+ cx.run_until_parked();
+
+ let rhs_editor = editor.read_with(cx, |editor, _| editor.rhs_editor.clone());
+
+ let block_ids = editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ let snapshot = rhs_editor.buffer().read(cx).snapshot(cx);
+ let anchor1 = snapshot.anchor_before(Point::new(2, 0));
+ let anchor2 = snapshot.anchor_before(Point::new(3, 0));
+ rhs_editor.insert_blocks(
+ [
+ BlockProperties {
+ placement: BlockPlacement::Above(anchor1),
+ height: Some(1),
+ style: BlockStyle::Fixed,
+ render: Arc::new(|_| div().into_any()),
+ priority: 0,
+ },
+ BlockProperties {
+ placement: BlockPlacement::Above(anchor2),
+ height: Some(1),
+ style: BlockStyle::Fixed,
+ render: Arc::new(|_| div().into_any()),
+ priority: 0,
+ },
+ ],
+ None,
+ cx,
+ )
+ })
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&rhs_editor, block_ids[0], cx, |_| {
+ "custom block 1".to_string()
+ });
+ set_block_content_for_tests(&rhs_editor, block_ids[1], cx, |_| {
+ "custom block 2".to_string()
+ });
+ });
+
+ cx.run_until_parked();
+
+ let rhs_content = editor_content_with_blocks_and_width(&rhs_editor, px(3000.0), &mut cx);
+ assert_eq!(
+ rhs_content,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ยง custom block 1
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "rhs content before split"
+ );
+
+ editor.update_in(cx, |splittable_editor, window, cx| {
+ splittable_editor.split(&SplitDiff, window, cx);
+ });
+
+ cx.run_until_parked();
+
+ let lhs_editor =
+ editor.read_with(cx, |editor, _| editor.lhs.as_ref().unwrap().editor.clone());
+
+ let (lhs_block_id_1, lhs_block_id_2) = lhs_editor.read_with(cx, |lhs_editor, cx| {
+ let display_map = lhs_editor.display_map.read(cx);
+ let companion = display_map.companion().unwrap().read(cx);
+ let mapping = companion.companion_custom_block_to_custom_block(
+ rhs_editor.read(cx).display_map.entity_id(),
+ );
+ (
+ *mapping.get(&block_ids[0]).unwrap(),
+ *mapping.get(&block_ids[1]).unwrap(),
+ )
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&lhs_editor, lhs_block_id_1, cx, |_| {
+ "custom block 1".to_string()
+ });
+ set_block_content_for_tests(&lhs_editor, lhs_block_id_2, cx, |_| {
+ "custom block 2".to_string()
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ยง custom block 1
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ยง custom block 1
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+
+ editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ rhs_editor.remove_blocks(HashSet::from_iter([block_ids[0]]), None, cx);
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+
+ editor.update_in(cx, |splittable_editor, window, cx| {
+ splittable_editor.unsplit(&UnsplitDiff, window, cx);
+ });
+
+ cx.run_until_parked();
+
+ editor.update_in(cx, |splittable_editor, window, cx| {
+ splittable_editor.split(&SplitDiff, window, cx);
+ });
+
+ cx.run_until_parked();
+
+ let lhs_editor =
+ editor.read_with(cx, |editor, _| editor.lhs.as_ref().unwrap().editor.clone());
+
+ let lhs_block_id_2 = lhs_editor.read_with(cx, |lhs_editor, cx| {
+ let display_map = lhs_editor.display_map.read(cx);
+ let companion = display_map.companion().unwrap().read(cx);
+ let mapping = companion.companion_custom_block_to_custom_block(
+ rhs_editor.read(cx).display_map.entity_id(),
+ );
+ *mapping.get(&block_ids[1]).unwrap()
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&lhs_editor, lhs_block_id_2, cx, |_| {
+ "custom block 2".to_string()
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+
+ let new_block_ids = editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ let snapshot = rhs_editor.buffer().read(cx).snapshot(cx);
+ let anchor = snapshot.anchor_before(Point::new(2, 0));
+ rhs_editor.insert_blocks(
+ [BlockProperties {
+ placement: BlockPlacement::Above(anchor),
+ height: Some(1),
+ style: BlockStyle::Fixed,
+ render: Arc::new(|_| div().into_any()),
+ priority: 0,
+ }],
+ None,
+ cx,
+ )
+ })
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&rhs_editor, new_block_ids[0], cx, |_| {
+ "custom block 3".to_string()
+ });
+ });
+
+ let lhs_block_id_3 = lhs_editor.read_with(cx, |lhs_editor, cx| {
+ let display_map = lhs_editor.display_map.read(cx);
+ let companion = display_map.companion().unwrap().read(cx);
+ let mapping = companion.companion_custom_block_to_custom_block(
+ rhs_editor.read(cx).display_map.entity_id(),
+ );
+ *mapping.get(&new_block_ids[0]).unwrap()
+ });
+
+ cx.update(|_, cx| {
+ set_block_content_for_tests(&lhs_editor, lhs_block_id_3, cx, |_| {
+ "custom block 3".to_string()
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ยง custom block 3
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ยง custom block 3
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+
+ editor.update(cx, |splittable_editor, cx| {
+ splittable_editor.rhs_editor.update(cx, |rhs_editor, cx| {
+ rhs_editor.remove_blocks(HashSet::from_iter([new_block_ids[0]]), None, cx);
+ });
+ });
+
+ cx.run_until_parked();
+
+ assert_split_content(
+ &editor,
+ "
+ ยง <no file>
+ ยง -----
+ aaa
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ "
+ ยง <no file>
+ ยง -----
+ ยง spacer
+ bbb
+ ccc
+ ยง custom block 2"
+ .unindent(),
+ &mut cx,
+ );
+ }
}