Add REPL dropdown menu to toolbar (#14493)

TODO: 


- [x] Actions run from menu not firing
- [x] Menu differentiates idle and busy for running kernel

Menu States:
- [x] No session && no support known

No session && no kernel installed for languages of known support
- (TODO after) Intro to REPL
- [x] Link to docs

No session but can start one
- [x] Start REPL
- (TODO after) More info -> Docs?

Yes Session

- [x] Info: Kernel name, language
  example: chatlab-3.7-adsf87fsa (Python)
  example: condapy-3.7 (Python)
- [x] Change Kernel -> https://zed.dev/docs/repl#change-kernel
- ---
- [x] Run
- [x] Interrupt
- [x] Clear Outputs
- ---
- [x] Shutdown


(Release notes left empty as the change will be documented in the REPL
release!)

Reserved for a follow on PR:

```
- [ ] Status should update when the menu is open (missing `cx.notify`?)
- [ ] Shutdown all kernels action
- [ ] Restart action
- [ ] [Default kernel changed - restart (this kernel) to apply] // todo!(kyle): need some kind of state thing that says if this has happened
```


Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
Co-authored-by: Kyle Kelley <rgbkrk@gmail.com>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
Nate Butler 2024-07-15 14:55:49 -04:00 committed by GitHub
parent 1856320516
commit fa3d29087d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 489 additions and 71 deletions

View file

@ -81,6 +81,52 @@ pub enum Kernel {
Shutdown,
}
#[derive(Debug, Clone)]
pub enum KernelStatus {
Idle,
Busy,
Starting,
Error,
ShuttingDown,
Shutdown,
}
impl KernelStatus {
pub fn is_connected(&self) -> bool {
match self {
KernelStatus::Idle | KernelStatus::Busy => true,
_ => false,
}
}
}
impl ToString for KernelStatus {
fn to_string(&self) -> String {
match self {
KernelStatus::Idle => "Idle".to_string(),
KernelStatus::Busy => "Busy".to_string(),
KernelStatus::Starting => "Starting".to_string(),
KernelStatus::Error => "Error".to_string(),
KernelStatus::ShuttingDown => "Shutting Down".to_string(),
KernelStatus::Shutdown => "Shutdown".to_string(),
}
}
}
impl From<&Kernel> for KernelStatus {
fn from(kernel: &Kernel) -> Self {
match kernel {
Kernel::RunningKernel(kernel) => match kernel.execution_state {
ExecutionState::Idle => KernelStatus::Idle,
ExecutionState::Busy => KernelStatus::Busy,
},
Kernel::StartingKernel(_) => KernelStatus::Starting,
Kernel::ErroredLaunch(_) => KernelStatus::Error,
Kernel::ShuttingDown => KernelStatus::ShuttingDown,
Kernel::Shutdown => KernelStatus::Shutdown,
}
}
}
impl Kernel {
pub fn dot(&self) -> Indicator {
match self {
@ -95,6 +141,10 @@ impl Kernel {
}
}
pub fn status(&self) -> KernelStatus {
self.into()
}
pub fn set_execution_state(&mut self, status: &ExecutionState) {
match self {
Kernel::RunningKernel(running_kernel) => {

View file

@ -12,7 +12,7 @@ mod stdio;
pub use jupyter_settings::JupyterSettings;
pub use kernels::{Kernel, KernelSpecification};
pub use runtime_panel::Run;
pub use runtime_panel::{ClearOutputs, Interrupt, Run, Shutdown};
pub use runtime_panel::{RuntimePanel, SessionSupport};
pub use runtimelib::ExecutionState;
pub use session::Session;

View file

@ -23,7 +23,7 @@ use workspace::{
Workspace,
};
actions!(repl, [Run, ClearOutputs]);
actions!(repl, [Run, ClearOutputs, Interrupt, Shutdown]);
actions!(repl_panel, [ToggleFocus]);
pub fn init(cx: &mut AppContext) {
@ -51,6 +51,8 @@ pub struct RuntimePanel {
pub enum ReplEvent {
Run(WeakView<Editor>),
ClearOutputs(WeakView<Editor>),
Interrupt(WeakView<Editor>),
Shutdown(WeakView<Editor>),
}
impl RuntimePanel {
@ -108,6 +110,40 @@ impl RuntimePanel {
},
)
.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();
},
),
];
@ -123,6 +159,12 @@ impl RuntimePanel {
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();
}
@ -320,11 +362,31 @@ impl RuntimePanel {
cx.notify();
}
}
pub fn interrupt(&mut self, editor: WeakView<Editor>, cx: &mut ViewContext<Self>) {
let entity_id = editor.entity_id();
if let Some(session) = self.sessions.get_mut(&entity_id) {
session.update(cx, |session, cx| {
session.interrupt(cx);
});
cx.notify();
}
}
pub fn shutdown(&self, editor: WeakView<Editor>, cx: &mut ViewContext<RuntimePanel>) {
let entity_id = editor.entity_id();
if let Some(session) = self.sessions.get(&entity_id) {
session.update(cx, |session, cx| {
session.shutdown(cx);
});
cx.notify();
}
}
}
pub enum SessionSupport {
ActiveSession(View<Session>),
Inactive(KernelSpecification),
Inactive(Box<KernelSpecification>),
RequiresSetup(Arc<str>),
Unsupported,
}
@ -350,7 +412,7 @@ impl RuntimePanel {
let kernelspec = self.kernelspec(&language, cx);
match kernelspec {
Some(kernelspec) => SessionSupport::Inactive(kernelspec),
Some(kernelspec) => SessionSupport::Inactive(Box::new(kernelspec)),
None => {
// If no kernelspec but language is one of typescript or python
// then we return RequiresSetup