Name the closure's own context where the framework now demands one

Stage 4 of decision 0023, finished. The dependency swap itself compiled the
whole closure on the first attempt; what did not compile was `apps/lumbridge`,
and the entire API delta across a ten-month jump turned out to be three
signatures over 19 call sites.

`Window::focus` is now `focus(&mut self, handle: &FocusHandle, cx: &mut App)`
-- 16 sites. `flex_shrink()` takes the factor it used to assume, so it is
`flex_shrink(1.0)` -- 2 sites. `Application::new()` is gone with the rest of
the entry point, and is `gpui_platform::application()` -- 1 site. That is all
of it. Nothing else in the framework's surface moved under this product in ten
months.

The interesting failure was in the fix, not the framework. Adding `, cx` to
every `window.focus` site is right in a method that owns a `cx` parameter and
wrong inside `cx.listener(...)`, whose closure is handed its own context as a
fourth argument that this code discarded as `_`. One such site capturing the
outer `cx` produced 35 errors -- E0596, two E0521s, an E0500, thirty E0501s and
two E0524s -- none of which named the actual mistake, and all of which vanished
when the discarded parameter was given the name it should have had. Worth
recording: a borrow-checker avalanche after a dependency bump is far more
likely to be one wrong capture than a framework that changed its ownership
model.

No crate outside `apps/lumbridge` needed a single change, because none of them
imports a gpui type. That boundary was not designed for this and paid for
itself anyway.

`scripts/workspace-guard.sh` now asserts what it exists to assert. It compared
`gpui` against version 0.2.2 from the crates.io registry, which after the swap
was a guard that would have failed on the correct state of the tree. It now
checks that `gpui` and `gpui_platform` both carry
git+https://github.com/zed-industries/zed.git at rev ce48461e -- the same rev,
because the pair are one framework snapshot and a disagreement between them
would compile against two. Their versions (0.2.2 and 0.1.0) are deliberately
not asserted; neither has been bumped upstream in ten months and neither means
anything. Both failure modes were induced before being trusted: a wrong
expected rev fails on both packages, and a lockfile where only gpui_platform's
rev is altered fails on that package alone.

One thing to fix next, not here: `cargo deny check sources` now fails. Decision
0023 predicted the move would bring two Git sources; the resolved graph brings
four. wasm_thread (via gpui_web and scheduler) and xim-rs, which supplies
zed-xim, xim-ctext and xim-parser to gpui_linux, are both unallowed, and
zed-industries/scap is in the lockfile too. deny.toml is named in AGENTS.md as
a file a decision record governs, so it is left alone and reported rather than
edited underneath this commit.

