editor: Add in_preview boolean context for keybindings (#61777)

# Objective

- Add an `in_preview` context that is available when the current editor
is in preview mode (opened from project panel or go to definition).
- closes https://github.com/zed-industries/zed/discussions/61765

## Solution

- In `editor.rs`, added a check in `key_context()` that detects if the
editor is the active preview item in its pane, and if so, adds
`"in_preview"` to the key context.
- Uses existing `workspace.pane_for_item_id()` and
`pane.is_active_preview_item()` methods — no new APIs introduced.

## Testing

- Added two tests in `editor_tests.rs`:
- `test_in_preview_context_added_when_in_preview_mode` — verifies
`in_preview` is present when editor is set as preview item.
- `test_in_preview_context_not_added_when_not_in_preview_mode` —
verifies `in_preview` is absent for regular (non-preview) tabs.
- Tested on Linux.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Added `in_preview` keybinding context for when an editor is in preview
mode.
This commit is contained in:
Nikita Demin authored and GitHub committed 2026-08-19 21:35:38 +00:00
1 parent 1155a5becd
commit 459fe185a4
3 files changed
+85 -3

No files matched your search

+8
View File
@@ -2787,6 +2787,14 @@ impl Editor {
key_context.add("diffs_expanded");
}
if self
.nav_history
.as_ref()
.is_some_and(ItemNavHistory::is_preview_item)
{
key_context.add("in_preview");
}
key_context
}
+71
View File
@@ -43364,3 +43364,74 @@ async fn test_select_delimiters_expansion(cx: &mut TestAppContext) {
cx.dispatch_action(SelectInsideDelimiters);
cx.assert_editor_state("foo(«x, { a: 1 }ˇ»);");
}
#[gpui::test]
async fn test_in_preview_key_context(cx: &mut TestAppContext) {
init_test(cx, |_| {});
cx.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings(cx, |settings: &mut SettingsContent| {
settings.preview_tabs.get_or_insert_default().enabled = Some(true);
});
});
});
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
path!("/root"),
serde_json::json!({
"test.txt": "hello",
}),
)
.await;
let project = Project::test(fs, [path!("/root").as_ref()], cx).await;
let (multi_workspace, cx) =
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id()
})
});
let editor = workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path_preview(
(worktree_id, rel_path("test.txt")),
None,
true,
true,
true,
window,
cx,
)
})
.unwrap()
.await
.downcast::<Editor>()
.unwrap();
let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
pane.read_with(cx, |pane, _| {
assert!(pane.is_active_preview_item(editor.entity_id()));
});
cx.focus(&editor);
editor.update_in(cx, |editor, window, cx| {
assert!(
editor.key_context(window, cx).contains("in_preview"),
"Expected 'in_preview' context when the editor is the pane's preview item"
);
});
pane.update(cx, |pane, _cx| {
pane.unpreview_item_if_preview(editor.entity_id());
});
editor.update_in(cx, |editor, window, cx| {
assert!(
!editor.key_context(window, cx).contains("in_preview"),
"Expected no 'in_preview' context after the tab is unpreviewed"
);
});
}
+6 -3
View File
@@ -4661,6 +4661,10 @@ impl Render for Pane {
}
impl ItemNavHistory {
pub fn is_preview_item(&self) -> bool {
self.history.0.lock().preview_item_id == Some(self.item.id())
}
pub fn push<D: 'static + Any + Send + Sync>(
&mut self,
data: Option<D>,
@@ -4672,19 +4676,18 @@ impl ItemNavHistory {
.upgrade()
.is_some_and(|item| item.include_in_nav_history())
{
let is_preview_item = self.history.0.lock().preview_item_id == Some(self.item.id());
let is_preview_item = self.is_preview_item();
self.history
.push(data, self.item.clone(), is_preview_item, row, cx);
}
}
pub fn navigation_entry(&self, data: Option<Arc<dyn Any + Send + Sync>>) -> NavigationEntry {
let is_preview_item = self.history.0.lock().preview_item_id == Some(self.item.id());
NavigationEntry {
item: self.item.clone(),
data,
timestamp: 0,
is_preview: is_preview_item,
is_preview: self.is_preview_item(),
row: None,
}
}