@@ -97,7 +97,7 @@ pub fn run(
};
let (runnable_ranges, next_cell_point) =
- runnable_ranges(&buffer.read(cx).snapshot(), selected_range);
+ runnable_ranges(&buffer.read(cx).snapshot(), selected_range, cx);
for runnable_range in runnable_ranges {
let Some(language) = multibuffer.read(cx).language_at(runnable_range.start, cx) else {
@@ -215,7 +215,8 @@ pub fn session(editor: WeakEntity<Editor>, cx: &mut App) -> SessionSupport {
match kernelspec {
Some(kernelspec) => SessionSupport::Inactive(kernelspec),
None => {
- if language_supported(&language.clone()) {
+ // For language_supported, need to check available kernels for language
+ if language_supported(&language.clone(), cx) {
SessionSupport::RequiresSetup(language.name())
} else {
SessionSupport::Unsupported
@@ -414,10 +415,11 @@ fn jupytext_cells(
fn runnable_ranges(
buffer: &BufferSnapshot,
range: Range<Point>,
+ cx: &mut App,
) -> (Vec<Range<Point>>, Option<Point>) {
if let Some(language) = buffer.language() {
if language.name() == "Markdown".into() {
- return (markdown_code_blocks(buffer, range.clone()), None);
+ return (markdown_code_blocks(buffer, range.clone(), cx), None);
}
}
@@ -442,21 +444,30 @@ fn runnable_ranges(
// We allow markdown code blocks to end in a trailing newline in order to render the output
// below the final code fence. This is different than our behavior for selections and Jupytext cells.
-fn markdown_code_blocks(buffer: &BufferSnapshot, range: Range<Point>) -> Vec<Range<Point>> {
+fn markdown_code_blocks(
+ buffer: &BufferSnapshot,
+ range: Range<Point>,
+ cx: &mut App,
+) -> Vec<Range<Point>> {
buffer
.injections_intersecting_range(range)
- .filter(|(_, language)| language_supported(language))
+ .filter(|(_, language)| language_supported(language, cx))
.map(|(content_range, _)| {
buffer.offset_to_point(content_range.start)..buffer.offset_to_point(content_range.end)
})
.collect()
}
-fn language_supported(language: &Arc<Language>) -> bool {
- match language.name().as_ref() {
- "TypeScript" | "Python" => true,
- _ => false,
- }
+fn language_supported(language: &Arc<Language>, cx: &mut App) -> bool {
+ let store = ReplStore::global(cx);
+ let store_read = store.read(cx);
+
+ // Since we're just checking for general language support, we only need to look at
+ // the pure Jupyter kernels - these are all the globally available ones
+ store_read.pure_jupyter_kernel_specifications().any(|spec| {
+ // Convert to lowercase for case-insensitive comparison since kernels might report "python" while our language is "Python"
+ spec.language().as_ref().to_lowercase() == language.name().as_ref().to_lowercase()
+ })
}
fn get_language(editor: WeakEntity<Editor>, cx: &mut App) -> Option<Arc<Language>> {
@@ -506,7 +517,7 @@ mod tests {
let snapshot = buffer.read(cx).snapshot();
// Single-point selection
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(0, 4)..Point::new(0, 4));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(0, 4)..Point::new(0, 4), cx);
let snippets = snippets
.into_iter()
.map(|range| snapshot.text_for_range(range).collect::<String>())
@@ -514,7 +525,7 @@ mod tests {
assert_eq!(snippets, vec!["print(1 + 1)"]);
// Multi-line selection
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(0, 5)..Point::new(2, 0));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(0, 5)..Point::new(2, 0), cx);
let snippets = snippets
.into_iter()
.map(|range| snapshot.text_for_range(range).collect::<String>())
@@ -527,7 +538,7 @@ mod tests {
);
// Trimming multiple trailing blank lines
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(0, 5)..Point::new(5, 0));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(0, 5)..Point::new(5, 0), cx);
let snippets = snippets
.into_iter()
@@ -580,7 +591,7 @@ mod tests {
let snapshot = buffer.read(cx).snapshot();
// Jupytext snippet surrounding an empty selection
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(2, 5)..Point::new(2, 5));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(2, 5)..Point::new(2, 5), cx);
let snippets = snippets
.into_iter()
@@ -596,7 +607,7 @@ mod tests {
);
// Jupytext snippets intersecting a non-empty selection
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(2, 5)..Point::new(6, 2));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(2, 5)..Point::new(6, 2), cx);
let snippets = snippets
.into_iter()
.map(|range| snapshot.text_for_range(range).collect::<String>())
@@ -623,6 +634,49 @@ mod tests {
#[gpui::test]
fn test_markdown_code_blocks(cx: &mut App) {
+ use crate::kernels::LocalKernelSpecification;
+ use jupyter_protocol::JupyterKernelspec;
+
+ // Initialize settings
+ settings::init(cx);
+ editor::init(cx);
+
+ // Initialize the ReplStore with a fake filesystem
+ let fs = Arc::new(project::RealFs::new(None, cx.background_executor().clone()));
+ ReplStore::init(fs, cx);
+
+ // Add mock kernel specifications for TypeScript and Python
+ let store = ReplStore::global(cx);
+ store.update(cx, |store, cx| {
+ let typescript_spec = KernelSpecification::Jupyter(LocalKernelSpecification {
+ name: "typescript".into(),
+ kernelspec: JupyterKernelspec {
+ argv: vec![],
+ display_name: "TypeScript".into(),
+ language: "typescript".into(),
+ interrupt_mode: None,
+ metadata: None,
+ env: None,
+ },
+ path: std::path::PathBuf::new(),
+ });
+
+ let python_spec = KernelSpecification::Jupyter(LocalKernelSpecification {
+ name: "python".into(),
+ kernelspec: JupyterKernelspec {
+ argv: vec![],
+ display_name: "Python".into(),
+ language: "python".into(),
+ interrupt_mode: None,
+ metadata: None,
+ env: None,
+ },
+ path: std::path::PathBuf::new(),
+ });
+
+ store.set_kernel_specs_for_testing(vec![typescript_spec, python_spec], cx);
+ });
+
let markdown = languages::language("markdown", tree_sitter_md::LANGUAGE.into());
let typescript = languages::language(
"typescript",
@@ -658,7 +712,7 @@ mod tests {
});
let snapshot = buffer.read(cx).snapshot();
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(3, 5)..Point::new(8, 5));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(3, 5)..Point::new(8, 5), cx);
let snippets = snippets
.into_iter()
.map(|range| snapshot.text_for_range(range).collect::<String>())
@@ -703,7 +757,7 @@ mod tests {
});
let snapshot = buffer.read(cx).snapshot();
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(3, 5)..Point::new(12, 5));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(3, 5)..Point::new(12, 5), cx);
let snippets = snippets
.into_iter()
.map(|range| snapshot.text_for_range(range).collect::<String>())
@@ -742,7 +796,7 @@ mod tests {
});
let snapshot = buffer.read(cx).snapshot();
- let (snippets, _) = runnable_ranges(&snapshot, Point::new(4, 5)..Point::new(5, 5));
+ let (snippets, _) = runnable_ranges(&snapshot, Point::new(4, 5)..Point::new(5, 5), cx);
let snippets = snippets
.into_iter()
.map(|range| snapshot.text_for_range(range).collect::<String>())