Stick REPL icon in quick action bar (#14064)

REPL Quick Actions

<img width="325" alt="image"
src="https://github.com/zed-industries/zed/assets/836375/faaf4c8f-ef12-4417-a9dd-158d5beae8ba">

When the Jupyter REPL is enabled and a kernel is available, show the
status in the editor bar:

![quick action bar
repl](https://github.com/zed-industries/zed/assets/836375/f3445283-f1fc-4714-895b-7aa842d4ab76)


Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
This commit is contained in:
Kyle Kelley 2024-07-10 09:20:52 -07:00 committed by GitHub
parent 9282bf97ae
commit 896b9bda23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 345 additions and 16 deletions

View file

@ -20,6 +20,7 @@ search.workspace = true
settings.workspace = true
ui.workspace = true
workspace.workspace = true
repl.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }

View file

@ -20,8 +20,11 @@ use workspace::{
item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
};
mod repl_menu;
pub struct QuickActionBar {
buffer_search_bar: View<BufferSearchBar>,
repl_menu: Option<View<ContextMenu>>,
toggle_settings_menu: Option<View<ContextMenu>>,
toggle_selections_menu: Option<View<ContextMenu>>,
active_item: Option<Box<dyn ItemHandle>>,
@ -40,6 +43,7 @@ impl QuickActionBar {
buffer_search_bar,
toggle_settings_menu: None,
toggle_selections_menu: None,
repl_menu: None,
active_item: None,
_inlay_hints_enabled_subscription: None,
workspace: workspace.weak_handle(),
@ -290,9 +294,13 @@ impl Render for QuickActionBar {
.child(
h_flex()
.gap(Spacing::Medium.rems(cx))
.children(self.render_repl_menu(cx))
.children(editor_selections_dropdown)
.child(editor_settings_dropdown),
)
.when_some(self.repl_menu.as_ref(), |el, repl_menu| {
el.child(Self::render_menu_overlay(repl_menu))
})
.when_some(
self.toggle_settings_menu.as_ref(),
|el, toggle_settings_menu| {

View file

@ -0,0 +1,116 @@
use gpui::AnyElement;
use repl::{
ExecutionState, JupyterSettings, Kernel, KernelSpecification, RuntimePanel, Session,
SessionSupport,
};
use ui::{prelude::*, ButtonLike, IconWithIndicator, IntoElement, Tooltip};
use crate::QuickActionBar;
const ZED_REPL_DOCUMENTATION: &str = "https://zed.dev/docs/repl";
impl QuickActionBar {
pub fn render_repl_menu(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
if !JupyterSettings::enabled(cx) {
return None;
}
let workspace = self.workspace.upgrade()?.read(cx);
let (editor, repl_panel) = if let (Some(editor), Some(repl_panel)) =
(self.active_editor(), workspace.panel::<RuntimePanel>(cx))
{
(editor, repl_panel)
} else {
return None;
};
let session = repl_panel.update(cx, |repl_panel, cx| {
repl_panel.session(editor.downgrade(), cx)
});
let session = match session {
SessionSupport::ActiveSession(session) => session.read(cx),
SessionSupport::Inactive(spec) => {
return self.render_repl_launch_menu(spec, cx);
}
SessionSupport::RequiresSetup(language) => {
return self.render_repl_setup(&language, cx);
}
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();
let tooltip = |session: &Session| match &session.kernel {
Kernel::RunningKernel(kernel) => match &kernel.execution_state {
ExecutionState::Idle => {
format!("Run code on {} ({})", kernel_name, kernel_language)
}
ExecutionState::Busy => format!("Interrupt {} ({})", kernel_name, kernel_language),
},
Kernel::StartingKernel(_) => format!("{} is starting", kernel_name),
Kernel::ErroredLaunch(e) => format!("Error with kernel {}: {}", kernel_name, e),
Kernel::ShuttingDown => format!("{} is shutting down", kernel_name),
Kernel::Shutdown => "Nothing running".to_string(),
};
let tooltip_text: SharedString = SharedString::from(tooltip(&session).clone());
let button = ButtonLike::new("toggle_repl_icon")
.child(
IconWithIndicator::new(Icon::new(IconName::Play), Some(session.kernel.dot()))
.indicator_border_color(Some(cx.theme().colors().border)),
)
.size(ButtonSize::Compact)
.style(ButtonStyle::Subtle)
.tooltip(move |cx| Tooltip::text(tooltip_text.clone(), cx))
.on_click(|_, cx| cx.dispatch_action(Box::new(repl::Run {})))
.on_click(|_, cx| cx.dispatch_action(Box::new(repl::Run {})))
.into_any_element();
Some(button)
}
pub fn render_repl_launch_menu(
&self,
kernel_specification: KernelSpecification,
_cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let tooltip: SharedString =
SharedString::from(format!("Start REPL for {}", kernel_specification.name));
Some(
IconButton::new("toggle_repl_icon", IconName::Play)
.size(ButtonSize::Compact)
.icon_color(Color::Muted)
.style(ButtonStyle::Subtle)
.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
.on_click(|_, cx| cx.dispatch_action(Box::new(repl::Run {})))
.into_any_element(),
)
}
pub fn render_repl_setup(
&self,
language: &str,
_cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let tooltip: SharedString = SharedString::from(format!("Setup Zed REPL for {}", language));
Some(
IconButton::new("toggle_repl_icon", IconName::Play)
.size(ButtonSize::Compact)
.icon_color(Color::Muted)
.style(ButtonStyle::Subtle)
.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
.on_click(|_, cx| cx.open_url(ZED_REPL_DOCUMENTATION))
.into_any_element(),
)
}
}