diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 610e30f4e1538fa1eb91768a91bd816b3cbd00dd..57b8eb8ef6c1b29cb99da3e2a4e731d0c828038e 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1003,6 +1003,10 @@ impl DisplayMap { &self.block_map.folded_buffers } + pub(super) fn clear_folded_buffer(&mut self, buffer_id: language::BufferId) { + self.block_map.folded_buffers.remove(&buffer_id); + } + #[instrument(skip_all)] pub fn insert_creases( &mut self, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index eb8601d59e1c9970f367177f3f365f4feb30811e..6f1961a97880e0f5f55577c406b77e3796568a8e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -24145,6 +24145,7 @@ impl Editor { self.display_map.update(cx, |display_map, cx| { display_map.invalidate_semantic_highlights(*buffer_id); display_map.clear_lsp_folding_ranges(*buffer_id, cx); + display_map.clear_folded_buffer(*buffer_id); }); } jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 525910ed276cdfe5fb5c3c2b784269d834c70316..2898954b75a97c7d7d0a922eae8e71c8b598a7d5 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -24308,6 +24308,77 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { ); } +#[gpui::test] +async fn test_folded_buffers_cleared_on_excerpts_removed(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree( + path!("/root"), + json!({ + "file_a.txt": "File A\nFile A\nFile A", + "file_b.txt": "File B\nFile B\nFile B", + }), + ) + .await; + + let project = Project::test(fs, [path!("/root").as_ref()], cx).await; + let window = cx.add_window(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*window, cx); + let worktree = project.update(cx, |project, cx| { + let mut worktrees = project.worktrees(cx).collect::>(); + assert_eq!(worktrees.len(), 1); + worktrees.pop().unwrap() + }); + let worktree_id = worktree.update(cx, |worktree, _| worktree.id()); + + let buffer_a = project + .update(cx, |project, cx| { + project.open_buffer((worktree_id, rel_path("file_a.txt")), cx) + }) + .await + .unwrap(); + let buffer_b = project + .update(cx, |project, cx| { + project.open_buffer((worktree_id, rel_path("file_b.txt")), cx) + }) + .await + .unwrap(); + + let multi_buffer = cx.new(|cx| { + let mut multi_buffer = MultiBuffer::new(ReadWrite); + let range_a = Point::new(0, 0)..Point::new(2, 4); + let range_b = Point::new(0, 0)..Point::new(2, 4); + + multi_buffer.set_excerpts_for_path(PathKey::sorted(0), buffer_a.clone(), [range_a], 0, cx); + multi_buffer.set_excerpts_for_path(PathKey::sorted(1), buffer_b.clone(), [range_b], 0, cx); + multi_buffer + }); + + let editor = cx.new_window_entity(|window, cx| { + Editor::new( + EditorMode::full(), + multi_buffer.clone(), + Some(project.clone()), + window, + cx, + ) + }); + + editor.update(cx, |editor, cx| { + editor.fold_buffer(buffer_a.read(cx).remote_id(), cx); + }); + assert!(editor.update(cx, |editor, cx| editor.has_any_buffer_folded(cx))); + + // When the excerpts for `buffer_a` are removed, a + // `multi_buffer::Event::ExcerptsRemoved` event is emitted, which should be + // picked up by the editor and update the display map accordingly. + multi_buffer.update(cx, |multi_buffer, cx| { + multi_buffer.remove_excerpts_for_path(PathKey::sorted(0), cx) + }); + assert!(!editor.update(cx, |editor, cx| editor.has_any_buffer_folded(cx))); +} + #[gpui::test] async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) { init_test(cx, |_| {}); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index a593280d245fd01d623051953e48128c9935df45..c991fd9a5cbfe451b3f86ff016f8467395373564 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -105,6 +105,8 @@ pub enum Event { }, ExcerptsRemoved { ids: Vec, + /// Contains only buffer IDs for which all excerpts have been removed. + /// Buffers that still have remaining excerpts are never included. removed_buffer_ids: Vec, }, ExcerptsExpanded {