Remove obsolete extensions and avoid loading or downloading them (#39254)

Max Brunsfeld created

Release Notes:

- N/A

Change summary

.config/hakari.toml                         |   2 
Cargo.lock                                  |  15 --
Cargo.toml                                  |   2 
crates/extension_host/src/extension_host.rs |  23 ++
extensions/ruff/Cargo.toml                  |  16 --
extensions/ruff/LICENSE-APACHE              |   1 
extensions/ruff/README.md                   |   3 
extensions/ruff/extension.toml              |  11 -
extensions/ruff/src/ruff.rs                 | 172 -----------------------
extensions/snippets/Cargo.toml              |  17 --
extensions/snippets/LICENSE-APACHE          |   1 
extensions/snippets/extension.toml          |  15 --
extensions/snippets/src/snippets.rs         | 132 -----------------
13 files changed, 21 insertions(+), 389 deletions(-)

Detailed changes

.config/hakari.toml 🔗

@@ -37,8 +37,6 @@ workspace-members = [
     "zed_glsl",
     "zed_html",
     "zed_proto",
-    "zed_ruff",
     "slash_commands_example",
-    "zed_snippets",
     "zed_test_extension",
 ]

Cargo.lock 🔗

@@ -20420,21 +20420,6 @@ dependencies = [
  "zed_extension_api 0.1.0",
 ]
 
-[[package]]
-name = "zed_ruff"
-version = "0.1.1"
-dependencies = [
- "zed_extension_api 0.1.0",
-]
-
-[[package]]
-name = "zed_snippets"
-version = "0.0.6"
-dependencies = [
- "serde_json",
- "zed_extension_api 0.1.0",
-]
-
 [[package]]
 name = "zed_test_extension"
 version = "0.1.0"

Cargo.toml 🔗

@@ -212,9 +212,7 @@ members = [
     "extensions/glsl",
     "extensions/html",
     "extensions/proto",
-    "extensions/ruff",
     "extensions/slash-commands-example",
-    "extensions/snippets",
     "extensions/test-extension",
 
     #

crates/extension_host/src/extension_host.rs 🔗

@@ -73,6 +73,12 @@ const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
 /// The current extension [`SchemaVersion`] supported by Zed.
 const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1);
 
+/// Extensions that should no longer be loaded or downloaded.
+///
+/// These snippets should no longer be downloaded or loaded, because their
+/// functionality has been integrated into the core editor.
+const SUPPRESSED_EXTENSIONS: &[&str] = &["snippets", "ruff", "ty", "basedpyright"];
+
 /// Returns the [`SchemaVersion`] range that is compatible with this version of Zed.
 pub fn schema_version_range() -> RangeInclusive<SchemaVersion> {
     SchemaVersion::ZERO..=CURRENT_SCHEMA_VERSION
@@ -682,7 +688,12 @@ impl ExtensionStore {
                 );
             }
 
-            let response: GetExtensionsResponse = serde_json::from_slice(&body)?;
+            let mut response: GetExtensionsResponse = serde_json::from_slice(&body)?;
+
+            response
+                .data
+                .retain(|extension| !SUPPRESSED_EXTENSIONS.contains(&extension.id.as_ref()));
+
             Ok(response.data)
         })
     }
@@ -1076,6 +1087,10 @@ impl ExtensionStore {
     ) -> Task<()> {
         let old_index = &self.extension_index;
 
+        new_index
+            .extensions
+            .retain(|extension_id, _| !SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref()));
+
         // Determine which extensions need to be loaded and unloaded, based
         // on the changes to the manifest and the extensions that we know have been
         // modified.
