From 984378e12cf331e494caaced614d7617da301ab1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 29 Dec 2021 23:47:03 -0800 Subject: [PATCH 1/2] Use anchors for line movement edits to support multi-buffers Because multi-buffers can contain the same content multiple times, we need to use anchors to track our desired insertion and removal locations when moving lines. This is because deleting a line in order to move it might end up deleting *multiple* lines. --- crates/editor/src/editor.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 028706b193b95aa917834768c2d7c427bc5ae7fe..62b4b1c984a22dbf3bb30bb94a93eecc59fb98fc 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1737,8 +1737,13 @@ impl Editor { .chain(['\n']) .collect::(); - edits.push((insertion_point..insertion_point, text)); - edits.push((range_to_move.clone(), String::new())); + edits.push(( + buffer.anchor_after(range_to_move.start) + ..buffer.anchor_before(range_to_move.end), + String::new(), + )); + let insertion_anchor = buffer.anchor_after(insertion_point); + edits.push((insertion_anchor.clone()..insertion_anchor, text)); let row_delta = range_to_move.start.row - insertion_point.row + 1; @@ -1773,7 +1778,7 @@ impl Editor { self.start_transaction(cx); self.unfold_ranges(unfold_ranges, cx); self.buffer.update(cx, |buffer, cx| { - for (range, text) in edits.into_iter().rev() { + for (range, text) in edits { buffer.edit([range], text, cx); } }); @@ -1828,8 +1833,13 @@ impl Editor { let mut text = String::from("\n"); text.extend(buffer.text_for_range(range_to_move.clone())); text.pop(); // Drop trailing newline - edits.push((range_to_move.clone(), String::new())); - edits.push((insertion_point..insertion_point, text)); + edits.push(( + buffer.anchor_after(range_to_move.start) + ..buffer.anchor_before(range_to_move.end), + String::new(), + )); + let insertion_anchor = buffer.anchor_after(insertion_point); + edits.push((insertion_anchor.clone()..insertion_anchor, text)); let row_delta = insertion_point.row - range_to_move.end.row + 1; @@ -1864,7 +1874,7 @@ impl Editor { self.start_transaction(cx); self.unfold_ranges(unfold_ranges, cx); self.buffer.update(cx, |buffer, cx| { - for (range, text) in edits.into_iter().rev() { + for (range, text) in edits { buffer.edit([range], text, cx); } }); From 6d6a82655ab4216e6f73aa45bb39926d666fdb92 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 30 Dec 2021 01:03:19 -0800 Subject: [PATCH 2/2] Create blocks with anchors to allow a bias to be specified This allows us to respect the bias on anchors we use to create excerpt headers so that they always remain above any content inserted at the start of an excerpt. --- crates/editor/src/display_map.rs | 9 +++---- crates/editor/src/display_map/block_map.rs | 29 +++++++++++----------- crates/editor/src/editor.rs | 14 +++++------ 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 8a2c95805019e6d51b36c82e6ed4ab24b783f6ab..cf436971a53102047b2c1b43b1b7d8514051e255 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -123,14 +123,11 @@ impl DisplayMap { self.block_map.read(snapshot, edits); } - pub fn insert_blocks

( + pub fn insert_blocks( &mut self, - blocks: impl IntoIterator>, + blocks: impl IntoIterator>, cx: &mut ModelContext, - ) -> Vec - where - P: ToOffset + Clone, - { + ) -> Vec { let snapshot = self.buffer.read(cx).snapshot(cx); let edits = self.buffer_subscription.consume().into_inner(); let (snapshot, edits) = self.fold_map.read(snapshot, edits); diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 7e0248530831cfd549d39bcf567cd1589d7a85ef..2d61a123b7807f8f4bcd31a679d28136ba71388d 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1,5 +1,5 @@ use super::wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot}; -use crate::{Anchor, ToOffset, ToPoint as _}; +use crate::{Anchor, ToPoint as _}; use collections::{HashMap, HashSet}; use gpui::{AppContext, ElementBox}; use language::Chunk; @@ -362,13 +362,10 @@ impl std::ops::DerefMut for BlockPoint { } impl<'a> BlockMapWriter<'a> { - pub fn insert

( + pub fn insert( &mut self, - blocks: impl IntoIterator>, - ) -> Vec - where - P: ToOffset + Clone, - { + blocks: impl IntoIterator>, + ) -> Vec { let mut ids = Vec::new(); let mut edits = Vec::>::new(); let wrap_snapshot = &*self.0.wrap_snapshot.lock(); @@ -378,7 +375,7 @@ impl<'a> BlockMapWriter<'a> { let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst)); ids.push(id); - let position = buffer.anchor_after(block.position); + let position = block.position; let point = position.to_point(&buffer); let wrap_row = wrap_snapshot .from_point(Point::new(point.row, 0), Bias::Left) @@ -903,8 +900,9 @@ mod tests { let text = "aaa\nbbb\nccc\nddd"; let buffer = MultiBuffer::build_simple(text, cx); + let buffer_snapshot = buffer.read(cx).snapshot(cx); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); - let (fold_map, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx)); + let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1); let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx); let mut block_map = BlockMap::new(wraps_snapshot.clone()); @@ -912,19 +910,19 @@ mod tests { let mut writer = block_map.write(wraps_snapshot.clone(), vec![]); writer.insert(vec![ BlockProperties { - position: Point::new(1, 0), + position: buffer_snapshot.anchor_after(Point::new(1, 0)), height: 1, disposition: BlockDisposition::Above, render: Arc::new(|_| Empty::new().named("block 1")), }, BlockProperties { - position: Point::new(1, 2), + position: buffer_snapshot.anchor_after(Point::new(1, 2)), height: 2, disposition: BlockDisposition::Above, render: Arc::new(|_| Empty::new().named("block 2")), }, BlockProperties { - position: Point::new(3, 3), + position: buffer_snapshot.anchor_after(Point::new(3, 3)), height: 3, disposition: BlockDisposition::Below, render: Arc::new(|_| Empty::new().named("block 3")), @@ -1071,7 +1069,8 @@ mod tests { let text = "one two three\nfour five six\nseven eight"; let buffer = MultiBuffer::build_simple(text, cx); - let (_, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx)); + let buffer_snapshot = buffer.read(cx).snapshot(cx); + let (_, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1); let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx); let mut block_map = BlockMap::new(wraps_snapshot.clone()); @@ -1079,13 +1078,13 @@ mod tests { let mut writer = block_map.write(wraps_snapshot.clone(), vec![]); writer.insert(vec![ BlockProperties { - position: Point::new(1, 12), + position: buffer_snapshot.anchor_after(Point::new(1, 12)), disposition: BlockDisposition::Above, render: Arc::new(|_| Empty::new().named("block 1")), height: 1, }, BlockProperties { - position: Point::new(1, 1), + position: buffer_snapshot.anchor_after(Point::new(1, 1)), disposition: BlockDisposition::Below, render: Arc::new(|_| Empty::new().named("block 2")), height: 1, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 62b4b1c984a22dbf3bb30bb94a93eecc59fb98fc..d626f28e7cd974aa0800d452c1549650b830fc5a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2972,7 +2972,7 @@ impl Editor { let message_height = diagnostic.message.lines().count() as u8; BlockProperties { - position: entry.range.start, + position: buffer.anchor_after(entry.range.start), height: message_height, render: diagnostic_block_renderer(diagnostic, true, build_settings), disposition: BlockDisposition::Below, @@ -3431,14 +3431,11 @@ impl Editor { } } - pub fn insert_blocks

( + pub fn insert_blocks( &mut self, - blocks: impl IntoIterator>, + blocks: impl IntoIterator>, cx: &mut ViewContext, - ) -> Vec - where - P: ToOffset + Clone, - { + ) -> Vec { let blocks = self .display_map .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx)); @@ -5140,12 +5137,13 @@ mod tests { fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) { let settings = EditorSettings::test(&cx); let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); + let snapshot = buffer.read(cx).snapshot(cx); let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); editor.update(cx, |editor, cx| { editor.insert_blocks( [BlockProperties { - position: Point::new(2, 0), + position: snapshot.anchor_after(Point::new(2, 0)), disposition: BlockDisposition::Below, height: 1, render: Arc::new(|_| Empty::new().boxed()),