From 7ad524661d4e0afd55441bd64a4e50e5cd8d8e07 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 2 Mar 2026 23:18:49 +0100 Subject: [PATCH] Remove Supermaven-related code from Zed (#50537) Follow-up of https://github.com/zed-industries/zed/pull/49317 See also https://supermaven.com/blog/sunsetting-supermaven - N/A --- .github/CODEOWNERS.hold | 2 - Cargo.lock | 45 -- Cargo.toml | 5 - crates/agent_ui/src/agent_ui.rs | 10 - crates/edit_prediction/src/edit_prediction.rs | 2 - crates/edit_prediction_ui/Cargo.toml | 1 - .../src/edit_prediction_button.rs | 153 ------ crates/editor/src/editor.rs | 2 +- crates/icons/src/icons.rs | 4 - crates/language/src/language_settings.rs | 3 +- crates/paths/src/paths.rs | 6 - crates/settings_content/src/language.rs | 5 - crates/supermaven/Cargo.toml | 44 -- crates/supermaven/LICENSE-GPL | 1 - crates/supermaven/src/messages.rs | 146 ------ crates/supermaven/src/supermaven.rs | 485 ------------------ .../supermaven_edit_prediction_delegate.rs | 303 ----------- crates/supermaven_api/Cargo.toml | 23 - crates/supermaven_api/LICENSE-GPL | 1 - crates/supermaven_api/src/supermaven_api.rs | 125 ----- crates/zed/Cargo.toml | 1 - crates/zed/src/main.rs | 1 - crates/zed/src/zed.rs | 1 - .../zed/src/zed/edit_prediction_registry.rs | 10 - docs/src/ai/overview.md | 2 +- docs/src/completions.md | 2 +- docs/src/reference/all-settings.md | 12 +- 27 files changed, 5 insertions(+), 1390 deletions(-) delete mode 100644 crates/supermaven/Cargo.toml delete mode 120000 crates/supermaven/LICENSE-GPL delete mode 100644 crates/supermaven/src/messages.rs delete mode 100644 crates/supermaven/src/supermaven.rs delete mode 100644 crates/supermaven/src/supermaven_edit_prediction_delegate.rs delete mode 100644 crates/supermaven_api/Cargo.toml delete mode 120000 crates/supermaven_api/LICENSE-GPL delete mode 100644 crates/supermaven_api/src/supermaven_api.rs diff --git a/.github/CODEOWNERS.hold b/.github/CODEOWNERS.hold index 449a5fd07315845787c9f2a73f0a0a22608e92c3..3d315b36401b2e27e29a2377aeabab8c09c75d39 100644 --- a/.github/CODEOWNERS.hold +++ b/.github/CODEOWNERS.hold @@ -62,8 +62,6 @@ /crates/rules_library/ @zed-industries/ai-team # SUGGESTED: Review needed - based on Richard Feldman (2 commits) /crates/shell_command_parser/ @zed-industries/ai-team -/crates/supermaven/ @zed-industries/ai-team -/crates/supermaven_api/ @zed-industries/ai-team /crates/vercel/ @zed-industries/ai-team /crates/x_ai/ @zed-industries/ai-team /crates/zeta_prompt/ @zed-industries/ai-team diff --git a/Cargo.lock b/Cargo.lock index 1a192869d6fee631d129e23c275c86e51168fe2f..9fce90755106d9159fe2bd206058a5a86761fdf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5403,7 +5403,6 @@ dependencies = [ "semver", "serde_json", "settings", - "supermaven", "telemetry", "text", "theme", @@ -16524,49 +16523,6 @@ dependencies = [ "ztracing", ] -[[package]] -name = "supermaven" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "edit_prediction_types", - "editor", - "env_logger 0.11.8", - "futures 0.3.31", - "gpui", - "http_client", - "language", - "log", - "postage", - "project", - "serde", - "serde_json", - "settings", - "smol", - "supermaven_api", - "text", - "theme", - "ui", - "unicode-segmentation", - "util", -] - -[[package]] -name = "supermaven_api" -version = "0.1.0" -dependencies = [ - "anyhow", - "futures 0.3.31", - "http_client", - "paths", - "serde", - "serde_json", - "smol", - "util", -] - [[package]] name = "sval" version = "2.15.0" @@ -21861,7 +21817,6 @@ dependencies = [ "smol", "snippet_provider", "snippets_ui", - "supermaven", "svg_preview", "sysinfo 0.37.2", "system_specs", diff --git a/Cargo.toml b/Cargo.toml index d505a5ee14b9587c874c33c36fc4b154d900680f..c50b329772669105a7ae3a5f19562fbd186d23ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -182,8 +182,6 @@ members = [ "crates/storybook", "crates/streaming_diff", "crates/sum_tree", - "crates/supermaven", - "crates/supermaven_api", "crates/svg_preview", "crates/system_specs", "crates/tab_switcher", @@ -427,8 +425,6 @@ sqlez_macros = { path = "crates/sqlez_macros" } story = { path = "crates/story" } streaming_diff = { path = "crates/streaming_diff" } sum_tree = { path = "crates/sum_tree" } -supermaven = { path = "crates/supermaven" } -supermaven_api = { path = "crates/supermaven_api" } codestral = { path = "crates/codestral" } system_specs = { path = "crates/system_specs" } tab_switcher = { path = "crates/tab_switcher" } @@ -900,7 +896,6 @@ sidebar = { codegen-units = 1 } snippet = { codegen-units = 1 } snippets_ui = { codegen-units = 1 } story = { codegen-units = 1 } -supermaven_api = { codegen-units = 1 } telemetry_events = { codegen-units = 1 } theme_selector = { codegen-units = 1 } time_format = { codegen-units = 1 } diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index ba188ccb592871c62c6f010f026a8948c8cf89fa..ad778ca496f7815d0155f98187c8fad3e81365eb 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -371,7 +371,6 @@ fn update_command_palette_filter(cx: &mut App) { filter.hide_namespace("agents"); filter.hide_namespace("assistant"); filter.hide_namespace("copilot"); - filter.hide_namespace("supermaven"); filter.hide_namespace("zed_predict_onboarding"); filter.hide_namespace("edit_prediction"); @@ -392,19 +391,11 @@ fn update_command_palette_filter(cx: &mut App) { EditPredictionProvider::None => { filter.hide_namespace("edit_prediction"); filter.hide_namespace("copilot"); - filter.hide_namespace("supermaven"); filter.hide_action_types(&edit_prediction_actions); } EditPredictionProvider::Copilot => { filter.show_namespace("edit_prediction"); filter.show_namespace("copilot"); - filter.hide_namespace("supermaven"); - filter.show_action_types(edit_prediction_actions.iter()); - } - EditPredictionProvider::Supermaven => { - filter.show_namespace("edit_prediction"); - filter.hide_namespace("copilot"); - filter.show_namespace("supermaven"); filter.show_action_types(edit_prediction_actions.iter()); } EditPredictionProvider::Zed @@ -416,7 +407,6 @@ fn update_command_palette_filter(cx: &mut App) { | EditPredictionProvider::Experimental(_) => { filter.show_namespace("edit_prediction"); filter.hide_namespace("copilot"); - filter.hide_namespace("supermaven"); filter.show_action_types(edit_prediction_actions.iter()); } } diff --git a/crates/edit_prediction/src/edit_prediction.rs b/crates/edit_prediction/src/edit_prediction.rs index 02ffcbe065e8b0334ab7c200c0e43b817cdad416..a29779d30de007043141b3958c0c449b230cc384 100644 --- a/crates/edit_prediction/src/edit_prediction.rs +++ b/crates/edit_prediction/src/edit_prediction.rs @@ -1836,7 +1836,6 @@ fn is_ep_store_provider(provider: EditPredictionProvider) -> bool { | EditPredictionProvider::Experimental(_) => true, EditPredictionProvider::None | EditPredictionProvider::Copilot - | EditPredictionProvider::Supermaven | EditPredictionProvider::Codestral => false, } } @@ -1877,7 +1876,6 @@ impl EditPredictionStore { EditPredictionProvider::OpenAiCompatibleApi => (false, 2), EditPredictionProvider::None | EditPredictionProvider::Copilot - | EditPredictionProvider::Supermaven | EditPredictionProvider::Codestral => { log::error!("queue_prediction_refresh called with non-store provider"); return; diff --git a/crates/edit_prediction_ui/Cargo.toml b/crates/edit_prediction_ui/Cargo.toml index d4a7c5d3ab800f54476a8e88914dcaaba3a26547..05afbabd2045e9bca591b6c2edba846e95953a4f 100644 --- a/crates/edit_prediction_ui/Cargo.toml +++ b/crates/edit_prediction_ui/Cargo.toml @@ -40,7 +40,6 @@ paths.workspace = true project.workspace = true regex.workspace = true settings.workspace = true -supermaven.workspace = true telemetry.workspace = true text.workspace = true theme.workspace = true diff --git a/crates/edit_prediction_ui/src/edit_prediction_button.rs b/crates/edit_prediction_ui/src/edit_prediction_button.rs index 729b901be1556f011c101258d34af9b98b45f272..c1fcd78f3f0cee24e6e8d936bf6af56f8d1ebda0 100644 --- a/crates/edit_prediction_ui/src/edit_prediction_button.rs +++ b/crates/edit_prediction_ui/src/edit_prediction_button.rs @@ -30,7 +30,6 @@ use std::{ sync::{Arc, LazyLock}, time::Duration, }; -use supermaven::{AccountStatus, Supermaven}; use ui::{ Clickable, ContextMenu, ContextMenuEntry, DocumentationSide, IconButton, IconButtonShape, Indicator, PopoverMenu, PopoverMenuHandle, ProgressBar, Tooltip, prelude::*, @@ -75,13 +74,6 @@ pub struct EditPredictionButton { project: WeakEntity, } -enum SupermavenButtonStatus { - Ready, - Errored(String), - NeedsActivation(String), - Initializing, -} - impl Render for EditPredictionButton { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { // Return empty div if AI is disabled @@ -188,101 +180,6 @@ impl Render for EditPredictionButton { .with_handle(self.popover_menu_handle.clone()), ) } - EditPredictionProvider::Supermaven => { - let Some(supermaven) = Supermaven::global(cx) else { - return div(); - }; - - let supermaven = supermaven.read(cx); - - let status = match supermaven { - Supermaven::Starting => SupermavenButtonStatus::Initializing, - Supermaven::FailedDownload { error } => { - SupermavenButtonStatus::Errored(error.to_string()) - } - Supermaven::Spawned(agent) => { - let account_status = agent.account_status.clone(); - match account_status { - AccountStatus::NeedsActivation { activate_url } => { - SupermavenButtonStatus::NeedsActivation(activate_url) - } - AccountStatus::Unknown => SupermavenButtonStatus::Initializing, - AccountStatus::Ready => SupermavenButtonStatus::Ready, - } - } - Supermaven::Error { error } => { - SupermavenButtonStatus::Errored(error.to_string()) - } - }; - - let icon = status.to_icon(); - let tooltip_text = status.to_tooltip(); - let has_menu = status.has_menu(); - let this = cx.weak_entity(); - let fs = self.fs.clone(); - let file = self.file.clone(); - let language = self.language.clone(); - let project = self.project.clone(); - - div().child( - PopoverMenu::new("supermaven") - .on_open({ - let file = file.clone(); - let language = language; - let project = project; - Rc::new(move |_window, cx| { - emit_edit_prediction_menu_opened( - "supermaven", - &file, - &language, - &project, - cx, - ); - }) - }) - .menu(move |window, cx| match &status { - SupermavenButtonStatus::NeedsActivation(activate_url) => { - Some(ContextMenu::build(window, cx, |menu, _, _| { - let fs = fs.clone(); - let activate_url = activate_url.clone(); - - menu.entry("Sign In", None, move |_, cx| { - cx.open_url(activate_url.as_str()) - }) - .entry( - "Use Zed AI", - None, - move |_, cx| { - set_completion_provider( - fs.clone(), - cx, - EditPredictionProvider::Zed, - ) - }, - ) - })) - } - SupermavenButtonStatus::Ready => this - .update(cx, |this, cx| { - this.build_supermaven_context_menu(window, cx) - }) - .ok(), - _ => None, - }) - .anchor(Corner::BottomRight) - .trigger_with_tooltip( - IconButton::new("supermaven-icon", icon), - move |window, cx| { - if has_menu { - Tooltip::for_action(tooltip_text.clone(), &ToggleMenu, cx) - } else { - Tooltip::text(tooltip_text.clone())(window, cx) - } - }, - ) - .with_handle(self.popover_menu_handle.clone()), - ) - } EditPredictionProvider::Codestral => { let enabled = self.editor_enabled.unwrap_or(true); let has_api_key = codestral::codestral_api_key(cx).is_some(); @@ -1120,21 +1017,6 @@ impl EditPredictionButton { }) } - fn build_supermaven_context_menu( - &self, - window: &mut Window, - cx: &mut Context, - ) -> Entity { - ContextMenu::build(window, cx, |menu, window, cx| { - let menu = self.build_language_settings_menu(menu, window, cx); - let menu = - self.add_provider_switching_section(menu, EditPredictionProvider::Supermaven, cx); - - menu.separator() - .action("Sign Out", supermaven::SignOut.boxed_clone()) - }) - } - fn build_codestral_context_menu( &self, window: &mut Window, @@ -1384,33 +1266,6 @@ impl StatusItemView for EditPredictionButton { } } -impl SupermavenButtonStatus { - fn to_icon(&self) -> IconName { - match self { - SupermavenButtonStatus::Ready => IconName::Supermaven, - SupermavenButtonStatus::Errored(_) => IconName::SupermavenError, - SupermavenButtonStatus::NeedsActivation(_) => IconName::SupermavenInit, - SupermavenButtonStatus::Initializing => IconName::SupermavenInit, - } - } - - fn to_tooltip(&self) -> String { - match self { - SupermavenButtonStatus::Ready => "Supermaven is ready".to_string(), - SupermavenButtonStatus::Errored(error) => format!("Supermaven error: {}", error), - SupermavenButtonStatus::NeedsActivation(_) => "Supermaven needs activation".to_string(), - SupermavenButtonStatus::Initializing => "Supermaven initializing".to_string(), - } - } - - fn has_menu(&self) -> bool { - match self { - SupermavenButtonStatus::Ready | SupermavenButtonStatus::NeedsActivation(_) => true, - SupermavenButtonStatus::Errored(_) | SupermavenButtonStatus::Initializing => false, - } - } -} - async fn open_disabled_globs_setting_in_editor( workspace: WeakEntity, cx: &mut AsyncWindowContext, @@ -1507,14 +1362,6 @@ pub fn get_available_providers(cx: &mut App) -> Vec { providers.push(EditPredictionProvider::Copilot); }; - if let Some(supermaven) = Supermaven::global(cx) { - if let Supermaven::Spawned(agent) = supermaven.read(cx) { - if matches!(agent.account_status, AccountStatus::Ready) { - providers.push(EditPredictionProvider::Supermaven); - } - } - } - if codestral::codestral_api_key(cx).is_some() { providers.push(EditPredictionProvider::Codestral); } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6f1961a97880e0f5f55577c406b77e3796568a8e..0c2699304830482ba5a9ac23d561d9ea9d8c5b61 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -28579,7 +28579,7 @@ fn edit_prediction_edit_text( } fn edit_prediction_fallback_text(edits: &[(Range, Arc)], cx: &App) -> HighlightedText { - // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven) + // Fallback for providers that don't provide edit_preview (like Copilot) // Just show the raw edit text with basic styling let mut text = String::new(); let mut highlights = Vec::new(); diff --git a/crates/icons/src/icons.rs b/crates/icons/src/icons.rs index 73db39afdc5e9bd15f084043370d27f0494569a6..07204548ff5f2884bb4a5429267a02981ab3e78f 100644 --- a/crates/icons/src/icons.rs +++ b/crates/icons/src/icons.rs @@ -225,10 +225,6 @@ pub enum IconName { Star, StarFilled, Stop, - Supermaven, - SupermavenDisabled, - SupermavenError, - SupermavenInit, SwatchBook, SweepAi, SweepAiDisabled, diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 40e3da789d4785cc5fd56589b09735ba8592ebc7..9a379697e8bddf9dc71d3d340d5e2a92d8b4405e 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -396,8 +396,7 @@ impl InlayHintSettings { } } -/// The settings for edit predictions, such as [GitHub Copilot](https://github.com/features/copilot) -/// or [Supermaven](https://supermaven.com). +/// The settings for edit predictions, such as [GitHub Copilot](https://github.com/features/copilot). #[derive(Clone, Debug, Default)] pub struct EditPredictionSettings { /// The provider that supplies edit predictions. diff --git a/crates/paths/src/paths.rs b/crates/paths/src/paths.rs index 656188e249fc864e1328c8f458bdc46aa7eaea3a..40e10fb3badaf2e00c6dbcc75af06e7b758faa81 100644 --- a/crates/paths/src/paths.rs +++ b/crates/paths/src/paths.rs @@ -419,12 +419,6 @@ pub fn copilot_dir() -> &'static PathBuf { COPILOT_DIR.get_or_init(|| data_dir().join("copilot")) } -/// Returns the path to the Supermaven directory. -pub fn supermaven_dir() -> &'static PathBuf { - static SUPERMAVEN_DIR: OnceLock = OnceLock::new(); - SUPERMAVEN_DIR.get_or_init(|| data_dir().join("supermaven")) -} - /// Returns the path to the default Prettier directory. pub fn default_prettier_dir() -> &'static PathBuf { static DEFAULT_PRETTIER_DIR: OnceLock = OnceLock::new(); diff --git a/crates/settings_content/src/language.rs b/crates/settings_content/src/language.rs index ab526c405a4b34962c298d68365cb828975628b1..db22f3a9e1448dbc529c133fb0195c422f02bc40 100644 --- a/crates/settings_content/src/language.rs +++ b/crates/settings_content/src/language.rs @@ -81,7 +81,6 @@ pub enum EditPredictionProvider { None, #[default] Copilot, - Supermaven, Zed, Codestral, Ollama, @@ -103,7 +102,6 @@ impl<'de> Deserialize<'de> for EditPredictionProvider { pub enum Content { None, Copilot, - Supermaven, Zed, Codestral, Ollama, @@ -116,7 +114,6 @@ impl<'de> Deserialize<'de> for EditPredictionProvider { Ok(match Content::deserialize(deserializer)? { Content::None => EditPredictionProvider::None, Content::Copilot => EditPredictionProvider::Copilot, - Content::Supermaven => EditPredictionProvider::Supermaven, Content::Zed => EditPredictionProvider::Zed, Content::Codestral => EditPredictionProvider::Codestral, Content::Ollama => EditPredictionProvider::Ollama, @@ -146,7 +143,6 @@ impl EditPredictionProvider { EditPredictionProvider::Zed => true, EditPredictionProvider::None | EditPredictionProvider::Copilot - | EditPredictionProvider::Supermaven | EditPredictionProvider::Codestral | EditPredictionProvider::Ollama | EditPredictionProvider::OpenAiCompatibleApi @@ -160,7 +156,6 @@ impl EditPredictionProvider { match self { EditPredictionProvider::Zed => Some("Zed AI"), EditPredictionProvider::Copilot => Some("GitHub Copilot"), - EditPredictionProvider::Supermaven => Some("Supermaven"), EditPredictionProvider::Codestral => Some("Codestral"), EditPredictionProvider::Sweep => Some("Sweep"), EditPredictionProvider::Mercury => Some("Mercury"), diff --git a/crates/supermaven/Cargo.toml b/crates/supermaven/Cargo.toml deleted file mode 100644 index c2d0c48a9e7733402eae32886c0863326882c134..0000000000000000000000000000000000000000 --- a/crates/supermaven/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "supermaven" -version = "0.1.0" -edition.workspace = true -publish.workspace = true -license = "GPL-3.0-or-later" - -[lints] -workspace = true - -[lib] -path = "src/supermaven.rs" -doctest = false - -[dependencies] -anyhow.workspace = true -client.workspace = true -collections.workspace = true -edit_prediction_types.workspace = true -futures.workspace = true -gpui.workspace = true -language.workspace = true -log.workspace = true -postage.workspace = true -serde.workspace = true -serde_json.workspace = true -settings.workspace = true -smol.workspace = true -supermaven_api.workspace = true -text.workspace = true -ui.workspace = true -unicode-segmentation.workspace = true -util.workspace = true - -[dev-dependencies] -editor = { workspace = true, features = ["test-support"] } -env_logger.workspace = true -gpui = { workspace = true, features = ["test-support"] } -language = { workspace = true, features = ["test-support"] } -project = { workspace = true, features = ["test-support"] } -settings = { workspace = true, features = ["test-support"] } -theme = { workspace = true, features = ["test-support"] } -util = { workspace = true, features = ["test-support"] } -http_client = { workspace = true, features = ["test-support"] } diff --git a/crates/supermaven/LICENSE-GPL b/crates/supermaven/LICENSE-GPL deleted file mode 120000 index 89e542f750cd3860a0598eff0dc34b56d7336dc4..0000000000000000000000000000000000000000 --- a/crates/supermaven/LICENSE-GPL +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE-GPL \ No newline at end of file diff --git a/crates/supermaven/src/messages.rs b/crates/supermaven/src/messages.rs deleted file mode 100644 index 9210343587bbb2cbf172a62a2eff73bbbb7cfb72..0000000000000000000000000000000000000000 --- a/crates/supermaven/src/messages.rs +++ /dev/null @@ -1,146 +0,0 @@ -use serde::{Deserialize, Serialize}; - -// Outbound messages -#[derive(Debug, Serialize)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum OutboundMessage { - StateUpdate(StateUpdateMessage), - #[allow(dead_code)] - UseFreeVersion, - Logout, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct StateUpdateMessage { - pub new_id: String, - pub updates: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum StateUpdate { - FileUpdate(FileUpdateMessage), - CursorUpdate(CursorPositionUpdateMessage), -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub struct FileUpdateMessage { - pub path: String, - pub content: String, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub struct CursorPositionUpdateMessage { - pub path: String, - pub offset: usize, -} - -// Inbound messages coming in on stdout - -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum ResponseItem { - // A completion - Text { text: String }, - // Vestigial message type from old versions -- safe to ignore - Del { text: String }, - // Be able to delete whitespace prior to the cursor, likely for the rest of the completion - Dedent { text: String }, - // When the completion is over - End, - // Got the closing parentheses and shouldn't show any more after - Barrier, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SupermavenResponse { - pub state_id: String, - pub items: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct SupermavenMetadataMessage { - pub dust_strings: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct SupermavenTaskUpdateMessage { - pub task: String, - pub status: TaskStatus, - pub percent_complete: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum TaskStatus { - InProgress, - Complete, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct SupermavenActiveRepoMessage { - pub repo_simple_name: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum SupermavenPopupAction { - OpenUrl { label: String, url: String }, - NoOp { label: String }, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub struct SupermavenPopupMessage { - pub message: String, - pub actions: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "kind", rename_all = "camelCase")] -pub struct ActivationRequest { - pub activate_url: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SupermavenSetMessage { - pub key: String, - pub value: serde_json::Value, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ServiceTier { - FreeNoLicense, - #[serde(other)] - Unknown, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum SupermavenMessage { - Response(SupermavenResponse), - Metadata(SupermavenMetadataMessage), - Apology { - message: Option, - }, - ActivationRequest(ActivationRequest), - ActivationSuccess, - Passthrough { - passthrough: Box, - }, - Popup(SupermavenPopupMessage), - TaskStatus(SupermavenTaskUpdateMessage), - ActiveRepo(SupermavenActiveRepoMessage), - ServiceTier { - service_tier: ServiceTier, - }, - - Set(SupermavenSetMessage), - #[serde(other)] - Unknown, -} diff --git a/crates/supermaven/src/supermaven.rs b/crates/supermaven/src/supermaven.rs deleted file mode 100644 index 96f9b9c58bf934ae3991375ee8ef15cbf990dcc4..0000000000000000000000000000000000000000 --- a/crates/supermaven/src/supermaven.rs +++ /dev/null @@ -1,485 +0,0 @@ -mod messages; -mod supermaven_edit_prediction_delegate; - -pub use supermaven_edit_prediction_delegate::*; - -use anyhow::{Context as _, Result}; -#[allow(unused_imports)] -use client::{Client, proto}; -use collections::BTreeMap; - -use futures::{AsyncBufReadExt, StreamExt, channel::mpsc, io::BufReader}; -use gpui::{App, AsyncApp, Context, Entity, EntityId, Global, Task, WeakEntity, actions}; -use language::{ - Anchor, Buffer, BufferSnapshot, ToOffset, language_settings::all_language_settings, -}; -use messages::*; -use postage::watch; -use serde::{Deserialize, Serialize}; -use settings::SettingsStore; -use smol::io::AsyncWriteExt; -use std::{path::PathBuf, sync::Arc}; -use ui::prelude::*; -use util::ResultExt; -use util::command::Child; -use util::command::Stdio; - -actions!( - supermaven, - [ - /// Signs out of Supermaven. - SignOut - ] -); - -pub fn init(client: Arc, cx: &mut App) { - let supermaven = cx.new(|_| Supermaven::Starting); - Supermaven::set_global(supermaven.clone(), cx); - - let mut provider = all_language_settings(None, cx).edit_predictions.provider; - if provider == language::language_settings::EditPredictionProvider::Supermaven { - supermaven.update(cx, |supermaven, cx| supermaven.start(client.clone(), cx)); - } - - cx.observe_global::(move |cx| { - let new_provider = all_language_settings(None, cx).edit_predictions.provider; - if new_provider != provider { - provider = new_provider; - if provider == language::language_settings::EditPredictionProvider::Supermaven { - supermaven.update(cx, |supermaven, cx| supermaven.start(client.clone(), cx)); - } else { - supermaven.update(cx, |supermaven, _cx| supermaven.stop()); - } - } - }) - .detach(); - - cx.on_action(|_: &SignOut, cx| { - if let Some(supermaven) = Supermaven::global(cx) { - supermaven.update(cx, |supermaven, _cx| supermaven.sign_out()); - } - }); -} - -pub enum Supermaven { - Starting, - FailedDownload { error: anyhow::Error }, - Spawned(SupermavenAgent), - Error { error: anyhow::Error }, -} - -#[derive(Clone)] -pub enum AccountStatus { - Unknown, - NeedsActivation { activate_url: String }, - Ready, -} - -#[derive(Clone)] -struct SupermavenGlobal(Entity); - -impl Global for SupermavenGlobal {} - -impl Supermaven { - pub fn global(cx: &App) -> Option> { - cx.try_global::() - .map(|model| model.0.clone()) - } - - pub fn set_global(supermaven: Entity, cx: &mut App) { - cx.set_global(SupermavenGlobal(supermaven)); - } - - pub fn start(&mut self, client: Arc, cx: &mut Context) { - if let Self::Starting = self { - cx.spawn(async move |this, cx| { - let binary_path = - supermaven_api::get_supermaven_agent_path(client.http_client()).await?; - - this.update(cx, |this, cx| { - if let Self::Starting = this { - *this = - Self::Spawned(SupermavenAgent::new(binary_path, client.clone(), cx)?); - } - anyhow::Ok(()) - }) - }) - .detach_and_log_err(cx) - } - } - - pub fn stop(&mut self) { - *self = Self::Starting; - } - - pub fn is_enabled(&self) -> bool { - matches!(self, Self::Spawned { .. }) - } - - pub fn complete( - &mut self, - buffer: &Entity, - cursor_position: Anchor, - cx: &App, - ) -> Option { - if let Self::Spawned(agent) = self { - let buffer_id = buffer.entity_id(); - let buffer = buffer.read(cx); - let path = buffer - .file() - .and_then(|file| Some(file.as_local()?.abs_path(cx))) - .unwrap_or_else(|| PathBuf::from("untitled")) - .to_string_lossy() - .to_string(); - let content = buffer.text(); - let offset = cursor_position.to_offset(buffer); - let state_id = agent.next_state_id; - agent.next_state_id.0 += 1; - - let (updates_tx, mut updates_rx) = watch::channel(); - postage::stream::Stream::try_recv(&mut updates_rx).unwrap(); - - agent.states.insert( - state_id, - SupermavenCompletionState { - buffer_id, - prefix_anchor: cursor_position, - prefix_offset: offset, - text: String::new(), - dedent: String::new(), - updates_tx, - }, - ); - // ensure the states map is max 1000 elements - if agent.states.len() > 1000 { - // state id is monotonic so it's sufficient to remove the first element - agent - .states - .remove(&agent.states.keys().next().unwrap().clone()); - } - - let _ = agent - .outgoing_tx - .unbounded_send(OutboundMessage::StateUpdate(StateUpdateMessage { - new_id: state_id.0.to_string(), - updates: vec![ - StateUpdate::FileUpdate(FileUpdateMessage { - path: path.clone(), - content, - }), - StateUpdate::CursorUpdate(CursorPositionUpdateMessage { path, offset }), - ], - })); - - Some(SupermavenCompletion { - id: state_id, - updates: updates_rx, - }) - } else { - None - } - } - - pub fn completion( - &self, - buffer: &Entity, - cursor_position: Anchor, - cx: &App, - ) -> Option<&str> { - if let Self::Spawned(agent) = self { - find_relevant_completion( - &agent.states, - buffer.entity_id(), - &buffer.read(cx).snapshot(), - cursor_position, - ) - } else { - None - } - } - - pub fn sign_out(&mut self) { - if let Self::Spawned(agent) = self { - agent - .outgoing_tx - .unbounded_send(OutboundMessage::Logout) - .ok(); - // The account status will get set to RequiresActivation or Ready when the next - // message from the agent comes in. Until that happens, set the status to Unknown - // to disable the button. - agent.account_status = AccountStatus::Unknown; - } - } -} - -fn find_relevant_completion<'a>( - states: &'a BTreeMap, - buffer_id: EntityId, - buffer: &BufferSnapshot, - cursor_position: Anchor, -) -> Option<&'a str> { - let mut best_completion: Option<&str> = None; - 'completions: for state in states.values() { - if state.buffer_id != buffer_id { - continue; - } - let Some(state_completion) = state.text.strip_prefix(&state.dedent) else { - continue; - }; - - let current_cursor_offset = cursor_position.to_offset(buffer); - if current_cursor_offset < state.prefix_offset { - continue; - } - - let original_cursor_offset = buffer.clip_offset(state.prefix_offset, text::Bias::Left); - let text_inserted_since_completion_request: String = buffer - .text_for_range(original_cursor_offset..current_cursor_offset) - .collect(); - let trimmed_completion = - match state_completion.strip_prefix(&text_inserted_since_completion_request) { - Some(suffix) => suffix, - None => continue 'completions, - }; - - if best_completion.is_some_and(|best| best.len() > trimmed_completion.len()) { - continue; - } - - best_completion = Some(trimmed_completion); - } - best_completion -} - -pub struct SupermavenAgent { - _process: Child, - next_state_id: SupermavenCompletionStateId, - states: BTreeMap, - outgoing_tx: mpsc::UnboundedSender, - _handle_outgoing_messages: Task>, - _handle_incoming_messages: Task>, - pub account_status: AccountStatus, - service_tier: Option, - #[allow(dead_code)] - client: Arc, -} - -impl SupermavenAgent { - fn new( - binary_path: PathBuf, - client: Arc, - cx: &mut Context, - ) -> Result { - let mut process = util::command::new_command(&binary_path) - .arg("stdio") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .kill_on_drop(true) - .spawn() - .context("failed to start the binary")?; - - let stdin = process - .stdin - .take() - .context("failed to get stdin for process")?; - let stdout = process - .stdout - .take() - .context("failed to get stdout for process")?; - - let (outgoing_tx, outgoing_rx) = mpsc::unbounded(); - - Ok(Self { - _process: process, - next_state_id: SupermavenCompletionStateId::default(), - states: BTreeMap::default(), - outgoing_tx, - _handle_outgoing_messages: cx.spawn(async move |_, _cx| { - Self::handle_outgoing_messages(outgoing_rx, stdin).await - }), - _handle_incoming_messages: cx.spawn(async move |this, cx| { - Self::handle_incoming_messages(this, stdout, cx).await - }), - account_status: AccountStatus::Unknown, - service_tier: None, - client, - }) - } - - async fn handle_outgoing_messages( - mut outgoing: mpsc::UnboundedReceiver, - mut stdin: W, - ) -> Result<()> { - while let Some(message) = outgoing.next().await { - let bytes = serde_json::to_vec(&message)?; - stdin.write_all(&bytes).await?; - stdin.write_all(&[b'\n']).await?; - } - Ok(()) - } - - async fn handle_incoming_messages( - this: WeakEntity, - stdout: R, - cx: &mut AsyncApp, - ) -> Result<()> { - const MESSAGE_PREFIX: &str = "SM-MESSAGE "; - - let stdout = BufReader::new(stdout); - let mut lines = stdout.lines(); - while let Some(line) = lines.next().await { - let Some(line) = line.context("failed to read line from stdout").log_err() else { - continue; - }; - let Some(line) = line.strip_prefix(MESSAGE_PREFIX) else { - continue; - }; - let Some(message) = serde_json::from_str::(line) - .with_context(|| format!("failed to deserialize line from stdout: {:?}", line)) - .log_err() - else { - continue; - }; - - this.update(cx, |this, _cx| { - if let Supermaven::Spawned(this) = this { - this.handle_message(message); - } - Task::ready(anyhow::Ok(())) - })? - .await?; - } - - Ok(()) - } - - fn handle_message(&mut self, message: SupermavenMessage) { - match message { - SupermavenMessage::ActivationRequest(request) => { - self.account_status = match request.activate_url { - Some(activate_url) => AccountStatus::NeedsActivation { activate_url }, - None => AccountStatus::Ready, - }; - } - SupermavenMessage::ActivationSuccess => { - self.account_status = AccountStatus::Ready; - } - SupermavenMessage::ServiceTier { service_tier } => { - self.account_status = AccountStatus::Ready; - self.service_tier = Some(service_tier); - } - SupermavenMessage::Response(response) => { - let state_id = SupermavenCompletionStateId(response.state_id.parse().unwrap()); - if let Some(state) = self.states.get_mut(&state_id) { - for item in &response.items { - match item { - ResponseItem::Text { text } => state.text.push_str(text), - ResponseItem::Dedent { text } => state.dedent.push_str(text), - _ => {} - } - } - *state.updates_tx.borrow_mut() = (); - } - } - SupermavenMessage::Passthrough { passthrough } => self.handle_message(*passthrough), - _ => { - log::warn!("unhandled message: {:?}", message); - } - } - } -} - -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] -pub struct SupermavenCompletionStateId(usize); - -#[allow(dead_code)] -pub struct SupermavenCompletionState { - buffer_id: EntityId, - prefix_anchor: Anchor, - // prefix_offset is tracked independently because the anchor biases left which - // doesn't allow us to determine if the prior text has been deleted. - prefix_offset: usize, - text: String, - dedent: String, - updates_tx: watch::Sender<()>, -} - -pub struct SupermavenCompletion { - pub id: SupermavenCompletionStateId, - pub updates: watch::Receiver<()>, -} - -#[cfg(test)] -mod tests { - use super::*; - use collections::BTreeMap; - use gpui::TestAppContext; - use language::Buffer; - - #[gpui::test] - async fn test_find_relevant_completion_no_first_letter_skip(cx: &mut TestAppContext) { - let buffer = cx.new(|cx| Buffer::local("hello world", cx)); - let buffer_snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot()); - - let mut states = BTreeMap::new(); - let state_id = SupermavenCompletionStateId(1); - let (updates_tx, _) = watch::channel(); - - states.insert( - state_id, - SupermavenCompletionState { - buffer_id: buffer.entity_id(), - prefix_anchor: buffer_snapshot.anchor_before(0), // Start of buffer - prefix_offset: 0, - text: "hello".to_string(), - dedent: String::new(), - updates_tx, - }, - ); - - let cursor_position = buffer_snapshot.anchor_after(1); - - let result = find_relevant_completion( - &states, - buffer.entity_id(), - &buffer_snapshot, - cursor_position, - ); - - assert_eq!(result, Some("ello")); - } - - #[gpui::test] - async fn test_find_relevant_completion_with_multiple_chars(cx: &mut TestAppContext) { - let buffer = cx.new(|cx| Buffer::local("hello world", cx)); - let buffer_snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot()); - - let mut states = BTreeMap::new(); - let state_id = SupermavenCompletionStateId(1); - let (updates_tx, _) = watch::channel(); - - states.insert( - state_id, - SupermavenCompletionState { - buffer_id: buffer.entity_id(), - prefix_anchor: buffer_snapshot.anchor_before(0), // Start of buffer - prefix_offset: 0, - text: "hello".to_string(), - dedent: String::new(), - updates_tx, - }, - ); - - let cursor_position = buffer_snapshot.anchor_after(3); - - let result = find_relevant_completion( - &states, - buffer.entity_id(), - &buffer_snapshot, - cursor_position, - ); - - assert_eq!(result, Some("lo")); - } -} diff --git a/crates/supermaven/src/supermaven_edit_prediction_delegate.rs b/crates/supermaven/src/supermaven_edit_prediction_delegate.rs deleted file mode 100644 index f9eb4a210cff705d609cad3de13924a86253655a..0000000000000000000000000000000000000000 --- a/crates/supermaven/src/supermaven_edit_prediction_delegate.rs +++ /dev/null @@ -1,303 +0,0 @@ -use crate::{Supermaven, SupermavenCompletionStateId}; -use anyhow::Result; -use edit_prediction_types::{ - EditPrediction, EditPredictionDelegate, EditPredictionDiscardReason, EditPredictionIconSet, -}; -use futures::StreamExt as _; -use gpui::{App, Context, Entity, EntityId, Task}; -use language::{Anchor, Buffer, BufferSnapshot}; -use std::{ - ops::{AddAssign, Range}, - path::Path, - sync::Arc, - time::Duration, -}; -use text::{ToOffset, ToPoint}; -use ui::prelude::*; -use unicode_segmentation::UnicodeSegmentation; - -pub const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75); - -pub struct SupermavenEditPredictionDelegate { - supermaven: Entity, - buffer_id: Option, - completion_id: Option, - completion_text: Option, - file_extension: Option, - pending_refresh: Option>>, - completion_position: Option, -} - -impl SupermavenEditPredictionDelegate { - pub fn new(supermaven: Entity) -> Self { - Self { - supermaven, - buffer_id: None, - completion_id: None, - completion_text: None, - file_extension: None, - pending_refresh: None, - completion_position: None, - } - } -} - -// Computes the edit prediction from the difference between the completion text. -// This is defined by greedily matching the buffer text against the completion text. -// Inlays are inserted for parts of the completion text that are not present in the buffer text. -// For example, given the completion text "axbyc" and the buffer text "xy", the rendered output in the editor would be "[a]x[b]y[c]". -// The parts in brackets are the inlays. -fn completion_from_diff( - snapshot: BufferSnapshot, - completion_text: &str, - position: Anchor, - delete_range: Range, -) -> EditPrediction { - let buffer_text = snapshot.text_for_range(delete_range).collect::(); - - let mut edits: Vec<(Range, Arc)> = Vec::new(); - - let completion_graphemes: Vec<&str> = completion_text.graphemes(true).collect(); - let buffer_graphemes: Vec<&str> = buffer_text.graphemes(true).collect(); - - let mut offset = position.to_offset(&snapshot); - - let mut i = 0; - let mut j = 0; - while i < completion_graphemes.len() && j < buffer_graphemes.len() { - // find the next instance of the buffer text in the completion text. - let k = completion_graphemes[i..] - .iter() - .position(|c| *c == buffer_graphemes[j]); - match k { - Some(k) => { - if k != 0 { - let offset = snapshot.anchor_after(offset); - // the range from the current position to item is an inlay. - let edit = ( - offset..offset, - completion_graphemes[i..i + k].join("").into(), - ); - edits.push(edit); - } - i += k + 1; - j += 1; - offset.add_assign(buffer_graphemes[j - 1].len()); - } - None => { - // there are no more matching completions, so drop the remaining - // completion text as an inlay. - break; - } - } - } - - if j == buffer_graphemes.len() && i < completion_graphemes.len() { - let offset = snapshot.anchor_after(offset); - // there is leftover completion text, so drop it as an inlay. - let edit_range = offset..offset; - let edit_text = completion_graphemes[i..].join(""); - edits.push((edit_range, edit_text.into())); - } - - EditPrediction::Local { - id: None, - edits, - cursor_position: None, - edit_preview: None, - } -} - -impl EditPredictionDelegate for SupermavenEditPredictionDelegate { - fn name() -> &'static str { - "supermaven" - } - - fn display_name() -> &'static str { - "Supermaven" - } - - fn show_predictions_in_menu() -> bool { - true - } - - fn show_tab_accept_marker() -> bool { - true - } - - fn supports_jump_to_edit() -> bool { - false - } - - fn icons(&self, _cx: &App) -> EditPredictionIconSet { - EditPredictionIconSet::new(IconName::Supermaven) - .with_disabled(IconName::SupermavenDisabled) - .with_error(IconName::SupermavenError) - } - - fn is_enabled(&self, _buffer: &Entity, _cursor_position: Anchor, cx: &App) -> bool { - self.supermaven.read(cx).is_enabled() - } - - fn is_refreshing(&self, _cx: &App) -> bool { - self.pending_refresh.is_some() && self.completion_id.is_none() - } - - fn refresh( - &mut self, - buffer_handle: Entity, - cursor_position: Anchor, - debounce: bool, - cx: &mut Context, - ) { - // Only make new completion requests when debounce is true (i.e., when text is typed) - // When debounce is false (i.e., cursor movement), we should not make new requests - if !debounce { - return; - } - - reset_completion_cache(self, cx); - - let Some(mut completion) = self.supermaven.update(cx, |supermaven, cx| { - supermaven.complete(&buffer_handle, cursor_position, cx) - }) else { - return; - }; - - self.pending_refresh = Some(cx.spawn(async move |this, cx| { - if debounce { - cx.background_executor().timer(DEBOUNCE_TIMEOUT).await; - } - - while let Some(()) = completion.updates.next().await { - this.update(cx, |this, cx| { - // Get the completion text and cache it - if let Some(text) = - this.supermaven - .read(cx) - .completion(&buffer_handle, cursor_position, cx) - { - this.completion_text = Some(text.to_string()); - - this.completion_position = Some(cursor_position); - } - - this.completion_id = Some(completion.id); - this.buffer_id = Some(buffer_handle.entity_id()); - this.file_extension = buffer_handle.read(cx).file().and_then(|file| { - Some( - Path::new(file.file_name(cx)) - .extension()? - .to_str()? - .to_string(), - ) - }); - cx.notify(); - })?; - } - Ok(()) - })); - } - - fn accept(&mut self, _cx: &mut Context) { - reset_completion_cache(self, _cx); - } - - fn discard(&mut self, _reason: EditPredictionDiscardReason, _cx: &mut Context) { - reset_completion_cache(self, _cx); - } - - fn suggest( - &mut self, - buffer: &Entity, - cursor_position: Anchor, - cx: &mut Context, - ) -> Option { - if self.buffer_id != Some(buffer.entity_id()) { - return None; - } - - if self.completion_id.is_none() { - return None; - } - - let completion_text = if let Some(cached_text) = &self.completion_text { - cached_text.as_str() - } else { - let text = self - .supermaven - .read(cx) - .completion(buffer, cursor_position, cx)?; - self.completion_text = Some(text.to_string()); - text - }; - - // Check if the cursor is still at the same position as the completion request - // If we don't have a completion position stored, don't show the completion - if let Some(completion_position) = self.completion_position { - if cursor_position != completion_position { - return None; - } - } else { - return None; - } - - let completion_text = trim_to_end_of_line_unless_leading_newline(completion_text); - - let completion_text = completion_text.trim_end(); - - if !completion_text.trim().is_empty() { - let snapshot = buffer.read(cx).snapshot(); - - // Calculate the range from cursor to end of line correctly - let cursor_point = cursor_position.to_point(&snapshot); - let end_of_line = snapshot.anchor_after(language::Point::new( - cursor_point.row, - snapshot.line_len(cursor_point.row), - )); - let delete_range = cursor_position..end_of_line; - - Some(completion_from_diff( - snapshot, - completion_text, - cursor_position, - delete_range, - )) - } else { - None - } - } -} - -fn reset_completion_cache( - provider: &mut SupermavenEditPredictionDelegate, - _cx: &mut Context, -) { - provider.pending_refresh = None; - provider.completion_id = None; - provider.completion_text = None; - provider.completion_position = None; - provider.buffer_id = None; -} - -fn trim_to_end_of_line_unless_leading_newline(text: &str) -> &str { - if has_leading_newline(text) { - text - } else if let Some(i) = text.find('\n') { - &text[..i] - } else { - text - } -} - -fn has_leading_newline(text: &str) -> bool { - for c in text.chars() { - if c == '\n' { - return true; - } - if !c.is_whitespace() { - return false; - } - } - false -} diff --git a/crates/supermaven_api/Cargo.toml b/crates/supermaven_api/Cargo.toml deleted file mode 100644 index 28868a9a7433f995e99b861cf7f6e9aeeb28942f..0000000000000000000000000000000000000000 --- a/crates/supermaven_api/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "supermaven_api" -version = "0.1.0" -edition.workspace = true -publish.workspace = true -license = "GPL-3.0-or-later" - -[lints] -workspace = true - -[lib] -path = "src/supermaven_api.rs" -doctest = false - -[dependencies] -anyhow.workspace = true -futures.workspace = true -http_client.workspace = true -paths.workspace = true -serde.workspace = true -serde_json.workspace = true -smol.workspace = true -util.workspace = true diff --git a/crates/supermaven_api/LICENSE-GPL b/crates/supermaven_api/LICENSE-GPL deleted file mode 120000 index 89e542f750cd3860a0598eff0dc34b56d7336dc4..0000000000000000000000000000000000000000 --- a/crates/supermaven_api/LICENSE-GPL +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE-GPL \ No newline at end of file diff --git a/crates/supermaven_api/src/supermaven_api.rs b/crates/supermaven_api/src/supermaven_api.rs deleted file mode 100644 index 97e70e58a18fc277d8cb17e2fb8fd3c71b884420..0000000000000000000000000000000000000000 --- a/crates/supermaven_api/src/supermaven_api.rs +++ /dev/null @@ -1,125 +0,0 @@ -use anyhow::{Context as _, Result}; -use futures::AsyncReadExt; -use futures::io::BufReader; -use http_client::{AsyncBody, HttpClient, Request as HttpRequest}; -use paths::supermaven_dir; -use serde::Deserialize; -use smol::fs::{self, File}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -use util::fs::{make_file_executable, remove_matching}; - -#[derive(Deserialize)] -pub struct SupermavenApiError { - pub message: String, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SupermavenDownloadResponse { - pub download_url: String, - pub version: u64, - pub sha256_hash: String, -} - -pub async fn latest_release( - client: Arc, - platform: &str, - arch: &str, -) -> Result { - let uri = format!( - "https://supermaven.com/api/download-path?platform={}&arch={}", - platform, arch - ); - - // Download is not authenticated - let request = HttpRequest::get(&uri); - - let mut response = client - .send(request.body(AsyncBody::default())?) - .await - .with_context(|| "Unable to acquire Supermaven Agent".to_string())?; - - let mut body = Vec::new(); - response.body_mut().read_to_end(&mut body).await?; - - if response.status().is_client_error() || response.status().is_server_error() { - let body_str = std::str::from_utf8(&body)?; - let error: SupermavenApiError = serde_json::from_str(body_str)?; - anyhow::bail!("Supermaven API error: {}", error.message); - } - - serde_json::from_slice::(&body) - .with_context(|| "Unable to parse Supermaven Agent response".to_string()) -} - -pub fn version_path(version: u64) -> PathBuf { - supermaven_dir().join(format!( - "sm-agent-{}{}", - version, - std::env::consts::EXE_SUFFIX - )) -} - -pub async fn has_version(version_path: &Path) -> bool { - fs::metadata(version_path).await.is_ok_and(|m| m.is_file()) -} - -pub async fn get_supermaven_agent_path(client: Arc) -> Result { - fs::create_dir_all(supermaven_dir()) - .await - .with_context(|| { - format!( - "Could not create Supermaven Agent Directory at {:?}", - supermaven_dir() - ) - })?; - - let platform = match std::env::consts::OS { - "macos" => "darwin", - "windows" => "windows", - "linux" => "linux", - unsupported => anyhow::bail!("unsupported platform {unsupported}"), - }; - - let arch = match std::env::consts::ARCH { - "x86_64" => "amd64", - "aarch64" => "arm64", - unsupported => anyhow::bail!("unsupported architecture {unsupported}"), - }; - - let download_info = latest_release(client.clone(), platform, arch).await?; - - let binary_path = version_path(download_info.version); - - if has_version(&binary_path).await { - // Due to an issue with the Supermaven binary not being made executable on - // earlier Zed versions and Supermaven releases not occurring that frequently, - // we ensure here that the found binary is actually executable. - make_file_executable(&binary_path).await?; - - return Ok(binary_path); - } - - let request = HttpRequest::get(&download_info.download_url); - - let mut response = client - .send(request.body(AsyncBody::default())?) - .await - .with_context(|| "Unable to download Supermaven Agent".to_string())?; - - let mut file = File::create(&binary_path) - .await - .with_context(|| format!("Unable to create file at {:?}", binary_path))?; - - futures::io::copy(BufReader::new(response.body_mut()), &mut file) - .await - .with_context(|| format!("Unable to write binary to file at {:?}", binary_path))?; - - make_file_executable(&binary_path).await?; - - remove_matching(supermaven_dir(), |file| file != binary_path).await; - - Ok(binary_path) -} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 82b730ee8f1b50f6f46a7400be908a9442e115d1..16385ccf75e245b4e4bf17cf37a1d04ef3ed9c6b 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -189,7 +189,6 @@ sidebar.workspace = true smol.workspace = true snippet_provider.workspace = true snippets_ui.workspace = true -supermaven.workspace = true svg_preview.workspace = true sysinfo.workspace = true tab_switcher.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 0921c12c2f06cea32ccba0e0bc58553d2fa91ab2..fa07355d69e1e9d6511301464e344533f6bdbd7d 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -638,7 +638,6 @@ fn main() { ); copilot_ui::init(&app_state, cx); - supermaven::init(app_state.client.clone(), cx); language_model::init(app_state.client.clone(), cx); language_models::init(app_state.user_store.clone(), app_state.client.clone(), cx); acp_tools::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index aa1870ba2a9cb07cd7ffee040c68ffa73759e728..55f185aae13e49c6b90610a50ad197ee47ee8a98 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -4860,7 +4860,6 @@ mod tests { "settings_profile_selector", "snippets", "stash_picker", - "supermaven", "svg", "syntax_tree_view", "tab_switcher", diff --git a/crates/zed/src/zed/edit_prediction_registry.rs b/crates/zed/src/zed/edit_prediction_registry.rs index 9381dae22b055b4bd008ee63d0d283581bd513f4..79b33093d86b306c3b0420f919bd555d9ea4ca7a 100644 --- a/crates/zed/src/zed/edit_prediction_registry.rs +++ b/crates/zed/src/zed/edit_prediction_registry.rs @@ -12,7 +12,6 @@ use settings::{ EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME, EditPredictionPromptFormat, SettingsStore, }; use std::{cell::RefCell, rc::Rc, sync::Arc}; -use supermaven::{Supermaven, SupermavenEditPredictionDelegate}; use ui::Window; pub fn init(client: Arc, user_store: Entity, cx: &mut App) { @@ -132,7 +131,6 @@ fn edit_prediction_provider_config_for_settings(cx: &App) -> Option None, EditPredictionProvider::Copilot => Some(EditPredictionProviderConfig::Copilot), - EditPredictionProvider::Supermaven => Some(EditPredictionProviderConfig::Supermaven), EditPredictionProvider::Zed => Some(EditPredictionProviderConfig::Zed( EditPredictionModel::Zeta1, )), @@ -204,7 +202,6 @@ fn infer_prompt_format(model: &str) -> Option { #[derive(Copy, Clone, PartialEq, Eq)] enum EditPredictionProviderConfig { Copilot, - Supermaven, Codestral, Zed(EditPredictionModel), } @@ -213,7 +210,6 @@ impl EditPredictionProviderConfig { fn name(&self) -> &'static str { match self { EditPredictionProviderConfig::Copilot => "Copilot", - EditPredictionProviderConfig::Supermaven => "Supermaven", EditPredictionProviderConfig::Codestral => "Codestral", EditPredictionProviderConfig::Zed(model) => match model { EditPredictionModel::Zeta1 => "Zeta1", @@ -306,12 +302,6 @@ fn assign_edit_prediction_provider( editor.set_edit_prediction_provider(Some(provider), window, cx); } } - Some(EditPredictionProviderConfig::Supermaven) => { - if let Some(supermaven) = Supermaven::global(cx) { - let provider = cx.new(|_| SupermavenEditPredictionDelegate::new(supermaven)); - editor.set_edit_prediction_provider(Some(provider), window, cx); - } - } Some(EditPredictionProviderConfig::Codestral) => { let http_client = client.http_client(); let provider = cx.new(|_| CodestralEditPredictionDelegate::new(http_client)); diff --git a/docs/src/ai/overview.md b/docs/src/ai/overview.md index b05b3ac6a7a3c9ce42e226e75d5e9e28420f8b03..9463f7bbb11cdcb204915fca138e584baa1f9640 100644 --- a/docs/src/ai/overview.md +++ b/docs/src/ai/overview.md @@ -28,7 +28,7 @@ The [Inline Assistant](./inline-assistant.md) works differently: select code or [Edit Prediction](./edit-prediction.md) provides AI code completions on every keystroke. Each keypress sends a request to the prediction provider, which returns single or multi-line suggestions you accept with `tab`. -The default provider is Zeta, Zed's open-source model trained on open data. You can also use GitHub Copilot, Supermaven, or Codestral. +The default provider is Zeta, Zed's open-source model trained on open data. You can also use GitHub Copilot, or Codestral. ## Text threads diff --git a/docs/src/completions.md b/docs/src/completions.md index 9962fd5f24c604bb22f73ba5a797de936f9cb0d4..81c2efa3514a4623408b2869325ab0991ce382d6 100644 --- a/docs/src/completions.md +++ b/docs/src/completions.md @@ -8,7 +8,7 @@ description: Zed's code completions from language servers and edit predictions. Zed supports two sources for completions: 1. "Code Completions" provided by Language Servers (LSPs) automatically installed by Zed or via [Zed Language Extensions](languages.md). -2. "Edit Predictions" provided by Zed's own Zeta model or by external providers like [GitHub Copilot](#github-copilot) or [Supermaven](#supermaven). +2. "Edit Predictions" provided by Zed's own Zeta model or by external providers like [GitHub Copilot](#github-copilot). ## Language Server Code Completions {#code-completions} diff --git a/docs/src/reference/all-settings.md b/docs/src/reference/all-settings.md index 23b59f0b91002c0a920df0df8d61088652281735..32fec4a84d56cf996dc85cf112e4daec7893311b 100644 --- a/docs/src/reference/all-settings.md +++ b/docs/src/reference/all-settings.md @@ -1800,17 +1800,7 @@ While other options may be changed at a runtime and should be placed under `sett } ``` -3. Use Supermaven as the edit prediction provider: - -```json [settings] -{ - "edit_predictions": { - "provider": "supermaven" - } -} -``` - -4. Turn off edit predictions across all providers +3. Turn off edit predictions across all providers ```json [settings] {