repl: Support kernel language aliases in REPL (#49762)
Add a `kernel_language_names` field to `LanguageConfig` that allows languages to declare alternative names that Jupyter kernels may use. This fixes REPL matching for cases where a kernel reports a different language identifier than Zed's language name. For example, the Nu extension would set `kernel_language_names = ["nushell", "nu"]` in its config.toml, enabling REPL support for nu-jupyter-kernel which reports `"language": "nushell"` in its kernelspec. The change consolidates kernel language matching logic into a single `Language::matches_kernel_language()` method that checks the code fence block name, language name, and the new aliases list (all case-insensitive). - [x] Done a self-review taking into account security and performance aspects Release Notes: - Added `kernel_language_names` field for extensions to self identify REPL mappings
This commit is contained in:
1 parent
194858aed0
commit
557ca4ea79
3 files changed
+30
-11
No files matched your search
@@ -835,6 +835,11 @@ pub struct LanguageConfig {
|
||||
pub name: LanguageName,
|
||||
/// The name of this language for a Markdown code fence block
|
||||
pub code_fence_block_name: Option<Arc<str>>,
|
||||
/// Alternative language names that Jupyter kernels may report for this language.
|
||||
/// Used when a kernel's `language` field differs from Zed's language name.
|
||||
/// For example, the Nu extension would set this to `["nushell"]`.
|
||||
#[serde(default)]
|
||||
pub kernel_language_names: Vec<Arc<str>>,
|
||||
// The name of the grammar in a WASM bundle (experimental).
|
||||
pub grammar: Option<Arc<str>>,
|
||||
/// The criteria for matching this language to a given file.
|
||||
@@ -1141,6 +1146,7 @@ impl Default for LanguageConfig {
|
||||
Self {
|
||||
name: LanguageName::new_static(""),
|
||||
code_fence_block_name: None,
|
||||
kernel_language_names: Default::default(),
|
||||
grammar: None,
|
||||
matcher: LanguageMatcher::default(),
|
||||
brackets: Default::default(),
|
||||
@@ -2075,6 +2081,23 @@ impl Language {
|
||||
.unwrap_or_else(|| self.config.name.as_ref().to_lowercase().into())
|
||||
}
|
||||
|
||||
pub fn matches_kernel_language(&self, kernel_language: &str) -> bool {
|
||||
let kernel_language_lower = kernel_language.to_lowercase();
|
||||
|
||||
if self.code_fence_block_name().to_lowercase() == kernel_language_lower {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.config.name.as_ref().to_lowercase() == kernel_language_lower {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.config
|
||||
.kernel_language_names
|
||||
.iter()
|
||||
.any(|name| name.to_lowercase() == kernel_language_lower)
|
||||
}
|
||||
|
||||
pub fn context_provider(&self) -> Option<Arc<dyn ContextProvider>> {
|
||||
self.context_provider.clone()
|
||||
}
|
||||
|
||||
@@ -636,12 +636,9 @@ 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()
|
||||
})
|
||||
store_read
|
||||
.pure_jupyter_kernel_specifications()
|
||||
.any(|spec| language.matches_kernel_language(spec.language().as_ref()))
|
||||
}
|
||||
|
||||
fn get_language(editor: WeakEntity<Editor>, cx: &mut App) -> Option<Arc<Language>> {
|
||||
|
||||
@@ -289,7 +289,6 @@ impl ReplStore {
|
||||
}
|
||||
|
||||
let language_at_cursor = language_at_cursor?;
|
||||
let language_name = language_at_cursor.code_fence_block_name().to_lowercase();
|
||||
|
||||
// Prefer the recommended (active toolchain) kernel if it has ipykernel
|
||||
if let Some(active_path) = self.active_python_toolchain_path(worktree_id) {
|
||||
@@ -297,7 +296,7 @@ impl ReplStore {
|
||||
.kernel_specifications_for_worktree(worktree_id)
|
||||
.find(|spec| {
|
||||
spec.has_ipykernel()
|
||||
&& spec.language().as_ref().to_lowercase() == language_name
|
||||
&& language_at_cursor.matches_kernel_language(spec.language().as_ref())
|
||||
&& spec.path().as_ref() == active_path.as_ref()
|
||||
})
|
||||
.cloned();
|
||||
@@ -312,7 +311,7 @@ impl ReplStore {
|
||||
.find(|spec| {
|
||||
matches!(spec, KernelSpecification::PythonEnv(_))
|
||||
&& spec.has_ipykernel()
|
||||
&& spec.language().as_ref().to_lowercase() == language_name
|
||||
&& language_at_cursor.matches_kernel_language(spec.language().as_ref())
|
||||
})
|
||||
.cloned();
|
||||
if python_env.is_some() {
|
||||
@@ -350,10 +349,10 @@ impl ReplStore {
|
||||
return Some(found_by_name);
|
||||
}
|
||||
|
||||
let language_name = language_at_cursor.code_fence_block_name().to_lowercase();
|
||||
self.kernel_specifications_for_worktree(worktree_id)
|
||||
.find(|spec| {
|
||||
spec.has_ipykernel() && spec.language().as_ref().to_lowercase() == language_name
|
||||
spec.has_ipykernel()
|
||||
&& language_at_cursor.matches_kernel_language(spec.language().as_ref())
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user