preview tabs: Allow replacing preview tab when using code navigation (#10730)
This PR adds support for replacing the current preview tab when using GoToDefinition. Previously a tab, that was navigated away from, was converted into a permanent tab and the new tab was opened as preview. Without `enable_preview_from_code_navigation`: https://github.com/zed-industries/zed/assets/53836821/99840724-d6ff-4738-a9c4-ee71a0001634 With `enable_preview_from_code_navigation`: https://github.com/zed-industries/zed/assets/53836821/8c60efcb-d597-40bf-b08b-13faf5a289b6 Note: In the future I would like to improve support for the navigation history, because right now tabs that are not "normal" project items, e.g. FindAllReferences cannot be reopened Release Notes: - Added support for replacing the current preview tab when using code navigation (`enable_preview_from_code_navigation`)
This commit is contained in:
parent
9ff847753e
commit
20625e98ad
19 changed files with 120 additions and 60 deletions
|
@ -45,6 +45,7 @@ pub struct ItemSettings {
|
|||
pub struct PreviewTabsSettings {
|
||||
pub enabled: bool,
|
||||
pub enable_preview_from_file_finder: bool,
|
||||
pub enable_preview_from_code_navigation: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
|
@ -78,15 +79,19 @@ pub struct ItemSettingsContent {
|
|||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PreviewTabsSettingsContent {
|
||||
/// Whether to show opened editors as preview editors.
|
||||
/// Preview editors do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic.
|
||||
/// Whether to show opened editors as preview tabs.
|
||||
/// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic.
|
||||
///
|
||||
/// Default: true
|
||||
enabled: Option<bool>,
|
||||
/// Whether to open a preview editor when opening a file using the file finder.
|
||||
/// Whether to open tabs in preview mode when selected from the file finder.
|
||||
///
|
||||
/// Default: false
|
||||
enable_preview_from_file_finder: Option<bool>,
|
||||
/// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
|
||||
///
|
||||
/// Default: false
|
||||
enable_preview_from_code_navigation: Option<bool>,
|
||||
}
|
||||
|
||||
impl Settings for ItemSettings {
|
||||
|
|
|
@ -623,21 +623,13 @@ impl Pane {
|
|||
self.activate_item(index, focus_item, focus_item, cx);
|
||||
existing_item
|
||||
} else {
|
||||
let mut destination_index = None;
|
||||
if allow_preview {
|
||||
// If we are opening a new item as preview and we have an existing preview tab, remove it.
|
||||
if let Some(item_idx) = self.preview_item_idx() {
|
||||
let prev_active_item_index = self.active_item_index;
|
||||
self.remove_item(item_idx, false, false, cx);
|
||||
self.active_item_index = prev_active_item_index;
|
||||
|
||||
// If the item is being opened as preview and we have an existing preview tab,
|
||||
// open the new item in the position of the existing preview tab.
|
||||
if item_idx < self.items.len() {
|
||||
destination_index = Some(item_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the item is being opened as preview and we have an existing preview tab,
|
||||
// open the new item in the position of the existing preview tab.
|
||||
let destination_index = if allow_preview {
|
||||
self.close_current_preview_item(cx)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let new_item = build_item(cx);
|
||||
|
||||
|
@ -651,6 +643,22 @@ impl Pane {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn close_current_preview_item(&mut self, cx: &mut ViewContext<Self>) -> Option<usize> {
|
||||
let Some(item_idx) = self.preview_item_idx() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let prev_active_item_index = self.active_item_index;
|
||||
self.remove_item(item_idx, false, false, cx);
|
||||
self.active_item_index = prev_active_item_index;
|
||||
|
||||
if item_idx < self.items.len() {
|
||||
Some(item_idx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_item(
|
||||
&mut self,
|
||||
item: Box<dyn ItemHandle>,
|
||||
|
|
|
@ -2061,14 +2061,20 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_item_to_active_pane(&mut self, item: Box<dyn ItemHandle>, cx: &mut WindowContext) {
|
||||
self.add_item(self.active_pane.clone(), item, cx)
|
||||
pub fn add_item_to_active_pane(
|
||||
&mut self,
|
||||
item: Box<dyn ItemHandle>,
|
||||
destination_index: Option<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
self.add_item(self.active_pane.clone(), item, destination_index, cx)
|
||||
}
|
||||
|
||||
pub fn add_item(
|
||||
&mut self,
|
||||
pane: View<Pane>,
|
||||
item: Box<dyn ItemHandle>,
|
||||
destination_index: Option<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
if let Some(text) = item.telemetry_event_text(cx) {
|
||||
|
@ -2077,7 +2083,9 @@ impl Workspace {
|
|||
.report_app_event(format!("{}: open", text));
|
||||
}
|
||||
|
||||
pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.add_item(item, true, true, destination_index, cx)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn split_item(
|
||||
|
@ -2087,7 +2095,7 @@ impl Workspace {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let new_pane = self.split_pane(self.active_pane.clone(), split_direction, cx);
|
||||
self.add_item(new_pane, item, cx);
|
||||
self.add_item(new_pane, item, None, cx);
|
||||
}
|
||||
|
||||
pub fn open_abs_path(
|
||||
|
@ -2259,10 +2267,21 @@ impl Workspace {
|
|||
}
|
||||
|
||||
let item = cx.new_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
|
||||
|
||||
let item_id = item.item_id();
|
||||
let mut destination_index = None;
|
||||
pane.update(cx, |pane, cx| {
|
||||
if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
|
||||
if let Some(preview_item_id) = pane.preview_item_id() {
|
||||
if preview_item_id != item_id {
|
||||
destination_index = pane.close_current_preview_item(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
pane.set_preview_item_id(Some(item.item_id()), cx)
|
||||
});
|
||||
self.add_item(pane, Box::new(item.clone()), cx);
|
||||
|
||||
self.add_item(pane, Box::new(item.clone()), destination_index, cx);
|
||||
item
|
||||
}
|
||||
|
||||
|
@ -5157,7 +5176,7 @@ mod tests {
|
|||
item
|
||||
});
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item1.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item1.clone()), None, cx);
|
||||
});
|
||||
item1.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(0)));
|
||||
|
||||
|
@ -5169,7 +5188,7 @@ mod tests {
|
|||
item
|
||||
});
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item2.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item2.clone()), None, cx);
|
||||
});
|
||||
item1.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
|
||||
item2.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
|
||||
|
@ -5183,7 +5202,7 @@ mod tests {
|
|||
item
|
||||
});
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item3.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item3.clone()), None, cx);
|
||||
});
|
||||
item1.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
|
||||
item2.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
|
||||
|
@ -5227,7 +5246,7 @@ mod tests {
|
|||
|
||||
// Add an item to an empty pane
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item1), cx)
|
||||
workspace.add_item_to_active_pane(Box::new(item1), None, cx)
|
||||
});
|
||||
project.update(cx, |project, cx| {
|
||||
assert_eq!(
|
||||
|
@ -5241,7 +5260,7 @@ mod tests {
|
|||
|
||||
// Add a second item to a non-empty pane
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item2), cx)
|
||||
workspace.add_item_to_active_pane(Box::new(item2), None, cx)
|
||||
});
|
||||
assert_eq!(cx.window_title().as_deref(), Some("two.txt — root1"));
|
||||
project.update(cx, |project, cx| {
|
||||
|
@ -5296,7 +5315,7 @@ mod tests {
|
|||
// When there are no dirty items, there's nothing to do.
|
||||
let item1 = cx.new_view(|cx| TestItem::new(cx));
|
||||
workspace.update(cx, |w, cx| {
|
||||
w.add_item_to_active_pane(Box::new(item1.clone()), cx)
|
||||
w.add_item_to_active_pane(Box::new(item1.clone()), None, cx)
|
||||
});
|
||||
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
|
||||
assert!(task.await.unwrap());
|
||||
|
@ -5310,8 +5329,8 @@ mod tests {
|
|||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
});
|
||||
workspace.update(cx, |w, cx| {
|
||||
w.add_item_to_active_pane(Box::new(item2.clone()), cx);
|
||||
w.add_item_to_active_pane(Box::new(item3.clone()), cx);
|
||||
w.add_item_to_active_pane(Box::new(item2.clone()), None, cx);
|
||||
w.add_item_to_active_pane(Box::new(item3.clone()), None, cx);
|
||||
});
|
||||
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
|
||||
cx.executor().run_until_parked();
|
||||
|
@ -5355,10 +5374,10 @@ mod tests {
|
|||
.with_project_items(&[TestProjectItem::new_untitled(cx)])
|
||||
});
|
||||
let pane = workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item1.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item2.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item3.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item4.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item1.clone()), None, cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item2.clone()), None, cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item3.clone()), None, cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item4.clone()), None, cx);
|
||||
workspace.active_pane().clone()
|
||||
});
|
||||
|
||||
|
@ -5480,9 +5499,9 @@ mod tests {
|
|||
// multi-entry items: (3, 4)
|
||||
let left_pane = workspace.update(cx, |workspace, cx| {
|
||||
let left_pane = workspace.active_pane().clone();
|
||||
workspace.add_item_to_active_pane(Box::new(item_2_3.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item_2_3.clone()), None, cx);
|
||||
for item in single_entry_items {
|
||||
workspace.add_item_to_active_pane(Box::new(item), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item), None, cx);
|
||||
}
|
||||
left_pane.update(cx, |pane, cx| {
|
||||
pane.activate_item(2, true, true, cx);
|
||||
|
@ -5553,7 +5572,7 @@ mod tests {
|
|||
});
|
||||
let item_id = item.entity_id();
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item.clone()), None, cx);
|
||||
});
|
||||
|
||||
// Autosave on window change.
|
||||
|
@ -5638,7 +5657,7 @@ mod tests {
|
|||
|
||||
// Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item.clone()), None, cx);
|
||||
});
|
||||
item.update(cx, |item, cx| {
|
||||
item.project_items[0].update(cx, |item, _| {
|
||||
|
@ -5676,7 +5695,7 @@ mod tests {
|
|||
let toolbar_notify_count = Rc::new(RefCell::new(0));
|
||||
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(item.clone()), cx);
|
||||
workspace.add_item_to_active_pane(Box::new(item.clone()), None, cx);
|
||||
let toolbar_notification_count = toolbar_notify_count.clone();
|
||||
cx.observe(&toolbar, move |_, _, _| {
|
||||
*toolbar_notification_count.borrow_mut() += 1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue