Detailed changes
@@ -9354,6 +9354,7 @@ dependencies = [
"editor",
"gpui",
"markdown_preview",
+ "picker",
"repl",
"search",
"settings",
@@ -9857,6 +9858,7 @@ dependencies = [
"menu",
"multi_buffer",
"nbformat",
+ "picker",
"project",
"runtimelib",
"schemars",
@@ -24,6 +24,7 @@ ui.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
+picker.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
@@ -1,13 +1,15 @@
use std::time::Duration;
use gpui::{percentage, Animation, AnimationExt, AnyElement, Transformation, View};
+use picker::Picker;
use repl::{
+ components::{KernelPickerDelegate, KernelSelector},
ExecutionState, JupyterSettings, Kernel, KernelSpecification, KernelStatus, Session,
SessionSupport,
};
use ui::{
prelude::*, ButtonLike, ContextMenu, IconWithIndicator, Indicator, IntoElement, PopoverMenu,
- Tooltip,
+ PopoverMenuHandle, Tooltip,
};
use gpui::ElementId;
@@ -58,7 +60,6 @@ impl QuickActionBar {
let session = match session {
SessionSupport::ActiveSession(session) => session,
SessionSupport::Inactive(spec) => {
- let spec = *spec;
return self.render_repl_launch_menu(spec, cx);
}
SessionSupport::RequiresSetup(language) => {
@@ -246,44 +247,120 @@ impl QuickActionBar {
Some(
h_flex()
+ .child(self.render_kernel_selector(cx))
.child(button)
.child(dropdown_menu)
.into_any_element(),
)
}
-
pub fn render_repl_launch_menu(
&self,
kernel_specification: KernelSpecification,
- _cx: &mut ViewContext<Self>,
+ cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let tooltip: SharedString =
- SharedString::from(format!("Start REPL for {}", kernel_specification.name));
+ SharedString::from(format!("Start REPL for {}", kernel_specification.name()));
Some(
- IconButton::new("toggle_repl_icon", IconName::ReplNeutral)
- .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 {})))
+ h_flex()
+ .child(self.render_kernel_selector(cx))
+ .child(
+ IconButton::new("toggle_repl_icon", IconName::ReplNeutral)
+ .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_kernel_selector(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+ let editor = if let Some(editor) = self.active_editor() {
+ editor
+ } else {
+ // todo!()
+ return div().into_any_element();
+ };
+
+ let session = repl::session(editor.downgrade(), cx);
+
+ let current_kernelspec = match session {
+ SessionSupport::ActiveSession(view) => Some(view.read(cx).kernel_specification.clone()),
+ SessionSupport::Inactive(kernel_specification) => Some(kernel_specification),
+ SessionSupport::RequiresSetup(_language_name) => None,
+ SessionSupport::Unsupported => None,
+ };
+
+ let current_kernel_name = current_kernelspec.as_ref().map(|spec| spec.name());
+
+ let menu_handle: PopoverMenuHandle<Picker<KernelPickerDelegate>> =
+ PopoverMenuHandle::default();
+ KernelSelector::new(
+ {
+ Box::new(move |kernelspec, cx| {
+ repl::assign_kernelspec(kernelspec, editor.downgrade(), cx).ok();
+ })
+ },
+ current_kernelspec.clone(),
+ ButtonLike::new("kernel-selector")
+ .style(ButtonStyle::Subtle)
+ .child(
+ h_flex()
+ .w_full()
+ .gap_0p5()
+ .child(
+ div()
+ .overflow_x_hidden()
+ .flex_grow()
+ .whitespace_nowrap()
+ .child(
+ Label::new(if let Some(name) = current_kernel_name {
+ name
+ } else {
+ SharedString::from("Select Kernel")
+ })
+ .size(LabelSize::Small)
+ .color(if current_kernelspec.is_some() {
+ Color::Default
+ } else {
+ Color::Placeholder
+ })
+ .into_any_element(),
+ ),
+ )
+ .child(
+ Icon::new(IconName::ChevronDown)
+ .color(Color::Muted)
+ .size(IconSize::XSmall),
+ ),
+ )
+ .tooltip(move |cx| Tooltip::text("Select Kernel", cx)),
+ )
+ .with_handle(menu_handle.clone())
+ .into_any_element()
+ }
+
pub fn render_repl_setup(
&self,
language: &str,
- _cx: &mut ViewContext<Self>,
+ cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let tooltip: SharedString = SharedString::from(format!("Setup Zed REPL for {}", language));
Some(
- IconButton::new("toggle_repl_icon", IconName::ReplNeutral)
- .size(ButtonSize::Compact)
- .icon_color(Color::Muted)
- .style(ButtonStyle::Subtle)
- .tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
- .on_click(|_, cx| cx.open_url(&format!("{}#installation", ZED_REPL_DOCUMENTATION)))
+ h_flex()
+ .child(self.render_kernel_selector(cx))
+ .child(
+ IconButton::new("toggle_repl_icon", IconName::ReplNeutral)
+ .size(ButtonSize::Compact)
+ .icon_color(Color::Muted)
+ .style(ButtonStyle::Subtle)
+ .tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
+ .on_click(|_, cx| {
+ cx.open_url(&format!("{}#installation", ZED_REPL_DOCUMENTATION))
+ }),
+ )
.into_any_element(),
)
}
@@ -292,13 +369,8 @@ 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 kernel_name = session.kernel_specification.name();
+ let kernel_language: SharedString = session.kernel_specification.language();
let fill_fields = || {
ReplMenuState {
@@ -45,6 +45,7 @@ ui.workspace = true
util.workspace = true
uuid.workspace = true
workspace.workspace = true
+picker.workspace = true
[target.'cfg(target_os = "windows")'.dependencies]
windows.workspace = true
@@ -1,3 +1,5 @@
mod kernel_list_item;
+mod kernel_options;
pub use kernel_list_item::*;
+pub use kernel_options::*;
@@ -46,7 +46,7 @@ impl ParentElement for KernelListItem {
impl RenderOnce for KernelListItem {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
- ListItem::new(SharedString::from(self.kernel_specification.name.clone()))
+ ListItem::new(self.kernel_specification.name())
.selectable(false)
.start_slot(
h_flex()
@@ -0,0 +1,201 @@
+use crate::kernels::KernelSpecification;
+use crate::repl_store::ReplStore;
+use crate::KERNEL_DOCS_URL;
+
+use gpui::DismissEvent;
+
+use picker::Picker;
+use picker::PickerDelegate;
+
+use std::sync::Arc;
+use ui::ListItemSpacing;
+
+use gpui::SharedString;
+use gpui::Task;
+use ui::{prelude::*, ListItem, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
+
+type OnSelect = Box<dyn Fn(KernelSpecification, &mut WindowContext)>;
+
+#[derive(IntoElement)]
+pub struct KernelSelector<T: PopoverTrigger> {
+ handle: Option<PopoverMenuHandle<Picker<KernelPickerDelegate>>>,
+ on_select: OnSelect,
+ trigger: T,
+ info_text: Option<SharedString>,
+ current_kernelspec: Option<KernelSpecification>,
+}
+
+pub struct KernelPickerDelegate {
+ all_kernels: Vec<KernelSpecification>,
+ filtered_kernels: Vec<KernelSpecification>,
+ selected_kernelspec: Option<KernelSpecification>,
+ on_select: OnSelect,
+}
+
+impl<T: PopoverTrigger> KernelSelector<T> {
+ pub fn new(
+ on_select: OnSelect,
+ current_kernelspec: Option<KernelSpecification>,
+ trigger: T,
+ ) -> Self {
+ KernelSelector {
+ on_select,
+ handle: None,
+ trigger,
+ info_text: None,
+ current_kernelspec,
+ }
+ }
+
+ pub fn with_handle(mut self, handle: PopoverMenuHandle<Picker<KernelPickerDelegate>>) -> Self {
+ self.handle = Some(handle);
+ self
+ }
+
+ pub fn with_info_text(mut self, text: impl Into<SharedString>) -> Self {
+ self.info_text = Some(text.into());
+ self
+ }
+}
+
+impl PickerDelegate for KernelPickerDelegate {
+ type ListItem = ListItem;
+
+ fn match_count(&self) -> usize {
+ self.filtered_kernels.len()
+ }
+
+ fn selected_index(&self) -> usize {
+ if let Some(kernelspec) = self.selected_kernelspec.as_ref() {
+ self.filtered_kernels
+ .iter()
+ .position(|k| k == kernelspec)
+ .unwrap_or(0)
+ } else {
+ 0
+ }
+ }
+
+ fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
+ self.selected_kernelspec = self.filtered_kernels.get(ix).cloned();
+ cx.notify();
+ }
+
+ fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
+ "Select a kernel...".into()
+ }
+
+ fn update_matches(&mut self, query: String, _cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
+ let all_kernels = self.all_kernels.clone();
+
+ if query.is_empty() {
+ self.filtered_kernels = all_kernels;
+ return Task::Ready(Some(()));
+ }
+
+ self.filtered_kernels = if query.is_empty() {
+ all_kernels
+ } else {
+ all_kernels
+ .into_iter()
+ .filter(|kernel| kernel.name().to_lowercase().contains(&query.to_lowercase()))
+ .collect()
+ };
+
+ return Task::Ready(Some(()));
+ }
+
+ fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
+ if let Some(kernelspec) = &self.selected_kernelspec {
+ (self.on_select)(kernelspec.clone(), cx.window_context());
+ cx.emit(DismissEvent);
+ }
+ }
+
+ fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
+
+ fn render_match(
+ &self,
+ ix: usize,
+ selected: bool,
+ _cx: &mut ViewContext<Picker<Self>>,
+ ) -> Option<Self::ListItem> {
+ let kernelspec = self.filtered_kernels.get(ix)?;
+
+ let is_selected = self.selected_kernelspec.as_ref() == Some(kernelspec);
+
+ Some(
+ ListItem::new(ix)
+ .inset(true)
+ .spacing(ListItemSpacing::Sparse)
+ .selected(selected)
+ .child(
+ h_flex().w_full().justify_between().min_w(px(200.)).child(
+ h_flex()
+ .gap_1p5()
+ .child(Label::new(kernelspec.name()))
+ .child(
+ Label::new(kernelspec.type_name())
+ .size(LabelSize::XSmall)
+ .color(Color::Muted),
+ ),
+ ),
+ )
+ .end_slot(div().when(is_selected, |this| {
+ this.child(
+ Icon::new(IconName::Check)
+ .color(Color::Accent)
+ .size(IconSize::Small),
+ )
+ })),
+ )
+ }
+
+ fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<gpui::AnyElement> {
+ Some(
+ h_flex()
+ .w_full()
+ .border_t_1()
+ .border_color(cx.theme().colors().border_variant)
+ .p_1()
+ .gap_4()
+ .child(
+ Button::new("kernel-docs", "Kernel Docs")
+ .icon(IconName::ExternalLink)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ .icon_position(IconPosition::End)
+ .on_click(move |_, cx| cx.open_url(KERNEL_DOCS_URL)),
+ )
+ .into_any(),
+ )
+ }
+}
+
+impl<T: PopoverTrigger> RenderOnce for KernelSelector<T> {
+ fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+ let store = ReplStore::global(cx).read(cx);
+ let all_kernels: Vec<KernelSpecification> =
+ store.kernel_specifications().cloned().collect();
+
+ let selected_kernelspec = self.current_kernelspec;
+
+ let delegate = KernelPickerDelegate {
+ on_select: self.on_select,
+ all_kernels: all_kernels.clone(),
+ filtered_kernels: all_kernels,
+ selected_kernelspec,
+ };
+
+ let picker_view = cx.new_view(|cx| {
+ let picker = Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into()));
+ picker
+ });
+
+ PopoverMenu::new("kernel-switcher")
+ .menu(move |_cx| Some(picker_view.clone()))
+ .trigger(self.trigger)
+ .attach(gpui::AnchorCorner::BottomLeft)
+ .when_some(self.handle, |menu, handle| menu.with_handle(handle))
+ }
+}
@@ -19,16 +19,82 @@ use std::{
path::PathBuf,
sync::Arc,
};
+use ui::SharedString;
use uuid::Uuid;
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum KernelSpecification {
+ Remote(RemoteKernelSpecification),
+ Jupyter(LocalKernelSpecification),
+ PythonEnv(LocalKernelSpecification),
+}
+
+impl KernelSpecification {
+ pub fn name(&self) -> SharedString {
+ match self {
+ Self::Jupyter(spec) => spec.name.clone().into(),
+ Self::PythonEnv(spec) => spec.name.clone().into(),
+ Self::Remote(spec) => spec.name.clone().into(),
+ }
+ }
+
+ pub fn type_name(&self) -> SharedString {
+ match self {
+ Self::Jupyter(_) => "Jupyter".into(),
+ Self::PythonEnv(_) => "Python Environment".into(),
+ Self::Remote(_) => "Remote".into(),
+ }
+ }
+
+ pub fn path(&self) -> SharedString {
+ SharedString::from(match self {
+ Self::Jupyter(spec) => spec.path.to_string_lossy().to_string(),
+ Self::PythonEnv(spec) => spec.path.to_string_lossy().to_string(),
+ Self::Remote(spec) => spec.url.to_string(),
+ })
+ }
+
+ pub fn language(&self) -> SharedString {
+ SharedString::from(match self {
+ Self::Jupyter(spec) => spec.kernelspec.language.clone(),
+ Self::PythonEnv(spec) => spec.kernelspec.language.clone(),
+ Self::Remote(spec) => spec.kernelspec.language.clone(),
+ })
+ }
+}
+
#[derive(Debug, Clone)]
-pub struct KernelSpecification {
+pub struct LocalKernelSpecification {
pub name: String,
pub path: PathBuf,
pub kernelspec: JupyterKernelspec,
}
-impl KernelSpecification {
+impl PartialEq for LocalKernelSpecification {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name && self.path == other.path
+ }
+}
+
+impl Eq for LocalKernelSpecification {}
+
+#[derive(Debug, Clone)]
+pub struct RemoteKernelSpecification {
+ pub name: String,
+ pub url: String,
+ pub token: String,
+ pub kernelspec: JupyterKernelspec,
+}
+
+impl PartialEq for RemoteKernelSpecification {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name && self.url == other.url
+ }
+}
+
+impl Eq for RemoteKernelSpecification {}
+
+impl LocalKernelSpecification {
#[must_use]
fn command(&self, connection_path: &PathBuf) -> Result<Command> {
let argv = &self.kernelspec.argv;
@@ -198,6 +264,17 @@ impl RunningKernel {
fs: Arc<dyn Fs>,
cx: &mut AppContext,
) -> Task<Result<(Self, JupyterMessageChannel)>> {
+ let kernel_specification = match kernel_specification {
+ KernelSpecification::Jupyter(spec) => spec,
+ KernelSpecification::PythonEnv(spec) => spec,
+ KernelSpecification::Remote(_spec) => {
+ // todo!(): Implement remote kernel specification
+ return Task::ready(Err(anyhow::anyhow!(
+ "Running remote kernels is not supported"
+ )));
+ }
+ };
+
cx.spawn(|cx| async move {
let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let ports = peek_ports(ip).await?;
@@ -344,7 +421,7 @@ async fn read_kernelspec_at(
// /usr/local/share/jupyter/kernels/python3
kernel_dir: PathBuf,
fs: &dyn Fs,
-) -> Result<KernelSpecification> {
+) -> Result<LocalKernelSpecification> {
let path = kernel_dir;
let kernel_name = if let Some(kernel_name) = path.file_name() {
kernel_name.to_string_lossy().to_string()
@@ -360,7 +437,7 @@ async fn read_kernelspec_at(
let spec = fs.load(expected_kernel_json.as_path()).await?;
let spec = serde_json::from_str::<JupyterKernelspec>(&spec)?;
- Ok(KernelSpecification {
+ Ok(LocalKernelSpecification {
name: kernel_name,
path,
kernelspec: spec,
@@ -368,7 +445,7 @@ async fn read_kernelspec_at(
}
/// Read a directory of kernelspec directories
-async fn read_kernels_dir(path: PathBuf, fs: &dyn Fs) -> Result<Vec<KernelSpecification>> {
+async fn read_kernels_dir(path: PathBuf, fs: &dyn Fs) -> Result<Vec<LocalKernelSpecification>> {
let mut kernelspec_dirs = fs.read_dir(&path).await?;
let mut valid_kernelspecs = Vec::new();
@@ -388,7 +465,7 @@ async fn read_kernels_dir(path: PathBuf, fs: &dyn Fs) -> Result<Vec<KernelSpecif
Ok(valid_kernelspecs)
}
-pub async fn kernel_specifications(fs: Arc<dyn Fs>) -> Result<Vec<KernelSpecification>> {
+pub async fn local_kernel_specifications(fs: Arc<dyn Fs>) -> Result<Vec<LocalKernelSpecification>> {
let mut data_dirs = dirs::data_dirs();
// Pick up any kernels from conda or conda environment
@@ -1,4 +1,4 @@
-mod components;
+pub mod components;
mod jupyter_settings;
mod kernels;
pub mod notebook;
@@ -26,6 +26,8 @@ use crate::repl_store::ReplStore;
pub use crate::session::Session;
use client::telemetry::Telemetry;
+pub const KERNEL_DOCS_URL: &str = "https://zed.dev/docs/repl#changing-kernels";
+
pub fn init(fs: Arc<dyn Fs>, telemetry: Arc<Telemetry>, cx: &mut AppContext) {
set_dispatcher(zed_dispatcher(cx));
JupyterSettings::register(cx);
@@ -12,6 +12,56 @@ use crate::repl_store::ReplStore;
use crate::session::SessionEvent;
use crate::{KernelSpecification, Session};
+pub fn assign_kernelspec(
+ kernel_specification: KernelSpecification,
+ weak_editor: WeakView<Editor>,
+ cx: &mut WindowContext,
+) -> Result<()> {
+ let store = ReplStore::global(cx);
+ if !store.read(cx).is_enabled() {
+ return Ok(());
+ }
+
+ let fs = store.read(cx).fs().clone();
+ let telemetry = store.read(cx).telemetry().clone();
+
+ if let Some(session) = store.read(cx).get_session(weak_editor.entity_id()).cloned() {
+ // Drop previous session, start new one
+ session.update(cx, |session, cx| {
+ session.clear_outputs(cx);
+ session.shutdown(cx);
+ cx.notify();
+ });
+ }
+
+ let session = cx
+ .new_view(|cx| Session::new(weak_editor.clone(), fs, telemetry, kernel_specification, cx));
+
+ weak_editor
+ .update(cx, |_editor, cx| {
+ cx.notify();
+
+ cx.subscribe(&session, {
+ let store = store.clone();
+ move |_this, _session, event, cx| match event {
+ SessionEvent::Shutdown(shutdown_event) => {
+ store.update(cx, |store, _cx| {
+ store.remove_session(shutdown_event.entity_id());
+ });
+ }
+ }
+ })
+ .detach();
+ })
+ .ok();
+
+ store.update(cx, |store, _cx| {
+ store.insert_session(weak_editor.entity_id(), session.clone());
+ });
+
+ Ok(())
+}
+
pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) -> Result<()> {
let store = ReplStore::global(cx);
if !store.read(cx).is_enabled() {
@@ -96,9 +146,10 @@ pub fn run(editor: WeakView<Editor>, move_down: bool, cx: &mut WindowContext) ->
anyhow::Ok(())
}
+#[allow(clippy::large_enum_variant)]
pub enum SessionSupport {
ActiveSession(View<Session>),
- Inactive(Box<KernelSpecification>),
+ Inactive(KernelSpecification),
RequiresSetup(LanguageName),
Unsupported,
}
@@ -119,7 +170,7 @@ pub fn session(editor: WeakView<Editor>, cx: &mut WindowContext) -> SessionSuppo
});
match kernelspec {
- Some(kernelspec) => SessionSupport::Inactive(Box::new(kernelspec)),
+ Some(kernelspec) => SessionSupport::Inactive(kernelspec),
None => {
if language_supported(&language) {
SessionSupport::RequiresSetup(language.name())
@@ -1,9 +1,9 @@
-use collections::HashMap;
use editor::Editor;
use gpui::{
actions, prelude::*, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView,
FontWeight, Subscription, View,
};
+use std::collections::HashMap;
use ui::{prelude::*, ButtonLike, ElevationIndex, KeyBinding, ListItem, Tooltip};
use util::ResultExt as _;
use workspace::item::ItemEvent;
@@ -12,7 +12,7 @@ use workspace::{item::Item, Workspace};
use crate::jupyter_settings::JupyterSettings;
use crate::repl_store::ReplStore;
-use crate::KernelSpecification;
+use crate::{KernelSpecification, KERNEL_DOCS_URL};
actions!(
repl,
@@ -238,14 +238,24 @@ impl Render for ReplSessionsPage {
);
}
- 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_default()
- .push(spec);
+ let mut kernels_by_language: HashMap<SharedString, Vec<&KernelSpecification>> =
+ kernel_specifications
+ .iter()
+ .map(|spec| (spec.language(), spec))
+ .fold(HashMap::new(), |mut acc, (language, spec)| {
+ acc.entry(language).or_default().push(spec);
+ acc
+ });
+
+ for kernels in kernels_by_language.values_mut() {
+ kernels.sort_by_key(|a| a.name())
}
+ // Convert to a sorted Vec of tuples
+ let mut sorted_kernels: Vec<(SharedString, Vec<&KernelSpecification>)> =
+ kernels_by_language.into_iter().collect();
+ sorted_kernels.sort_by(|a, b| a.0.cmp(&b.0));
+
let kernels_available = v_flex()
.child(Label::new("Kernels available").size(LabelSize::Large))
.gap_2()
@@ -262,11 +272,11 @@ impl Render for ReplSessionsPage {
.child(Label::new("REPL documentation"))
.child(Icon::new(IconName::Link))
.on_click(move |_, cx| {
- cx.open_url("https://zed.dev/docs/repl#changing-kernels")
+ cx.open_url(KERNEL_DOCS_URL)
}),
),
)
- .children(kernels_by_language.into_iter().map(|(language, specs)| {
+ .children(sorted_kernels.into_iter().map(|(language, specs)| {
let chosen_kernel = store.read(cx).kernelspec(&language, cx);
v_flex()
@@ -274,13 +284,12 @@ impl Render for ReplSessionsPage {
.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
+ chosen_kernel == spec
} else {
false
};
- let path = SharedString::from(spec.path.to_string_lossy().to_string());
+ let path = spec.path();
ListItem::new(path.clone())
.selectable(false)
@@ -290,7 +299,7 @@ impl Render for ReplSessionsPage {
.child(
h_flex()
.gap_1()
- .child(div().id(path.clone()).child(Label::new(spec.name.clone())))
+ .child(div().id(path.clone()).child(Label::new(spec.name())))
.when(is_choice, |el| {
let language = language.clone();
@@ -10,7 +10,7 @@ use gpui::{
use project::Fs;
use settings::{Settings, SettingsStore};
-use crate::kernels::kernel_specifications;
+use crate::kernels::local_kernel_specifications;
use crate::{JupyterSettings, KernelSpecification, Session};
struct GlobalReplStore(Model<ReplStore>);
@@ -106,12 +106,17 @@ impl ReplStore {
}
pub fn refresh_kernelspecs(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
- let kernel_specifications = kernel_specifications(self.fs.clone());
+ let local_kernel_specifications = local_kernel_specifications(self.fs.clone());
cx.spawn(|this, mut cx| async move {
- let kernel_specifications = kernel_specifications.await?;
+ let local_kernel_specifications = local_kernel_specifications.await?;
+
+ let mut kernel_options = Vec::new();
+ for kernel_specification in local_kernel_specifications {
+ kernel_options.push(KernelSpecification::Jupyter(kernel_specification));
+ }
this.update(&mut cx, |this, cx| {
- this.kernel_specifications = kernel_specifications;
+ this.kernel_specifications = kernel_options;
cx.notify();
})
})
@@ -125,7 +130,9 @@ impl ReplStore {
.kernel_specifications
.iter()
.find(|runtime_specification| {
- if let Some(selected) = selected_kernel {
+ if let (Some(selected), KernelSpecification::Jupyter(runtime_specification)) =
+ (selected_kernel, runtime_specification)
+ {
// Top priority is the selected kernel
return runtime_specification.name.to_lowercase() == selected.to_lowercase();
}
@@ -139,9 +146,13 @@ impl ReplStore {
self.kernel_specifications
.iter()
- .find(|runtime_specification| {
- runtime_specification.kernelspec.language.to_lowercase()
- == language_name.to_lowercase()
+ .find(|kernel_option| match kernel_option {
+ KernelSpecification::Jupyter(runtime_specification) => {
+ runtime_specification.kernelspec.language.to_lowercase()
+ == language_name.to_lowercase()
+ }
+ // todo!()
+ _ => false,
})
.cloned()
}
@@ -1,8 +1,8 @@
use crate::components::KernelListItem;
-use crate::KernelStatus;
use crate::{
kernels::{Kernel, KernelSpecification, RunningKernel},
outputs::{ExecutionStatus, ExecutionView},
+ KernelStatus,
};
use client::telemetry::Telemetry;
use collections::{HashMap, HashSet};
@@ -224,7 +224,7 @@ impl Session {
}
fn start_kernel(&mut self, cx: &mut ViewContext<Self>) {
- let kernel_language = self.kernel_specification.kernelspec.language.clone();
+ let kernel_language = self.kernel_specification.language();
let entity_id = self.editor.entity_id();
let working_directory = self
.editor
@@ -233,7 +233,7 @@ impl Session {
.unwrap_or_else(temp_dir);
self.telemetry.report_repl_event(
- kernel_language.clone(),
+ kernel_language.into(),
KernelStatus::Starting.to_string(),
cx.entity_id().to_string(),
);
@@ -556,7 +556,7 @@ impl Session {
self.kernel.set_execution_state(&status.execution_state);
self.telemetry.report_repl_event(
- self.kernel_specification.kernelspec.language.clone(),
+ self.kernel_specification.language().into(),
KernelStatus::from(&self.kernel).to_string(),
cx.entity_id().to_string(),
);
@@ -607,7 +607,7 @@ impl Session {
}
let kernel_status = KernelStatus::from(&kernel).to_string();
- let kernel_language = self.kernel_specification.kernelspec.language.clone();
+ let kernel_language = self.kernel_specification.language().into();
self.telemetry.report_repl_event(
kernel_language,
@@ -749,7 +749,7 @@ impl Render for Session {
Kernel::Shutdown => Color::Disabled,
Kernel::Restarting => Color::Modified,
})
- .child(Label::new(self.kernel_specification.name.clone()))
+ .child(Label::new(self.kernel_specification.name()))
.children(status_text.map(|status_text| Label::new(format!("({status_text})"))))
.button(
Button::new("shutdown", "Shutdown")