workspace: Add Close Multibuffers pane context menu entry (#39199)
Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
@@ -196,6 +196,13 @@ pub enum TabTooltipContent {
|
||||
Custom(Box<dyn Fn(&mut Window, &mut App) -> AnyView>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ItemBufferKind {
|
||||
Multibuffer,
|
||||
Singleton,
|
||||
None,
|
||||
}
|
||||
|
||||
pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
|
||||
type Event;
|
||||
|
||||
@@ -260,8 +267,8 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
|
||||
_: &mut dyn FnMut(EntityId, &dyn project::ProjectItem),
|
||||
) {
|
||||
}
|
||||
fn is_singleton(&self, _cx: &App) -> bool {
|
||||
false
|
||||
fn buffer_kind(&self, _cx: &App) -> ItemBufferKind {
|
||||
ItemBufferKind::None
|
||||
}
|
||||
fn set_nav_history(&mut self, _: ItemNavHistory, _window: &mut Window, _: &mut Context<Self>) {}
|
||||
fn clone_on_split(
|
||||
@@ -467,7 +474,7 @@ pub trait ItemHandle: 'static + Send {
|
||||
_: &App,
|
||||
_: &mut dyn FnMut(EntityId, &dyn project::ProjectItem),
|
||||
);
|
||||
fn is_singleton(&self, cx: &App) -> bool;
|
||||
fn buffer_kind(&self, cx: &App) -> ItemBufferKind;
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle>;
|
||||
fn clone_on_split(
|
||||
&self,
|
||||
@@ -616,7 +623,7 @@ impl<T: Item> ItemHandle for Entity<T> {
|
||||
fn project_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||
let this = self.read(cx);
|
||||
let mut result = None;
|
||||
if this.is_singleton(cx) {
|
||||
if this.buffer_kind(cx) == ItemBufferKind::Singleton {
|
||||
this.for_each_project_item(cx, &mut |_, item| {
|
||||
result = item.project_path(cx);
|
||||
});
|
||||
@@ -674,8 +681,8 @@ impl<T: Item> ItemHandle for Entity<T> {
|
||||
self.read(cx).for_each_project_item(cx, f)
|
||||
}
|
||||
|
||||
fn is_singleton(&self, cx: &App) -> bool {
|
||||
self.read(cx).is_singleton(cx)
|
||||
fn buffer_kind(&self, cx: &App) -> ItemBufferKind {
|
||||
self.read(cx).buffer_kind(cx)
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn ItemHandle> {
|
||||
@@ -1292,7 +1299,10 @@ impl<T: FollowableItem> WeakFollowableItemHandle for WeakEntity<T> {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod test {
|
||||
use super::{Item, ItemEvent, SerializableItem, TabContentParams};
|
||||
use crate::{ItemId, ItemNavHistory, Workspace, WorkspaceId, item::SaveOptions};
|
||||
use crate::{
|
||||
ItemId, ItemNavHistory, Workspace, WorkspaceId,
|
||||
item::{ItemBufferKind, SaveOptions},
|
||||
};
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext as _, Context, Entity, EntityId, EventEmitter, Focusable,
|
||||
InteractiveElement, IntoElement, Render, SharedString, Task, WeakEntity, Window,
|
||||
@@ -1315,7 +1325,7 @@ pub mod test {
|
||||
pub save_as_count: usize,
|
||||
pub reload_count: usize,
|
||||
pub is_dirty: bool,
|
||||
pub is_singleton: bool,
|
||||
pub buffer_kind: ItemBufferKind,
|
||||
pub has_conflict: bool,
|
||||
pub has_deleted_file: bool,
|
||||
pub project_items: Vec<Entity<TestProjectItem>>,
|
||||
@@ -1399,7 +1409,7 @@ pub mod test {
|
||||
has_conflict: false,
|
||||
has_deleted_file: false,
|
||||
project_items: Vec::new(),
|
||||
is_singleton: true,
|
||||
buffer_kind: ItemBufferKind::Singleton,
|
||||
nav_history: None,
|
||||
tab_descriptions: None,
|
||||
tab_detail: Default::default(),
|
||||
@@ -1420,8 +1430,8 @@ pub mod test {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_singleton(mut self, singleton: bool) -> Self {
|
||||
self.is_singleton = singleton;
|
||||
pub fn with_buffer_kind(mut self, buffer_kind: ItemBufferKind) -> Self {
|
||||
self.buffer_kind = buffer_kind;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1516,8 +1526,8 @@ pub mod test {
|
||||
.for_each(|item| f(item.entity_id(), item.read(cx)))
|
||||
}
|
||||
|
||||
fn is_singleton(&self, _: &App) -> bool {
|
||||
self.is_singleton
|
||||
fn buffer_kind(&self, _: &App) -> ItemBufferKind {
|
||||
self.buffer_kind
|
||||
}
|
||||
|
||||
fn set_nav_history(
|
||||
@@ -1564,7 +1574,7 @@ pub mod test {
|
||||
save_as_count: self.save_as_count,
|
||||
reload_count: self.reload_count,
|
||||
is_dirty: self.is_dirty,
|
||||
is_singleton: self.is_singleton,
|
||||
buffer_kind: self.buffer_kind,
|
||||
has_conflict: self.has_conflict,
|
||||
has_deleted_file: self.has_deleted_file,
|
||||
project_items: self.project_items.clone(),
|
||||
@@ -1598,7 +1608,7 @@ pub mod test {
|
||||
}
|
||||
|
||||
fn can_save_as(&self, _cx: &App) -> bool {
|
||||
self.is_singleton
|
||||
self.buffer_kind == ItemBufferKind::Singleton
|
||||
}
|
||||
|
||||
fn save(
|
||||
|
||||
+234
-21
@@ -4,9 +4,9 @@ use crate::{
|
||||
WorkspaceItemBuilder,
|
||||
invalid_buffer_view::InvalidBufferView,
|
||||
item::{
|
||||
ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
|
||||
ProjectItemKind, SaveOptions, ShowCloseButton, ShowDiagnostics, TabContentParams,
|
||||
TabTooltipContent, WeakItemHandle,
|
||||
ActivateOnClose, ClosePosition, Item, ItemBufferKind, ItemHandle, ItemSettings,
|
||||
PreviewTabsSettings, ProjectItemKind, SaveOptions, ShowCloseButton, ShowDiagnostics,
|
||||
TabContentParams, TabTooltipContent, WeakItemHandle,
|
||||
},
|
||||
move_item,
|
||||
notifications::NotifyResultExt,
|
||||
@@ -125,6 +125,17 @@ pub struct CloseOtherItems {
|
||||
pub close_pinned: bool,
|
||||
}
|
||||
|
||||
/// Closes all multibuffers in the pane.
|
||||
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default, Action)]
|
||||
#[action(namespace = pane)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CloseMultibufferItems {
|
||||
#[serde(default)]
|
||||
pub save_intent: Option<SaveIntent>,
|
||||
#[serde(default)]
|
||||
pub close_pinned: bool,
|
||||
}
|
||||
|
||||
/// Closes all items in the pane.
|
||||
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default, Action)]
|
||||
#[action(namespace = pane)]
|
||||
@@ -900,7 +911,7 @@ impl Pane {
|
||||
let mut existing_item = None;
|
||||
if let Some(project_entry_id) = project_entry_id {
|
||||
for (index, item) in self.items.iter().enumerate() {
|
||||
if item.is_singleton(cx)
|
||||
if item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& item.project_entry_ids(cx).as_slice() == [project_entry_id]
|
||||
{
|
||||
let item = item.boxed_clone();
|
||||
@@ -910,7 +921,9 @@ impl Pane {
|
||||
}
|
||||
} else {
|
||||
for (index, item) in self.items.iter().enumerate() {
|
||||
if item.is_singleton(cx) && item.project_path(cx).as_ref() == Some(&project_path) {
|
||||
if item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& item.project_path(cx).as_ref() == Some(&project_path)
|
||||
{
|
||||
let item = item.boxed_clone();
|
||||
existing_item = Some((index, item));
|
||||
break;
|
||||
@@ -1046,7 +1059,7 @@ impl Pane {
|
||||
self.close_items_on_item_open(window, cx);
|
||||
}
|
||||
|
||||
if item.is_singleton(cx)
|
||||
if item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& let Some(&entry_id) = item.project_entry_ids(cx).first()
|
||||
{
|
||||
let Some(project) = self.project.upgrade() else {
|
||||
@@ -1077,7 +1090,7 @@ impl Pane {
|
||||
};
|
||||
|
||||
// Does the item already exist?
|
||||
let project_entry_id = if item.is_singleton(cx) {
|
||||
let project_entry_id = if item.buffer_kind(cx) == ItemBufferKind::Singleton {
|
||||
item.project_entry_ids(cx).first().copied()
|
||||
} else {
|
||||
None
|
||||
@@ -1086,7 +1099,7 @@ impl Pane {
|
||||
let existing_item_index = self.items.iter().position(|existing_item| {
|
||||
if existing_item.item_id() == item.item_id() {
|
||||
true
|
||||
} else if existing_item.is_singleton(cx) {
|
||||
} else if existing_item.buffer_kind(cx) == ItemBufferKind::Singleton {
|
||||
existing_item
|
||||
.project_entry_ids(cx)
|
||||
.first()
|
||||
@@ -1202,7 +1215,9 @@ impl Pane {
|
||||
cx: &App,
|
||||
) -> Option<Box<dyn ItemHandle>> {
|
||||
self.items.iter().find_map(|item| {
|
||||
if item.is_singleton(cx) && (item.project_entry_ids(cx).as_slice() == [entry_id]) {
|
||||
if item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& (item.project_entry_ids(cx).as_slice() == [entry_id])
|
||||
{
|
||||
Some(item.boxed_clone())
|
||||
} else {
|
||||
None
|
||||
@@ -1216,7 +1231,8 @@ impl Pane {
|
||||
cx: &App,
|
||||
) -> Option<Box<dyn ItemHandle>> {
|
||||
self.items.iter().find_map(move |item| {
|
||||
if item.is_singleton(cx) && (item.project_path(cx).as_slice() == [project_path.clone()])
|
||||
if item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& (item.project_path(cx).as_slice() == [project_path.clone()])
|
||||
{
|
||||
Some(item.boxed_clone())
|
||||
} else {
|
||||
@@ -1475,6 +1491,30 @@ impl Pane {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn close_multibuffer_items(
|
||||
&mut self,
|
||||
action: &CloseMultibufferItems,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
if self.items.is_empty() {
|
||||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
let pinned_item_ids = self.pinned_item_ids();
|
||||
let multibuffer_items = self.multibuffer_item_ids(cx);
|
||||
|
||||
self.close_items(
|
||||
window,
|
||||
cx,
|
||||
action.save_intent.unwrap_or(SaveIntent::Close),
|
||||
move |item_id| {
|
||||
(action.close_pinned || !pinned_item_ids.contains(&item_id))
|
||||
&& multibuffer_items.contains(&item_id)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn close_clean_items(
|
||||
&mut self,
|
||||
action: &CloseCleanItems,
|
||||
@@ -1637,14 +1677,14 @@ impl Pane {
|
||||
}
|
||||
});
|
||||
if dirty_project_item_ids.is_empty() {
|
||||
return !(item.is_singleton(cx) && item.is_dirty(cx));
|
||||
return !(item.buffer_kind(cx) == ItemBufferKind::Singleton && item.is_dirty(cx));
|
||||
}
|
||||
|
||||
for open_item in workspace.items(cx) {
|
||||
if open_item.item_id() == item.item_id() {
|
||||
continue;
|
||||
}
|
||||
if !open_item.is_singleton(cx) {
|
||||
if open_item.buffer_kind(cx) != ItemBufferKind::Singleton {
|
||||
continue;
|
||||
}
|
||||
let other_project_item_ids = open_item.project_item_model_ids(cx);
|
||||
@@ -1999,7 +2039,7 @@ impl Pane {
|
||||
item.is_dirty(cx),
|
||||
item.can_save(cx),
|
||||
item.can_save_as(cx),
|
||||
item.is_singleton(cx),
|
||||
item.buffer_kind(cx) == ItemBufferKind::Singleton,
|
||||
item.has_deleted_file(cx),
|
||||
)
|
||||
})?;
|
||||
@@ -2281,7 +2321,9 @@ impl Pane {
|
||||
cx: &mut Context<Pane>,
|
||||
) -> Option<()> {
|
||||
let item_id = self.items().find_map(|item| {
|
||||
if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [entry_id] {
|
||||
if item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& item.project_entry_ids(cx).as_slice() == [entry_id]
|
||||
{
|
||||
Some(item.item_id())
|
||||
} else {
|
||||
None
|
||||
@@ -2701,12 +2743,15 @@ impl Pane {
|
||||
.child(label),
|
||||
);
|
||||
|
||||
let single_entry_to_resolve = self.items[ix]
|
||||
.is_singleton(cx)
|
||||
let single_entry_to_resolve = (self.items[ix].buffer_kind(cx) == ItemBufferKind::Singleton)
|
||||
.then(|| self.items[ix].project_entry_ids(cx).get(0).copied())
|
||||
.flatten();
|
||||
|
||||
let total_items = self.items.len();
|
||||
let has_multibuffer_items = self
|
||||
.items
|
||||
.iter()
|
||||
.any(|item| item.buffer_kind(cx) == ItemBufferKind::Multibuffer);
|
||||
let has_items_to_left = ix > 0;
|
||||
let has_items_to_right = ix < total_items - 1;
|
||||
let has_clean_items = self.items.iter().any(|item| !item.is_dirty(cx));
|
||||
@@ -2727,6 +2772,10 @@ impl Pane {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
};
|
||||
let close_multibuffers_action = CloseMultibufferItems {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
};
|
||||
let close_items_to_the_left_action = CloseItemsToTheLeft {
|
||||
close_pinned: false,
|
||||
};
|
||||
@@ -2764,6 +2813,24 @@ impl Pane {
|
||||
.detach_and_log_err(cx);
|
||||
})),
|
||||
))
|
||||
// We make this optional, instead of using disabled as to not overwhelm the context menu unnecessarily
|
||||
.extend(has_multibuffer_items.then(|| {
|
||||
ContextMenuItem::Entry(
|
||||
ContextMenuEntry::new("Close Multibuffers")
|
||||
.action(Box::new(close_multibuffers_action.clone()))
|
||||
.handler(window.handler_for(
|
||||
&pane,
|
||||
move |pane, window, cx| {
|
||||
pane.close_multibuffer_items(
|
||||
&close_multibuffers_action,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
},
|
||||
)),
|
||||
)
|
||||
}))
|
||||
.separator()
|
||||
.item(ContextMenuItem::Entry(
|
||||
ContextMenuEntry::new("Close Left")
|
||||
@@ -3485,6 +3552,13 @@ impl Pane {
|
||||
}
|
||||
}
|
||||
|
||||
fn multibuffer_item_ids(&self, cx: &mut Context<Pane>) -> Vec<EntityId> {
|
||||
self.items()
|
||||
.filter(|item| item.buffer_kind(cx) == ItemBufferKind::Multibuffer)
|
||||
.map(|item| item.item_id())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn drag_split_direction(&self) -> Option<SplitDirection> {
|
||||
self.drag_split_direction
|
||||
}
|
||||
@@ -3706,6 +3780,12 @@ impl Render for Pane {
|
||||
.detach_and_log_err(cx)
|
||||
}),
|
||||
)
|
||||
.on_action(cx.listener(
|
||||
|pane: &mut Self, action: &CloseMultibufferItems, window, cx| {
|
||||
pane.close_multibuffer_items(action, window, cx)
|
||||
.detach_and_log_err(cx)
|
||||
},
|
||||
))
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &RevealInProjectPanel, _, cx| {
|
||||
let entry_id = action
|
||||
@@ -5655,7 +5735,7 @@ mod tests {
|
||||
pane.add_item(
|
||||
Box::new(cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_singleton(true)
|
||||
.with_buffer_kind(ItemBufferKind::Singleton)
|
||||
.with_label("buffer 1")
|
||||
.with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
|
||||
})),
|
||||
@@ -5673,7 +5753,7 @@ mod tests {
|
||||
pane.add_item(
|
||||
Box::new(cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_singleton(true)
|
||||
.with_buffer_kind(ItemBufferKind::Singleton)
|
||||
.with_label("buffer 1")
|
||||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
})),
|
||||
@@ -5691,7 +5771,7 @@ mod tests {
|
||||
pane.add_item(
|
||||
Box::new(cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_singleton(true)
|
||||
.with_buffer_kind(ItemBufferKind::Singleton)
|
||||
.with_label("buffer 2")
|
||||
.with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
|
||||
})),
|
||||
@@ -5709,7 +5789,7 @@ mod tests {
|
||||
pane.add_item(
|
||||
Box::new(cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_singleton(false)
|
||||
.with_buffer_kind(ItemBufferKind::Multibuffer)
|
||||
.with_label("multibuffer 1")
|
||||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
})),
|
||||
@@ -5727,7 +5807,7 @@ mod tests {
|
||||
pane.add_item(
|
||||
Box::new(cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_singleton(false)
|
||||
.with_buffer_kind(ItemBufferKind::Multibuffer)
|
||||
.with_label("multibuffer 1b")
|
||||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
})),
|
||||
@@ -6281,6 +6361,139 @@ mod tests {
|
||||
assert_item_labels(&pane, [], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_close_multibuffer_items(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
let add_labeled_item = |pane: &Entity<Pane>,
|
||||
label,
|
||||
is_dirty,
|
||||
kind: ItemBufferKind,
|
||||
cx: &mut VisualTestContext| {
|
||||
pane.update_in(cx, |pane, window, cx| {
|
||||
let labeled_item = Box::new(cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_label(label)
|
||||
.with_dirty(is_dirty)
|
||||
.with_buffer_kind(kind)
|
||||
}));
|
||||
pane.add_item(labeled_item.clone(), false, false, None, window, cx);
|
||||
labeled_item
|
||||
})
|
||||
};
|
||||
|
||||
let item_a = add_labeled_item(&pane, "A", false, ItemBufferKind::Multibuffer, cx);
|
||||
add_labeled_item(&pane, "B", false, ItemBufferKind::Multibuffer, cx);
|
||||
add_labeled_item(&pane, "C", false, ItemBufferKind::Singleton, cx);
|
||||
assert_item_labels(&pane, ["A", "B", "C*"], cx);
|
||||
|
||||
pane.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.pin_tab_at(ix, window, cx);
|
||||
pane.close_multibuffer_items(
|
||||
&CloseMultibufferItems {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_item_labels(&pane, ["A!", "C*"], cx);
|
||||
|
||||
pane.update_in(cx, |pane, window, cx| {
|
||||
let ix = pane.index_for_item_id(item_a.item_id()).unwrap();
|
||||
pane.unpin_tab_at(ix, window, cx);
|
||||
pane.close_multibuffer_items(
|
||||
&CloseMultibufferItems {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_item_labels(&pane, ["C*"], cx);
|
||||
|
||||
add_labeled_item(&pane, "A", true, ItemBufferKind::Singleton, cx).update(cx, |item, cx| {
|
||||
item.project_items
|
||||
.push(TestProjectItem::new_dirty(1, "A.txt", cx))
|
||||
});
|
||||
add_labeled_item(&pane, "B", true, ItemBufferKind::Multibuffer, cx).update(
|
||||
cx,
|
||||
|item, cx| {
|
||||
item.project_items
|
||||
.push(TestProjectItem::new_dirty(2, "B.txt", cx))
|
||||
},
|
||||
);
|
||||
add_labeled_item(&pane, "D", true, ItemBufferKind::Multibuffer, cx).update(
|
||||
cx,
|
||||
|item, cx| {
|
||||
item.project_items
|
||||
.push(TestProjectItem::new_dirty(3, "D.txt", cx))
|
||||
},
|
||||
);
|
||||
assert_item_labels(&pane, ["C", "A^", "B^", "D*^"], cx);
|
||||
|
||||
let save = pane.update_in(cx, |pane, window, cx| {
|
||||
pane.close_multibuffer_items(
|
||||
&CloseMultibufferItems {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
cx.executor().run_until_parked();
|
||||
cx.simulate_prompt_answer("Save all");
|
||||
save.await.unwrap();
|
||||
assert_item_labels(&pane, ["C", "A*^"], cx);
|
||||
|
||||
add_labeled_item(&pane, "B", true, ItemBufferKind::Multibuffer, cx).update(
|
||||
cx,
|
||||
|item, cx| {
|
||||
item.project_items
|
||||
.push(TestProjectItem::new_dirty(2, "B.txt", cx))
|
||||
},
|
||||
);
|
||||
add_labeled_item(&pane, "D", true, ItemBufferKind::Multibuffer, cx).update(
|
||||
cx,
|
||||
|item, cx| {
|
||||
item.project_items
|
||||
.push(TestProjectItem::new_dirty(3, "D.txt", cx))
|
||||
},
|
||||
);
|
||||
assert_item_labels(&pane, ["C", "A^", "B^", "D*^"], cx);
|
||||
let save = pane.update_in(cx, |pane, window, cx| {
|
||||
pane.close_multibuffer_items(
|
||||
&CloseMultibufferItems {
|
||||
save_intent: None,
|
||||
close_pinned: false,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
cx.executor().run_until_parked();
|
||||
cx.simulate_prompt_answer("Discard all");
|
||||
save.await.unwrap();
|
||||
assert_item_labels(&pane, ["C", "A*^"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_close_with_save_intent(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
@@ -60,7 +60,10 @@ use notifications::{
|
||||
simple_message_notification::MessageNotification,
|
||||
};
|
||||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
pub use pane_group::{
|
||||
ActivePaneDecorator, HANDLE_HITBOX_SIZE, Member, PaneAxis, PaneGroup, PaneRenderContext,
|
||||
SplitDirection,
|
||||
};
|
||||
use persistence::{DB, SerializedWindowBounds, model::SerializedWorkspace};
|
||||
pub use persistence::{
|
||||
DB as WORKSPACE_DB, WorkspaceDb, delete_unloaded_items,
|
||||
@@ -117,11 +120,11 @@ pub use workspace_settings::{
|
||||
};
|
||||
use zed_actions::{Spawn, feedback::FileBugReport};
|
||||
|
||||
use crate::notifications::NotificationId;
|
||||
use crate::persistence::{
|
||||
SerializedAxis,
|
||||
model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},
|
||||
};
|
||||
use crate::{item::ItemBufferKind, notifications::NotificationId};
|
||||
|
||||
pub const SERIALIZATION_THROTTLE_TIME: Duration = Duration::from_millis(200);
|
||||
|
||||
@@ -2521,8 +2524,12 @@ impl Workspace {
|
||||
};
|
||||
|
||||
for (pane, item) in dirty_items {
|
||||
let (singleton, project_entry_ids) =
|
||||
cx.update(|_, cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?;
|
||||
let (singleton, project_entry_ids) = cx.update(|_, cx| {
|
||||
(
|
||||
item.buffer_kind(cx) == ItemBufferKind::Singleton,
|
||||
item.project_entry_ids(cx),
|
||||
)
|
||||
})?;
|
||||
if (singleton || !project_entry_ids.is_empty())
|
||||
&& !Pane::save_item(project.clone(), &pane, &*item, save_intent, cx).await?
|
||||
{
|
||||
@@ -4805,7 +4812,7 @@ impl Workspace {
|
||||
.items()
|
||||
.find_map(|item| {
|
||||
let item = item.to_followable_item_handle(cx)?;
|
||||
if item.is_singleton(cx)
|
||||
if item.buffer_kind(cx) == ItemBufferKind::Singleton
|
||||
&& item.project_item_model_ids(cx).as_slice()
|
||||
== [buffer_entity_id]
|
||||
{
|
||||
@@ -8192,7 +8199,7 @@ mod tests {
|
||||
use crate::{
|
||||
dock::{PanelEvent, test::TestPanel},
|
||||
item::{
|
||||
ItemEvent,
|
||||
ItemBufferKind, ItemEvent,
|
||||
test::{TestItem, TestProjectItem},
|
||||
},
|
||||
};
|
||||
@@ -8553,7 +8560,7 @@ mod tests {
|
||||
let item_2_3 = cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_dirty(true)
|
||||
.with_singleton(false)
|
||||
.with_buffer_kind(ItemBufferKind::Multibuffer)
|
||||
.with_project_items(&[
|
||||
single_entry_items[2].read(cx).project_items[0].clone(),
|
||||
single_entry_items[3].read(cx).project_items[0].clone(),
|
||||
@@ -8562,7 +8569,7 @@ mod tests {
|
||||
let item_3_4 = cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_dirty(true)
|
||||
.with_singleton(false)
|
||||
.with_buffer_kind(ItemBufferKind::Multibuffer)
|
||||
.with_project_items(&[
|
||||
single_entry_items[3].read(cx).project_items[0].clone(),
|
||||
single_entry_items[4].read(cx).project_items[0].clone(),
|
||||
@@ -9583,7 +9590,7 @@ mod tests {
|
||||
let dirty_multi_buffer_with_both = cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_dirty(true)
|
||||
.with_singleton(false)
|
||||
.with_buffer_kind(ItemBufferKind::Multibuffer)
|
||||
.with_label("Fake Project Search")
|
||||
.with_project_items(&[
|
||||
dirty_regular_buffer.read(cx).project_items[0].clone(),
|
||||
@@ -9734,7 +9741,7 @@ mod tests {
|
||||
let dirty_multi_buffer_with_both = cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_dirty(true)
|
||||
.with_singleton(false)
|
||||
.with_buffer_kind(ItemBufferKind::Multibuffer)
|
||||
.with_label("Fake Project Search")
|
||||
.with_project_items(&[
|
||||
dirty_regular_buffer.read(cx).project_items[0].clone(),
|
||||
@@ -10148,7 +10155,7 @@ mod tests {
|
||||
let dirty_multi_buffer = cx.new(|cx| {
|
||||
TestItem::new(cx)
|
||||
.with_dirty(true)
|
||||
.with_singleton(false)
|
||||
.with_buffer_kind(ItemBufferKind::Multibuffer)
|
||||
.with_label("Fake Project Search")
|
||||
.with_project_items(&[
|
||||
dirty_regular_buffer.read(cx).project_items[0].clone(),
|
||||
|
||||
Reference in New Issue
Block a user