diff --git a/scripts/zed-intergration/patches/0001-add-new-module-mega-and-mega_panel.patch b/scripts/zed-intergration/patches/0001-add-new-module-mega-and-mega_panel.patch index 94b62ca34..c5ebff0e2 100644 --- a/scripts/zed-intergration/patches/0001-add-new-module-mega-and-mega_panel.patch +++ b/scripts/zed-intergration/patches/0001-add-new-module-mega-and-mega_panel.patch @@ -1,7 +1,7 @@ From 006bd0e01b5deba6792af824a5a73c32e96efa2c Mon Sep 17 00:00:00 2001 From: Neon Date: Sat, 19 Oct 2024 21:24:00 +0800 -Subject: [PATCH 01/14] add new module mega and mega_panel +Subject: [PATCH 01/24] add new module mega and mega_panel --- Cargo.lock | 23 ++++++ diff --git a/scripts/zed-intergration/patches/0002-mega-integrate-basic-panel-code-with-zed.patch b/scripts/zed-intergration/patches/0002-mega-integrate-basic-panel-code-with-zed.patch index 555228fbb..97bf4f9f6 100644 --- a/scripts/zed-intergration/patches/0002-mega-integrate-basic-panel-code-with-zed.patch +++ b/scripts/zed-intergration/patches/0002-mega-integrate-basic-panel-code-with-zed.patch @@ -1,7 +1,7 @@ From afca3aee776df6674d0c6d17858d944c64ec56ae Mon Sep 17 00:00:00 2001 From: Neon Date: Mon, 21 Oct 2024 09:18:40 +0800 -Subject: [PATCH 02/14] mega: integrate basic panel code with zed +Subject: [PATCH 02/24] mega: integrate basic panel code with zed --- Cargo.lock | 7 +- diff --git a/scripts/zed-intergration/patches/0003-add-mega-panel-trait-implementation.patch b/scripts/zed-intergration/patches/0003-add-mega-panel-trait-implementation.patch index 6ce1cbb17..37eeefa16 100644 --- a/scripts/zed-intergration/patches/0003-add-mega-panel-trait-implementation.patch +++ b/scripts/zed-intergration/patches/0003-add-mega-panel-trait-implementation.patch @@ -1,7 +1,7 @@ From a6636b00e11cfa284e2e152cb2ac0a6d92508a34 Mon Sep 17 00:00:00 2001 From: Neon Date: Mon, 21 Oct 2024 09:33:24 +0800 -Subject: [PATCH 03/14] add: mega `panel` trait implementation +Subject: [PATCH 03/24] add: mega `panel` trait implementation --- Cargo.lock | 1 + diff --git a/scripts/zed-intergration/patches/0004-fix-make-mega_panel-runnable.patch b/scripts/zed-intergration/patches/0004-fix-make-mega_panel-runnable.patch index ad3727602..a010f21cf 100644 --- a/scripts/zed-intergration/patches/0004-fix-make-mega_panel-runnable.patch +++ b/scripts/zed-intergration/patches/0004-fix-make-mega_panel-runnable.patch @@ -1,7 +1,7 @@ From 5e676b9b1669d078a25bcee9a0d1b87743862a30 Mon Sep 17 00:00:00 2001 From: Neon Date: Mon, 21 Oct 2024 10:00:36 +0800 -Subject: [PATCH 04/14] fix: make mega_panel runnable +Subject: [PATCH 04/24] fix: make mega_panel runnable --- assets/settings/default.json | 8 ++++++++ diff --git a/scripts/zed-intergration/patches/0005-mega_panel-very-basic-info-render-support.patch b/scripts/zed-intergration/patches/0005-mega_panel-very-basic-info-render-support.patch index 0980ab501..45f5ccb31 100644 --- a/scripts/zed-intergration/patches/0005-mega_panel-very-basic-info-render-support.patch +++ b/scripts/zed-intergration/patches/0005-mega_panel-very-basic-info-render-support.patch @@ -1,7 +1,7 @@ From e04d4b01af7991ed4b40fcd9553f20e67f2ec53f Mon Sep 17 00:00:00 2001 From: Neon Date: Tue, 22 Oct 2024 16:15:01 +0800 -Subject: [PATCH 05/14] mega_panel: very basic info render support +Subject: [PATCH 05/24] mega_panel: very basic info render support --- Cargo.lock | 4 +- diff --git a/scripts/zed-intergration/patches/0006-mega-basic-rest-api-support.patch b/scripts/zed-intergration/patches/0006-mega-basic-rest-api-support.patch index 6d6cca426..b1981ec2e 100644 --- a/scripts/zed-intergration/patches/0006-mega-basic-rest-api-support.patch +++ b/scripts/zed-intergration/patches/0006-mega-basic-rest-api-support.patch @@ -1,7 +1,7 @@ From 8b57a9f693c95e92ee5299ff30b6cd791c08da71 Mon Sep 17 00:00:00 2001 From: Neon Date: Tue, 22 Oct 2024 19:12:08 +0800 -Subject: [PATCH 06/14] mega: basic rest api support +Subject: [PATCH 06/24] mega: basic rest api support --- Cargo.lock | 1 + diff --git a/scripts/zed-intergration/patches/0007-mega_panel-control-pad-and-information-display.patch b/scripts/zed-intergration/patches/0007-mega_panel-control-pad-and-information-display.patch index caf27d49c..0b0062e84 100644 --- a/scripts/zed-intergration/patches/0007-mega_panel-control-pad-and-information-display.patch +++ b/scripts/zed-intergration/patches/0007-mega_panel-control-pad-and-information-display.patch @@ -1,7 +1,7 @@ From 0ae6bfb40025aca376878982579a322eaa231143 Mon Sep 17 00:00:00 2001 From: Neon Date: Sat, 26 Oct 2024 22:02:19 +0800 -Subject: [PATCH 07/14] mega_panel: control pad and information display +Subject: [PATCH 07/24] mega_panel: control pad and information display --- crates/mega/src/mega.rs | 56 ++++++++--- diff --git a/scripts/zed-intergration/patches/0008-mega-feat-toggle-fuse-mount.patch b/scripts/zed-intergration/patches/0008-mega-feat-toggle-fuse-mount.patch index e7823b524..dbcd35266 100644 --- a/scripts/zed-intergration/patches/0008-mega-feat-toggle-fuse-mount.patch +++ b/scripts/zed-intergration/patches/0008-mega-feat-toggle-fuse-mount.patch @@ -1,7 +1,7 @@ From 8f8d3a30a45fa4f3b2359060d63a51f065c327d9 Mon Sep 17 00:00:00 2001 From: Neon Date: Tue, 29 Oct 2024 18:50:39 +0800 -Subject: [PATCH 08/14] mega: feat toggle fuse mount +Subject: [PATCH 08/24] mega: feat toggle fuse mount --- Cargo.lock | 6 +- diff --git a/scripts/zed-intergration/patches/0009-Fix-dont-restore-previous-workspace.patch b/scripts/zed-intergration/patches/0009-Fix-dont-restore-previous-workspace.patch index 81893938b..4ded2c52a 100644 --- a/scripts/zed-intergration/patches/0009-Fix-dont-restore-previous-workspace.patch +++ b/scripts/zed-intergration/patches/0009-Fix-dont-restore-previous-workspace.patch @@ -1,7 +1,7 @@ From 69f92bb2dbb1b2facf7b60cb6c6e1c0d211c4f4d Mon Sep 17 00:00:00 2001 From: Neon Date: Tue, 29 Oct 2024 21:08:56 +0800 -Subject: [PATCH 09/14] Fix: dont restore previous workspace +Subject: [PATCH 09/24] Fix: dont restore previous workspace --- crates/project/src/project.rs | 106 ++++++++++++++++++++++ diff --git a/scripts/zed-intergration/patches/0010-Fix-checkout-in-project_panel.patch b/scripts/zed-intergration/patches/0010-Fix-checkout-in-project_panel.patch index f23549b68..50aafd162 100644 --- a/scripts/zed-intergration/patches/0010-Fix-checkout-in-project_panel.patch +++ b/scripts/zed-intergration/patches/0010-Fix-checkout-in-project_panel.patch @@ -1,7 +1,7 @@ From f2551513e804e8f43f6fac39d58befffd9dcda39 Mon Sep 17 00:00:00 2001 From: Neon Date: Wed, 30 Oct 2024 10:53:59 +0800 -Subject: [PATCH 10/14] Fix checkout in project_panel +Subject: [PATCH 10/24] Fix checkout in project_panel --- crates/mega/src/mega.rs | 18 +++++++++- diff --git a/scripts/zed-intergration/patches/0011-Add-basic-mega-rest-api-support.patch b/scripts/zed-intergration/patches/0011-Add-basic-mega-rest-api-support.patch index 053b6ffdc..846112f5c 100644 --- a/scripts/zed-intergration/patches/0011-Add-basic-mega-rest-api-support.patch +++ b/scripts/zed-intergration/patches/0011-Add-basic-mega-rest-api-support.patch @@ -1,7 +1,7 @@ From a880b2c68d47990c8229656313b9dbeb1000b87c Mon Sep 17 00:00:00 2001 From: Neon Date: Sun, 10 Nov 2024 22:19:15 +0800 -Subject: [PATCH 11/14] Add basic mega rest api support +Subject: [PATCH 11/24] Add basic mega rest api support --- Cargo.lock | 30 +++ diff --git a/scripts/zed-intergration/patches/0012-fix-api-call-logic.patch b/scripts/zed-intergration/patches/0012-fix-api-call-logic.patch index 0e6c3db5e..7c8be3b75 100644 --- a/scripts/zed-intergration/patches/0012-fix-api-call-logic.patch +++ b/scripts/zed-intergration/patches/0012-fix-api-call-logic.patch @@ -1,7 +1,7 @@ From 3c45fed1fd917dac0051917d7d9685371f35cd60 Mon Sep 17 00:00:00 2001 From: Neon Date: Tue, 12 Nov 2024 16:56:00 +0800 -Subject: [PATCH 12/14] fix api call logic +Subject: [PATCH 12/24] fix api call logic --- Cargo.lock | 54 +++--- diff --git a/scripts/zed-intergration/patches/0013-fix-features-wrongly-opened.patch b/scripts/zed-intergration/patches/0013-fix-features-wrongly-opened.patch index 40c70de01..53aac48b7 100644 --- a/scripts/zed-intergration/patches/0013-fix-features-wrongly-opened.patch +++ b/scripts/zed-intergration/patches/0013-fix-features-wrongly-opened.patch @@ -1,7 +1,7 @@ From 42916a9fae734c3a0b3dcc8d90f77669378a20c2 Mon Sep 17 00:00:00 2001 From: Neon Date: Tue, 12 Nov 2024 17:55:27 +0800 -Subject: [PATCH 13/14] fix features wrongly opened +Subject: [PATCH 13/24] fix features wrongly opened --- crates/mega_panel/Cargo.toml | 8 ++++++-- diff --git a/scripts/zed-intergration/patches/0014-fix-checkout-in-context-menu.patch b/scripts/zed-intergration/patches/0014-fix-checkout-in-context-menu.patch index c6d81c94f..f575ec002 100644 --- a/scripts/zed-intergration/patches/0014-fix-checkout-in-context-menu.patch +++ b/scripts/zed-intergration/patches/0014-fix-checkout-in-context-menu.patch @@ -1,7 +1,7 @@ From 14806e63bfc717cd17f933fb1aecd6cded410497 Mon Sep 17 00:00:00 2001 From: Neon Date: Tue, 12 Nov 2024 19:12:29 +0800 -Subject: [PATCH 14/14] fix checkout in context menu +Subject: [PATCH 14/24] fix checkout in context menu --- crates/mega/src/mega.rs | 47 ++++++----------------- diff --git a/scripts/zed-intergration/patches/0015-fix-reading-mega-settings.patch b/scripts/zed-intergration/patches/0015-fix-reading-mega-settings.patch new file mode 100644 index 000000000..a703f85ba --- /dev/null +++ b/scripts/zed-intergration/patches/0015-fix-reading-mega-settings.patch @@ -0,0 +1,221 @@ +From 1f25b260f410733580a628a4d116a4d4cfbbc286 Mon Sep 17 00:00:00 2001 +From: Neon +Date: Sun, 17 Nov 2024 11:59:31 +0800 +Subject: [PATCH 15/24] fix reading mega settings + +--- + crates/mega/src/mega.rs | 46 +++++++++++++++++++++----------- + crates/mega/src/mega_settings.rs | 10 +++---- + 2 files changed, 36 insertions(+), 20 deletions(-) + +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 41f89a7c1d..b33c487662 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -26,9 +26,11 @@ use serde::Serialize; + use settings::Settings; + use std::collections::{BTreeMap, BTreeSet, HashSet}; + use std::ffi::OsStr; ++use std::fmt::{Debug, Formatter}; + use std::hash::{DefaultHasher, Hash, Hasher}; + use std::io::Read; + use std::path::PathBuf; ++use std::process::Command; + use std::sync::{Arc, RwLock}; + + mod api; +@@ -49,7 +51,10 @@ struct CheckoutState { + path: PathBuf, + mounted: bool, + } ++ + pub struct Mega { ++ fuse_executable: PathBuf, ++ + fuse_running: bool, + fuse_mounted: bool, + +@@ -65,6 +70,17 @@ pub struct MegaFuse {} + + impl EventEmitter for Mega {} + ++impl Debug for Mega { ++ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { ++ write!(f, ++ "fuse_executable: {:?}, mega_url: {}, fuse_url: {}", ++ self.fuse_executable, ++ self.mega_url, ++ self.fuse_url ++ ) ++ } ++} ++ + impl Mega { + pub fn init_settings(cx: &mut AppContext) { + MegaSettings::register(cx); +@@ -75,9 +91,10 @@ impl Mega { + } + + pub fn new(cx: &mut AppContext) -> Self { +- let mount_path = PathBuf::from(MegaSettings::get_global(cx).mount_point.clone()); ++ let mount_path = MegaSettings::get_global(cx).mount_point.clone(); + let mega_url = MegaSettings::get_global(cx).mega_url.clone(); + let fuse_url = MegaSettings::get_global(cx).fuse_url.clone(); ++ let fuse_executable = MegaSettings::get_global(cx).fuse_executable.clone(); + + // To not affected by global proxy settings. + let client = ReqwestClient::new(); +@@ -88,7 +105,9 @@ impl Mega { + None + }; + +- Mega { ++ let mega = Mega { ++ fuse_executable, ++ + fuse_running: false, + fuse_mounted: false, + +@@ -98,7 +117,10 @@ impl Mega { + mega_url, + fuse_url, + http_client: Arc::new(client), +- } ++ }; ++ println!("Mega New: {mega:?}"); ++ ++ mega + } + + pub fn update_status(&mut self, cx: &mut ModelContext) { +@@ -168,8 +190,7 @@ impl Mega { + let path = PathBuf::from(p); // FIXME is there a better way? + cx.spawn(|mega, mut cx| async move { + let recv = mega.update(&mut cx, |this, cx| { +- let param = PathBuf::from(path); +- this.checkout_path(cx, param) ++ this.checkout_path(cx, path) + }).expect("mega delegate not be dropped"); + + if let Ok(Some(resp)) = recv.await { +@@ -227,7 +248,6 @@ impl Mega { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); + let uri = format!( +- // FIXME: settings not work, currently + "{base}/api/fs/mount", + base = self.fuse_url + ); +@@ -240,7 +260,7 @@ impl Mega { + cx.spawn(|_this, _cx| async move { + if let Ok(mut resp) = client + .post_json( +- "http://127.0.0.1:2725/api/fs/mount", ++ uri.as_str(), + AsyncBody::from(body), + ) + .await +@@ -277,7 +297,6 @@ impl Mega { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); + let uri = format!( +- // FIXME: settings not work, currently + "{base}/api/fs/umount", + base = self.fuse_url + ); +@@ -293,7 +312,7 @@ impl Mega { + cx.spawn(|_this, _cx| async move { + if let Ok(mut resp) = client + .post_json( +- "http://127.0.0.1:2725/api/fs/umount", ++ uri.as_str(), + AsyncBody::from(body), + ) + .await +@@ -321,7 +340,6 @@ impl Mega { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); + let uri = format!( +- // FIXME: settings not work, currently + "{base}/api/fs/mpoint", + base = self.fuse_url + ); +@@ -329,7 +347,7 @@ impl Mega { + cx.spawn(|_this, _cx| async move { + if let Ok(mut resp) = client + .get( +- "http://127.0.0.1:2725/api/fs/mpoint", ++ uri.as_str(), + AsyncBody::empty(), + false, + ) +@@ -358,7 +376,6 @@ impl Mega { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); + let uri = format!( +- // FIXME: settings not work, currently + "{base}/api/config", + base = self.fuse_url + ); +@@ -366,7 +383,7 @@ impl Mega { + cx.spawn(|_this, _cx| async move { + if let Ok(mut resp) = client + .get( +- "http://127.0.0.1:2725/api/config", ++ uri.as_str(), + AsyncBody::empty(), + false, + ) +@@ -395,7 +412,6 @@ impl Mega { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); + let uri = format!( +- // FIXME: settings not work, currently + "{base}/api/config", + base = self.fuse_url + ); +@@ -409,7 +425,7 @@ impl Mega { + + cx.spawn(|_this, _cx| async move { + if let Ok(mut resp) = client +- .post_json("http://127.0.0.1:2725/api/config", config.into()) ++ .post_json(uri.as_str(), config.into()) + .await + { + if resp.status().is_success() { +diff --git a/crates/mega/src/mega_settings.rs b/crates/mega/src/mega_settings.rs +index 82721208a1..bada5ccd80 100644 +--- a/crates/mega/src/mega_settings.rs ++++ b/crates/mega/src/mega_settings.rs +@@ -17,23 +17,23 @@ pub struct MegaSettingsContent { + /// Url to communicate with mega + /// + /// Default: http://localhost:8000 +- pub mega_url: String, ++ pub mega_url: Option, + /// Url to communicate with fuse + /// + /// Default: http://localhost:2725 +- pub fuse_url: String, ++ pub fuse_url: Option, + /// Default mount point for fuse + /// + /// Default: "/" (for now) +- pub mount_point: PathBuf, ++ pub mount_point: Option, + /// Path for mega executable + /// + /// Default: "mega" (for now) +- pub mega_executable: PathBuf, ++ pub mega_executable: Option, + /// Path for fuse executable + /// + /// Default: "scorpio" (for now) +- pub fuse_executable: PathBuf, ++ pub fuse_executable: Option, + } + + impl Settings for MegaSettings { +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0016-add-heartbeat-to-detect-if-fuse-running.patch b/scripts/zed-intergration/patches/0016-add-heartbeat-to-detect-if-fuse-running.patch new file mode 100644 index 000000000..73b7f84cd --- /dev/null +++ b/scripts/zed-intergration/patches/0016-add-heartbeat-to-detect-if-fuse-running.patch @@ -0,0 +1,240 @@ +From d45f194e2ef79da93b772a28342a37d0b27193cc Mon Sep 17 00:00:00 2001 +From: Neon +Date: Sun, 17 Nov 2024 17:16:58 +0800 +Subject: [PATCH 16/24] add heartbeat to detect if fuse running + +--- + crates/mega/src/mega.rs | 59 +++++++++++++++++++++++++---- + crates/mega_panel/src/mega_panel.rs | 23 ++++++----- + crates/zed/src/main.rs | 1 - + 3 files changed, 64 insertions(+), 19 deletions(-) + +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index b33c487662..19e9222cf3 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -6,7 +6,6 @@ + // This should be both warrantied by this module and scorpio + // 2. At least one daemon on this machine when zed startup. + // 3. Complete docs. +-// 4. Add settings for this module + + use crate::api::{ + ConfigRequest, ConfigResponse, MountRequest, MountResponse, MountsResponse, UmountRequest, +@@ -18,7 +17,7 @@ use futures::channel::oneshot; + use futures::channel::oneshot::Receiver; + use futures::{AsyncReadExt, FutureExt, SinkExt, TryFutureExt}; + use gpui::http_client::{AsyncBody, HttpClient, HttpRequestExt}; +-use gpui::{AppContext, Context, EventEmitter, ModelContext, Path}; ++use gpui::{AppContext, Context, EventEmitter, ModelContext, Path, Task}; + use radix_trie::{Trie, TrieCommon}; + use reqwest_client::ReqwestClient; + use schemars::_private::NoSerialize; +@@ -32,6 +31,9 @@ use std::io::Read; + use std::path::PathBuf; + use std::process::Command; + use std::sync::{Arc, RwLock}; ++use std::thread::sleep; ++use std::time::Duration; ++use futures::future::MaybeDone::Future; + + mod api; + mod mega_settings; +@@ -57,6 +59,7 @@ pub struct Mega { + + fuse_running: bool, + fuse_mounted: bool, ++ heartbeat: bool, + + mount_point: Option, + checkout_path: Trie, +@@ -110,6 +113,7 @@ impl Mega { + + fuse_running: false, + fuse_mounted: false, ++ heartbeat: false, + + mount_point, + checkout_path: Default::default(), +@@ -118,8 +122,8 @@ impl Mega { + fuse_url, + http_client: Arc::new(client), + }; +- println!("Mega New: {mega:?}"); + ++ println!("Mega New: {mega:?}"); + mega + } + +@@ -135,6 +139,7 @@ impl Mega { + // So we can assume that fuse has been dead. + this.update(&mut cx, |mega, cx| { + mega.fuse_running = false; ++ mega.fuse_mounted = false; + cx.emit(Event::FuseRunning(false)); + cx.emit(Event::FuseMounted(None)); + }) +@@ -159,6 +164,7 @@ impl Mega { + } else { Ok(()) }.unwrap(); + + // When mount point changed, emit an event. ++ // update mount point if it's none. + if let Ok(Some(config)) = config.await { + this.update(&mut cx, |this, cx| { + let path = PathBuf::from(config.config.mount_path); +@@ -170,6 +176,9 @@ impl Mega { + cx.emit(Event::FuseMounted(this.mount_point.clone())); + } + } ++ } else if this.fuse_running && this.mount_point.is_none() { ++ this.mount_point = Some(path); ++ cx.emit(Event::FuseMounted(this.mount_point.clone())); + } + }) + } else { Ok(()) } +@@ -181,9 +190,16 @@ impl Mega { + (self.fuse_running, self.fuse_mounted) + } + ++ /// ## Toggle Fuse checkouts ++ /// Checkout or un-checkout the paths in zed. ++ /// Does nothing if fuse not running. + pub fn toggle_fuse(&mut self, cx: &mut ModelContext) { + self.update_status(cx); + let paths = &self.checkout_path; ++ ++ if !self.fuse_running { ++ return; ++ } + + if !self.fuse_mounted { + for (_, (p, _)) in paths.iter().enumerate() { +@@ -206,8 +222,7 @@ impl Mega { + } + + self.fuse_mounted = true; +- // FIXME: A configurable path from fuse api is needed. +- cx.emit(Event::FuseMounted(Some(PathBuf::from("/home/neon/dic")))); ++ cx.emit(Event::FuseMounted(self.mount_point.clone())); + } else { + for (_, (p, &n)) in paths.iter().enumerate() { + let path = PathBuf::from(p); // FIXME is there a better way? +@@ -234,10 +249,19 @@ impl Mega { + } + } + ++ /// ## Toggle Fuse Mount ++ /// In fact, we cannot `mount` or `umount` a fuse from zed. ++ /// ++ /// This function only opens up a new scorpio executable if it detects fuse not running. + pub fn toggle_mount(&mut self, cx: &mut ModelContext) { +- // FIXME should be able to restart fuse +- self.fuse_running = !self.fuse_running; +- cx.emit(Event::FuseRunning(self.fuse_running)); ++ // We only start it, not stop it. ++ if !self.fuse_running { ++ let _ = Command::new(self.fuse_executable.as_os_str()) ++ .spawn() ++ .expect("Fuse Executable path not right"); ++ ++ self.update_status(cx); ++ } + } + + pub fn checkout_path( +@@ -447,6 +471,25 @@ impl Mega { + rx + } + ++ pub fn heartbeat(&mut self, cx: &mut ModelContext) { ++ if self.heartbeat { ++ return; ++ } else { ++ self.heartbeat = true; ++ } ++ ++ cx.spawn(|this, mut cx| async move { ++ loop { ++ this.update(&mut cx, |mega, cx| { ++ mega.update_status(cx); ++ }).expect("mega delegate not be dropped"); ++ ++ let dur = Duration::from_secs(30); ++ cx.background_executor().timer(dur).await; ++ } ++ }).detach(); ++ } ++ + pub fn is_path_checkout(&self, path: &String) -> bool { + let set = &self.checkout_path; + set.get_ancestor(path).is_some() +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index 5dec15a182..4ea88e5b4c 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -200,7 +200,6 @@ impl MegaPanel { + fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> View { + let mega_panel = cx.new_view(|cx| { + let mega = workspace.mega(); +- + let focus_handle = cx.focus_handle(); + cx.on_focus(&focus_handle, Self::focus_in).detach(); + +@@ -208,6 +207,10 @@ impl MegaPanel { + cx.subscribe(mega, |this, mega, event, cx| { + // TODO: listen for mega events + }).detach(); ++ ++ mega.update(cx, |this, cx| { ++ this.heartbeat(cx); ++ }); + + Self { + mega_handle: mega.clone(), +@@ -281,6 +284,15 @@ impl MegaPanel { + .id("mega-control-pad") + .size_full() + .children([ ++ encap_btn(Button::new("btn_toggle_mount", "Toggle Fuse Running") ++ .full_width() ++ .icon(IconName::Context) ++ .icon_position(IconPosition::Start) ++ .on_click(cx.listener(|this, _, cx| { ++ this.mega_handle.update(cx, |mega, cx| mega.toggle_mount(cx)); ++ this.warn_unimplemented(cx); ++ })) ++ ), + encap_btn(Button::new("btn_toggle_scorpio", "Toggle Scorpio Checkouts") + .full_width() + .icon(IconName::Plus) +@@ -289,15 +301,6 @@ impl MegaPanel { + this.mega_handle.update(cx, |mega, cx| mega.toggle_fuse(cx)); + })) + ), +- // encap_btn(Button::new("btn_toggle_mount", "Toggle Mount") +- // .full_width() +- // .icon(IconName::Context) +- // .icon_position(IconPosition::Start) +- // .on_click(cx.listener(|this, _, cx| { +- // this.mega_handle.update(cx, |mega, cx| mega.toggle_mount(cx)); +- // this.warn_unimplemented(cx); +- // })) +- // ), + // encap_btn(Button::new("btn_checkout", "Checkout Path") + // .full_width() + // .icon(IconName::Check) +diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs +index 9df417f3e5..f507133200 100644 +--- a/crates/zed/src/main.rs ++++ b/crates/zed/src/main.rs +@@ -26,7 +26,6 @@ use gpui::{ + use http_client::{read_proxy_from_env, Uri}; + use language::LanguageRegistry; + use log::LevelFilter; +-use remote::SshConnectionOptions; + use reqwest_client::ReqwestClient; + + use assets::Assets; +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0017-add-commit-button.patch b/scripts/zed-intergration/patches/0017-add-commit-button.patch new file mode 100644 index 000000000..d38324cfc --- /dev/null +++ b/scripts/zed-intergration/patches/0017-add-commit-button.patch @@ -0,0 +1,846 @@ +From e14fe02ac8e203261d09a12119a3e19472fb083b Mon Sep 17 00:00:00 2001 +From: Neon +Date: Sun, 17 Nov 2024 21:20:35 +0800 +Subject: [PATCH 17/24] add commit button + +--- + assets/settings/default.json | 2 +- + crates/mega/src/api.rs | 47 +++++ + crates/mega/src/mega.rs | 166 ++++++++---------- + crates/mega_panel/src/mega_panel.rs | 28 --- + crates/project_panel/src/project_panel.rs | 205 +++++++++++++--------- + 5 files changed, 242 insertions(+), 206 deletions(-) + +diff --git a/assets/settings/default.json b/assets/settings/default.json +index 6ea0fff594..466e83655d 100644 +--- a/assets/settings/default.json ++++ b/assets/settings/default.json +@@ -385,7 +385,7 @@ + // Url to communicate with fuse + "fuse_url": "http://localhost:2725", + // Default mount point for fuse +- "mount_point": "/home/neon/projects", ++ "mount_point": "/home/neon/dic", + // Path for mega executable + "mega_executable": "mega", + // Path for fuse executable +diff --git a/crates/mega/src/api.rs b/crates/mega/src/api.rs +index efbf0d1752..3251888e60 100644 +--- a/crates/mega/src/api.rs ++++ b/crates/mega/src/api.rs +@@ -1,5 +1,7 @@ + use serde::{Deserialize, Serialize}; + ++const SUCCESS: &str = "Success"; ++const FAIL : &str = "Fail"; + #[derive(Debug, Deserialize, Serialize)] + pub struct MountRequest<'a> { + pub path: &'a str, +@@ -55,4 +57,49 @@ pub struct ConfigRequest { + pub mega_url: Option, + pub mount_path: Option, + pub store_path: Option, ++} ++ ++pub trait FuseResponse { ++ fn is_resp_succeed(&self) -> bool; ++ fn is_resp_failed(&self) -> bool; ++} ++ ++impl FuseResponse for MountResponse { ++ fn is_resp_succeed(&self) -> bool { ++ self.status.eq(SUCCESS) ++ } ++ ++ fn is_resp_failed(&self) -> bool { ++ self.status.eq(FAIL) ++ } ++} ++ ++impl FuseResponse for MountsResponse { ++ fn is_resp_succeed(&self) -> bool { ++ self.status.eq(SUCCESS) ++ } ++ ++ fn is_resp_failed(&self) -> bool { ++ self.status.eq(FAIL) ++ } ++} ++ ++impl FuseResponse for UmountResponse { ++ fn is_resp_succeed(&self) -> bool { ++ self.status.eq(SUCCESS) ++ } ++ ++ fn is_resp_failed(&self) -> bool { ++ self.status.eq(FAIL) ++ } ++} ++ ++impl FuseResponse for ConfigResponse { ++ fn is_resp_succeed(&self) -> bool { ++ self.status.eq(SUCCESS) ++ } ++ ++ fn is_resp_failed(&self) -> bool { ++ self.status.eq(FAIL) ++ } + } +\ No newline at end of file +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 19e9222cf3..9003361e4f 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -15,6 +15,7 @@ use crate::mega_settings::MegaSettings; + use crate::Event::FuseMounted; + use futures::channel::oneshot; + use futures::channel::oneshot::Receiver; ++use futures::future::MaybeDone::Future; + use futures::{AsyncReadExt, FutureExt, SinkExt, TryFutureExt}; + use gpui::http_client::{AsyncBody, HttpClient, HttpRequestExt}; + use gpui::{AppContext, Context, EventEmitter, ModelContext, Path, Task}; +@@ -33,9 +34,8 @@ use std::process::Command; + use std::sync::{Arc, RwLock}; + use std::thread::sleep; + use std::time::Duration; +-use futures::future::MaybeDone::Future; + +-mod api; ++pub mod api; + mod mega_settings; + + pub fn init(cx: &mut AppContext) { +@@ -52,6 +52,7 @@ pub enum Event { + struct CheckoutState { + path: PathBuf, + mounted: bool, ++ notify: bool, + } + + pub struct Mega { +@@ -75,11 +76,10 @@ impl EventEmitter for Mega {} + + impl Debug for Mega { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +- write!(f, +- "fuse_executable: {:?}, mega_url: {}, fuse_url: {}", +- self.fuse_executable, +- self.mega_url, +- self.fuse_url ++ write!( ++ f, ++ "fuse_executable: {:?}, mega_url: {}, fuse_url: {}", ++ self.fuse_executable, self.mega_url, self.fuse_url + ) + } + } +@@ -148,28 +148,32 @@ impl Mega { + // Check if checkout-ed paths are correct + this.update(&mut cx, |mega, cx| { + mega.fuse_running = true; +- ++ + let trie = &mut mega.checkout_path; + for ref i in info.mounts { + let missing = trie.get_ancestor(&i.path).is_none(); + if missing { + // Should not happen unless on startup. + trie.insert(i.path.clone(), i.inode); +- cx.emit(Event::FuseCheckout(Some(PathBuf::from(i.path.clone())))) ++ cx.emit(Event::FuseCheckout(Some(PathBuf::from( ++ i.path.clone(), ++ )))) + } + } + }) + } + } +- } else { Ok(()) }.unwrap(); ++ } else { ++ Ok(()) ++ } ++ .unwrap(); + + // When mount point changed, emit an event. + // update mount point if it's none. + if let Ok(Some(config)) = config.await { + this.update(&mut cx, |this, cx| { + let path = PathBuf::from(config.config.mount_path); +- if (this.fuse_mounted && this.fuse_running) +- && this.mount_point.is_some() { ++ if (this.fuse_mounted && this.fuse_running) && this.mount_point.is_some() { + if let Some(inner) = &this.mount_point { + if !inner.eq(&path) { + this.mount_point = Some(path); +@@ -181,7 +185,9 @@ impl Mega { + cx.emit(Event::FuseMounted(this.mount_point.clone())); + } + }) +- } else { Ok(()) } ++ } else { ++ Ok(()) ++ } + }) + .detach(); + } +@@ -196,7 +202,7 @@ impl Mega { + pub fn toggle_fuse(&mut self, cx: &mut ModelContext) { + self.update_status(cx); + let paths = &self.checkout_path; +- ++ + if !self.fuse_running { + return; + } +@@ -205,9 +211,9 @@ impl Mega { + for (_, (p, _)) in paths.iter().enumerate() { + let path = PathBuf::from(p); // FIXME is there a better way? + cx.spawn(|mega, mut cx| async move { +- let recv = mega.update(&mut cx, |this, cx| { +- this.checkout_path(cx, path) +- }).expect("mega delegate not be dropped"); ++ let recv = mega ++ .update(&mut cx, |this, cx| this.checkout_path(cx, path)) ++ .expect("mega delegate not be dropped"); + + if let Ok(Some(resp)) = recv.await { + mega.update(&mut cx, |this, cx| { +@@ -218,19 +224,20 @@ impl Mega { + Ok(()) + } + }) +- .detach(); ++ .detach(); + } + + self.fuse_mounted = true; + cx.emit(Event::FuseMounted(self.mount_point.clone())); + } else { +- for (_, (p, &n)) in paths.iter().enumerate() { ++ for (_, (p, _)) in paths.iter().enumerate() { + let path = PathBuf::from(p); // FIXME is there a better way? + cx.spawn(|mega, mut cx| async move { +- let recv = mega.update(&mut cx, |this, cx| { +- let param = PathBuf::from(path); +- this.restore_path(cx, param, n) +- }).expect("mega delegate not be dropped"); ++ let recv = mega ++ .update(&mut cx, |this, cx| { ++ this.restore_path(cx, path) ++ }) ++ .expect("mega delegate not be dropped"); + + if let Ok(Some(resp)) = recv.await { + mega.update(&mut cx, |this, cx| { +@@ -241,7 +248,7 @@ impl Mega { + Ok(()) + } + }) +- .detach(); ++ .detach(); + } + + self.fuse_mounted = false; +@@ -251,7 +258,7 @@ impl Mega { + + /// ## Toggle Fuse Mount + /// In fact, we cannot `mount` or `umount` a fuse from zed. +- /// ++ /// + /// This function only opens up a new scorpio executable if it detects fuse not running. + pub fn toggle_mount(&mut self, cx: &mut ModelContext) { + // We only start it, not stop it. +@@ -259,7 +266,7 @@ impl Mega { + let _ = Command::new(self.fuse_executable.as_os_str()) + .spawn() + .expect("Fuse Executable path not right"); +- ++ + self.update_status(cx); + } + } +@@ -271,10 +278,7 @@ impl Mega { + ) -> Receiver> { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); +- let uri = format!( +- "{base}/api/fs/mount", +- base = self.fuse_url +- ); ++ let uri = format!("{base}/api/fs/mount", base = self.fuse_url); + + // If it panics, that means there's a bug in code. + let path = path.to_str().unwrap(); +@@ -282,13 +286,7 @@ impl Mega { + let body = serde_json::to_string(&req).unwrap(); + + cx.spawn(|_this, _cx| async move { +- if let Ok(mut resp) = client +- .post_json( +- uri.as_str(), +- AsyncBody::from(body), +- ) +- .await +- { ++ if let Ok(mut resp) = client.post_json(uri.as_str(), AsyncBody::from(body)).await { + if resp.status().is_success() { + let mut buf = Vec::new(); + resp.body_mut().read_to_end(&mut buf).await.unwrap(); +@@ -308,39 +306,27 @@ impl Mega { + rx + } + +- pub fn checkout_multi_path(&mut self, cx: &mut ModelContext, mut path: Vec) { +- unimplemented!() +- } +- + pub fn restore_path( + &self, + cx: &ModelContext, + path: PathBuf, +- inode: u64, + ) -> Receiver> { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); +- let uri = format!( +- "{base}/api/fs/umount", +- base = self.fuse_url +- ); ++ let uri = format!("{base}/api/fs/umount", base = self.fuse_url); + +- // If it panics, that means there's a bug in code. ++ // If panics here, that means there's a bug in code. ++ // maybe we should ensure every path absolute? + let path = path.to_str().unwrap(); ++ let inode = self.checkout_path.get_ancestor_value(path); + let req = UmountRequest { + path: Some(path), +- inode: Some(inode), ++ inode: Some(inode.unwrap().to_owned()), + }; + let body = serde_json::to_string(&req).unwrap(); + + cx.spawn(|_this, _cx| async move { +- if let Ok(mut resp) = client +- .post_json( +- uri.as_str(), +- AsyncBody::from(body), +- ) +- .await +- { ++ if let Ok(mut resp) = client.post_json(uri.as_str(), AsyncBody::from(body)).await { + if resp.status().is_success() { + let mut buf = Vec::new(); + resp.body_mut().read_to_end(&mut buf).await.unwrap(); +@@ -363,20 +349,10 @@ impl Mega { + pub fn get_checkout_paths(&self, cx: &ModelContext) -> Receiver> { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); +- let uri = format!( +- "{base}/api/fs/mpoint", +- base = self.fuse_url +- ); ++ let uri = format!("{base}/api/fs/mpoint", base = self.fuse_url); + + cx.spawn(|_this, _cx| async move { +- if let Ok(mut resp) = client +- .get( +- uri.as_str(), +- AsyncBody::empty(), +- false, +- ) +- .await +- { ++ if let Ok(mut resp) = client.get(uri.as_str(), AsyncBody::empty(), false).await { + if resp.status().is_success() { + let mut buf = Vec::new(); + resp.body_mut().read_to_end(&mut buf).await.unwrap(); +@@ -399,20 +375,10 @@ impl Mega { + pub fn get_fuse_config(&self, cx: &ModelContext) -> Receiver> { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); +- let uri = format!( +- "{base}/api/config", +- base = self.fuse_url +- ); ++ let uri = format!("{base}/api/config", base = self.fuse_url); + + cx.spawn(|_this, _cx| async move { +- if let Ok(mut resp) = client +- .get( +- uri.as_str(), +- AsyncBody::empty(), +- false, +- ) +- .await +- { ++ if let Ok(mut resp) = client.get(uri.as_str(), AsyncBody::empty(), false).await { + if resp.status().is_success() { + let mut buf = Vec::new(); + resp.body_mut().read_to_end(&mut buf).await.unwrap(); +@@ -435,10 +401,7 @@ impl Mega { + pub fn set_fuse_config(&self, cx: &mut ModelContext) -> Receiver> { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); +- let uri = format!( +- "{base}/api/config", +- base = self.fuse_url +- ); ++ let uri = format!("{base}/api/config", base = self.fuse_url); + let config = ConfigRequest { + mega_url: None, + mount_path: None, +@@ -448,10 +411,7 @@ impl Mega { + let config = serde_json::to_string(&config).unwrap(); + + cx.spawn(|_this, _cx| async move { +- if let Ok(mut resp) = client +- .post_json(uri.as_str(), config.into()) +- .await +- { ++ if let Ok(mut resp) = client.post_json(uri.as_str(), config.into()).await { + if resp.status().is_success() { + let mut buf = Vec::new(); + resp.body_mut().read_to_end(&mut buf).await.unwrap(); +@@ -470,28 +430,44 @@ impl Mega { + + rx + } +- ++ + pub fn heartbeat(&mut self, cx: &mut ModelContext) { + if self.heartbeat { + return; + } else { + self.heartbeat = true; + } +- ++ + cx.spawn(|this, mut cx| async move { + loop { + this.update(&mut cx, |mega, cx| { + mega.update_status(cx); +- }).expect("mega delegate not be dropped"); +- ++ }) ++ .expect("mega delegate not be dropped"); ++ + let dur = Duration::from_secs(30); + cx.background_executor().timer(dur).await; + } +- }).detach(); ++ }) ++ .detach(); + } +- +- pub fn is_path_checkout(&self, path: &String) -> bool { ++ ++ pub fn is_path_checkout(&self, path: PathBuf) -> bool { + let set = &self.checkout_path; +- set.get_ancestor(path).is_some() ++ ++ set.get_ancestor(path.to_str().unwrap()).is_some() ++ } ++ ++ pub fn mark_checkout(&mut self, cx: &mut ModelContext, path: String, inode: u64) { ++ if self.mount_point.is_some() { ++ let path = self.mount_point.clone().unwrap().to_str().unwrap().to_string() + path.as_str(); ++ self.checkout_path.insert(path, inode); ++ cx.emit(Event::FuseCheckout(None)); ++ } ++ } ++ ++ pub fn mark_commited(&mut self, cx: &mut ModelContext, path: PathBuf) { ++ self.checkout_path.remove(path.to_str().unwrap()); ++ cx.emit(Event::FuseCheckout(None)); + } + } +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index 4ea88e5b4c..142f627ed8 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -290,7 +290,6 @@ impl MegaPanel { + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.mega_handle.update(cx, |mega, cx| mega.toggle_mount(cx)); +- this.warn_unimplemented(cx); + })) + ), + encap_btn(Button::new("btn_toggle_scorpio", "Toggle Scorpio Checkouts") +@@ -301,33 +300,6 @@ impl MegaPanel { + this.mega_handle.update(cx, |mega, cx| mega.toggle_fuse(cx)); + })) + ), +- // encap_btn(Button::new("btn_checkout", "Checkout Path") +- // .full_width() +- // .icon(IconName::Check) +- // .icon_position(IconPosition::Start) +- // .on_click(cx.listener(|this, _, cx| { +- // this.warn_unimplemented(cx); +- // let options = PathPromptOptions { +- // files: true, +- // directories: true, +- // multiple: false, +- // }; +- // +- // let abs_path = cx.prompt_for_paths(options); +- // +- // // Why so annoying... +- // let mega = this.mega_handle.clone(); +- // cx.spawn(|this, mut cx|async move { +- // // mega.update(&mut cx, |this, cx| async move { +- // // if let Ok(Ok(Some(result))) = abs_path.await { +- // // this.checkout_multi_path(cx, result); +- // // } +- // // }).log_err(); +- // +- // }) .detach(); +- // +- // })) +- // ), + ]) + } + +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index a83346ae60..047c3b0eaa 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -15,6 +15,7 @@ use file_icons::FileIcons; + use anyhow::{anyhow, Context as _, Result}; + use collections::{hash_map, BTreeSet, HashMap}; + use core::f32; ++use db::write_and_log; + use git::repository::GitFileStatus; + use gpui::{ + actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AnyElement, +@@ -26,6 +27,7 @@ use gpui::{ + WindowContext, + }; + use indexmap::IndexMap; ++use mega::{api::FuseResponse, Mega}; + use menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev}; + use project::{ + relativize_path, Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath, Worktree, +@@ -43,8 +45,6 @@ use std::{ + sync::Arc, + time::Duration, + }; +-use db::write_and_log; +-use mega::Mega; + use theme::ThemeSettings; + use ui::{prelude::*, v_flex, ContextMenu, Icon, KeyBinding, Label, ListItem, Tooltip}; + use util::{maybe, ResultExt, TryFutureExt}; +@@ -59,7 +59,6 @@ const PROJECT_PANEL_KEY: &str = "ProjectPanel"; + const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX; + + pub struct ProjectPanel { +- // TODO: use segment tree to fastly store multiple checkout path, + project: Model, + mega: Model, + fs: Arc, +@@ -70,7 +69,6 @@ pub struct ProjectPanel { + /// Relevant only for auto-fold dirs, where a single project panel entry may actually consist of several + /// project entries (and all non-leaf nodes are guaranteed to be directories). + ancestors: HashMap, +- checkout_entry_id: Option, + last_worktree_root_id: Option, + last_external_paths_drag_over_entry: Option, + expanded_dir_ids: HashMap>, +@@ -169,6 +167,7 @@ actions!( + FoldDirectory, + SelectParent, + CheckoutPath, ++ CommitPath, + ] + ); + +@@ -298,19 +297,19 @@ impl ProjectPanel { + cx.spawn(|this, mut cx| async move { + if let Some(task) = this + .update(&mut cx, |this, cx| { +- this.open_workspace_for_paths(false, vec!(path), cx) ++ this.open_workspace_for_paths(false, vec![path], cx) + }) + .log_err() + { + task.await.log_err(); + } + }) +- .detach() ++ .detach() + }) + .log_err(); +- ++ + this.focus_in(cx); +- }, ++ } + mega::Event::FuseMounted(None) => { + // TODO: close the workspace + } +@@ -318,7 +317,8 @@ impl ProjectPanel { + // It's not important, for now. + } + _ => {} +- }).detach(); ++ }) ++ .detach(); + + cx.observe_global::(|_, cx| { + cx.notify(); +@@ -343,7 +343,6 @@ impl ProjectPanel { + focus_handle, + visible_entries: Default::default(), + ancestors: Default::default(), +- checkout_entry_id: Default::default(), + last_worktree_root_id: Default::default(), + last_external_paths_drag_over_entry: None, + expanded_dir_ids: Default::default(), +@@ -519,6 +518,7 @@ impl ProjectPanel { + ) { + let this = cx.view().clone(); + let project = self.project.read(cx); ++ let mega = self.mega.read(cx); + + let worktree_id = if let Some(id) = project.worktree_id_for_entry(entry_id, cx) { + id +@@ -542,6 +542,7 @@ impl ProjectPanel { + let is_read_only = project.is_read_only(cx); + let is_remote = project.is_via_collab() && project.dev_server_project_id().is_none(); + let is_local = project.is_local(); ++ let is_checkout = mega.is_path_checkout(entry.path.to_path_buf()); + + let context_menu = ContextMenu::build(cx, |menu, cx| { + menu.context(self.focus_handle.clone()).map(|menu| { +@@ -550,73 +551,81 @@ impl ProjectPanel { + menu.action("Search Inside", Box::new(NewSearchInDirectory)) + }) + } else { +- menu +- .action("Checkout Path", Box::new(CheckoutPath)) +- .separator() +- .action("New File", Box::new(NewFile)) +- .action("New Folder", Box::new(NewDirectory)) +- .separator() +- .when(is_local && cfg!(target_os = "macos"), |menu| { +- menu.action("Reveal in Finder", Box::new(RevealInFileManager)) +- }) +- .when(is_local && cfg!(not(target_os = "macos")), |menu| { +- menu.action("Reveal in File Manager", Box::new(RevealInFileManager)) +- }) +- .when(is_local, |menu| { +- menu.action("Open in Default App", Box::new(OpenWithSystem)) +- }) +- .action("Open in Terminal", Box::new(OpenInTerminal)) +- .when(is_dir, |menu| { +- menu.separator() +- .action("Find in Folder…", Box::new(NewSearchInDirectory)) +- }) +- .when(is_unfoldable, |menu| { +- menu.action("Unfold Directory", Box::new(UnfoldDirectory)) +- }) +- .when(is_foldable, |menu| { +- menu.action("Fold Directory", Box::new(FoldDirectory)) +- }) +- .separator() +- .action("Cut", Box::new(Cut)) +- .action("Copy", Box::new(Copy)) +- .action("Duplicate", Box::new(Duplicate)) +- // TODO: Paste should always be visible, cbut disabled when clipboard is empty +- .map(|menu| { +- if self.clipboard.as_ref().is_some() { +- menu.action("Paste", Box::new(Paste)) +- } else { +- menu.disabled_action("Paste", Box::new(Paste)) +- } +- }) +- .separator() +- .action("Copy Path", Box::new(CopyPath)) +- .action("Copy Relative Path", Box::new(CopyRelativePath)) +- .separator() +- .action("Rename", Box::new(Rename)) +- .when(!is_root, |menu| { +- menu.action("Trash", Box::new(Trash { skip_prompt: false })) +- .action("Delete", Box::new(Delete { skip_prompt: false })) +- }) +- .when(!is_remote & is_root, |menu| { +- menu.separator() +- .action( +- "Add Folder to Project…", +- Box::new(workspace::AddFolderToProject), +- ) +- .entry( +- "Remove from Project", +- None, +- cx.handler_for(&this, move |this, cx| { +- this.project.update(cx, |project, cx| { +- project.remove_worktree(worktree_id, cx) +- }); +- }), ++ menu.when(!is_checkout && !is_root, |menu| { ++ menu.action("Checkout Path", Box::new(CheckoutPath)) ++ }) ++ .when(is_checkout, |menu| { ++ menu.action("Commit Path", Box::new(CommitPath)) ++ .separator() ++ .action("New File", Box::new(NewFile)) ++ .action("New Folder", Box::new(NewDirectory)) ++ .separator() ++ .when(is_local && cfg!(target_os = "macos"), |menu| { ++ menu.action("Reveal in Finder", Box::new(RevealInFileManager)) ++ }) ++ .when(is_local && cfg!(not(target_os = "macos")), |menu| { ++ menu.action( ++ "Reveal in File Manager", ++ Box::new(RevealInFileManager), + ) +- }) +- .when(is_root, |menu| { +- menu.separator() +- .action("Collapse All", Box::new(CollapseAllEntries)) +- }) ++ }) ++ .when(is_local, |menu| { ++ menu.action("Open in Default App", Box::new(OpenWithSystem)) ++ }) ++ .action("Open in Terminal", Box::new(OpenInTerminal)) ++ .when(is_dir, |menu| { ++ menu.separator() ++ .action("Find in Folder…", Box::new(NewSearchInDirectory)) ++ }) ++ .when(is_unfoldable, |menu| { ++ menu.action("Unfold Directory", Box::new(UnfoldDirectory)) ++ }) ++ .when(is_foldable, |menu| { ++ menu.action("Fold Directory", Box::new(FoldDirectory)) ++ }) ++ .separator() ++ .action("Cut", Box::new(Cut)) ++ .action("Copy", Box::new(Copy)) ++ .action("Duplicate", Box::new(Duplicate)) ++ // TODO: Paste should always be visible, cbut disabled when clipboard is empty ++ .map(|menu| { ++ if self.clipboard.as_ref().is_some() { ++ menu.action("Paste", Box::new(Paste)) ++ } else { ++ menu.disabled_action("Paste", Box::new(Paste)) ++ } ++ }) ++ .separator() ++ .action("Copy Path", Box::new(CopyPath)) ++ .action("Copy Relative Path", Box::new(CopyRelativePath)) ++ .separator() ++ // Fuse not support renaming, currently. ++ // .action("Rename", Box::new(Rename)) ++ .when(!is_root, |menu| { ++ menu.action("Trash", Box::new(Trash { skip_prompt: false })) ++ .action("Delete", Box::new(Delete { skip_prompt: false })) ++ }) ++ .when(!is_remote & is_root, |menu| { ++ menu.separator() ++ .action( ++ "Add Folder to Project…", ++ Box::new(workspace::AddFolderToProject), ++ ) ++ .entry( ++ "Remove from Project", ++ None, ++ cx.handler_for(&this, move |this, cx| { ++ this.project.update(cx, |project, cx| { ++ project.remove_worktree(worktree_id, cx) ++ }); ++ }), ++ ) ++ }) ++ .when(is_root, |menu| { ++ menu.separator() ++ .action("Collapse All", Box::new(CollapseAllEntries)) ++ }) ++ }) + } + }) + }); +@@ -1340,11 +1349,42 @@ impl ProjectPanel { + if let Some((worktree, entry)) = self.selected_entry_handle(cx) { + let path = entry.path.clone(); + self.mega.update(cx, |mega, cx| { +- let recv = mega.checkout_path(cx, path.to_path_buf()); +- cx.spawn(|_, _| async move { ++ let recv = mega.checkout_path(cx, path.to_path_buf()); ++ cx.spawn(|this, mut cx| async move { ++ let resp = recv.await; ++ println!("Response: {resp:?}"); ++ if let Ok(Some(resp)) = resp { ++ if resp.is_resp_succeed() { ++ this.update(&mut cx, |mega, cx| { ++ mega.mark_checkout(cx, resp.mount.path, resp.mount.inode); ++ }) ++ .expect("Mega delegate not been dropped"); ++ } ++ } ++ }) ++ .detach(); ++ }); ++ } ++ } ++ ++ fn commit_specific_path(&mut self, _: &CommitPath, cx: &mut ViewContext) { ++ if let Some((worktree, entry)) = self.selected_entry_handle(cx) { ++ let path = entry.path.clone(); ++ self.mega.update(cx, |mega, cx| { ++ let recv = mega.restore_path(cx, path.to_path_buf()); ++ cx.spawn(|this, mut cx| async move { + let resp = recv.await; + println!("Response: {resp:?}"); +- }).detach(); ++ if let Ok(Some(resp)) = resp { ++ if resp.is_resp_succeed() { ++ this.update(&mut cx, |mega, cx| { ++ mega.mark_commited(cx, path.to_path_buf()); ++ }) ++ .expect("Mega delegate not been dropped"); ++ } ++ } ++ }) ++ .detach(); + }); + } + } +@@ -2358,6 +2398,7 @@ impl ProjectPanel { + details: EntryDetails, + cx: &mut ViewContext, + ) -> Stateful
{ ++ let mega = self.mega.read(cx); + let kind = details.kind; + let settings = ProjectPanelSettings::get_global(cx); + let show_editor = details.is_editing && !details.is_processing; +@@ -2370,7 +2411,7 @@ impl ProjectPanel { + .selection + .map_or(false, |selection| selection.entry_id == entry_id); + let width = self.size(cx); +- let filename_text_color = ++ let filename_text_color = + entry_git_aware_label_color(details.git_status, details.is_ignored, is_marked); + let file_name = details.filename.clone(); + let mut icon = details.icon.clone(); +@@ -2963,6 +3004,7 @@ impl Render for ProjectPanel { + .on_action(cx.listener(Self::unfold_directory)) + .on_action(cx.listener(Self::fold_directory)) + .on_action(cx.listener(Self::checkout_specific_path)) ++ .on_action(cx.listener(Self::commit_specific_path)) + .when(!project.is_read_only(cx), |el| { + el.on_action(cx.listener(Self::new_file)) + .on_action(cx.listener(Self::new_directory)) +@@ -3057,8 +3099,7 @@ impl Render for ProjectPanel { + // .update(cx, |workspace, cx| workspace.open(&workspace::Open, cx)) + // .log_err(); + // })), +- Label::new("Run mega daemon to mount the working directories") +- ++ Label::new("Run mega daemon to mount the working directories"), + ) + .drag_over::(|style, _, cx| { + style.bg(cx.theme().colors().drop_target_background) +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0018-adjust-context-menu-for-fuse.patch b/scripts/zed-intergration/patches/0018-adjust-context-menu-for-fuse.patch new file mode 100644 index 000000000..dfd85ce3f --- /dev/null +++ b/scripts/zed-intergration/patches/0018-adjust-context-menu-for-fuse.patch @@ -0,0 +1,104 @@ +From cc5def5cd4729cb1705c01896710157e43c0829d Mon Sep 17 00:00:00 2001 +From: Neon +Date: Sun, 17 Nov 2024 21:39:21 +0800 +Subject: [PATCH 18/24] adjust context menu for fuse + +--- + crates/project_panel/src/project_panel.rs | 57 +++++++++++------------ + 1 file changed, 27 insertions(+), 30 deletions(-) + +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index 047c3b0eaa..baec6a00a5 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -15,7 +15,6 @@ use file_icons::FileIcons; + use anyhow::{anyhow, Context as _, Result}; + use collections::{hash_map, BTreeSet, HashMap}; + use core::f32; +-use db::write_and_log; + use git::repository::GitFileStatus; + use gpui::{ + actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AnyElement, +@@ -587,7 +586,7 @@ impl ProjectPanel { + .action("Cut", Box::new(Cut)) + .action("Copy", Box::new(Copy)) + .action("Duplicate", Box::new(Duplicate)) +- // TODO: Paste should always be visible, cbut disabled when clipboard is empty ++ // TODO: Paste should always be visible, but disabled when clipboard is empty + .map(|menu| { + if self.clipboard.as_ref().is_some() { + menu.action("Paste", Box::new(Paste)) +@@ -599,32 +598,30 @@ impl ProjectPanel { + .action("Copy Path", Box::new(CopyPath)) + .action("Copy Relative Path", Box::new(CopyRelativePath)) + .separator() +- // Fuse not support renaming, currently. +- // .action("Rename", Box::new(Rename)) +- .when(!is_root, |menu| { +- menu.action("Trash", Box::new(Trash { skip_prompt: false })) +- .action("Delete", Box::new(Delete { skip_prompt: false })) +- }) +- .when(!is_remote & is_root, |menu| { +- menu.separator() +- .action( +- "Add Folder to Project…", +- Box::new(workspace::AddFolderToProject), +- ) +- .entry( +- "Remove from Project", +- None, +- cx.handler_for(&this, move |this, cx| { +- this.project.update(cx, |project, cx| { +- project.remove_worktree(worktree_id, cx) +- }); +- }), +- ) +- }) +- .when(is_root, |menu| { +- menu.separator() +- .action("Collapse All", Box::new(CollapseAllEntries)) +- }) ++ .action("Trash", Box::new(Trash { skip_prompt: false })) ++ .action("Delete", Box::new(Delete { skip_prompt: false })) ++ // Fuse not support renaming, currently. ++ // .action("Rename", Box::new(Rename)) ++ }) ++ .when(!is_remote & is_root, |menu| { ++ menu ++ .action( ++ "Add Folder to Project…", ++ Box::new(workspace::AddFolderToProject), ++ ) ++ .entry( ++ "Remove from Project", ++ None, ++ cx.handler_for(&this, move |this, cx| { ++ this.project.update(cx, |project, cx| { ++ project.remove_worktree(worktree_id, cx) ++ }); ++ }), ++ ) ++ }) ++ .when(is_root, |menu| { ++ menu.separator() ++ .action("Collapse All", Box::new(CollapseAllEntries)) + }) + } + }) +@@ -1380,11 +1377,11 @@ impl ProjectPanel { + this.update(&mut cx, |mega, cx| { + mega.mark_commited(cx, path.to_path_buf()); + }) +- .expect("Mega delegate not been dropped"); ++ .expect("Mega delegate not been dropped"); + } + } + }) +- .detach(); ++ .detach(); + }); + } + } +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0019-fix-warnings.patch b/scripts/zed-intergration/patches/0019-fix-warnings.patch new file mode 100644 index 000000000..688996ba1 --- /dev/null +++ b/scripts/zed-intergration/patches/0019-fix-warnings.patch @@ -0,0 +1,164 @@ +From f205678bc18d6b7b1ffc57aa923120348271623b Mon Sep 17 00:00:00 2001 +From: Neon +Date: Sun, 17 Nov 2024 22:07:53 +0800 +Subject: [PATCH 19/24] fix warnings + +--- + crates/mega/src/mega.rs | 44 ++++++++--------------- + crates/project_panel/src/project_panel.rs | 13 ++++--- + 2 files changed, 20 insertions(+), 37 deletions(-) + +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 9003361e4f..2d85052e63 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -1,38 +1,20 @@ +-// This crate delegate mega and its fuse daemon. +-// The following requirements should be met: +-// +-// TODO: +-// 1. Only one daemon on this machine. +-// This should be both warrantied by this module and scorpio +-// 2. At least one daemon on this machine when zed startup. +-// 3. Complete docs. +- + use crate::api::{ + ConfigRequest, ConfigResponse, MountRequest, MountResponse, MountsResponse, UmountRequest, + UmountResponse, + }; + use crate::mega_settings::MegaSettings; +-use crate::Event::FuseMounted; + use futures::channel::oneshot; + use futures::channel::oneshot::Receiver; +-use futures::future::MaybeDone::Future; +-use futures::{AsyncReadExt, FutureExt, SinkExt, TryFutureExt}; +-use gpui::http_client::{AsyncBody, HttpClient, HttpRequestExt}; +-use gpui::{AppContext, Context, EventEmitter, ModelContext, Path, Task}; ++use futures::AsyncReadExt; ++use gpui::http_client::{AsyncBody, HttpClient}; ++use gpui::{AppContext, EventEmitter, ModelContext}; + use radix_trie::{Trie, TrieCommon}; + use reqwest_client::ReqwestClient; +-use schemars::_private::NoSerialize; +-use serde::Serialize; + use settings::Settings; +-use std::collections::{BTreeMap, BTreeSet, HashSet}; +-use std::ffi::OsStr; + use std::fmt::{Debug, Formatter}; +-use std::hash::{DefaultHasher, Hash, Hasher}; +-use std::io::Read; + use std::path::PathBuf; + use std::process::Command; +-use std::sync::{Arc, RwLock}; +-use std::thread::sleep; ++use std::sync::Arc; + use std::time::Duration; + + pub mod api; +@@ -234,12 +216,10 @@ impl Mega { + let path = PathBuf::from(p); // FIXME is there a better way? + cx.spawn(|mega, mut cx| async move { + let recv = mega +- .update(&mut cx, |this, cx| { +- this.restore_path(cx, path) +- }) ++ .update(&mut cx, |this, cx| this.restore_path(cx, path)) + .expect("mega delegate not be dropped"); + +- if let Ok(Some(resp)) = recv.await { ++ if let Ok(Some(_resp)) = recv.await { + mega.update(&mut cx, |this, cx| { + // TODO use a new check out state struct + cx.emit(Event::FuseCheckout(None)); +@@ -250,9 +230,6 @@ impl Mega { + }) + .detach(); + } +- +- self.fuse_mounted = false; +- cx.emit(Event::FuseMounted(None)); + } + } + +@@ -460,7 +437,14 @@ impl Mega { + + pub fn mark_checkout(&mut self, cx: &mut ModelContext, path: String, inode: u64) { + if self.mount_point.is_some() { +- let path = self.mount_point.clone().unwrap().to_str().unwrap().to_string() + path.as_str(); ++ let path = self ++ .mount_point ++ .clone() ++ .unwrap() ++ .to_str() ++ .unwrap() ++ .to_string() ++ + path.as_str(); + self.checkout_path.insert(path, inode); + cx.emit(Event::FuseCheckout(None)); + } +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index baec6a00a5..70ad294cd9 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -45,7 +45,7 @@ use std::{ + time::Duration, + }; + use theme::ThemeSettings; +-use ui::{prelude::*, v_flex, ContextMenu, Icon, KeyBinding, Label, ListItem, Tooltip}; ++use ui::{prelude::*, v_flex, ContextMenu, Icon, Label, ListItem, Tooltip}; + use util::{maybe, ResultExt, TryFutureExt}; + use workspace::{ + dock::{DockPosition, Panel, PanelEvent}, +@@ -287,12 +287,12 @@ impl ProjectPanel { + ) + .detach(); + +- cx.subscribe(&mega, |this, mega, mega_event, cx| match mega_event { ++ cx.subscribe(&mega, |this, _, mega_event, cx| match mega_event { + mega::Event::FuseMounted(Some(path)) => { + println!("Fuse Mounted: {path:?}"); + let path = path.to_owned(); + this.workspace +- .update(cx, |workspace, cx| { ++ .update(cx, |_, cx| { + cx.spawn(|this, mut cx| async move { + if let Some(task) = this + .update(&mut cx, |this, cx| { +@@ -312,7 +312,7 @@ impl ProjectPanel { + mega::Event::FuseMounted(None) => { + // TODO: close the workspace + } +- mega::Event::FuseCheckout(path) => { ++ mega::Event::FuseCheckout(_path) => { + // It's not important, for now. + } + _ => {} +@@ -1343,7 +1343,7 @@ impl ProjectPanel { + } + + fn checkout_specific_path(&mut self, _: &CheckoutPath, cx: &mut ViewContext) { +- if let Some((worktree, entry)) = self.selected_entry_handle(cx) { ++ if let Some((_, entry)) = self.selected_entry_handle(cx) { + let path = entry.path.clone(); + self.mega.update(cx, |mega, cx| { + let recv = mega.checkout_path(cx, path.to_path_buf()); +@@ -1365,7 +1365,7 @@ impl ProjectPanel { + } + + fn commit_specific_path(&mut self, _: &CommitPath, cx: &mut ViewContext) { +- if let Some((worktree, entry)) = self.selected_entry_handle(cx) { ++ if let Some((_, entry)) = self.selected_entry_handle(cx) { + let path = entry.path.clone(); + self.mega.update(cx, |mega, cx| { + let recv = mega.restore_path(cx, path.to_path_buf()); +@@ -2395,7 +2395,6 @@ impl ProjectPanel { + details: EntryDetails, + cx: &mut ViewContext, + ) -> Stateful
{ +- let mega = self.mega.read(cx); + let kind = details.kind; + let settings = ProjectPanelSettings::get_global(cx); + let show_editor = details.is_editing && !details.is_processing; +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0020-fix-crate-collab-compile-error-with-mega.patch b/scripts/zed-intergration/patches/0020-fix-crate-collab-compile-error-with-mega.patch new file mode 100644 index 000000000..c9f359269 --- /dev/null +++ b/scripts/zed-intergration/patches/0020-fix-crate-collab-compile-error-with-mega.patch @@ -0,0 +1,82 @@ +From bcb691e423f44aadc34837d514e61bb23f86b32a Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 19 Nov 2024 09:36:55 +0800 +Subject: [PATCH 20/24] fix crate/collab compile error with mega + +--- + Cargo.lock | 1 + + crates/collab/Cargo.toml | 1 + + crates/collab/src/tests/test_server.rs | 5 +++++ + 3 files changed, 7 insertions(+) + +diff --git a/Cargo.lock b/Cargo.lock +index 0483c524c4..a69e9c68c9 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -2569,6 +2569,7 @@ dependencies = [ + "live_kit_server", + "log", + "lsp", ++ "mega", + "menu", + "multi_buffer", + "nanoid", +diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml +index ad2c013668..78dcfb30ec 100644 +--- a/crates/collab/Cargo.toml ++++ b/crates/collab/Cargo.toml +@@ -101,6 +101,7 @@ language = { workspace = true, features = ["test-support"] } + language_model = { workspace = true, features = ["test-support"] } + live_kit_client = { workspace = true, features = ["test-support"] } + lsp = { workspace = true, features = ["test-support"] } ++mega.workspace = true + menu.workspace = true + multi_buffer = { workspace = true, features = ["test-support"] } + node_runtime.workspace = true +diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs +index 210a049e0b..bb9fb96f70 100644 +--- a/crates/collab/src/tests/test_server.rs ++++ b/crates/collab/src/tests/test_server.rs +@@ -44,6 +44,7 @@ use std::{ + Arc, + }, + }; ++use mega::Mega; + use workspace::{Workspace, WorkspaceStore}; + + pub struct TestServer { +@@ -271,6 +272,7 @@ impl TestServer { + let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); + let language_registry = Arc::new(LanguageRegistry::test(cx.executor())); + let session = cx.new_model(|cx| AppSession::new(Session::test(), cx)); ++ let mega = cx.new_model(|cx| Mega::new(cx)); + let app_state = Arc::new(workspace::AppState { + client: client.clone(), + user_store: user_store.clone(), +@@ -280,6 +282,7 @@ impl TestServer { + build_window_options: |_, _| Default::default(), + node_runtime: NodeRuntime::unavailable(), + session, ++ mega, + }); + + let os_keymap = "keymaps/default-macos.json"; +@@ -401,6 +404,7 @@ impl TestServer { + let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); + let language_registry = Arc::new(LanguageRegistry::test(cx.executor())); + let session = cx.new_model(|cx| AppSession::new(Session::test(), cx)); ++ let mega = cx.new_model(|cx| Mega::new(cx)); + let app_state = Arc::new(workspace::AppState { + client: client.clone(), + user_store: user_store.clone(), +@@ -410,6 +414,7 @@ impl TestServer { + build_window_options: |_, _| Default::default(), + node_runtime: NodeRuntime::unavailable(), + session, ++ mega, + }); + + cx.update(|cx| { +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0021-improve-code-quality-format-and-fix-warnings.patch b/scripts/zed-intergration/patches/0021-improve-code-quality-format-and-fix-warnings.patch new file mode 100644 index 000000000..8ee972ccd --- /dev/null +++ b/scripts/zed-intergration/patches/0021-improve-code-quality-format-and-fix-warnings.patch @@ -0,0 +1,401 @@ +From 7b8bce244556a88a95672ffa79cfc21fb0da2efe Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 19 Nov 2024 11:13:48 +0800 +Subject: [PATCH 21/24] improve code quality, format and fix warnings + +--- + crates/mega/src/mega.rs | 16 ++- + crates/mega/src/utils.rs | 1 + + crates/mega/src/{ => utils}/api.rs | 0 + crates/mega_panel/src/mega_panel.rs | 171 ++++++++++++++-------------- + 4 files changed, 92 insertions(+), 96 deletions(-) + create mode 100644 crates/mega/src/utils.rs + rename crates/mega/src/{ => utils}/api.rs (100%) + +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 2d85052e63..abc46f1651 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -1,4 +1,4 @@ +-use crate::api::{ ++use utils::api::{ + ConfigRequest, ConfigResponse, MountRequest, MountResponse, MountsResponse, UmountRequest, + UmountResponse, + }; +@@ -17,8 +17,8 @@ use std::process::Command; + use std::sync::Arc; + use std::time::Duration; + +-pub mod api; + mod mega_settings; ++pub mod utils; + + pub fn init(cx: &mut AppContext) { + Mega::init(cx); +@@ -31,6 +31,7 @@ pub enum Event { + FuseCheckout(Option), + } + ++#[allow(unused)] + struct CheckoutState { + path: PathBuf, + mounted: bool, +@@ -90,7 +91,7 @@ impl Mega { + None + }; + +- let mega = Mega { ++ Mega { + fuse_executable, + + fuse_running: false, +@@ -103,10 +104,7 @@ impl Mega { + mega_url, + fuse_url, + http_client: Arc::new(client), +- }; +- +- println!("Mega New: {mega:?}"); +- mega ++ } + } + + pub fn update_status(&mut self, cx: &mut ModelContext) { +@@ -198,7 +196,7 @@ impl Mega { + .expect("mega delegate not be dropped"); + + if let Ok(Some(resp)) = recv.await { +- mega.update(&mut cx, |this, cx| { ++ mega.update(&mut cx, |_, cx| { + let buf = PathBuf::from(resp.mount.path.clone()); + cx.emit(Event::FuseCheckout(Some(buf))); + }) +@@ -220,7 +218,7 @@ impl Mega { + .expect("mega delegate not be dropped"); + + if let Ok(Some(_resp)) = recv.await { +- mega.update(&mut cx, |this, cx| { ++ mega.update(&mut cx, |_, cx| { + // TODO use a new check out state struct + cx.emit(Event::FuseCheckout(None)); + }) +diff --git a/crates/mega/src/utils.rs b/crates/mega/src/utils.rs +new file mode 100644 +index 0000000000..e5fdf85eed +--- /dev/null ++++ b/crates/mega/src/utils.rs +@@ -0,0 +1 @@ ++pub mod api; +diff --git a/crates/mega/src/api.rs b/crates/mega/src/utils/api.rs +similarity index 100% +rename from crates/mega/src/api.rs +rename to crates/mega/src/utils/api.rs +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index 142f627ed8..c5105988e0 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -1,18 +1,25 @@ +-use std::path::PathBuf; + use crate::mega_panel_settings::{MegaPanelDockPosition, MegaPanelSettings}; + use anyhow::{anyhow, Context}; + use db::kvp::KEY_VALUE_STORE; + use fs::Fs; + use gpui::private::serde_derive::{Deserialize, Serialize}; + use gpui::private::serde_json; +-use gpui::{actions, div, Action, AppContext, AssetSource, AsyncWindowContext, Div, ElementId, EventEmitter, Flatten, FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, ParentElement, PathPromptOptions, Pixels, PromptLevel, Render, SharedString, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext}; ++use gpui::{ ++ actions, div, Action, AppContext, AssetSource, AsyncWindowContext, Div, ElementId, ++ EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, ++ ParentElement, Pixels, PromptLevel, Render, SharedString, Stateful, Styled, Task ++ , View, ViewContext, VisualContext, WeakView, WindowContext, ++}; + use mega::Mega; + use settings::Settings; + use std::sync::Arc; + use text::BufferId; + use util::{ResultExt, TryFutureExt}; + use workspace::dock::{DockPosition, Panel, PanelEvent}; +-use workspace::ui::{h_flex, v_flex, Button, Clickable, Color, FixedWidth, IconName, IconPosition, Label, LabelCommon, LabelSize, StyledExt, StyledTypography}; ++use workspace::ui::{ ++ h_flex, v_flex, Button, Clickable, Color, FixedWidth, IconName, IconPosition, Label, ++ LabelCommon, LabelSize, StyledExt, StyledTypography, ++}; + use workspace::Workspace; + use worktree::{ProjectEntryId, WorktreeId}; + +@@ -20,22 +27,15 @@ mod mega_panel_settings; + + const MEGA_PANEL_KEY: &str = "MegaPanel"; + +-actions!( +- mega_panel, +- [ +- ToggleFocus, +- ToggleFuseMount, +- CheckoutPath, +- ] +-); ++actions!(mega_panel, [ToggleFocus, ToggleFuseMount, CheckoutPath,]); + + pub struct MegaPanel { + mega_handle: Model, +- workspace: WeakView, ++ // workspace: WeakView, + focus_handle: FocusHandle, +- scroll_handle: UniformListScrollHandle, ++ // scroll_handle: UniformListScrollHandle, + fs: Arc, +- pending_serialization: Task>, // TODO check how to use it ++ pending_serialization: Task>, + width: Option, + } + +@@ -44,6 +44,7 @@ struct SerializedMegaPanel { + width: Option, + } + ++#[allow(unused)] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + enum MegaEntry { + Dir(WorktreeId, ProjectEntryId), +@@ -68,7 +69,7 @@ pub fn init(assets: impl AssetSource, cx: &mut AppContext) { + workspace.toggle_panel_focus::(cx); + }); + }) +- .detach(); ++ .detach(); + } + + impl EventEmitter for MegaPanel {} +@@ -91,7 +92,8 @@ impl Render for MegaPanel { + Label::new("Mega Control Panel") + .single_line() + .weight(FontWeight::BOLD) +- .size(LabelSize::Large)) ++ .size(LabelSize::Large), ++ ), + ) + .child(horizontal_separator(cx)) + .child(self.render_status(cx)) +@@ -182,19 +184,16 @@ impl MegaPanel { + .log_err() + .flatten(); + +- workspace.update( +- &mut cx, +- |workspace, cx| { +- let panel = MegaPanel::new(workspace, cx); +- if let Some(serialized_panel) = serialized_panel { +- panel.update(cx, |panel, cx| { +- panel.width = serialized_panel.width.map(|px| px.round()); +- cx.notify(); +- }); +- } +- panel +- }, +- ) ++ workspace.update(&mut cx, |workspace, cx| { ++ let panel = MegaPanel::new(workspace, cx); ++ if let Some(serialized_panel) = serialized_panel { ++ panel.update(cx, |panel, cx| { ++ panel.width = serialized_panel.width.map(|px| px.round()); ++ cx.notify(); ++ }); ++ } ++ panel ++ }) + } + + fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> View { +@@ -206,17 +205,18 @@ impl MegaPanel { + #[allow(unused)] + cx.subscribe(mega, |this, mega, event, cx| { + // TODO: listen for mega events +- }).detach(); +- ++ }) ++ .detach(); ++ + mega.update(cx, |this, cx| { + this.heartbeat(cx); + }); + + Self { + mega_handle: mega.clone(), +- workspace: workspace.weak_handle(), ++ // workspace: workspace.weak_handle(), + focus_handle, +- scroll_handle: UniformListScrollHandle::new(), ++ // scroll_handle: UniformListScrollHandle::new(), + fs: workspace.app_state().fs.clone(), + pending_serialization: Task::ready(None), + width: None, +@@ -238,7 +238,7 @@ impl MegaPanel { + .await?; + anyhow::Ok(()) + } +- .log_err(), ++ .log_err(), + ); + } + +@@ -251,77 +251,78 @@ impl MegaPanel { + pub fn checkout_path(&mut self, _: &CheckoutPath, cx: &mut ViewContext) { + self.warn_unimplemented(cx); + } +- ++ + pub fn toggle_fuse_mount(&mut self, _: &ToggleFuseMount, cx: &mut ViewContext) { +- self.mega_handle.update(cx, |this, cx | { ++ self.mega_handle.update(cx, |this, cx| { + this.toggle_mount(cx); + }); + } + + fn render_status(&mut self, cx: &mut ViewContext) -> Div { +- let ( +- fuse_running, +- fuse_mounted +- ) = self.mega_handle.read(cx).status(); +- +- v_flex() +- .gap_1() +- .children([ +- self.status_unit(cx, "Scorpio Backend:", fuse_running), +- self.status_unit(cx, "Fuse Mounted:", fuse_mounted), +- ]) ++ let (fuse_running, fuse_mounted) = self.mega_handle.read(cx).status(); ++ ++ v_flex().gap_1().children([ ++ self.status_unit(cx, "Scorpio Backend:", fuse_running), ++ self.status_unit(cx, "Fuse Mounted:", fuse_mounted), ++ ]) + } + + fn render_buttons(&mut self, cx: &mut ViewContext) -> impl IntoElement { + fn encap_btn(btn: Button) -> Div { +- div() +- .m_1() +- .border_1() +- .child(btn) ++ div().m_1().border_1().child(btn) + } +- +- v_flex() +- .id("mega-control-pad") +- .size_full() +- .children([ +- encap_btn(Button::new("btn_toggle_mount", "Toggle Fuse Running") ++ ++ v_flex().id("mega-control-pad").size_full().children([ ++ encap_btn( ++ Button::new("btn_toggle_mount", "Toggle Fuse Running") + .full_width() + .icon(IconName::Context) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { +- this.mega_handle.update(cx, |mega, cx| mega.toggle_mount(cx)); +- })) +- ), +- encap_btn(Button::new("btn_toggle_scorpio", "Toggle Scorpio Checkouts") ++ this.mega_handle ++ .update(cx, |mega, cx| mega.toggle_mount(cx)); ++ })), ++ ), ++ encap_btn( ++ Button::new("btn_toggle_scorpio", "Toggle Scorpio Checkouts") + .full_width() + .icon(IconName::Plus) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.mega_handle.update(cx, |mega, cx| mega.toggle_fuse(cx)); +- })) +- ), +- ]) ++ })), ++ ), ++ ]) + } + +- fn status_unit(&self, cx: &mut ViewContext, name: &'static str, state: bool) -> Stateful
{ ++ fn status_unit( ++ &self, ++ cx: &mut ViewContext, ++ name: &'static str, ++ state: bool, ++ ) -> Stateful
{ + let unit_id = ElementId::from(SharedString::from(format!("status_{}", name))); +- div() +- .text_ui(cx) +- .id(unit_id) +- .child( +- h_flex() +- .justify_between() +- .child(Label::new(name)) +- .child(match state { +- true => Label::new("Active").color(Color::Success), +- false => Label::new("Inactive").color(Color::Error) +- }) +- ) ++ div().text_ui(cx).id(unit_id).child( ++ h_flex() ++ .justify_between() ++ .child(Label::new(name)) ++ .child(match state { ++ true => Label::new("Active").color(Color::Success), ++ false => Label::new("Inactive").color(Color::Error), ++ }), ++ ) + } + + fn warn_unimplemented(&self, cx: &mut ViewContext) { +- let message = String::from("This operation is not implemented yet, functions may not behave correctly"); +- let _ = cx.prompt(PromptLevel::Warning, "Unimplemented", Some(&message), &["Got it"]); ++ let message = String::from( ++ "This operation is not implemented yet, functions may not behave correctly", ++ ); ++ let _ = cx.prompt( ++ PromptLevel::Warning, ++ "Unimplemented", ++ Some(&message), ++ &["Got it"], ++ ); + } + } + +@@ -331,10 +332,7 @@ fn horizontal_separator(cx: &mut WindowContext) -> Div { + + #[cfg(test)] + mod tests { +- use std::future::Future; +- use db::smol::future::FutureExt; +- use super::*; +- use gpui::{TestAppContext, VisualTestContext}; ++ use gpui::TestAppContext; + use workspace::AppState; + + #[gpui::test] +@@ -344,7 +342,7 @@ mod tests { + mega::init(cx); + state + }); +- ++ + state.mega.update(cx, |this, cx| { + let recv = this.get_fuse_config(cx); + cx.spawn(|_this, _cx| async move { +@@ -354,9 +352,8 @@ mod tests { + eprintln!("{config:?}"); + } + } +- }).detach(); ++ }) ++ .detach(); + }); +- + } +- + } +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0022-improve-context-button-display.patch b/scripts/zed-intergration/patches/0022-improve-context-button-display.patch new file mode 100644 index 000000000..0970a4124 --- /dev/null +++ b/scripts/zed-intergration/patches/0022-improve-context-button-display.patch @@ -0,0 +1,592 @@ +From e16d73a6bda38e6eeced4d6fabc403acd3f0eaeb Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 19 Nov 2024 19:31:44 +0800 +Subject: [PATCH 22/24] improve context button display + +--- + Cargo.lock | 1 + + crates/mega/Cargo.toml | 1 + + crates/mega/src/mega.rs | 162 ++++++++++--------- + crates/mega/src/mega_settings.rs | 6 +- + crates/mega/src/utils/api.rs | 6 +- + crates/mega_panel/src/mega_panel.rs | 19 ++- + crates/mega_panel/src/mega_panel_settings.rs | 6 +- + crates/project_panel/src/project_panel.rs | 65 +++++--- + 8 files changed, 155 insertions(+), 111 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index a69e9c68c9..0671471f30 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -6899,6 +6899,7 @@ dependencies = [ + "bytes 1.7.2", + "futures 0.3.30", + "gpui", ++ "log", + "radix_trie", + "reqwest_client", + "schemars", +diff --git a/crates/mega/Cargo.toml b/crates/mega/Cargo.toml +index a9a4f9d9b7..8c5596d677 100644 +--- a/crates/mega/Cargo.toml ++++ b/crates/mega/Cargo.toml +@@ -23,3 +23,4 @@ bytes.workspace = true + schemars.workspace = true + anyhow.workspace = true + radix_trie.workspace = true ++log.workspace = true +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index abc46f1651..628fe311d9 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -1,21 +1,23 @@ +-use utils::api::{ +- ConfigRequest, ConfigResponse, MountRequest, MountResponse, MountsResponse, UmountRequest, +- UmountResponse, +-}; + use crate::mega_settings::MegaSettings; + use futures::channel::oneshot; + use futures::channel::oneshot::Receiver; + use futures::AsyncReadExt; + use gpui::http_client::{AsyncBody, HttpClient}; +-use gpui::{AppContext, EventEmitter, ModelContext}; ++use gpui::{hash, AppContext, EventEmitter, ModelContext}; + use radix_trie::{Trie, TrieCommon}; + use reqwest_client::ReqwestClient; + use settings::Settings; ++use std::collections::BTreeSet; + use std::fmt::{Debug, Formatter}; +-use std::path::PathBuf; ++use std::io::BufWriter; ++use std::path::{Path, PathBuf}; + use std::process::Command; + use std::sync::Arc; + use std::time::Duration; ++use utils::api::{ ++ ConfigRequest, ConfigResponse, MountRequest, MountResponse, MountsResponse, UmountRequest, ++ UmountResponse, ++}; + + mod mega_settings; + pub mod utils; +@@ -46,7 +48,8 @@ pub struct Mega { + heartbeat: bool, + + mount_point: Option, +- checkout_path: Trie, ++ checkout_lut: Trie, ++ checkout_path: BTreeSet, + + mega_url: String, + fuse_url: String, +@@ -59,10 +62,13 @@ impl EventEmitter for Mega {} + + impl Debug for Mega { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { ++ let lut = self.checkout_lut.keys().map(|key| key.to_path_buf()).collect::>(); ++ + write!( + f, +- "fuse_executable: {:?}, mega_url: {}, fuse_url: {}", +- self.fuse_executable, self.mega_url, self.fuse_url ++ "fuse_executable: {:?}, mega_url: {}, fuse_url: {}\n\ ++ LUT: {:?}", ++ self.fuse_executable, self.mega_url, self.fuse_url, lut + ) + } + } +@@ -85,12 +91,15 @@ impl Mega { + // To not affected by global proxy settings. + let client = ReqwestClient::new(); + ++ println!("Mount point: {mount_path:?}"); ++ + let mount_point = if mount_path.exists() { + Some(mount_path) + } else { ++ log::error!("Mount point in setting does not exist"); + None + }; +- ++ + Mega { + fuse_executable, + +@@ -99,6 +108,7 @@ impl Mega { + heartbeat: false, + + mount_point, ++ checkout_lut: Trie::default(), + checkout_path: Default::default(), + + mega_url, +@@ -108,65 +118,66 @@ impl Mega { + } + + pub fn update_status(&mut self, cx: &mut ModelContext) { +- let checkouts = self.get_checkout_paths(cx); + let config = self.get_fuse_config(cx); ++ let checkouts = self.get_checkout_paths(cx); + + cx.spawn(|this, mut cx| async move { +- if let Ok(opt) = checkouts.await { ++ // When mount point changed, emit an event. ++ // update mount point if it's none. ++ if let Ok(opt) = config.await { + match opt { + None => { + // This means we cannot connect to a localhost port. + // So we can assume that fuse has been dead. +- this.update(&mut cx, |mega, cx| { ++ let _ = this.update(&mut cx, |mega, cx| { + mega.fuse_running = false; + mega.fuse_mounted = false; + cx.emit(Event::FuseRunning(false)); + cx.emit(Event::FuseMounted(None)); +- }) ++ return; ++ }); + } +- Some(info) => { +- // Check if checkout-ed paths are correct +- this.update(&mut cx, |mega, cx| { +- mega.fuse_running = true; +- +- let trie = &mut mega.checkout_path; +- for ref i in info.mounts { +- let missing = trie.get_ancestor(&i.path).is_none(); +- if missing { +- // Should not happen unless on startup. +- trie.insert(i.path.clone(), i.inode); +- cx.emit(Event::FuseCheckout(Some(PathBuf::from( +- i.path.clone(), +- )))) ++ ++ Some(config) => { ++ let _ = this.update(&mut cx, |this, cx| { ++ let path = PathBuf::from(config.config.mount_path); ++ if (this.fuse_mounted && this.fuse_running) && this.mount_point.is_some() { ++ if let Some(inner) = &this.mount_point { ++ if !inner.eq(&path) { ++ this.mount_point = Some(path); ++ cx.emit(Event::FuseMounted(this.mount_point.clone())); ++ } ++ } ++ } else if this.mount_point.is_none() { ++ this.mount_point = Some(path); ++ if this.fuse_running { ++ this.fuse_mounted = true; ++ cx.emit(Event::FuseMounted(this.mount_point.clone())); + } + } +- }) ++ }); + } + } +- } else { +- Ok(()) + } +- .unwrap(); +- +- // When mount point changed, emit an event. +- // update mount point if it's none. +- if let Ok(Some(config)) = config.await { +- this.update(&mut cx, |this, cx| { +- let path = PathBuf::from(config.config.mount_path); +- if (this.fuse_mounted && this.fuse_running) && this.mount_point.is_some() { +- if let Some(inner) = &this.mount_point { +- if !inner.eq(&path) { +- this.mount_point = Some(path); +- cx.emit(Event::FuseMounted(this.mount_point.clone())); +- } ++ ++ if let Ok(Some(info)) = checkouts.await { ++ // Check if checkout-ed paths are correct ++ let _ = this.update(&mut cx, |mega, cx| { ++ mega.fuse_running = true; ++ ++ let trie = &mut mega.checkout_lut; ++ for i in info.mounts.iter() { ++ let path = i.path.parse().unwrap(); ++ ++ let missing = trie.get_ancestor(&path).is_none(); ++ if missing { ++ // Should not happen unless on startup. ++ mega.checkout_path.insert(hash(&path)); ++ trie.insert(path, i.inode); ++ cx.emit(Event::FuseCheckout(Some(PathBuf::from(i.path.clone())))); + } +- } else if this.fuse_running && this.mount_point.is_none() { +- this.mount_point = Some(path); +- cx.emit(Event::FuseMounted(this.mount_point.clone())); + } +- }) +- } else { +- Ok(()) ++ }); + } + }) + .detach(); +@@ -181,7 +192,7 @@ impl Mega { + /// Does nothing if fuse not running. + pub fn toggle_fuse(&mut self, cx: &mut ModelContext) { + self.update_status(cx); +- let paths = &self.checkout_path; ++ let paths = &self.checkout_lut; + + if !self.fuse_running { + return; +@@ -214,7 +225,7 @@ impl Mega { + let path = PathBuf::from(p); // FIXME is there a better way? + cx.spawn(|mega, mut cx| async move { + let recv = mega +- .update(&mut cx, |this, cx| this.restore_path(cx, path)) ++ .update(&mut cx, |this, cx| this.restore_path(cx, &path)) + .expect("mega delegate not be dropped"); + + if let Ok(Some(_resp)) = recv.await { +@@ -284,18 +295,16 @@ impl Mega { + pub fn restore_path( + &self, + cx: &ModelContext, +- path: PathBuf, ++ path: &PathBuf, + ) -> Receiver> { + let (tx, rx) = oneshot::channel(); + let client = self.http_client.clone(); + let uri = format!("{base}/api/fs/umount", base = self.fuse_url); + + // If panics here, that means there's a bug in code. +- // maybe we should ensure every path absolute? +- let path = path.to_str().unwrap(); +- let inode = self.checkout_path.get_ancestor_value(path); ++ let inode = self.checkout_lut.get_ancestor_value(path); + let req = UmountRequest { +- path: Some(path), ++ path: Some(path.to_str().unwrap()), + inode: Some(inode.unwrap().to_owned()), + }; + let body = serde_json::to_string(&req).unwrap(); +@@ -427,29 +436,36 @@ impl Mega { + .detach(); + } + +- pub fn is_path_checkout(&self, path: PathBuf) -> bool { +- let set = &self.checkout_path; ++ pub fn is_path_checkout(&self, path: &PathBuf) -> bool { ++ // FIXME this function calls every time project_panel refresh * entry numbers ++ // the PathBuf construction happens right before calling this ++ // because the trie cannot receive a Arc for finding PathBuf members. ++ // LAG? ++ self.checkout_lut.get_ancestor(path).is_some() ++ } + +- set.get_ancestor(path.to_str().unwrap()).is_some() ++ pub fn is_path_checkout_root(&self, path: &PathBuf) -> bool { ++ self.checkout_path.contains(&hash(path)) + } + +- pub fn mark_checkout(&mut self, cx: &mut ModelContext, path: String, inode: u64) { +- if self.mount_point.is_some() { +- let path = self +- .mount_point +- .clone() +- .unwrap() +- .to_str() +- .unwrap() +- .to_string() +- + path.as_str(); +- self.checkout_path.insert(path, inode); ++ pub fn mark_checkout(&mut self, cx: &mut ModelContext, path: PathBuf, inode: u64) { ++ if let Some(base) = &self.mount_point { ++ self.checkout_path.insert(hash(&path)); ++ self.checkout_lut.insert(path, inode); + cx.emit(Event::FuseCheckout(None)); + } + } + +- pub fn mark_commited(&mut self, cx: &mut ModelContext, path: PathBuf) { +- self.checkout_path.remove(path.to_str().unwrap()); ++ pub fn mark_commited(&mut self, cx: &mut ModelContext, path: &PathBuf) { ++ self.checkout_path.remove(&hash(path)); ++ self.checkout_lut.remove(path); + cx.emit(Event::FuseCheckout(None)); + } ++ ++ pub fn mount_point(&self) -> Option<&PathBuf> { ++ match self.mount_point { ++ Some(ref path) => Some(path), ++ None => None ++ } ++ } + } +diff --git a/crates/mega/src/mega_settings.rs b/crates/mega/src/mega_settings.rs +index bada5ccd80..38916f01fb 100644 +--- a/crates/mega/src/mega_settings.rs ++++ b/crates/mega/src/mega_settings.rs +@@ -1,7 +1,7 @@ +-use std::path::PathBuf; +-use schemars::JsonSchema; + use gpui::private::serde_derive::{Deserialize, Serialize}; ++use schemars::JsonSchema; + use settings::{Settings, SettingsSources}; ++use std::path::PathBuf; + + #[derive(Default, Deserialize, Debug, Clone, PartialEq)] + pub struct MegaSettings { +@@ -47,4 +47,4 @@ impl Settings for MegaSettings { + ) -> anyhow::Result { + sources.json_merge() + } +-} +\ No newline at end of file ++} +diff --git a/crates/mega/src/utils/api.rs b/crates/mega/src/utils/api.rs +index 3251888e60..f96be81187 100644 +--- a/crates/mega/src/utils/api.rs ++++ b/crates/mega/src/utils/api.rs +@@ -1,7 +1,7 @@ + use serde::{Deserialize, Serialize}; + +-const SUCCESS: &str = "Success"; +-const FAIL : &str = "Fail"; ++const SUCCESS: &str = "Success"; ++const FAIL: &str = "Fail"; + #[derive(Debug, Deserialize, Serialize)] + pub struct MountRequest<'a> { + pub path: &'a str, +@@ -102,4 +102,4 @@ impl FuseResponse for ConfigResponse { + fn is_resp_failed(&self) -> bool { + self.status.eq(FAIL) + } +-} +\ No newline at end of file ++} +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index c5105988e0..c31a341b84 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -7,8 +7,8 @@ use gpui::private::serde_json; + use gpui::{ + actions, div, Action, AppContext, AssetSource, AsyncWindowContext, Div, ElementId, + EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, +- ParentElement, Pixels, PromptLevel, Render, SharedString, Stateful, Styled, Task +- , View, ViewContext, VisualContext, WeakView, WindowContext, ++ ParentElement, Pixels, PromptLevel, Render, SharedString, Stateful, Styled, Task, View, ++ ViewContext, VisualContext, WeakView, WindowContext, + }; + use mega::Mega; + use settings::Settings; +@@ -273,14 +273,25 @@ impl MegaPanel { + } + + v_flex().id("mega-control-pad").size_full().children([ ++ encap_btn( ++ Button::new("refresh_mega_status", "Refresh Status") ++ .full_width() ++ .icon(IconName::Control) ++ .icon_position(IconPosition::Start) ++ .on_click(cx.listener(|this, _, cx | { ++ this.mega_handle.update(cx, |mega, cx| { ++ mega.update_status(cx); ++ println!("{mega:?}"); ++ }) ++ })) ++ ), + encap_btn( + Button::new("btn_toggle_mount", "Toggle Fuse Running") + .full_width() + .icon(IconName::Context) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { +- this.mega_handle +- .update(cx, |mega, cx| mega.toggle_mount(cx)); ++ this.mega_handle.update(cx, |mega, cx| mega.toggle_mount(cx)); + })), + ), + encap_btn( +diff --git a/crates/mega_panel/src/mega_panel_settings.rs b/crates/mega_panel/src/mega_panel_settings.rs +index bfd4b337d0..60c8229f77 100644 +--- a/crates/mega_panel/src/mega_panel_settings.rs ++++ b/crates/mega_panel/src/mega_panel_settings.rs +@@ -1,6 +1,6 @@ +-use schemars::JsonSchema; +-use gpui::Pixels; + use gpui::private::serde::{Deserialize, Serialize}; ++use gpui::Pixels; ++use schemars::JsonSchema; + use settings::{Settings, SettingsSources}; + + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] +@@ -44,4 +44,4 @@ impl Settings for MegaPanelSettings { + ) -> anyhow::Result { + sources.json_merge() + } +-} +\ No newline at end of file ++} +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index 70ad294cd9..fcf0e733b1 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -26,7 +26,8 @@ use gpui::{ + WindowContext, + }; + use indexmap::IndexMap; +-use mega::{api::FuseResponse, Mega}; ++use mega::utils::api::FuseResponse; ++use mega::Mega; + use menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev}; + use project::{ + relativize_path, Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath, Worktree, +@@ -44,6 +45,7 @@ use std::{ + sync::Arc, + time::Duration, + }; ++use std::ops::Deref; + use theme::ThemeSettings; + use ui::{prelude::*, v_flex, ContextMenu, Icon, Label, ListItem, Tooltip}; + use util::{maybe, ResultExt, TryFutureExt}; +@@ -114,6 +116,8 @@ struct EntryDetails { + path: Arc, + depth: usize, + kind: EntryKind, ++ is_checkout: bool, ++ is_checkout_root: bool, + is_ignored: bool, + is_expanded: bool, + is_selected: bool, +@@ -530,8 +534,8 @@ impl ProjectPanel { + entry_id, + }); + +- // FIXME add fuse dir specific behaviors + if let Some((worktree, entry)) = self.selected_sub_entry(cx) { ++ let pb: PathBuf = entry.path.to_path_buf(); + let auto_fold_dirs = ProjectPanelSettings::get_global(cx).auto_fold_dirs; + let is_root = Some(entry) == worktree.root_entry(); + let is_dir = entry.is_dir(); +@@ -541,7 +545,8 @@ impl ProjectPanel { + let is_read_only = project.is_read_only(cx); + let is_remote = project.is_via_collab() && project.dev_server_project_id().is_none(); + let is_local = project.is_local(); +- let is_checkout = mega.is_path_checkout(entry.path.to_path_buf()); ++ let is_checkout = mega.is_path_checkout(&pb); ++ let is_checkout_root = mega.is_path_checkout_root(&pb); + + let context_menu = ContextMenu::build(cx, |menu, cx| { + menu.context(self.focus_handle.clone()).map(|menu| { +@@ -553,9 +558,11 @@ impl ProjectPanel { + menu.when(!is_checkout && !is_root, |menu| { + menu.action("Checkout Path", Box::new(CheckoutPath)) + }) +- .when(is_checkout, |menu| { ++ .when(is_checkout && is_checkout_root, |menu| { + menu.action("Commit Path", Box::new(CommitPath)) +- .separator() ++ }) ++ .when(is_checkout && !is_checkout_root, |menu| { ++ menu.separator() + .action("New File", Box::new(NewFile)) + .action("New Folder", Box::new(NewDirectory)) + .separator() +@@ -604,20 +611,19 @@ impl ProjectPanel { + // .action("Rename", Box::new(Rename)) + }) + .when(!is_remote & is_root, |menu| { +- menu +- .action( +- "Add Folder to Project…", +- Box::new(workspace::AddFolderToProject), +- ) +- .entry( +- "Remove from Project", +- None, +- cx.handler_for(&this, move |this, cx| { +- this.project.update(cx, |project, cx| { +- project.remove_worktree(worktree_id, cx) +- }); +- }), +- ) ++ menu.action( ++ "Add Folder to Project…", ++ Box::new(workspace::AddFolderToProject), ++ ) ++ .entry( ++ "Remove from Project", ++ None, ++ cx.handler_for(&this, move |this, cx| { ++ this.project.update(cx, |project, cx| { ++ project.remove_worktree(worktree_id, cx) ++ }); ++ }), ++ ) + }) + .when(is_root, |menu| { + menu.separator() +@@ -1344,16 +1350,18 @@ impl ProjectPanel { + + fn checkout_specific_path(&mut self, _: &CheckoutPath, cx: &mut ViewContext) { + if let Some((_, entry)) = self.selected_entry_handle(cx) { +- let path = entry.path.clone(); ++ let path = entry.path.to_path_buf(); + self.mega.update(cx, |mega, cx| { +- let recv = mega.checkout_path(cx, path.to_path_buf()); ++ println!("Checkout: {:?}", path); ++ let recv = mega.checkout_path(cx, path); + cx.spawn(|this, mut cx| async move { + let resp = recv.await; + println!("Response: {resp:?}"); + if let Ok(Some(resp)) = resp { + if resp.is_resp_succeed() { + this.update(&mut cx, |mega, cx| { +- mega.mark_checkout(cx, resp.mount.path, resp.mount.inode); ++ let path = resp.mount.path.parse().unwrap(); ++ mega.mark_checkout(cx, path, resp.mount.inode); + }) + .expect("Mega delegate not been dropped"); + } +@@ -1365,17 +1373,18 @@ impl ProjectPanel { + } + + fn commit_specific_path(&mut self, _: &CommitPath, cx: &mut ViewContext) { ++ // TODO close windows that are in the commited paths. + if let Some((_, entry)) = self.selected_entry_handle(cx) { +- let path = entry.path.clone(); ++ let path = entry.path.to_path_buf(); + self.mega.update(cx, |mega, cx| { +- let recv = mega.restore_path(cx, path.to_path_buf()); ++ let recv = mega.restore_path(cx, &path); + cx.spawn(|this, mut cx| async move { + let resp = recv.await; + println!("Response: {resp:?}"); + if let Ok(Some(resp)) = resp { + if resp.is_resp_succeed() { + this.update(&mut cx, |mega, cx| { +- mega.mark_commited(cx, path.to_path_buf()); ++ mega.mark_commited(cx, &path); + }) + .expect("Mega delegate not been dropped"); + } +@@ -2302,12 +2311,18 @@ impl ProjectPanel { + worktree_id: snapshot.id(), + entry_id: entry.id, + }; ++ ++ let pb = entry.path.to_path_buf(); ++ let is_checkout = self.mega.read(cx).is_path_checkout(&pb); ++ let is_checkout_root = self.mega.read(cx).is_path_checkout_root(&pb); + let mut details = EntryDetails { + filename, + icon, + path: entry.path.clone(), + depth, + kind: entry.kind, ++ is_checkout, ++ is_checkout_root, + is_ignored: entry.is_ignored, + is_expanded, + is_selected: self.selection == Some(selection), +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0023-display-checkout-points-in-mega_panel.patch b/scripts/zed-intergration/patches/0023-display-checkout-points-in-mega_panel.patch new file mode 100644 index 000000000..ab75add38 --- /dev/null +++ b/scripts/zed-intergration/patches/0023-display-checkout-points-in-mega_panel.patch @@ -0,0 +1,135 @@ +From 3bdfb29034ba58d16539c2cb2f9cfeb636abd5ff Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 19 Nov 2024 20:19:53 +0800 +Subject: [PATCH 23/24] display checkout points in mega_panel + +--- + crates/mega/src/mega.rs | 27 ++++++++++++++++++++------- + crates/mega_panel/src/mega_panel.rs | 21 +++++++++++++++------ + 2 files changed, 35 insertions(+), 13 deletions(-) + +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 628fe311d9..c9473097ee 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -62,8 +62,12 @@ impl EventEmitter for Mega {} + + impl Debug for Mega { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +- let lut = self.checkout_lut.keys().map(|key| key.to_path_buf()).collect::>(); +- ++ let lut = self ++ .checkout_lut ++ .keys() ++ .map(|key| key.to_path_buf()) ++ .collect::>(); ++ + write!( + f, + "fuse_executable: {:?}, mega_url: {}, fuse_url: {}\n\ +@@ -99,7 +103,7 @@ impl Mega { + log::error!("Mount point in setting does not exist"); + None + }; +- ++ + Mega { + fuse_executable, + +@@ -141,7 +145,9 @@ impl Mega { + Some(config) => { + let _ = this.update(&mut cx, |this, cx| { + let path = PathBuf::from(config.config.mount_path); +- if (this.fuse_mounted && this.fuse_running) && this.mount_point.is_some() { ++ if (this.fuse_mounted && this.fuse_running) ++ && this.mount_point.is_some() ++ { + if let Some(inner) = &this.mount_point { + if !inner.eq(&path) { + this.mount_point = Some(path); +@@ -159,7 +165,7 @@ impl Mega { + } + } + } +- ++ + if let Ok(Some(info)) = checkouts.await { + // Check if checkout-ed paths are correct + let _ = this.update(&mut cx, |mega, cx| { +@@ -187,6 +193,13 @@ impl Mega { + (self.fuse_running, self.fuse_mounted) + } + ++ pub fn checkout_points(&self) -> Vec { ++ self.checkout_lut ++ .keys() ++ .map(|key| key.as_path().to_str().unwrap().to_string()) ++ .collect() ++ } ++ + /// ## Toggle Fuse checkouts + /// Checkout or un-checkout the paths in zed. + /// Does nothing if fuse not running. +@@ -461,11 +474,11 @@ impl Mega { + self.checkout_lut.remove(path); + cx.emit(Event::FuseCheckout(None)); + } +- ++ + pub fn mount_point(&self) -> Option<&PathBuf> { + match self.mount_point { + Some(ref path) => Some(path), +- None => None ++ None => None, + } + } + } +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index c31a341b84..69e42259b6 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -4,12 +4,7 @@ use db::kvp::KEY_VALUE_STORE; + use fs::Fs; + use gpui::private::serde_derive::{Deserialize, Serialize}; + use gpui::private::serde_json; +-use gpui::{ +- actions, div, Action, AppContext, AssetSource, AsyncWindowContext, Div, ElementId, +- EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, +- ParentElement, Pixels, PromptLevel, Render, SharedString, Stateful, Styled, Task, View, +- ViewContext, VisualContext, WeakView, WindowContext, +-}; ++use gpui::{actions, div, Action, AppContext, AssetSource, AsyncWindowContext, Div, Element, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, ParentElement, Pixels, PromptLevel, Render, SharedString, Stateful, Styled, Task, View, ViewContext, VisualContext, WeakView, WindowContext}; + use mega::Mega; + use settings::Settings; + use std::sync::Arc; +@@ -98,6 +93,8 @@ impl Render for MegaPanel { + .child(horizontal_separator(cx)) + .child(self.render_status(cx)) + .child(horizontal_separator(cx)) ++ .child(self.render_checkout_points(cx)) ++ .child(horizontal_separator(cx)) + .child(self.render_buttons(cx)); + + mega_panel +@@ -267,6 +264,18 @@ impl MegaPanel { + ]) + } + ++ fn render_checkout_points(&mut self, cx: &mut ViewContext) -> Stateful
{ ++ let points = self.mega_handle.read(cx).checkout_points(); ++ let points: Vec