repl: Refactor editor registration (#14819)

Cleans up action registration with the editors and also fixes a major
bug where only one workspace's panel was getting session info (due to my
not understanding that `cx.observe_new_views` is for the whole app).

Release Notes:

- N/A

Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Kyle Kelley 2024-07-19 08:58:57 -07:00 committed by GitHub
parent 4c7f1032a4
commit 5467e18a5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -6,7 +6,6 @@ use crate::{
use anyhow::{Context as _, Result};
use collections::HashMap;
use editor::{Anchor, Editor, RangeToAnchorExt};
use futures::StreamExt as _;
use gpui::{
actions, prelude::*, AppContext, AsyncWindowContext, EntityId, EventEmitter, FocusHandle,
FocusOutEvent, FocusableView, Subscription, Task, View, WeakView,
@ -46,6 +45,102 @@ pub fn init(cx: &mut AppContext) {
},
)
.detach();
cx.observe_new_views(move |editor: &mut Editor, cx: &mut ViewContext<Editor>| {
// Only allow editors that support vim mode and are singleton buffers
if !editor.use_modal_editing() || !editor.buffer().read(cx).is_singleton() {
return;
}
editor
.register_action(cx.listener(
move |editor: &mut Editor, _: &Run, cx: &mut ViewContext<Editor>| {
if !JupyterSettings::enabled(cx) {
return;
}
let Some(workspace) = editor.workspace() else {
return;
};
let Some(panel) = workspace.read(cx).panel::<RuntimePanel>(cx) else {
return;
};
let weak_editor = cx.view().downgrade();
panel.update(cx, |_, cx| {
cx.defer(|panel, cx| {
panel.run(weak_editor, cx).log_err();
});
})
},
))
.detach();
editor
.register_action(cx.listener(
move |editor: &mut Editor, _: &ClearOutputs, cx: &mut ViewContext<Editor>| {
if !JupyterSettings::enabled(cx) {
return;
}
let Some(workspace) = editor.workspace() else {
return;
};
let Some(panel) = workspace.read(cx).panel::<RuntimePanel>(cx) else {
return;
};
let weak_editor = cx.view().downgrade();
panel.update(cx, |_, cx| {
cx.defer(|panel, cx| {
panel.clear_outputs(weak_editor, cx);
});
})
},
))
.detach();
editor
.register_action(cx.listener(
move |editor: &mut Editor, _: &Interrupt, cx: &mut ViewContext<Editor>| {
if !JupyterSettings::enabled(cx) {
return;
}
let Some(workspace) = editor.workspace() else {
return;
};
let Some(panel) = workspace.read(cx).panel::<RuntimePanel>(cx) else {
return;
};
let weak_editor = cx.view().downgrade();
panel.update(cx, |_, cx| {
cx.defer(|panel, cx| {
panel.interrupt(weak_editor, cx);
});
})
},
))
.detach();
editor
.register_action(cx.listener(
move |editor: &mut Editor, _: &Shutdown, cx: &mut ViewContext<Editor>| {
if !JupyterSettings::enabled(cx) {
return;
}
let Some(workspace) = editor.workspace() else {
return;
};
let Some(panel) = workspace.read(cx).panel::<RuntimePanel>(cx) else {
return;
};
let weak_editor = cx.view().downgrade();
panel.update(cx, |_, cx| {
cx.defer(|panel, cx| {
panel.shutdown(weak_editor, cx);
});
})
},
))
.detach();
})
.detach();
}
pub struct RuntimePanel {
@ -56,14 +151,6 @@ pub struct RuntimePanel {
sessions: HashMap<EntityId, View<Session>>,
kernel_specifications: Vec<KernelSpecification>,
_subscriptions: Vec<Subscription>,
_editor_events_task: Task<()>,
}
pub enum ReplEvent {
Run(WeakView<Editor>),
ClearOutputs(WeakView<Editor>),
Interrupt(WeakView<Editor>),
Shutdown(WeakView<Editor>),
}
impl RuntimePanel {
@ -78,110 +165,14 @@ impl RuntimePanel {
let fs = workspace.app_state().fs.clone();
// Make a channel that we receive editor events on (for repl::Run, repl::ClearOutputs)
// This allows us to inject actions on the editor from the repl panel without requiring the editor to
// depend on the `repl` crate.
let (repl_editor_event_tx, mut repl_editor_event_rx) =
futures::channel::mpsc::unbounded::<ReplEvent>();
let subscriptions = vec![
cx.on_focus_in(&focus_handle, Self::focus_in),
cx.on_focus_out(&focus_handle, Self::focus_out),
cx.observe_global::<SettingsStore>(move |this, cx| {
this.set_enabled(JupyterSettings::enabled(cx), cx);
}),
cx.observe_new_views(
move |editor: &mut Editor, cx: &mut ViewContext<Editor>| {
let editor_view = cx.view().downgrade();
let run_event_tx = repl_editor_event_tx.clone();
let clear_event_tx = repl_editor_event_tx.clone();
editor
.register_action(move |_: &Run, cx: &mut WindowContext| {
if !JupyterSettings::enabled(cx) {
return;
}
run_event_tx
.unbounded_send(ReplEvent::Run(editor_view.clone()))
.ok();
})
.detach();
let editor_view = cx.view().downgrade();
editor
.register_action(
move |_: &ClearOutputs, cx: &mut WindowContext| {
if !JupyterSettings::enabled(cx) {
return;
}
clear_event_tx
.unbounded_send(ReplEvent::ClearOutputs(
editor_view.clone(),
))
.ok();
},
)
.detach();
editor
.register_action({
let editor = cx.view().downgrade();
let repl_editor_event_tx = repl_editor_event_tx.clone();
move |_: &Interrupt, cx: &mut WindowContext| {
if !JupyterSettings::enabled(cx) {
return;
}
repl_editor_event_tx
.unbounded_send(ReplEvent::Interrupt(
editor.clone(),
))
.ok();
}
})
.detach();
editor
.register_action({
let editor = cx.view().downgrade();
let repl_editor_event_tx = repl_editor_event_tx.clone();
move |_: &Shutdown, cx: &mut WindowContext| {
if !JupyterSettings::enabled(cx) {
return;
}
repl_editor_event_tx
.unbounded_send(ReplEvent::Shutdown(editor.clone()))
.ok();
}
})
.detach();
},
),
];
// Listen for events from the editor on the `repl_editor_event_rx` channel
let _editor_events_task = cx.spawn(
move |this: WeakView<RuntimePanel>, mut cx: AsyncWindowContext| async move {
while let Some(event) = repl_editor_event_rx.next().await {
this.update(&mut cx, |runtime_panel, cx| match event {
ReplEvent::Run(editor) => {
runtime_panel.run(editor, cx).log_err();
}
ReplEvent::ClearOutputs(editor) => {
runtime_panel.clear_outputs(editor, cx);
}
ReplEvent::Interrupt(editor) => {
runtime_panel.interrupt(editor, cx);
}
ReplEvent::Shutdown(editor) => {
runtime_panel.shutdown(editor, cx);
}
})
.ok();
}
},
);
let runtime_panel = Self {
fs: fs.clone(),
width: None,
@ -190,7 +181,6 @@ impl RuntimePanel {
sessions: Default::default(),
_subscriptions: subscriptions,
enabled: JupyterSettings::enabled(cx),
_editor_events_task,
};
runtime_panel