Skip to content

Commit b9454bb

Browse files
HactarCEsmitbarmasecole-miller
authored andcommitted
Add more preview tab settings and fix janky behavior (zed-industries#43921)
Closes zed-industries#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 <[email protected]> Co-authored-by: Cole Miller <[email protected]>
1 parent eab10a9 commit b9454bb

File tree

14 files changed

+478
-135
lines changed

14 files changed

+478
-135
lines changed

assets/settings/default.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,13 +1100,22 @@
11001100
"preview_tabs": {
11011101
// Whether preview tabs should be enabled.
11021102
// Preview tabs allow you to open files in preview mode, where they close automatically
1103-
// when you switch to another file unless you explicitly pin them.
1103+
// when you open another preview tab.
11041104
// This is useful for quickly viewing files without cluttering your workspace.
11051105
"enabled": true,
1106+
// Whether to open tabs in preview mode when opened from the project panel with a single click.
1107+
"enable_preview_from_project_panel": true,
11061108
// Whether to open tabs in preview mode when selected from the file finder.
11071109
"enable_preview_from_file_finder": false,
1108-
// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab.
1109-
"enable_preview_from_code_navigation": false
1110+
// Whether to open tabs in preview mode when opened from a multibuffer.
1111+
"enable_preview_from_multibuffer": true,
1112+
// Whether to open tabs in preview mode when code navigation is used to open a multibuffer.
1113+
"enable_preview_multibuffer_from_code_navigation": false,
1114+
// Whether to open tabs in preview mode when code navigation is used to open a single file.
1115+
"enable_preview_file_from_code_navigation": true,
1116+
// Whether to keep tabs in preview mode when code navigation is used to navigate away from them.
1117+
// 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.
1118+
"enable_keep_preview_on_code_navigation": false
11101119
},
11111120
// Settings related to the file finder.
11121121
"file_finder": {

crates/editor/src/editor.rs

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17012,7 +17012,9 @@ impl Editor {
1701217012
})
1701317013
.collect();
1701417014

17015-
let workspace = self.workspace();
17015+
let Some(workspace) = self.workspace() else {
17016+
return Task::ready(Ok(Navigated::No));
17017+
};
1701617018

1701717019
cx.spawn_in(window, async move |editor, cx| {
1701817020
let locations: Vec<Location> = future::join_all(definitions)
@@ -17038,10 +17040,6 @@ impl Editor {
1703817040
}
1703917041

1704017042
if num_locations > 1 {
17041-
let Some(workspace) = workspace else {
17042-
return Ok(Navigated::No);
17043-
};
17044-
1704517043
let tab_kind = match kind {
1704617044
Some(GotoDefinitionKind::Implementation) => "Implementations",
1704717045
Some(GotoDefinitionKind::Symbol) | None => "Definitions",
@@ -17073,11 +17071,14 @@ impl Editor {
1707317071

1707417072
let opened = workspace
1707517073
.update_in(cx, |workspace, window, cx| {
17074+
let allow_preview = PreviewTabsSettings::get_global(cx)
17075+
.enable_preview_multibuffer_from_code_navigation;
1707617076
Self::open_locations_in_multibuffer(
1707717077
workspace,
1707817078
locations,
1707917079
title,
1708017080
split,
17081+
allow_preview,
1708117082
MultibufferSelectionMode::First,
1708217083
window,
1708317084
cx,
@@ -17094,10 +17095,9 @@ impl Editor {
1709417095
Ok(Navigated::Yes)
1709517096
}
1709617097
Some(Either::Right(path)) => {
17097-
let Some(workspace) = workspace else {
17098-
return Ok(Navigated::No);
17099-
};
17100-
17098+
// TODO(andrew): respect preview tab settings
17099+
// `enable_keep_preview_on_code_navigation` and
17100+
// `enable_preview_file_from_code_navigation`
1710117101
workspace
1710217102
.update_in(cx, |workspace, window, cx| {
1710317103
workspace.open_resolved_path(path, window, cx)
@@ -17108,10 +17108,6 @@ impl Editor {
1710817108
None => Ok(Navigated::No),
1710917109
}
1711017110
} else {
17111-
let Some(workspace) = workspace else {
17112-
return Ok(Navigated::No);
17113-
};
17114-
1711517111
let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
1711617112
let target_range = target_ranges.first().unwrap().clone();
1711717113

@@ -17135,11 +17131,19 @@ impl Editor {
1713517131
workspace.active_pane().clone()
1713617132
};
1713717133

17134+
let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
17135+
let keep_old_preview = preview_tabs_settings
17136+
.enable_keep_preview_on_code_navigation;
17137+
let allow_new_preview = preview_tabs_settings
17138+
.enable_preview_file_from_code_navigation;
17139+
1713817140
workspace.open_project_item(
1713917141
pane,
1714017142
target_buffer.clone(),
1714117143
true,
1714217144
true,
17145+
keep_old_preview,
17146+
allow_new_preview,
1714317147
window,
1714417148
cx,
1714517149
)
@@ -17416,11 +17420,14 @@ impl Editor {
1741617420
} else {
1741717421
format!("References to {target}")
1741817422
};
17423+
let allow_preview = PreviewTabsSettings::get_global(cx)
17424+
.enable_preview_multibuffer_from_code_navigation;
1741917425
Self::open_locations_in_multibuffer(
1742017426
workspace,
1742117427
locations,
1742217428
title,
1742317429
false,
17430+
allow_preview,
1742417431
MultibufferSelectionMode::First,
1742517432
window,
1742617433
cx,
@@ -17436,6 +17443,7 @@ impl Editor {
1743617443
locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
1743717444
title: String,
1743817445
split: bool,
17446+
allow_preview: bool,
1743917447
multibuffer_selection_mode: MultibufferSelectionMode,
1744017448
window: &mut Window,
1744117449
cx: &mut Context<Workspace>,
@@ -17483,6 +17491,7 @@ impl Editor {
1748317491
.is_some_and(|it| *it == key)
1748417492
})
1748517493
});
17494+
let was_existing = existing.is_some();
1748617495
let editor = existing.unwrap_or_else(|| {
1748717496
cx.new(|cx| {
1748817497
let mut editor = Editor::for_multibuffer(
@@ -17523,29 +17532,23 @@ impl Editor {
1752317532
});
1752417533

1752517534
let item = Box::new(editor);
17526-
let item_id = item.item_id();
17527-
17528-
if split {
17529-
let pane = workspace.adjacent_pane(window, cx);
17530-
workspace.add_item(pane, item, None, true, true, window, cx);
17531-
} else if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
17532-
let (preview_item_id, preview_item_idx) =
17533-
workspace.active_pane().read_with(cx, |pane, _| {
17534-
(pane.preview_item_id(), pane.preview_item_idx())
17535-
});
1753617535

17537-
workspace.add_item_to_active_pane(item, preview_item_idx, true, window, cx);
17536+
let pane = if split {
17537+
workspace.adjacent_pane(window, cx)
17538+
} else {
17539+
workspace.active_pane().clone()
17540+
};
17541+
let activate_pane = split;
1753817542

17539-
if let Some(preview_item_id) = preview_item_id {
17540-
workspace.active_pane().update(cx, |pane, cx| {
17541-
pane.remove_item(preview_item_id, false, false, window, cx);
17542-
});
17543+
let mut destination_index = None;
17544+
pane.update(cx, |pane, cx| {
17545+
if allow_preview && !was_existing {
17546+
destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
1754317547
}
17544-
} else {
17545-
workspace.add_item_to_active_pane(item, None, true, window, cx);
17546-
}
17547-
workspace.active_pane().update(cx, |pane, cx| {
17548-
pane.set_preview_item_id(Some(item_id), cx);
17548+
if was_existing && !allow_preview {
17549+
pane.unpreview_item_if_preview(item.item_id());
17550+
}
17551+
pane.add_item(item, activate_pane, true, destination_index, window, cx);
1754917552
});
1755017553
}
1755117554

@@ -20783,6 +20786,7 @@ impl Editor {
2078320786
locations,
2078420787
format!("Selections for '{title}'"),
2078520788
false,
20789+
false,
2078620790
MultibufferSelectionMode::All,
2078720791
window,
2078820792
cx,
@@ -22002,29 +22006,40 @@ impl Editor {
2200222006
// Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
2200322007
// so `workspace.open_project_item` will never find them, always opening a new editor.
2200422008
// Instead, we try to activate the existing editor in the pane first.
22005-
let (editor, pane_item_index) =
22009+
let (editor, pane_item_index, pane_item_id) =
2200622010
pane.read(cx).items().enumerate().find_map(|(i, item)| {
2200722011
let editor = item.downcast::<Editor>()?;
2200822012
let singleton_buffer =
2200922013
editor.read(cx).buffer().read(cx).as_singleton()?;
2201022014
if singleton_buffer == buffer {
22011-
Some((editor, i))
22015+
Some((editor, i, item.item_id()))
2201222016
} else {
2201322017
None
2201422018
}
2201522019
})?;
2201622020
pane.update(cx, |pane, cx| {
22017-
pane.activate_item(pane_item_index, true, true, window, cx)
22021+
pane.activate_item(pane_item_index, true, true, window, cx);
22022+
if !PreviewTabsSettings::get_global(cx)
22023+
.enable_preview_from_multibuffer
22024+
{
22025+
pane.unpreview_item_if_preview(pane_item_id);
22026+
}
2201822027
});
2201922028
Some(editor)
2202022029
})
2202122030
.flatten()
2202222031
.unwrap_or_else(|| {
22032+
let keep_old_preview = PreviewTabsSettings::get_global(cx)
22033+
.enable_keep_preview_on_code_navigation;
22034+
let allow_new_preview =
22035+
PreviewTabsSettings::get_global(cx).enable_preview_from_multibuffer;
2202322036
workspace.open_project_item::<Self>(
2202422037
pane.clone(),
2202522038
buffer,
2202622039
true,
2202722040
true,
22041+
keep_old_preview,
22042+
allow_new_preview,
2202822043
window,
2202922044
cx,
2203022045
)

crates/migrator/src/migrations.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,9 @@ pub(crate) mod m_2025_11_25 {
153153

154154
pub(crate) use settings::remove_context_server_source;
155155
}
156+
157+
pub(crate) mod m_2025_12_01 {
158+
mod settings;
159+
160+
pub(crate) use settings::SETTINGS_PATTERNS;
161+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::ops::Range;
2+
use tree_sitter::{Query, QueryMatch};
3+
4+
use crate::MigrationPatterns;
5+
use crate::patterns::SETTINGS_NESTED_KEY_VALUE_PATTERN;
6+
7+
pub const SETTINGS_PATTERNS: MigrationPatterns = &[(
8+
SETTINGS_NESTED_KEY_VALUE_PATTERN,
9+
rename_enable_preview_from_code_navigation_setting,
10+
)];
11+
12+
fn rename_enable_preview_from_code_navigation_setting(
13+
contents: &str,
14+
mat: &QueryMatch,
15+
query: &Query,
16+
) -> Option<(Range<usize>, String)> {
17+
if !is_enable_preview_from_code_navigation(contents, mat, query) {
18+
return None;
19+
}
20+
21+
let setting_name_ix = query.capture_index_for_name("setting_name")?;
22+
let setting_name_range = mat
23+
.nodes_for_capture_index(setting_name_ix)
24+
.next()?
25+
.byte_range();
26+
27+
Some((
28+
setting_name_range,
29+
"enable_keep_preview_on_code_navigation".to_string(),
30+
))
31+
}
32+
33+
fn is_enable_preview_from_code_navigation(contents: &str, mat: &QueryMatch, query: &Query) -> bool {
34+
let parent_key_ix = match query.capture_index_for_name("parent_key") {
35+
Some(ix) => ix,
36+
None => return false,
37+
};
38+
let parent_range = match mat.nodes_for_capture_index(parent_key_ix).next() {
39+
Some(node) => node.byte_range(),
40+
None => return false,
41+
};
42+
if contents.get(parent_range) != Some("preview_tabs") {
43+
return false;
44+
}
45+
46+
let setting_name_ix = match query.capture_index_for_name("setting_name") {
47+
Some(ix) => ix,
48+
None => return false,
49+
};
50+
let setting_name_range = match mat.nodes_for_capture_index(setting_name_ix).next() {
51+
Some(node) => node.byte_range(),
52+
None => return false,
53+
};
54+
contents.get(setting_name_range) == Some("enable_preview_from_code_navigation")
55+
}

0 commit comments

Comments
 (0)