Refactor repl context menu (#14587)

This commit is contained in:
Kyle Kelley 2024-07-16 12:18:06 -07:00 committed by GitHub
parent cb6fc11abc
commit 8028e7f1b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 112 additions and 88 deletions

View file

@ -1,8 +1,9 @@
use std::time::Duration;
use gpui::{percentage, Animation, AnimationExt, AnyElement, Transformation};
use gpui::{percentage, Animation, AnimationExt, AnyElement, Transformation, View};
use repl::{
ExecutionState, JupyterSettings, Kernel, KernelSpecification, RuntimePanel, SessionSupport,
ExecutionState, JupyterSettings, Kernel, KernelSpecification, KernelStatus, RuntimePanel,
Session, SessionSupport,
};
use ui::{
prelude::*, ButtonLike, ContextMenu, IconWithIndicator, Indicator, IntoElement, PopoverMenu,
@ -16,6 +17,22 @@ use crate::QuickActionBar;
const ZED_REPL_DOCUMENTATION: &str = "https://zed.dev/docs/repl";
struct ReplMenuState {
tooltip: SharedString,
icon: IconName,
icon_color: Color,
icon_is_animating: bool,
popover_disabled: bool,
indicator: Option<Indicator>,
status: KernelStatus,
kernel_name: SharedString,
kernel_language: SharedString,
// TODO: Persist rotation state so the
// icon doesn't reset on every state change
// current_delta: Duration,
}
impl QuickActionBar {
pub fn render_repl_menu(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
if !JupyterSettings::enabled(cx) {
@ -50,7 +67,7 @@ impl QuickActionBar {
});
let session = match session {
SessionSupport::ActiveSession(session) => session.read(cx),
SessionSupport::ActiveSession(session) => session,
SessionSupport::Inactive(spec) => {
let spec = *spec;
return self.render_repl_launch_menu(spec, cx);
@ -61,101 +78,25 @@ impl QuickActionBar {
SessionSupport::Unsupported => return None,
};
let kernel_name: SharedString = session.kernel_specification.name.clone().into();
let kernel_language: SharedString = session
.kernel_specification
.kernelspec
.language
.clone()
.into();
struct ReplMenuState {
tooltip: SharedString,
icon: IconName,
icon_color: Color,
icon_is_animating: bool,
popover_disabled: bool,
indicator: Option<Indicator>,
// TODO: Persist rotation state so the
// icon doesn't reset on every state change
// current_delta: Duration,
}
impl Default for ReplMenuState {
fn default() -> Self {
Self {
tooltip: "Nothing running".into(),
icon: IconName::ReplNeutral,
icon_color: Color::Default,
icon_is_animating: false,
popover_disabled: false,
indicator: None,
// current_delta: Duration::default(),
}
}
}
let menu_state = match &session.kernel {
Kernel::RunningKernel(kernel) => match &kernel.execution_state {
ExecutionState::Idle => ReplMenuState {
tooltip: format!("Run code on {} ({})", kernel_name, kernel_language).into(),
indicator: Some(Indicator::dot().color(Color::Success)),
..Default::default()
},
ExecutionState::Busy => ReplMenuState {
tooltip: format!("Interrupt {} ({})", kernel_name, kernel_language).into(),
icon_is_animating: true,
popover_disabled: false,
indicator: None,
..Default::default()
},
},
Kernel::StartingKernel(_) => ReplMenuState {
tooltip: format!("{} is starting", kernel_name).into(),
icon_is_animating: true,
popover_disabled: true,
icon_color: Color::Muted,
indicator: Some(Indicator::dot().color(Color::Muted)),
..Default::default()
},
Kernel::ErroredLaunch(e) => ReplMenuState {
tooltip: format!("Error with kernel {}: {}", kernel_name, e).into(),
popover_disabled: false,
indicator: Some(Indicator::dot().color(Color::Error)),
..Default::default()
},
Kernel::ShuttingDown => ReplMenuState {
tooltip: format!("{} is shutting down", kernel_name).into(),
popover_disabled: true,
icon_color: Color::Muted,
indicator: Some(Indicator::dot().color(Color::Muted)),
..Default::default()
},
Kernel::Shutdown => ReplMenuState::default(),
};
let menu_state = session_state(session.clone(), cx);
let id = "repl-menu".to_string();
let element_id = |suffix| ElementId::Name(format!("{}-{}", id, suffix).into());
let kernel = &session.kernel;
let status_borrow = &kernel.status();
let status = status_borrow.clone();
let panel_clone = repl_panel.clone();
let editor_clone = editor.downgrade();
let dropdown_menu = PopoverMenu::new(element_id("menu"))
.menu(move |cx| {
let kernel_name = kernel_name.clone();
let kernel_language = kernel_language.clone();
let status = status.clone();
let panel_clone = panel_clone.clone();
let editor_clone = editor_clone.clone();
ContextMenu::build(cx, move |menu, _cx| {
let session = session.clone();
ContextMenu::build(cx, move |menu, cx| {
let menu_state = session_state(session, cx);
let status = menu_state.status;
let editor_clone = editor_clone.clone();
let panel_clone = panel_clone.clone();
let kernel_name = kernel_name.clone();
let kernel_language = kernel_language.clone();
let status = status.clone();
menu.when_else(
status.is_connected(),
|running| {
@ -166,8 +107,8 @@ impl QuickActionBar {
.child(
Label::new(format!(
"kernel: {} ({})",
kernel_name.clone(),
kernel_language.clone()
menu_state.kernel_name.clone(),
menu_state.kernel_language.clone()
))
.size(LabelSize::Small)
.color(Color::Muted),
@ -371,3 +312,86 @@ impl QuickActionBar {
)
}
}
fn session_state(session: View<Session>, cx: &WindowContext) -> ReplMenuState {
let session = session.read(cx);
let kernel_name: SharedString = session.kernel_specification.name.clone().into();
let kernel_language: SharedString = session
.kernel_specification
.kernelspec
.language
.clone()
.into();
let fill_fields = || {
ReplMenuState {
tooltip: "Nothing running".into(),
icon: IconName::ReplNeutral,
icon_color: Color::Default,
icon_is_animating: false,
popover_disabled: false,
indicator: None,
kernel_name: kernel_name.clone(),
kernel_language: kernel_language.clone(),
// todo!(): Technically not shutdown, but indeterminate
status: KernelStatus::Shutdown,
// current_delta: Duration::default(),
}
};
let menu_state = match &session.kernel {
Kernel::RunningKernel(kernel) => match &kernel.execution_state {
ExecutionState::Idle => ReplMenuState {
tooltip: format!("Run code on {} ({})", kernel_name, kernel_language).into(),
indicator: Some(Indicator::dot().color(Color::Success)),
status: session.kernel.status(),
..fill_fields()
},
ExecutionState::Busy => ReplMenuState {
tooltip: format!("Interrupt {} ({})", kernel_name, kernel_language).into(),
icon_is_animating: true,
popover_disabled: false,
indicator: None,
status: session.kernel.status(),
..fill_fields()
},
},
Kernel::StartingKernel(_) => ReplMenuState {
tooltip: format!("{} is starting", kernel_name).into(),
icon_is_animating: true,
popover_disabled: true,
icon_color: Color::Muted,
indicator: Some(Indicator::dot().color(Color::Muted)),
status: session.kernel.status(),
..fill_fields()
},
Kernel::ErroredLaunch(e) => ReplMenuState {
tooltip: format!("Error with kernel {}: {}", kernel_name, e).into(),
popover_disabled: false,
indicator: Some(Indicator::dot().color(Color::Error)),
status: session.kernel.status(),
..fill_fields()
},
Kernel::ShuttingDown => ReplMenuState {
tooltip: format!("{} is shutting down", kernel_name).into(),
popover_disabled: true,
icon_color: Color::Muted,
indicator: Some(Indicator::dot().color(Color::Muted)),
status: session.kernel.status(),
..fill_fields()
},
Kernel::Shutdown => ReplMenuState {
tooltip: "Nothing running".into(),
icon: IconName::ReplNeutral,
icon_color: Color::Default,
icon_is_animating: false,
popover_disabled: false,
indicator: None,
status: KernelStatus::Shutdown,
..fill_fields()
},
};
menu_state
}