Add more preview tab settings and fix janky behavior (#43921)
Closes #41495 Known issues: - File path links always open as non-preview tabs. Fixing this is not technically too difficult but requires more invasive changes and so should be done in a future PR. Release Notes: - Fixed strange behavior when reopening closed preview tabs - Overhauled preview tabs settings: - Added setting `preview_tabs.enable_preview_from_project_panel` (default `true`) - Kept setting `preview_tabs.enable_preview_from_file_finder` (default `false`) - Added setting `preview_tabs.enable_preview_from_multibuffer` (default `true`) - Added setting `preview_tabs.enable_preview_multibuffer_from_code_navigation` (default `false`) - Added setting `preview_tabs.enable_preview_file_from_code_navigation` (default `true`) - Renamed setting `preview_tabs.enable_preview_from_code_navigation` to `preview_tabs.enable_keep_preview_on_code_navigation` (default `false`) --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
@@ -1100,13 +1100,22 @@
|
||||
"preview_tabs": {
|
||||
// Whether preview tabs should be enabled.
|
||||
// Preview tabs allow you to open files in preview mode, where they close automatically
|
||||
// when you switch to another file unless you explicitly pin them.
|
||||
// when you open another preview tab.
|
||||
// This is useful for quickly viewing files without cluttering your workspace.
|
||||
"enabled": true,
|
||||
// Whether to open tabs in preview mode when opened from the project panel with a single click.
|
||||
"enable_preview_from_project_panel": true,
|
||||
// Whether to open tabs in preview mode when selected from the file finder.
|
||||
"enable_preview_from_file_finder": false,
|
||||
// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
|
||||
"enable_preview_from_code_navigation": false
|
||||
// Whether to open tabs in preview mode when opened from a multibuffer.
|
||||
"enable_preview_from_multibuffer": true,
|
||||
// Whether to open tabs in preview mode when code navigation is used to open a multibuffer.
|
||||
"enable_preview_multibuffer_from_code_navigation": false,
|
||||
// Whether to open tabs in preview mode when code navigation is used to open a single file.
|
||||
"enable_preview_file_from_code_navigation": true,
|
||||
// Whether to keep tabs in preview mode when code navigation is used to navigate away from them.
|
||||
// If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.
|
||||
"enable_keep_preview_on_code_navigation": false
|
||||
},
|
||||
// Settings related to the file finder.
|
||||
"file_finder": {
|
||||
|
||||
@@ -17012,7 +17012,9 @@ impl Editor {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let workspace = self.workspace();
|
||||
let Some(workspace) = self.workspace() else {
|
||||
return Task::ready(Ok(Navigated::No));
|
||||
};
|
||||
|
||||
cx.spawn_in(window, async move |editor, cx| {
|
||||
let locations: Vec<Location> = future::join_all(definitions)
|
||||
@@ -17038,10 +17040,6 @@ impl Editor {
|
||||
}
|
||||
|
||||
if num_locations > 1 {
|
||||
let Some(workspace) = workspace else {
|
||||
return Ok(Navigated::No);
|
||||
};
|
||||
|
||||
let tab_kind = match kind {
|
||||
Some(GotoDefinitionKind::Implementation) => "Implementations",
|
||||
Some(GotoDefinitionKind::Symbol) | None => "Definitions",
|
||||
@@ -17073,11 +17071,14 @@ impl Editor {
|
||||
|
||||
let opened = workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
let allow_preview = PreviewTabsSettings::get_global(cx)
|
||||
.enable_preview_multibuffer_from_code_navigation;
|
||||
Self::open_locations_in_multibuffer(
|
||||
workspace,
|
||||
locations,
|
||||
title,
|
||||
split,
|
||||
allow_preview,
|
||||
MultibufferSelectionMode::First,
|
||||
window,
|
||||
cx,
|
||||
@@ -17094,10 +17095,9 @@ impl Editor {
|
||||
Ok(Navigated::Yes)
|
||||
}
|
||||
Some(Either::Right(path)) => {
|
||||
let Some(workspace) = workspace else {
|
||||
return Ok(Navigated::No);
|
||||
};
|
||||
|
||||
// TODO(andrew): respect preview tab settings
|
||||
// `enable_keep_preview_on_code_navigation` and
|
||||
// `enable_preview_file_from_code_navigation`
|
||||
workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.open_resolved_path(path, window, cx)
|
||||
@@ -17108,10 +17108,6 @@ impl Editor {
|
||||
None => Ok(Navigated::No),
|
||||
}
|
||||
} else {
|
||||
let Some(workspace) = workspace else {
|
||||
return Ok(Navigated::No);
|
||||
};
|
||||
|
||||
let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
|
||||
let target_range = target_ranges.first().unwrap().clone();
|
||||
|
||||
@@ -17135,11 +17131,19 @@ impl Editor {
|
||||
workspace.active_pane().clone()
|
||||
};
|
||||
|
||||
let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
|
||||
let keep_old_preview = preview_tabs_settings
|
||||
.enable_keep_preview_on_code_navigation;
|
||||
let allow_new_preview = preview_tabs_settings
|
||||
.enable_preview_file_from_code_navigation;
|
||||
|
||||
workspace.open_project_item(
|
||||
pane,
|
||||
target_buffer.clone(),
|
||||
true,
|
||||
true,
|
||||
keep_old_preview,
|
||||
allow_new_preview,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -17416,11 +17420,14 @@ impl Editor {
|
||||
} else {
|
||||
format!("References to {target}")
|
||||
};
|
||||
let allow_preview = PreviewTabsSettings::get_global(cx)
|
||||
.enable_preview_multibuffer_from_code_navigation;
|
||||
Self::open_locations_in_multibuffer(
|
||||
workspace,
|
||||
locations,
|
||||
title,
|
||||
false,
|
||||
allow_preview,
|
||||
MultibufferSelectionMode::First,
|
||||
window,
|
||||
cx,
|
||||
@@ -17436,6 +17443,7 @@ impl Editor {
|
||||
locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
|
||||
title: String,
|
||||
split: bool,
|
||||
allow_preview: bool,
|
||||
multibuffer_selection_mode: MultibufferSelectionMode,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
@@ -17483,6 +17491,7 @@ impl Editor {
|
||||
.is_some_and(|it| *it == key)
|
||||
})
|
||||
});
|
||||
let was_existing = existing.is_some();
|
||||
let editor = existing.unwrap_or_else(|| {
|
||||
cx.new(|cx| {
|
||||
let mut editor = Editor::for_multibuffer(
|
||||
@@ -17523,29 +17532,23 @@ impl Editor {
|
||||
});
|
||||
|
||||
let item = Box::new(editor);
|
||||
let item_id = item.item_id();
|
||||
|
||||
if split {
|
||||
let pane = workspace.adjacent_pane(window, cx);
|
||||
workspace.add_item(pane, item, None, true, true, window, cx);
|
||||
} else if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
|
||||
let (preview_item_id, preview_item_idx) =
|
||||
workspace.active_pane().read_with(cx, |pane, _| {
|
||||
(pane.preview_item_id(), pane.preview_item_idx())
|
||||
});
|
||||
|
||||
workspace.add_item_to_active_pane(item, preview_item_idx, true, window, cx);
|
||||
|
||||
if let Some(preview_item_id) = preview_item_id {
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
pane.remove_item(preview_item_id, false, false, window, cx);
|
||||
});
|
||||
}
|
||||
let pane = if split {
|
||||
workspace.adjacent_pane(window, cx)
|
||||
} else {
|
||||
workspace.add_item_to_active_pane(item, None, true, window, cx);
|
||||
}
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
pane.set_preview_item_id(Some(item_id), cx);
|
||||
workspace.active_pane().clone()
|
||||
};
|
||||
let activate_pane = split;
|
||||
|
||||
let mut destination_index = None;
|
||||
pane.update(cx, |pane, cx| {
|
||||
if allow_preview && !was_existing {
|
||||
destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
|
||||
}
|
||||
if was_existing && !allow_preview {
|
||||
pane.unpreview_item_if_preview(item.item_id());
|
||||
}
|
||||
pane.add_item(item, activate_pane, true, destination_index, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20783,6 +20786,7 @@ impl Editor {
|
||||
locations,
|
||||
format!("Selections for '{title}'"),
|
||||
false,
|
||||
false,
|
||||
MultibufferSelectionMode::All,
|
||||
window,
|
||||
cx,
|
||||
@@ -22002,29 +22006,40 @@ impl Editor {
|
||||
// Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
|
||||
// so `workspace.open_project_item` will never find them, always opening a new editor.
|
||||
// Instead, we try to activate the existing editor in the pane first.
|
||||
let (editor, pane_item_index) =
|
||||
let (editor, pane_item_index, pane_item_id) =
|
||||
pane.read(cx).items().enumerate().find_map(|(i, item)| {
|
||||
let editor = item.downcast::<Editor>()?;
|
||||
let singleton_buffer =
|
||||
editor.read(cx).buffer().read(cx).as_singleton()?;
|
||||
if singleton_buffer == buffer {
|
||||
Some((editor, i))
|
||||
Some((editor, i, item.item_id()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.activate_item(pane_item_index, true, true, window, cx)
|
||||
pane.activate_item(pane_item_index, true, true, window, cx);
|
||||
if !PreviewTabsSettings::get_global(cx)
|
||||
.enable_preview_from_multibuffer
|
||||
{
|
||||
pane.unpreview_item_if_preview(pane_item_id);
|
||||
}
|
||||
});
|
||||
Some(editor)
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or_else(|| {
|
||||
let keep_old_preview = PreviewTabsSettings::get_global(cx)
|
||||
.enable_keep_preview_on_code_navigation;
|
||||
let allow_new_preview =
|
||||
PreviewTabsSettings::get_global(cx).enable_preview_from_multibuffer;
|
||||
workspace.open_project_item::<Self>(
|
||||
pane.clone(),
|
||||
buffer,
|
||||
true,
|
||||
true,
|
||||
keep_old_preview,
|
||||
allow_new_preview,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -153,3 +153,9 @@ pub(crate) mod m_2025_11_25 {
|
||||
|
||||
pub(crate) use settings::remove_context_server_source;
|
||||
}
|
||||
|
||||
pub(crate) mod m_2025_12_01 {
|
||||
mod settings;
|
||||
|
||||
pub(crate) use settings::SETTINGS_PATTERNS;
|
||||
}
|
||||
|
||||
55
crates/migrator/src/migrations/m_2025_12_01/settings.rs
Normal file
55
crates/migrator/src/migrations/m_2025_12_01/settings.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use std::ops::Range;
|
||||
use tree_sitter::{Query, QueryMatch};
|
||||
|
||||
use crate::MigrationPatterns;
|
||||
use crate::patterns::SETTINGS_NESTED_KEY_VALUE_PATTERN;
|
||||
|
||||
pub const SETTINGS_PATTERNS: MigrationPatterns = &[(
|
||||
SETTINGS_NESTED_KEY_VALUE_PATTERN,
|
||||
rename_enable_preview_from_code_navigation_setting,
|
||||
)];
|
||||
|
||||
fn rename_enable_preview_from_code_navigation_setting(
|
||||
contents: &str,
|
||||
mat: &QueryMatch,
|
||||
query: &Query,
|
||||
) -> Option<(Range<usize>, String)> {
|
||||
if !is_enable_preview_from_code_navigation(contents, mat, query) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let setting_name_ix = query.capture_index_for_name("setting_name")?;
|
||||
let setting_name_range = mat
|
||||
.nodes_for_capture_index(setting_name_ix)
|
||||
.next()?
|
||||
.byte_range();
|
||||
|
||||
Some((
|
||||
setting_name_range,
|
||||
"enable_keep_preview_on_code_navigation".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn is_enable_preview_from_code_navigation(contents: &str, mat: &QueryMatch, query: &Query) -> bool {
|
||||
let parent_key_ix = match query.capture_index_for_name("parent_key") {
|
||||
Some(ix) => ix,
|
||||
None => return false,
|
||||
};
|
||||
let parent_range = match mat.nodes_for_capture_index(parent_key_ix).next() {
|
||||
Some(node) => node.byte_range(),
|
||||
None => return false,
|
||||
};
|
||||
if contents.get(parent_range) != Some("preview_tabs") {
|
||||
return false;
|
||||
}
|
||||
|
||||
let setting_name_ix = match query.capture_index_for_name("setting_name") {
|
||||
Some(ix) => ix,
|
||||
None => return false,
|
||||
};
|
||||
let setting_name_range = match mat.nodes_for_capture_index(setting_name_ix).next() {
|
||||
Some(node) => node.byte_range(),
|
||||
None => return false,
|
||||
};
|
||||
contents.get(setting_name_range) == Some("enable_preview_from_code_navigation")
|
||||
}
|
||||
@@ -219,6 +219,10 @@ pub fn migrate_settings(text: &str) -> Result<Option<String>> {
|
||||
migrations::m_2025_11_12::SETTINGS_PATTERNS,
|
||||
&SETTINGS_QUERY_2025_11_12,
|
||||
),
|
||||
MigrationType::TreeSitter(
|
||||
migrations::m_2025_12_01::SETTINGS_PATTERNS,
|
||||
&SETTINGS_QUERY_2025_12_01,
|
||||
),
|
||||
MigrationType::TreeSitter(
|
||||
migrations::m_2025_11_20::SETTINGS_PATTERNS,
|
||||
&SETTINGS_QUERY_2025_11_20,
|
||||
@@ -346,6 +350,10 @@ define_query!(
|
||||
SETTINGS_QUERY_2025_11_12,
|
||||
migrations::m_2025_11_12::SETTINGS_PATTERNS
|
||||
);
|
||||
define_query!(
|
||||
SETTINGS_QUERY_2025_12_01,
|
||||
migrations::m_2025_12_01::SETTINGS_PATTERNS
|
||||
);
|
||||
define_query!(
|
||||
SETTINGS_QUERY_2025_11_20,
|
||||
migrations::m_2025_11_20::SETTINGS_PATTERNS
|
||||
@@ -2262,51 +2270,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_project_panel_open_file_on_paste_migration() {
|
||||
assert_migrate_settings(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"open_file_on_paste": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"auto_open": { "on_paste": true }
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"open_file_on_paste": false
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"auto_open": { "on_paste": false }
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_context_server_source() {
|
||||
assert_migrate_settings(
|
||||
@@ -2354,4 +2317,102 @@ mod tests {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_project_panel_open_file_on_paste_migration() {
|
||||
assert_migrate_settings(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"open_file_on_paste": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"auto_open": { "on_paste": true }
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"open_file_on_paste": false
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
{
|
||||
"project_panel": {
|
||||
"auto_open": { "on_paste": false }
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enable_preview_from_code_navigation_migration() {
|
||||
assert_migrate_settings(
|
||||
&r#"
|
||||
{
|
||||
"other_setting_1": 1,
|
||||
"preview_tabs": {
|
||||
"other_setting_2": 2,
|
||||
"enable_preview_from_code_navigation": false
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
{
|
||||
"other_setting_1": 1,
|
||||
"preview_tabs": {
|
||||
"other_setting_2": 2,
|
||||
"enable_keep_preview_on_code_navigation": false
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings(
|
||||
&r#"
|
||||
{
|
||||
"other_setting_1": 1,
|
||||
"preview_tabs": {
|
||||
"other_setting_2": 2,
|
||||
"enable_preview_from_code_navigation": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
{
|
||||
"other_setting_1": 1,
|
||||
"preview_tabs": {
|
||||
"other_setting_2": 2,
|
||||
"enable_keep_preview_on_code_navigation": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1529,7 +1529,8 @@ impl ProjectPanel {
|
||||
}
|
||||
|
||||
fn open(&mut self, _: &Open, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let preview_tabs_enabled = PreviewTabsSettings::get_global(cx).enabled;
|
||||
let preview_tabs_enabled =
|
||||
PreviewTabsSettings::get_global(cx).enable_preview_from_project_panel;
|
||||
self.open_internal(true, !preview_tabs_enabled, None, window, cx);
|
||||
}
|
||||
|
||||
@@ -4819,7 +4820,7 @@ impl ProjectPanel {
|
||||
project_panel.toggle_expanded(entry_id, window, cx);
|
||||
}
|
||||
} else {
|
||||
let preview_tabs_enabled = PreviewTabsSettings::get_global(cx).enabled;
|
||||
let preview_tabs_enabled = PreviewTabsSettings::get_global(cx).enable_preview_from_project_panel;
|
||||
let click_count = event.click_count();
|
||||
let focus_opened_item = click_count > 1;
|
||||
let allow_preview = preview_tabs_enabled && click_count == 1;
|
||||
|
||||
@@ -133,8 +133,9 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
||||
workspace.active_pane().clone()
|
||||
};
|
||||
|
||||
let editor =
|
||||
workspace.open_project_item::<Editor>(pane, buffer, true, true, window, cx);
|
||||
let editor = workspace.open_project_item::<Editor>(
|
||||
pane, buffer, true, true, true, true, window, cx,
|
||||
);
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(
|
||||
|
||||
@@ -152,14 +152,31 @@ pub struct PreviewTabsSettingsContent {
|
||||
///
|
||||
/// Default: true
|
||||
pub enabled: Option<bool>,
|
||||
/// Whether to open tabs in preview mode when opened from the project panel with a single click.
|
||||
///
|
||||
/// Default: true
|
||||
pub enable_preview_from_project_panel: Option<bool>,
|
||||
/// Whether to open tabs in preview mode when selected from the file finder.
|
||||
///
|
||||
/// Default: false
|
||||
pub enable_preview_from_file_finder: Option<bool>,
|
||||
/// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
|
||||
/// Whether to open tabs in preview mode when opened from a multibuffer.
|
||||
///
|
||||
/// Default: true
|
||||
pub enable_preview_from_multibuffer: Option<bool>,
|
||||
/// Whether to open tabs in preview mode when code navigation is used to open a multibuffer.
|
||||
///
|
||||
/// Default: false
|
||||
pub enable_preview_from_code_navigation: Option<bool>,
|
||||
pub enable_preview_multibuffer_from_code_navigation: Option<bool>,
|
||||
/// Whether to open tabs in preview mode when code navigation is used to open a single file.
|
||||
///
|
||||
/// Default: true
|
||||
pub enable_preview_file_from_code_navigation: Option<bool>,
|
||||
/// Whether to keep tabs in preview mode when code navigation is used to navigate away from them.
|
||||
/// If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.
|
||||
///
|
||||
/// Default: false
|
||||
pub enable_keep_preview_on_code_navigation: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
||||
@@ -619,9 +619,13 @@ impl VsCodeSettings {
|
||||
fn preview_tabs_settings_content(&self) -> Option<PreviewTabsSettingsContent> {
|
||||
skip_default(PreviewTabsSettingsContent {
|
||||
enabled: self.read_bool("workbench.editor.enablePreview"),
|
||||
enable_preview_from_project_panel: None,
|
||||
enable_preview_from_file_finder: self
|
||||
.read_bool("workbench.editor.enablePreviewFromQuickOpen"),
|
||||
enable_preview_from_code_navigation: self
|
||||
enable_preview_from_multibuffer: None,
|
||||
enable_preview_multibuffer_from_code_navigation: None,
|
||||
enable_preview_file_from_code_navigation: None,
|
||||
enable_keep_preview_on_code_navigation: self
|
||||
.read_bool("workbench.editor.enablePreviewFromCodeNavigation"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3145,7 +3145,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
|
||||
SettingsPageItem::SectionHeader("Preview Tabs"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Preview Tabs Enabled",
|
||||
description: "Show opened editors as Preview tabs.",
|
||||
description: "Show opened editors as preview tabs.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("preview_tabs.enabled"),
|
||||
pick: |settings_content| {
|
||||
@@ -3161,9 +3161,31 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
|
||||
metadata: None,
|
||||
files: USER,
|
||||
}),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Enable Preview From Project Panel",
|
||||
description: "Whether to open tabs in preview mode when opened from the project panel with a single click.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("preview_tabs.enable_preview_from_project_panel"),
|
||||
pick: |settings_content| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.as_ref()?
|
||||
.enable_preview_from_project_panel
|
||||
.as_ref()
|
||||
},
|
||||
write: |settings_content, value| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.get_or_insert_default()
|
||||
.enable_preview_from_project_panel = value;
|
||||
},
|
||||
}),
|
||||
metadata: None,
|
||||
files: USER,
|
||||
}),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Enable Preview From File Finder",
|
||||
description: "Whether to open tabs in Preview mode when selected from the file finder.",
|
||||
description: "Whether to open tabs in preview mode when selected from the file finder.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("preview_tabs.enable_preview_from_file_finder"),
|
||||
pick: |settings_content| {
|
||||
@@ -3184,22 +3206,88 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
|
||||
files: USER,
|
||||
}),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Enable Preview From Code Navigation",
|
||||
description: "Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.",
|
||||
title: "Enable Preview From Multibuffer",
|
||||
description: "Whether to open tabs in preview mode when opened from a multibuffer.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("preview_tabs.enable_preview_from_code_navigation"),
|
||||
json_path: Some("preview_tabs.enable_preview_from_multibuffer"),
|
||||
pick: |settings_content| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.as_ref()?
|
||||
.enable_preview_from_code_navigation
|
||||
.enable_preview_from_multibuffer
|
||||
.as_ref()
|
||||
},
|
||||
write: |settings_content, value| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.get_or_insert_default()
|
||||
.enable_preview_from_code_navigation = value;
|
||||
.enable_preview_from_multibuffer = value;
|
||||
},
|
||||
}),
|
||||
metadata: None,
|
||||
files: USER,
|
||||
}),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Enable Preview Multibuffer From Code Navigation",
|
||||
description: "Whether to open tabs in preview mode when code navigation is used to open a multibuffer.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("preview_tabs.enable_preview_multibuffer_from_code_navigation"),
|
||||
pick: |settings_content| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.as_ref()?
|
||||
.enable_preview_multibuffer_from_code_navigation
|
||||
.as_ref()
|
||||
},
|
||||
write: |settings_content, value| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.get_or_insert_default()
|
||||
.enable_preview_multibuffer_from_code_navigation = value;
|
||||
},
|
||||
}),
|
||||
metadata: None,
|
||||
files: USER,
|
||||
}),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Enable Preview File From Code Navigation",
|
||||
description: "Whether to open tabs in preview mode when code navigation is used to open a single file.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("preview_tabs.enable_preview_file_from_code_navigation"),
|
||||
pick: |settings_content| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.as_ref()?
|
||||
.enable_preview_file_from_code_navigation
|
||||
.as_ref()
|
||||
},
|
||||
write: |settings_content, value| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.get_or_insert_default()
|
||||
.enable_preview_file_from_code_navigation = value;
|
||||
},
|
||||
}),
|
||||
metadata: None,
|
||||
files: USER,
|
||||
}),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
title: "Enable Keep Preview On Code Navigation",
|
||||
description: "Whether to keep tabs in preview mode when code navigation is used to navigate away from them. If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.",
|
||||
field: Box::new(SettingField {
|
||||
json_path: Some("preview_tabs.enable_keep_preview_on_code_navigation"),
|
||||
pick: |settings_content| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.as_ref()?
|
||||
.enable_keep_preview_on_code_navigation
|
||||
.as_ref()
|
||||
},
|
||||
write: |settings_content, value| {
|
||||
settings_content
|
||||
.preview_tabs
|
||||
.get_or_insert_default()
|
||||
.enable_keep_preview_on_code_navigation = value;
|
||||
},
|
||||
}),
|
||||
metadata: None,
|
||||
|
||||
@@ -64,8 +64,12 @@ pub struct ItemSettings {
|
||||
#[derive(RegisterSetting)]
|
||||
pub struct PreviewTabsSettings {
|
||||
pub enabled: bool,
|
||||
pub enable_preview_from_project_panel: bool,
|
||||
pub enable_preview_from_file_finder: bool,
|
||||
pub enable_preview_from_code_navigation: bool,
|
||||
pub enable_preview_from_multibuffer: bool,
|
||||
pub enable_preview_multibuffer_from_code_navigation: bool,
|
||||
pub enable_preview_file_from_code_navigation: bool,
|
||||
pub enable_keep_preview_on_code_navigation: bool,
|
||||
}
|
||||
|
||||
impl Settings for ItemSettings {
|
||||
@@ -87,9 +91,19 @@ impl Settings for PreviewTabsSettings {
|
||||
let preview_tabs = content.preview_tabs.as_ref().unwrap();
|
||||
Self {
|
||||
enabled: preview_tabs.enabled.unwrap(),
|
||||
enable_preview_from_project_panel: preview_tabs
|
||||
.enable_preview_from_project_panel
|
||||
.unwrap(),
|
||||
enable_preview_from_file_finder: preview_tabs.enable_preview_from_file_finder.unwrap(),
|
||||
enable_preview_from_code_navigation: preview_tabs
|
||||
.enable_preview_from_code_navigation
|
||||
enable_preview_from_multibuffer: preview_tabs.enable_preview_from_multibuffer.unwrap(),
|
||||
enable_preview_multibuffer_from_code_navigation: preview_tabs
|
||||
.enable_preview_multibuffer_from_code_navigation
|
||||
.unwrap(),
|
||||
enable_preview_file_from_code_navigation: preview_tabs
|
||||
.enable_preview_file_from_code_navigation
|
||||
.unwrap(),
|
||||
enable_keep_preview_on_code_navigation: preview_tabs
|
||||
.enable_keep_preview_on_code_navigation
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,10 +873,35 @@ impl Pane {
|
||||
self.preview_item_id == Some(item_id)
|
||||
}
|
||||
|
||||
/// Promotes the item with the given ID to not be a preview item.
|
||||
/// This does nothing if it wasn't already a preview item.
|
||||
pub fn unpreview_item_if_preview(&mut self, item_id: EntityId) {
|
||||
if self.is_active_preview_item(item_id) {
|
||||
self.preview_item_id = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks the item with the given ID as the preview item.
|
||||
/// This will be ignored if the global setting `preview_tabs` is disabled.
|
||||
pub fn set_preview_item_id(&mut self, item_id: Option<EntityId>, cx: &App) {
|
||||
if PreviewTabsSettings::get_global(cx).enabled {
|
||||
///
|
||||
/// The old preview item (if there was one) is closed and its index is returned.
|
||||
pub fn replace_preview_item_id(
|
||||
&mut self,
|
||||
item_id: EntityId,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize> {
|
||||
let idx = self.close_current_preview_item(window, cx);
|
||||
self.set_preview_item_id(Some(item_id), cx);
|
||||
idx
|
||||
}
|
||||
|
||||
/// Marks the item with the given ID as the preview item.
|
||||
/// This will be ignored if the global setting `preview_tabs` is disabled.
|
||||
///
|
||||
/// This is a low-level method. Prefer `unpreview_item_if_preview()` or `set_new_preview_item()`.
|
||||
pub(crate) fn set_preview_item_id(&mut self, item_id: Option<EntityId>, cx: &App) {
|
||||
if item_id.is_none() || PreviewTabsSettings::get_global(cx).enabled {
|
||||
self.preview_item_id = item_id;
|
||||
}
|
||||
}
|
||||
@@ -895,7 +920,7 @@ impl Pane {
|
||||
&& preview_item.item_id() == item_id
|
||||
&& !preview_item.preserve_preview(cx)
|
||||
{
|
||||
self.set_preview_item_id(None, cx);
|
||||
self.unpreview_item_if_preview(item_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,14 +961,8 @@ impl Pane {
|
||||
|
||||
let set_up_existing_item =
|
||||
|index: usize, pane: &mut Self, window: &mut Window, cx: &mut Context<Self>| {
|
||||
// If the item is already open, and the item is a preview item
|
||||
// and we are not allowing items to open as preview, mark the item as persistent.
|
||||
if let Some(preview_item_id) = pane.preview_item_id
|
||||
&& let Some(tab) = pane.items.get(index)
|
||||
&& tab.item_id() == preview_item_id
|
||||
&& !allow_preview
|
||||
{
|
||||
pane.set_preview_item_id(None, cx);
|
||||
if !allow_preview && let Some(item) = pane.items.get(index) {
|
||||
pane.unpreview_item_if_preview(item.item_id());
|
||||
}
|
||||
if activate {
|
||||
pane.activate_item(index, focus_item, focus_item, window, cx);
|
||||
@@ -955,7 +974,7 @@ impl Pane {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>| {
|
||||
if allow_preview {
|
||||
pane.set_preview_item_id(Some(new_item.item_id()), cx);
|
||||
pane.replace_preview_item_id(new_item.item_id(), window, cx);
|
||||
}
|
||||
|
||||
if let Some(text) = new_item.telemetry_event_text(cx) {
|
||||
@@ -1036,6 +1055,7 @@ impl Pane {
|
||||
) -> Option<usize> {
|
||||
let item_idx = self.preview_item_idx()?;
|
||||
let id = self.preview_item_id()?;
|
||||
self.set_preview_item_id(None, cx);
|
||||
|
||||
let prev_active_item_index = self.active_item_index;
|
||||
self.remove_item(id, false, false, window, cx);
|
||||
@@ -1981,9 +2001,7 @@ impl Pane {
|
||||
item.on_removed(cx);
|
||||
self.nav_history.set_mode(mode);
|
||||
|
||||
if self.is_active_preview_item(item.item_id()) {
|
||||
self.set_preview_item_id(None, cx);
|
||||
}
|
||||
self.unpreview_item_if_preview(item.item_id());
|
||||
|
||||
if let Some(path) = item.project_path(cx) {
|
||||
let abs_path = self
|
||||
@@ -2194,9 +2212,7 @@ impl Pane {
|
||||
|
||||
if can_save {
|
||||
pane.update_in(cx, |pane, window, cx| {
|
||||
if pane.is_active_preview_item(item.item_id()) {
|
||||
pane.set_preview_item_id(None, cx);
|
||||
}
|
||||
pane.unpreview_item_if_preview(item.item_id());
|
||||
item.save(
|
||||
SaveOptions {
|
||||
format: should_format,
|
||||
@@ -2450,8 +2466,8 @@ impl Pane {
|
||||
let id = self.item_for_index(ix)?.item_id();
|
||||
let should_activate = ix == self.active_item_index;
|
||||
|
||||
if matches!(operation, PinOperation::Pin) && self.is_active_preview_item(id) {
|
||||
self.set_preview_item_id(None, cx);
|
||||
if matches!(operation, PinOperation::Pin) {
|
||||
self.unpreview_item_if_preview(id);
|
||||
}
|
||||
|
||||
match operation {
|
||||
@@ -2624,12 +2640,9 @@ impl Pane {
|
||||
)
|
||||
.on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(move |pane, event: &MouseDownEvent, _, cx| {
|
||||
if let Some(id) = pane.preview_item_id
|
||||
&& id == item_id
|
||||
&& event.click_count > 1
|
||||
{
|
||||
pane.set_preview_item_id(None, cx);
|
||||
cx.listener(move |pane, event: &MouseDownEvent, _, _| {
|
||||
if event.click_count > 1 {
|
||||
pane.unpreview_item_if_preview(item_id);
|
||||
}
|
||||
}),
|
||||
)
|
||||
@@ -3272,11 +3285,7 @@ impl Pane {
|
||||
let mut to_pane = cx.entity();
|
||||
let split_direction = self.drag_split_direction;
|
||||
let item_id = dragged_tab.item.item_id();
|
||||
if let Some(preview_item_id) = self.preview_item_id
|
||||
&& item_id == preview_item_id
|
||||
{
|
||||
self.set_preview_item_id(None, cx);
|
||||
}
|
||||
self.unpreview_item_if_preview(item_id);
|
||||
|
||||
let is_clone = cfg!(target_os = "macos") && window.modifiers().alt
|
||||
|| cfg!(not(target_os = "macos")) && window.modifiers().control;
|
||||
@@ -3788,15 +3797,17 @@ impl Render for Pane {
|
||||
.on_action(cx.listener(Self::toggle_pin_tab))
|
||||
.on_action(cx.listener(Self::unpin_all_tabs))
|
||||
.when(PreviewTabsSettings::get_global(cx).enabled, |this| {
|
||||
this.on_action(cx.listener(|pane: &mut Pane, _: &TogglePreviewTab, _, cx| {
|
||||
if let Some(active_item_id) = pane.active_item().map(|i| i.item_id()) {
|
||||
if pane.is_active_preview_item(active_item_id) {
|
||||
pane.set_preview_item_id(None, cx);
|
||||
} else {
|
||||
pane.set_preview_item_id(Some(active_item_id), cx);
|
||||
this.on_action(
|
||||
cx.listener(|pane: &mut Pane, _: &TogglePreviewTab, window, cx| {
|
||||
if let Some(active_item_id) = pane.active_item().map(|i| i.item_id()) {
|
||||
if pane.is_active_preview_item(active_item_id) {
|
||||
pane.unpreview_item_if_preview(active_item_id);
|
||||
} else {
|
||||
pane.replace_preview_item_id(active_item_id, window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}),
|
||||
)
|
||||
})
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &CloseActiveItem, window, cx| {
|
||||
|
||||
@@ -3636,14 +3636,33 @@ impl Workspace {
|
||||
project_item: Entity<T::Item>,
|
||||
activate_pane: bool,
|
||||
focus_item: bool,
|
||||
keep_old_preview: bool,
|
||||
allow_new_preview: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Entity<T>
|
||||
where
|
||||
T: ProjectItem,
|
||||
{
|
||||
let old_item_id = pane.read(cx).active_item().map(|item| item.item_id());
|
||||
|
||||
if let Some(item) = self.find_project_item(&pane, &project_item, cx) {
|
||||
if !keep_old_preview
|
||||
&& let Some(old_id) = old_item_id
|
||||
&& old_id != item.item_id()
|
||||
{
|
||||
// switching to a different item, so unpreview old active item
|
||||
pane.update(cx, |pane, _| {
|
||||
pane.unpreview_item_if_preview(old_id);
|
||||
});
|
||||
}
|
||||
|
||||
self.activate_item(&item, activate_pane, focus_item, window, cx);
|
||||
if !allow_new_preview {
|
||||
pane.update(cx, |pane, _| {
|
||||
pane.unpreview_item_if_preview(item.item_id());
|
||||
});
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -3652,16 +3671,14 @@ impl Workspace {
|
||||
T::for_project_item(self.project().clone(), Some(pane), project_item, window, 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
|
||||
&& let Some(preview_item_id) = pane.preview_item_id()
|
||||
&& preview_item_id != item_id
|
||||
{
|
||||
destination_index = pane.close_current_preview_item(window, cx);
|
||||
if !keep_old_preview && let Some(old_id) = old_item_id {
|
||||
pane.unpreview_item_if_preview(old_id);
|
||||
}
|
||||
if allow_new_preview {
|
||||
destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
|
||||
}
|
||||
pane.set_preview_item_id(Some(item.item_id()), cx)
|
||||
});
|
||||
|
||||
self.add_item(
|
||||
|
||||
@@ -2861,11 +2861,25 @@ Configuration object for defining settings profiles. Example:
|
||||
```json [settings]
|
||||
"preview_tabs": {
|
||||
"enabled": true,
|
||||
"enable_preview_from_project_panel": true,
|
||||
"enable_preview_from_file_finder": false,
|
||||
"enable_preview_from_code_navigation": false,
|
||||
"enable_preview_from_multibuffer": true,
|
||||
"enable_preview_multibuffer_from_code_navigation": false,
|
||||
"enable_preview_file_from_code_navigation": true,
|
||||
"enable_keep_preview_on_code_navigation": false,
|
||||
}
|
||||
```
|
||||
|
||||
### Enable preview from project panel
|
||||
|
||||
- Description: Determines whether to open files in preview mode when opened from the project panel with a single click.
|
||||
- Setting: `enable_preview_from_project_panel`
|
||||
- Default: `true`
|
||||
|
||||
**Options**
|
||||
|
||||
`boolean` values
|
||||
|
||||
### Enable preview from file finder
|
||||
|
||||
- Description: Determines whether to open files in preview mode when selected from the file finder.
|
||||
@@ -2876,10 +2890,40 @@ Configuration object for defining settings profiles. Example:
|
||||
|
||||
`boolean` values
|
||||
|
||||
### Enable preview from code navigation
|
||||
### Enable preview from multibuffer
|
||||
|
||||
- Description: Determines whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
|
||||
- Setting: `enable_preview_from_code_navigation`
|
||||
- Description: Determines whether to open files in preview mode when opened from a multibuffer.
|
||||
- Setting: `enable_preview_from_multibuffer`
|
||||
- Default: `true`
|
||||
|
||||
**Options**
|
||||
|
||||
`boolean` values
|
||||
|
||||
### Enable preview multibuffer from code navigation
|
||||
|
||||
- Description: Determines whether to open tabs in preview mode when code navigation is used to open a multibuffer.
|
||||
- Setting: `enable_preview_multibuffer_from_code_navigation`
|
||||
- Default: `false`
|
||||
|
||||
**Options**
|
||||
|
||||
`boolean` values
|
||||
|
||||
### Enable preview file from code navigation
|
||||
|
||||
- Description: Determines whether to open tabs in preview mode when code navigation is used to open a single file.
|
||||
- Setting: `enable_preview_file_from_code_navigation`
|
||||
- Default: `true`
|
||||
|
||||
**Options**
|
||||
|
||||
`boolean` values
|
||||
|
||||
### Enable keep preview on code navigation
|
||||
|
||||
- Description: Determines whether to keep tabs in preview mode when code navigation is used to navigate away from them. If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.
|
||||
- Setting: `enable_keep_preview_on_code_navigation`
|
||||
- Default: `false`
|
||||
|
||||
**Options**
|
||||
|
||||
Reference in New Issue
Block a user