repl: Improve kernelspec discoverability (#15886)
<img width="862" alt="image" src="https://github.com/user-attachments/assets/ae8c479d-d9f9-4c46-bb1a-be411ab07876"> Release Notes: - Added additional context about available to kernel sessions - Fixed bug in kernelspec launch choosing first available kernel matching the language rather than selected name --------- Co-authored-by: Jason <jason@zed.dev>
This commit is contained in:
parent
a54e16b7ea
commit
6065db174a
3 changed files with 97 additions and 30 deletions
|
@ -37,7 +37,7 @@ pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) ->
|
|||
|
||||
let kernel_specification = store.update(cx, |store, cx| {
|
||||
store
|
||||
.kernelspec(&language, cx)
|
||||
.kernelspec(language.code_fence_block_name().as_ref(), cx)
|
||||
.with_context(|| format!("No kernel found for language: {}", language.name()))
|
||||
})?;
|
||||
|
||||
|
@ -114,7 +114,9 @@ pub fn session(editor: WeakView<Editor>, cx: &mut AppContext) -> SessionSupport
|
|||
let Some(language) = get_language(editor, cx) else {
|
||||
return SessionSupport::Unsupported;
|
||||
};
|
||||
let kernelspec = store.update(cx, |store, cx| store.kernelspec(&language, cx));
|
||||
let kernelspec = store.update(cx, |store, cx| {
|
||||
store.kernelspec(language.code_fence_block_name().as_ref(), cx)
|
||||
});
|
||||
|
||||
match kernelspec {
|
||||
Some(kernelspec) => SessionSupport::Inactive(Box::new(kernelspec)),
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
use collections::HashMap;
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
actions, prelude::*, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView,
|
||||
Subscription, View,
|
||||
FontWeight, Subscription, View,
|
||||
};
|
||||
use ui::{prelude::*, ButtonLike, ElevationIndex, KeyBinding};
|
||||
use ui::{prelude::*, ButtonLike, ElevationIndex, KeyBinding, ListItem, Tooltip};
|
||||
use util::ResultExt as _;
|
||||
use workspace::item::ItemEvent;
|
||||
use workspace::WorkspaceId;
|
||||
use workspace::{item::Item, Workspace};
|
||||
|
||||
use crate::components::KernelListItem;
|
||||
use crate::jupyter_settings::JupyterSettings;
|
||||
use crate::repl_store::ReplStore;
|
||||
use crate::KernelSpecification;
|
||||
|
||||
actions!(
|
||||
repl,
|
||||
|
@ -216,13 +217,79 @@ impl Render for ReplSessionsPage {
|
|||
.child(Label::new("Install Kernels"))
|
||||
.on_click(move |_, cx| {
|
||||
cx.open_url(
|
||||
"https://docs.jupyter.org/en/latest/install/kernels.html",
|
||||
"https://zed.dev/docs/repl#language-specific-instructions",
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let mut kernels_by_language: HashMap<String, Vec<KernelSpecification>> = HashMap::default();
|
||||
for spec in kernel_specifications {
|
||||
kernels_by_language
|
||||
.entry(spec.kernelspec.language.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(spec);
|
||||
}
|
||||
|
||||
let kernels_available = v_flex()
|
||||
.child(Label::new("Kernels available").size(LabelSize::Large))
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.child(Label::new(
|
||||
"Defaults indicated with a checkmark. Learn how to change your default kernel in the ",
|
||||
))
|
||||
.child(
|
||||
ButtonLike::new("configure-kernels")
|
||||
.style(ButtonStyle::Filled)
|
||||
// .size(ButtonSize::Compact)
|
||||
.layer(ElevationIndex::Surface)
|
||||
.child(Label::new("REPL documentation"))
|
||||
.child(Icon::new(IconName::Link))
|
||||
.on_click(move |_, cx| {
|
||||
cx.open_url("https://zed.dev/docs/repl#changing-kernels")
|
||||
}),
|
||||
),
|
||||
)
|
||||
.children(kernels_by_language.into_iter().map(|(language, specs)| {
|
||||
let chosen_kernel = store.read(cx).kernelspec(&language, cx);
|
||||
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(Label::new(language.clone()).weight(FontWeight::BOLD))
|
||||
.children(specs.into_iter().map(|spec| {
|
||||
let is_choice = if let Some(chosen_kernel) = &chosen_kernel {
|
||||
chosen_kernel.name.to_lowercase() == spec.name.to_lowercase()
|
||||
&& chosen_kernel.path == spec.path
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let path = SharedString::from(spec.path.to_string_lossy().to_string());
|
||||
|
||||
ListItem::new(path.clone())
|
||||
.selectable(false)
|
||||
.tooltip({
|
||||
let path = path.clone();
|
||||
move |cx| Tooltip::text(path.clone(), cx)})
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(div().id(path.clone()).child(Label::new(spec.name.clone())))
|
||||
.when(is_choice, |el| {
|
||||
|
||||
let language = language.clone();
|
||||
|
||||
el.child(
|
||||
|
||||
div().id("check").tooltip(move |cx| Tooltip::text(format!("Default Kernel for {language}"), cx))
|
||||
.child(Icon::new(IconName::Check)))}),
|
||||
)
|
||||
|
||||
}))
|
||||
}));
|
||||
|
||||
// When there are no sessions, show the command to run code in an editor
|
||||
if sessions.is_empty() {
|
||||
let instructions = "To run code in a Jupyter kernel, select some code and use the 'repl::Run' command.";
|
||||
|
@ -233,18 +300,12 @@ impl Render for ReplSessionsPage {
|
|||
.child(Label::new(instructions))
|
||||
.children(KeyBinding::for_action(&Run, cx)),
|
||||
)
|
||||
.child(Label::new("Kernels available").size(LabelSize::Large))
|
||||
.children(kernel_specifications.into_iter().map(|spec| {
|
||||
KernelListItem::new(spec.clone()).child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(Label::new(spec.name))
|
||||
.child(Label::new(spec.kernelspec.language).color(Color::Muted)),
|
||||
)
|
||||
}));
|
||||
.child(div().pt_3().child(kernels_available));
|
||||
}
|
||||
|
||||
ReplSessionsContainer::new("Jupyter Kernel Sessions").children(sessions)
|
||||
ReplSessionsContainer::new("Jupyter Kernel Sessions")
|
||||
.children(sessions)
|
||||
.child(kernels_available)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ use command_palette_hooks::CommandPaletteFilter;
|
|||
use gpui::{
|
||||
prelude::*, AppContext, EntityId, Global, Model, ModelContext, Subscription, Task, View,
|
||||
};
|
||||
use language::Language;
|
||||
use project::Fs;
|
||||
use settings::{Settings, SettingsStore};
|
||||
|
||||
|
@ -118,26 +117,31 @@ impl ReplStore {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn kernelspec(
|
||||
&self,
|
||||
language: &Language,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Option<KernelSpecification> {
|
||||
pub fn kernelspec(&self, language_name: &str, cx: &AppContext) -> Option<KernelSpecification> {
|
||||
let settings = JupyterSettings::get_global(cx);
|
||||
let language_name = language.code_fence_block_name();
|
||||
let selected_kernel = settings.kernel_selections.get(language_name.as_ref());
|
||||
let selected_kernel = settings.kernel_selections.get(language_name);
|
||||
|
||||
self.kernel_specifications
|
||||
let found_by_name = self
|
||||
.kernel_specifications
|
||||
.iter()
|
||||
.find(|runtime_specification| {
|
||||
if let Some(selected) = selected_kernel {
|
||||
// Top priority is the selected kernel
|
||||
runtime_specification.name.to_lowercase() == selected.to_lowercase()
|
||||
} else {
|
||||
// Otherwise, we'll try to find a kernel that matches the language
|
||||
runtime_specification.kernelspec.language.to_lowercase()
|
||||
== language_name.to_lowercase()
|
||||
return runtime_specification.name.to_lowercase() == selected.to_lowercase();
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.cloned();
|
||||
|
||||
if let Some(found_by_name) = found_by_name {
|
||||
return Some(found_by_name);
|
||||
}
|
||||
|
||||
self.kernel_specifications
|
||||
.iter()
|
||||
.find(|runtime_specification| {
|
||||
runtime_specification.kernelspec.language.to_lowercase()
|
||||
== language_name.to_lowercase()
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue