@@ -3449,6 +3449,21 @@ impl Editor {
Subscription::join(other_subscription, this_subscription)
}
+ fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
+ if self.buffer().read(cx).is_singleton() {
+ return;
+ }
+ let snapshot = self.buffer.read(cx).snapshot(cx);
+ let buffer_ids: HashSet<BufferId> = self
+ .selections
+ .disjoint_anchor_ranges()
+ .flat_map(|range| snapshot.buffer_ids_for_range(range))
+ .collect();
+ for buffer_id in buffer_ids {
+ self.unfold_buffer(buffer_id, cx);
+ }
+ }
+
/// Changes selections using the provided mutation function. Changes to `self.selections` occur
/// immediately, but when run within `transact` or `with_selection_effects_deferred` other
/// effects of selection change occur at the end of the transaction.
@@ -4190,6 +4205,8 @@ impl Editor {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
+ self.unfold_buffers_with_selections(cx);
+
let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
let mut bracket_inserted = false;
let mut edits = Vec::new();
@@ -18879,10 +18896,17 @@ impl Editor {
if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
return;
}
+
let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
self.display_map.update(cx, |display_map, cx| {
display_map.fold_buffers([buffer_id], cx)
});
+
+ let snapshot = self.display_snapshot(cx);
+ self.selections.change_with(&snapshot, |selections| {
+ selections.remove_selections_from_buffer(buffer_id);
+ });
+
cx.emit(EditorEvent::BufferFoldToggled {
ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
folded: true,
@@ -22378,7 +22378,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) {
assert_eq!(
multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
- "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
+ "\n\naaaa\nBbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
"After unfolding the first buffer, its and 2nd buffer's text should be displayed"
);
@@ -22387,7 +22387,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) {
});
assert_eq!(
multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
- "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
+ "\n\naaaa\nBbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
"After unfolding the all buffers, all original text should be displayed"
);
}
@@ -27453,3 +27453,213 @@ async fn test_next_prev_reference(cx: &mut TestAppContext) {
_move(Direction::Prev, 2, &mut cx).await;
cx.assert_editor_state(CYCLE_POSITIONS[1]);
}
+
+#[gpui::test]
+async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) {
+ init_test(cx, |_| {});
+
+ let (editor, cx) = cx.add_window_view(|window, cx| {
+ let multi_buffer = MultiBuffer::build_multi(
+ [
+ ("1\n2\n3\n", vec![Point::row_range(0..3)]),
+ ("1\n2\n3\n", vec![Point::row_range(0..3)]),
+ ],
+ cx,
+ );
+ Editor::new(EditorMode::full(), multi_buffer, None, window, cx)
+ });
+
+ let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
+ let buffer_ids = cx.multibuffer(|mb, _| mb.excerpt_buffer_ids());
+
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ ˇ1
+ 2
+ 3
+ [EXCERPT]
+ 1
+ 2
+ 3
+ "});
+
+ // Scenario 1: Unfolded buffers, position cursor on "2", select all matches, then insert
+ cx.update_editor(|editor, window, cx| {
+ editor.change_selections(None.into(), window, cx, |s| {
+ s.select_ranges([2..3]);
+ });
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ 1
+ 2ˇ
+ 3
+ [EXCERPT]
+ 1
+ 2
+ 3
+ "});
+
+ cx.update_editor(|editor, window, cx| {
+ editor
+ .select_all_matches(&SelectAllMatches, window, cx)
+ .unwrap();
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ 1
+ 2ˇ
+ 3
+ [EXCERPT]
+ 1
+ 2ˇ
+ 3
+ "});
+
+ cx.update_editor(|editor, window, cx| {
+ editor.handle_input("X", window, cx);
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ 1
+ Xˇ
+ 3
+ [EXCERPT]
+ 1
+ Xˇ
+ 3
+ "});
+
+ // Scenario 2: Select "2", then fold second buffer before insertion
+ cx.update_multibuffer(|mb, cx| {
+ for buffer_id in buffer_ids.iter() {
+ let buffer = mb.buffer(*buffer_id).unwrap();
+ buffer.update(cx, |buffer, cx| {
+ buffer.edit([(0..buffer.len(), "1\n2\n3\n")], None, cx);
+ });
+ }
+ });
+
+ // Select "2" and select all matches
+ cx.update_editor(|editor, window, cx| {
+ editor.change_selections(None.into(), window, cx, |s| {
+ s.select_ranges([2..3]);
+ });
+ editor
+ .select_all_matches(&SelectAllMatches, window, cx)
+ .unwrap();
+ });
+
+ // Fold second buffer - should remove selections from folded buffer
+ cx.update_editor(|editor, _, cx| {
+ editor.fold_buffer(buffer_ids[1], cx);
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ 1
+ 2ˇ
+ 3
+ [EXCERPT]
+ [FOLDED]
+ "});
+
+ // Insert text - should only affect first buffer
+ cx.update_editor(|editor, window, cx| {
+ editor.handle_input("Y", window, cx);
+ });
+ cx.update_editor(|editor, _, cx| {
+ editor.unfold_buffer(buffer_ids[1], cx);
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ 1
+ Yˇ
+ 3
+ [EXCERPT]
+ 1
+ 2
+ 3
+ "});
+
+ // Scenario 3: Select "2", then fold first buffer before insertion
+ cx.update_multibuffer(|mb, cx| {
+ for buffer_id in buffer_ids.iter() {
+ let buffer = mb.buffer(*buffer_id).unwrap();
+ buffer.update(cx, |buffer, cx| {
+ buffer.edit([(0..buffer.len(), "1\n2\n3\n")], None, cx);
+ });
+ }
+ });
+
+ // Select "2" and select all matches
+ cx.update_editor(|editor, window, cx| {
+ editor.change_selections(None.into(), window, cx, |s| {
+ s.select_ranges([2..3]);
+ });
+ editor
+ .select_all_matches(&SelectAllMatches, window, cx)
+ .unwrap();
+ });
+
+ // Fold first buffer - should remove selections from folded buffer
+ cx.update_editor(|editor, _, cx| {
+ editor.fold_buffer(buffer_ids[0], cx);
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ [FOLDED]
+ [EXCERPT]
+ 1
+ 2ˇ
+ 3
+ "});
+
+ // Insert text - should only affect second buffer
+ cx.update_editor(|editor, window, cx| {
+ editor.handle_input("Z", window, cx);
+ });
+ cx.update_editor(|editor, _, cx| {
+ editor.unfold_buffer(buffer_ids[0], cx);
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ 1
+ 2
+ 3
+ [EXCERPT]
+ 1
+ Zˇ
+ 3
+ "});
+
+ // Edge case scenario: fold all buffers, then try to insert
+ cx.update_editor(|editor, _, cx| {
+ editor.fold_buffer(buffer_ids[0], cx);
+ editor.fold_buffer(buffer_ids[1], cx);
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ ˇ[FOLDED]
+ [EXCERPT]
+ [FOLDED]
+ "});
+
+ // Insert should work via default selection
+ cx.update_editor(|editor, window, cx| {
+ editor.handle_input("W", window, cx);
+ });
+ cx.update_editor(|editor, _, cx| {
+ editor.unfold_buffer(buffer_ids[0], cx);
+ editor.unfold_buffer(buffer_ids[1], cx);
+ });
+ cx.assert_excerpts_with_selections(indoc! {"
+ [EXCERPT]
+ Wˇ1
+ 2
+ 3
+ [EXCERPT]
+ 1
+ Z
+ 3
+ "});
+}