@@ -1256,7 +1271,7 @@ impl ExtensionStore {
         self.proxy.register_grammars(grammars_to_add);
         let languages_to_add = new_index
             .languages
-            .iter_mut()
+            .iter()
             .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
             .collect::<Vec<_>>();
         for (language_name, language) in languages_to_add {
@@ -1506,6 +1521,10 @@ impl ExtensionStore {
         let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
         let extension_id = extension_manifest.id.clone();
 
+        if SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref()) {
+            return Ok(());
+        }
+
         // TODO: distinguish dev extensions more explicitly, by the absence
         // of a checksum file that we'll create when downloading normal extensions.
         let is_dev = fs

extensions/ruff/Cargo.toml 🔗

@@ -1,16 +0,0 @@
-[package]
-name = "zed_ruff"
-version = "0.1.1"
-edition.workspace = true
-publish.workspace = true
-license = "Apache-2.0"
-
-[lints]
-workspace = true
-
-[lib]
-path = "src/ruff.rs"
-crate-type = ["cdylib"]
-
-[dependencies]
-zed_extension_api = "0.1.0"

extensions/ruff/README.md 🔗

@@ -1,3 +0,0 @@
-# ruff
-
-Documentation for configuring Zed to use the Ruff extension can be found [here](https://docs.astral.sh/ruff/editors/setup/#zed).

extensions/ruff/extension.toml 🔗

@@ -1,11 +0,0 @@
-id = "ruff"
-name = "Ruff"
-description = "Support for Ruff, the Python linter and formatter"
-version = "0.1.1"
-schema_version = 1
-authors = []
-repository = "https://github.com/zed-industries/zed"
-
-[language_servers.ruff]
-name = "Ruff"
-languages = ["Python"]

extensions/ruff/src/ruff.rs 🔗

@@ -1,172 +0,0 @@
-use std::fs;
-use zed::LanguageServerId;
-use zed_extension_api::{self as zed, Result, settings::LspSettings};
-
-struct RuffBinary {
-    path: String,
-    args: Option<Vec<String>>,
-}
-
-struct RuffExtension {
-    cached_binary_path: Option<String>,
-}
-
-impl RuffExtension {
-    fn language_server_binary(
-        &mut self,
-        language_server_id: &LanguageServerId,
-        worktree: &zed::Worktree,
-    ) -> Result<RuffBinary> {
-        let binary_settings = LspSettings::for_worktree("ruff", worktree)
-            .ok()
-            .and_then(|lsp_settings| lsp_settings.binary);
-        let binary_args = binary_settings
-            .as_ref()
-            .and_then(|binary_settings| binary_settings.arguments.clone());
-
-        if let Some(path) = binary_settings.and_then(|binary_settings| binary_settings.path) {
-            return Ok(RuffBinary {
-                path,
-                args: binary_args,
-            });
-        }
-
-        if let Some(path) = worktree.which("ruff") {
-            return Ok(RuffBinary {
-                path,
-                args: binary_args,
-            });
-        }
-
-        if let Some(path) = &self.cached_binary_path
-            && fs::metadata(path).is_ok_and(|stat| stat.is_file())
-        {
-            return Ok(RuffBinary {
-                path: path.clone(),
-                args: binary_args,
-            });
-        }
-
-        zed::set_language_server_installation_status(
-            language_server_id,
-            &zed::LanguageServerInstallationStatus::CheckingForUpdate,
-        );
-        let release = zed::latest_github_release(
-            "astral-sh/ruff",
-            zed::GithubReleaseOptions {
-                require_assets: true,
-                pre_release: false,
-            },
-        )?;
-
-        let (platform, arch) = zed::current_platform();
-
-        let asset_stem = format!(
-            "ruff-{arch}-{os}",
-            arch = match arch {
-                zed::Architecture::Aarch64 => "aarch64",
-                zed::Architecture::X86 => "x86",
-                zed::Architecture::X8664 => "x86_64",
-            },
-            os = match platform {
-                zed::Os::Mac => "apple-darwin",
-                zed::Os::Linux => "unknown-linux-gnu",
-                zed::Os::Windows => "pc-windows-msvc",
-            }
-        );
-        let asset_name = format!(
-            "{asset_stem}.{suffix}",
-            suffix = match platform {
-                zed::Os::Windows => "zip",
-                _ => "tar.gz",
-            }
-        );
-
-        let asset = release
-            .assets
-            .iter()
-            .find(|asset| asset.name == asset_name)
-            .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
-
-        let version_dir = format!("ruff-{}", release.version);
-        let binary_path = match platform {
-            zed::Os::Windows => format!("{version_dir}/ruff.exe"),
-            _ => format!("{version_dir}/{asset_stem}/ruff"),
-        };
-
-        if !fs::metadata(&binary_path).is_ok_and(|stat| stat.is_file()) {
-            zed::set_language_server_installation_status(
-                language_server_id,
-                &zed::LanguageServerInstallationStatus::Downloading,
-            );
-            let file_kind = match platform {
-                zed::Os::Windows => zed::DownloadedFileType::Zip,
-                _ => zed::DownloadedFileType::GzipTar,
-            };
-            zed::download_file(&asset.download_url, &version_dir, file_kind)
-                .map_err(|e| format!("failed to download file: {e}"))?;
-
-            let entries =
-                fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
-            for entry in entries {
-                let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
-                if entry.file_name().to_str() != Some(&version_dir) {
-                    fs::remove_dir_all(entry.path()).ok();
-                }
-            }
-        }
-
-        self.cached_binary_path = Some(binary_path.clone());
-        Ok(RuffBinary {
-            path: binary_path,
-            args: binary_args,
-        })
-    }
-}
-
-impl zed::Extension for RuffExtension {
-    fn new() -> Self {
-        Self {
-            cached_binary_path: None,
-        }
-    }
-
-    fn language_server_command(
-        &mut self,
-        language_server_id: &LanguageServerId,
-        worktree: &zed::Worktree,
-    ) -> Result<zed::Command> {
-        let ruff_binary = self.language_server_binary(language_server_id, worktree)?;
-        Ok(zed::Command {
-            command: ruff_binary.path,
-            args: ruff_binary.args.unwrap_or_else(|| vec!["server".into()]),
-            env: vec![],
-        })
-    }
-
-    fn language_server_initialization_options(
-        &mut self,
-        server_id: &LanguageServerId,
-        worktree: &zed_extension_api::Worktree,
-    ) -> Result<Option<zed_extension_api::serde_json::Value>> {
-        let settings = LspSettings::for_worktree(server_id.as_ref(), worktree)
-            .ok()
-            .and_then(|lsp_settings| lsp_settings.initialization_options)
-            .unwrap_or_default();
-        Ok(Some(settings))
-    }
-
-    fn language_server_workspace_configuration(
-        &mut self,
-        server_id: &LanguageServerId,
-        worktree: &zed_extension_api::Worktree,
-    ) -> Result<Option<zed_extension_api::serde_json::Value>> {
-        let settings = LspSettings::for_worktree(server_id.as_ref(), worktree)
-            .ok()
-            .and_then(|lsp_settings| lsp_settings.settings)
-            .unwrap_or_default();
-        Ok(Some(settings))
-    }
-}
-
-zed::register_extension!(RuffExtension);

extensions/snippets/Cargo.toml 🔗

@@ -1,17 +0,0 @@
-[package]
-name = "zed_snippets"
-version = "0.0.6"
-edition.workspace = true
-publish.workspace = true
-license = "Apache-2.0"
-
-[lints]
-workspace = true
-
-[lib]
-path = "src/snippets.rs"
-crate-type = ["cdylib"]
-
-[dependencies]
-zed_extension_api = "0.1.0"
-serde_json = "1.0"

extensions/snippets/extension.toml 🔗

@@ -1,15 +0,0 @@
-id = "snippets"
-name = "Snippets"
-description = "Support for language-agnostic snippets, provided by simple-completion-language-server"
-version = "0.0.6"
-schema_version = 1
-authors = ["Zed Industries <hi@zed.dev>"]
-repository = "https://github.com/zed-industries/zed"
-
-[language_servers.snippet-completion-server]
-name = "Snippet Completion Server"
-languages = ["Astro", "Clojure", "C", "C++", "C#", "Dart", "Dockerfile", "Elixir", "Elm", "ERB", "Erlang",
-    "Gleam","GLSL", "Go",  "Haskell", "HCL", "HEEX", "HTML", "JavaScript","JSDoc", "JSON", "Lua",
-    "Markdown","OCaml", "PHP", "Python", "Prisma", "PureScript", "Racket", "Ruby", "Rust", "Scheme",
-    "Shell Script", "Svelte", "Terraform", "TOML", "TypeScript", "TSX", "Uiua", "Vue.js", "Zig"]
-language_ids = { TypeScript = "typescript", TSX = "typescriptreact", JavaScript = "javascript", "Vue.js" = "vue", Terraform = "terraform", "Terraform Vars" = "terraform-vars", PHP = "php", HTML = "html", CSS = "css" }

extensions/snippets/src/snippets.rs 🔗

@@ -1,132 +0,0 @@
-use serde_json::json;
-use std::fs;
-use zed::LanguageServerId;
-use zed_extension_api::{self as zed, Result, settings::LspSettings};
-
-struct SnippetExtension {
-    cached_binary_path: Option<String>,
-}
-
-impl SnippetExtension {
-    fn language_server_binary_path(
-        &mut self,
-        language_server_id: &LanguageServerId,
-        worktree: &zed::Worktree,
-    ) -> Result<String> {
-        if let Some(path) = worktree.which("simple-completion-language-server") {
-            return Ok(path);
-        }
-
-        if let Some(path) = &self.cached_binary_path
-            && fs::metadata(path).is_ok_and(|stat| stat.is_file())
-        {
-            return Ok(path.clone());
-        }
-
-        zed::set_language_server_installation_status(
-            language_server_id,
-            &zed::LanguageServerInstallationStatus::CheckingForUpdate,
-        );
-        let release = zed::latest_github_release(
-            "zed-industries/simple-completion-language-server",
-            zed::GithubReleaseOptions {
-                require_assets: true,
-                pre_release: false,
-            },
-        )?;
-
-        let (platform, arch) = zed::current_platform();
-        let asset_name = format!(
-            "simple-completion-language-server-{arch}-{os}.tar.gz",
-            arch = match arch {
-                zed::Architecture::Aarch64 => "aarch64",
-                zed::Architecture::X86 => "x86",
-                zed::Architecture::X8664 => "x86_64",
-            },
-            os = match platform {
-                zed::Os::Mac => "apple-darwin",
-                zed::Os::Linux => "unknown-linux-gnu",
-                zed::Os::Windows => "pc-windows-msvc",
-            },
-        );
-
-        let asset = release
-            .assets
-            .iter()
-            .find(|asset| asset.name == asset_name)
-            .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
-
-        let version_dir = format!("simple-completion-language-server-{}", release.version);
-        let binary_path = format!("{version_dir}/simple-completion-language-server");
-
-        if !fs::metadata(&binary_path).is_ok_and(|stat| stat.is_file()) {
-            zed::set_language_server_installation_status(
-                language_server_id,
-                &zed::LanguageServerInstallationStatus::Downloading,
-            );
-
-            zed::download_file(
-                &asset.download_url,
-                &version_dir,
-                zed::DownloadedFileType::GzipTar,
-            )
-            .map_err(|e| format!("failed to download file: {e}"))?;
-
-            let entries =
-                fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
-            for entry in entries {
-                let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
-                if entry.file_name().to_str() != Some(&version_dir) {
-                    fs::remove_dir_all(entry.path()).ok();
-                }
-            }
-        }
-
-        self.cached_binary_path = Some(binary_path.clone());
-        Ok(binary_path)
-    }
-}
-
-impl zed::Extension for SnippetExtension {
-    fn new() -> Self {
-        Self {
-            cached_binary_path: None,
-        }
-    }
-
-    fn language_server_command(
-        &mut self,
-        language_server_id: &LanguageServerId,
-        worktree: &zed::Worktree,
-    ) -> Result<zed::Command> {
-        Ok(zed::Command {
-            command: self.language_server_binary_path(language_server_id, worktree)?,
-            args: vec![],
-            env: vec![("SCLS_CONFIG_SUBDIRECTORY".to_owned(), "zed".to_owned())],
-        })
-    }
-
-    fn language_server_workspace_configuration(
-        &mut self,
-        server_id: &LanguageServerId,
-        worktree: &zed_extension_api::Worktree,
-    ) -> Result<Option<zed_extension_api::serde_json::Value>> {
-        let settings = LspSettings::for_worktree(server_id.as_ref(), worktree)
-            .ok()
-            .and_then(|lsp_settings| lsp_settings.settings)
-            .unwrap_or_else(|| {
-                json!({
-                    "max_completion_items": 20,
-                    "snippets_first": true,
-                    "feature_words": false,
-                    "feature_snippets": true,
-                    // We disable `feature_paths` by default, because it's bad UX to assume that any `/` that is typed
-                    // is the start of a path.
-                    "feature_paths": false
-                })
-            });
-        Ok(Some(settings))
-    }
-}
-
-zed::register_extension!(SnippetExtension);