The UI gate is green: workspace-guard, fmt, clippy with -D warnings over
--all-targets --all-features, and 79 tests passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPYebLiN2w4TqnHUYGdECq
This commit is contained in:
Metal Agent
2026-09-01 13:22:50 -07:00
co-authored by Claude Opus 5
parent 378e3eb0eb
commit 3cb5e1002c
5 changed files with 1683 additions and 1677 deletions
+24 -20
View File
@@ -16,9 +16,13 @@ use std::collections::BTreeMap;
use std::time::Duration;
use gpui::{
App, Application, Bounds, Context, FocusHandle, FontWeight, KeyDownEvent, Pixels, Rgba, Size,
Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, relative, rgb, size,
App, Bounds, Context, FocusHandle, FontWeight, KeyDownEvent, Pixels, Rgba, Size, Window,
WindowBounds, WindowOptions, actions, div, prelude::*, px, relative, rgb, size,
};
// The application entry point lives in the platform crate at this revision, not
// in gpui itself: the backends moved out of the publishable crate, and the
// constructor moved with them. See decision 0023.
use gpui_platform::application;
use lumbridge_core::UsageProvenance;
use lumbridge_runtime::{
CommandConfig, PtyOptions, RuntimeActorError, RuntimeActorOptions, RuntimeCommand,
@@ -466,7 +470,7 @@ impl LumbridgeShell {
impl LumbridgeShell {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let root_focus = cx.focus_handle();
window.focus(&root_focus);
window.focus(&root_focus, cx);
Self::drive_synthetic_surfaces(cx);
Self::poll_runtime_events(cx);
@@ -765,7 +769,7 @@ impl LumbridgeShell {
if self.panels.select(pane) {
self.persist_panels();
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -785,7 +789,7 @@ impl LumbridgeShell {
if self.panels.select(pane) {
self.persist_panels();
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -806,7 +810,7 @@ impl LumbridgeShell {
if self.panels.select(pane) {
self.persist_panels();
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -815,7 +819,7 @@ impl LumbridgeShell {
if self.panels.select_attached_at(index) {
self.persist_panels();
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -835,7 +839,7 @@ impl LumbridgeShell {
self.persist_panels();
self.resize_terminal_for_workspace(window, cx);
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -858,7 +862,7 @@ impl LumbridgeShell {
self.add_panel_chooser_open = false;
self.persist_panels();
self.resize_terminal_for_workspace(window, cx);
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -868,7 +872,7 @@ impl LumbridgeShell {
self.persist_panels();
self.resize_terminal_for_workspace(window, cx);
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -906,7 +910,7 @@ impl LumbridgeShell {
self.live_terminals.insert(pane, terminal);
self.attention.clear(pane);
self.pending_terminate = None;
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -935,7 +939,7 @@ impl LumbridgeShell {
Err(error) => LiveRuntimeStatus::Fault(error.to_string()),
};
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -977,7 +981,7 @@ impl LumbridgeShell {
Some(_) => None,
None => Some(Page::UsageAndQuota),
};
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -1096,7 +1100,7 @@ impl LumbridgeShell {
let rows = sidebar::model::flatten(&self.sidebar_input(), &self.sidebar);
self.sidebar.cursor = sidebar::model::move_cursor(&rows, None, 1);
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -1174,7 +1178,7 @@ impl LumbridgeShell {
fn add_panel_action(&mut self, _: &AddPanel, window: &mut Window, cx: &mut Context<Self>) {
self.dispatch(ShellAction::CloseCommandPalette);
self.add_panel_chooser_open = !self.add_panel_chooser_open;
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -1192,7 +1196,7 @@ impl LumbridgeShell {
if self.panels.move_horizontal(delta) {
self.persist_panels();
}
window.focus(&self.root_focus);
window.focus(&self.root_focus, cx);
cx.notify();
}
@@ -2591,7 +2595,7 @@ impl LumbridgeShell {
.on_click(cx.listener(|shell, _, window, cx| {
shell.dispatch(ShellAction::CloseCommandPalette);
shell.add_panel_chooser_open = !shell.add_panel_chooser_open;
window.focus(&shell.root_focus);
window.focus(&shell.root_focus, cx);
cx.notify();
})),
)
@@ -2622,8 +2626,8 @@ impl LumbridgeShell {
.relative()
.track_focus(&self.root_focus)
.key_context("LumbridgeShell")
.on_click(cx.listener(|shell, _, window, _| {
window.focus(&shell.root_focus);
.on_click(cx.listener(|shell, _, window, cx| {
window.focus(&shell.root_focus, cx);
}))
.on_action(cx.listener(Self::focus_left))
.on_action(cx.listener(Self::focus_right))
@@ -3429,7 +3433,7 @@ fn spawn_live_runtime(
}
fn main() {
Application::new().run(|cx: &mut App| {
application().run(|cx: &mut App| {
cx.bind_keys(keymap::bindings());
let bounds = Bounds::centered(None, size(px(1500.0), px(960.0)), cx);
+2 -2
View File
@@ -198,7 +198,7 @@ pub(crate) fn row(row: &SidebarRow, style: &RowStyle) -> gpui::AnyElement {
// that says which pane to look at.
.child(
div()
.flex_shrink()
.flex_shrink(1.0)
.min_w(px(0.0))
.truncate()
.text_xs()
@@ -261,7 +261,7 @@ pub(crate) fn row(row: &SidebarRow, style: &RowStyle) -> gpui::AnyElement {
)
.child(
div()
.flex_shrink()
.flex_shrink(1.0)
.min_w(px(0.0))
.truncate()
.text_xs()