Implement RunningKernel trait for native and remote kernels (#20934)

This PR introduces a unified interface for both native and remote
kernels through the `RunningKernel` trait. When either the native kernel
or the remote kernels are started, they return a `Box<dyn
RunningKernel>` to make it easier to work with the session. As a bonus
of this refactor, I've dropped some of the mpsc channels to instead opt
for passing messages directly to `session.route(message)`. There was a
lot of simplification of `Session` by moving responsibilities to
`NativeRunningKernel`.

No release notes yet until this is finalized.

* [x] Detect remote kernelspecs from configured remote servers
* [x] Launch kernel on demand

For now, this allows you to set env vars `JUPYTER_SERVER` and
`JUPYTER_TOKEN` to access a remote server. `JUPYTER_SERVER` should be a
base path like `http://localhost:8888` or
`https://notebooks.gesis.org/binder/jupyter/user/rubydata-binder-w6igpy4l/`

Release Notes:

- N/A
This commit is contained in:
Kyle Kelley 2024-11-21 14:00:19 -08:00 committed by GitHub
parent f74f670865
commit 72613b7668
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 478 additions and 230 deletions

View file

@ -34,6 +34,16 @@ pub struct KernelPickerDelegate {
on_select: OnSelect,
}
// Helper function to truncate long paths
fn truncate_path(path: &SharedString, max_length: usize) -> SharedString {
if path.len() <= max_length {
path.to_string().into()
} else {
let truncated = path.chars().rev().take(max_length - 3).collect::<String>();
format!("...{}", truncated.chars().rev().collect::<String>()).into()
}
}
impl<T: PopoverTrigger> KernelSelector<T> {
pub fn new(on_select: OnSelect, worktree_id: WorktreeId, trigger: T) -> Self {
KernelSelector {
@ -116,11 +126,25 @@ impl PickerDelegate for KernelPickerDelegate {
&self,
ix: usize,
selected: bool,
_cx: &mut ViewContext<Picker<Self>>,
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);
let icon = kernelspec.icon(cx);
let (name, kernel_type, path_or_url) = match kernelspec {
KernelSpecification::Jupyter(_) => (kernelspec.name(), "Jupyter", None),
KernelSpecification::PythonEnv(_) => (
kernelspec.name(),
"Python Env",
Some(truncate_path(&kernelspec.path(), 42)),
),
KernelSpecification::Remote(_) => (
kernelspec.name(),
"Remote",
Some(truncate_path(&kernelspec.path(), 42)),
),
};
Some(
ListItem::new(ix)
@ -128,25 +152,46 @@ impl PickerDelegate for KernelPickerDelegate {
.spacing(ListItemSpacing::Sparse)
.selected(selected)
.child(
v_flex()
.min_w(px(600.))
h_flex()
.w_full()
.gap_0p5()
.gap_3()
.child(icon.color(Color::Default).size(IconSize::Medium))
.child(
h_flex()
.w_full()
.gap_1()
.child(Label::new(kernelspec.name()).weight(FontWeight::MEDIUM))
v_flex()
.flex_grow()
.gap_0p5()
.child(
Label::new(kernelspec.language())
.size(LabelSize::Small)
.color(Color::Muted),
h_flex()
.justify_between()
.child(
div().w_48().text_ellipsis().child(
Label::new(name)
.weight(FontWeight::MEDIUM)
.size(LabelSize::Default),
),
)
.when_some(path_or_url.clone(), |flex, path| {
flex.text_ellipsis().child(
Label::new(path)
.size(LabelSize::Small)
.color(Color::Muted),
)
}),
)
.child(
h_flex()
.gap_1()
.child(
Label::new(kernelspec.language())
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(
Label::new(kernel_type)
.size(LabelSize::Small)
.color(Color::Muted),
),
),
)
.child(
Label::new(kernelspec.path())
.size(LabelSize::XSmall)
.color(Color::Muted),
),
)
.when(is_selected, |item| {
@ -199,7 +244,9 @@ impl<T: PopoverTrigger> RenderOnce for KernelSelector<T> {
};
let picker_view = cx.new_view(|cx| {
let picker = Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into()));
let picker = Picker::uniform_list(delegate, cx)
.width(rems(30.))
.max_height(Some(rems(20.).into()));
picker
});