diff --git a/scripts/zed-intergration/build.sh b/scripts/zed-intergration/build.sh index 41d4775ec..f2a9a48a0 100755 --- a/scripts/zed-intergration/build.sh +++ b/scripts/zed-intergration/build.sh @@ -3,7 +3,7 @@ # Clone zed and switch to the specific version ZED_ROOT='./zed' ZED_REPO='https://github.com/zed-industries/zed.git' -TARGET_PATCH_VERSION='04ee5e3e6e563fbcc6ea37f8733aaf6897428773' +TARGET_PATCH_VERSION='a56f946a7d0734839f820d2943dabe7fa09a4b22' # Place patch files here PATCH_DIR='./patches/' 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 new file mode 100644 index 000000000..24cba31b3 --- /dev/null +++ b/scripts/zed-intergration/patches/0001-add-new-module-mega-and-mega_panel.patch @@ -0,0 +1,262 @@ +From 006bd0e01b5deba6792af824a5a73c32e96efa2c Mon Sep 17 00:00:00 2001 +From: Neon +Date: Sat, 19 Oct 2024 21:24:00 +0800 +Subject: [PATCH 01/10] add new module mega and mega_panel + +--- + Cargo.lock | 23 ++++++ + Cargo.toml | 4 + + crates/mega/LICENSE-GPL | 1 + + crates/mega/src/delegate.rs | 0 + crates/mega_panel/LICENSE-GPL | 1 + + crates/mega_panel/src/mega_panel_settings.rs | 80 ++++++++++++++++++++ + crates/zed/Cargo.toml | 1 + + crates/zed/src/zed.rs | 12 +++ + 8 files changed, 122 insertions(+) + create mode 120000 crates/mega/LICENSE-GPL + create mode 100644 crates/mega/src/delegate.rs + create mode 120000 crates/mega_panel/LICENSE-GPL + create mode 100644 crates/mega_panel/src/mega_panel_settings.rs + +diff --git a/Cargo.lock b/Cargo.lock +index 6f1d7b2c9c..d39aae09ee 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -6884,6 +6884,28 @@ dependencies = [ + "objc", + ] + ++[[package]] ++name = "mega" ++version = "0.1.0" ++dependencies = [ ++ "reqwest 0.12.8", ++ "serde", ++] ++ ++[[package]] ++name = "mega_panel" ++version = "0.1.0" ++dependencies = [ ++ "anyhow", ++ "editor", ++ "file_icons", ++ "gpui", ++ "schemars", ++ "serde", ++ "settings", ++ "workspace", ++] ++ + [[package]] + name = "memchr" + version = "2.7.4" +@@ -14631,6 +14653,7 @@ dependencies = [ + "libc", + "log", + "markdown_preview", ++ "mega_panel", + "menu", + "mimalloc", + "nix", +diff --git a/Cargo.toml b/Cargo.toml +index 4a4ddb4424..6a02b98478 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -64,6 +64,8 @@ members = [ + "crates/markdown", + "crates/markdown_preview", + "crates/media", ++ "crates/mega", ++ "crates/mega_panel", + "crates/menu", + "crates/multi_buffer", + "crates/node_runtime", +@@ -240,6 +242,8 @@ lsp = { path = "crates/lsp" } + markdown = { path = "crates/markdown" } + markdown_preview = { path = "crates/markdown_preview" } + media = { path = "crates/media" } ++mega = { path = "crates/mega" } ++mega_panel = { path = "crates/mega_panel" } + menu = { path = "crates/menu" } + multi_buffer = { path = "crates/multi_buffer" } + node_runtime = { path = "crates/node_runtime" } +diff --git a/crates/mega/LICENSE-GPL b/crates/mega/LICENSE-GPL +new file mode 120000 +index 0000000000..89e542f750 +--- /dev/null ++++ b/crates/mega/LICENSE-GPL +@@ -0,0 +1 @@ ++../../LICENSE-GPL +\ No newline at end of file +diff --git a/crates/mega/src/delegate.rs b/crates/mega/src/delegate.rs +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/crates/mega_panel/LICENSE-GPL b/crates/mega_panel/LICENSE-GPL +new file mode 120000 +index 0000000000..89e542f750 +--- /dev/null ++++ b/crates/mega_panel/LICENSE-GPL +@@ -0,0 +1 @@ ++../../LICENSE-GPL +\ No newline at end of file +diff --git a/crates/mega_panel/src/mega_panel_settings.rs b/crates/mega_panel/src/mega_panel_settings.rs +new file mode 100644 +index 0000000000..1ca1149b01 +--- /dev/null ++++ b/crates/mega_panel/src/mega_panel_settings.rs +@@ -0,0 +1,80 @@ ++use schemars::JsonSchema; ++use gpui::Pixels; ++use gpui::private::serde::{Deserialize, Serialize}; ++use settings::{Settings, SettingsSources}; ++ ++#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] ++#[serde(rename_all = "snake_case")] ++pub enum MegaPanelDockPosition { ++ Left, ++ Right, ++} ++ ++#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] ++pub struct MegaPanelSettings { ++ pub button: bool, ++ pub default_width: Pixels, ++ pub dock: MegaPanelDockPosition, ++ pub file_icons: bool, ++ pub folder_icons: bool, ++ pub git_status: bool, ++ pub indent_size: f32, ++ pub auto_reveal_entries: bool, ++ pub auto_fold_dirs: bool, ++} ++ ++#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] ++pub struct MegaPanelSettingsContent { ++ /// Whether to show the outline panel button in the status bar. ++ /// ++ /// Default: true ++ pub button: Option, ++ /// Customize default width (in pixels) taken by outline panel ++ /// ++ /// Default: 240 ++ pub default_width: Option, ++ /// The position of outline panel ++ /// ++ /// Default: left ++ pub dock: Option, ++ /// Whether to show file icons in the outline panel. ++ /// ++ /// Default: true ++ pub file_icons: Option, ++ /// Whether to show folder icons or chevrons for directories in the outline panel. ++ /// ++ /// Default: true ++ pub folder_icons: Option, ++ /// Whether to show the git status in the outline panel. ++ /// ++ /// Default: true ++ pub git_status: Option, ++ /// Amount of indentation (in pixels) for nested items. ++ /// ++ /// Default: 20 ++ pub indent_size: Option, ++ /// Whether to reveal it in the outline panel automatically, ++ /// when a corresponding project entry becomes active. ++ /// Gitignored entries are never auto revealed. ++ /// ++ /// Default: true ++ pub auto_reveal_entries: Option, ++ /// Whether to fold directories automatically ++ /// when directory has only one directory inside. ++ /// ++ /// Default: true ++ pub auto_fold_dirs: Option, ++} ++ ++impl Settings for MegaPanelSettings { ++ const KEY: Option<&'static str> = Some("outline_panel"); ++ ++ type FileContent = MegaPanelSettingsContent; ++ ++ fn load( ++ sources: SettingsSources, ++ _: &mut gpui::AppContext, ++ ) -> anyhow::Result { ++ sources.json_merge() ++ } ++} +\ No newline at end of file +diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml +index 69ca3aa98d..81243cf62f 100644 +--- a/crates/zed/Cargo.toml ++++ b/crates/zed/Cargo.toml +@@ -66,6 +66,7 @@ languages = { workspace = true, features = ["load-grammars"] } + libc.workspace = true + log.workspace = true + markdown_preview.workspace = true ++mega_panel.workspace = true + menu.workspace = true + mimalloc = { version = "0.1", optional = true } + nix = { workspace = true, features = ["pthread", "signal"] } +diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs +index c33cef4a4b..545e42551a 100644 +--- a/crates/zed/src/zed.rs ++++ b/crates/zed/src/zed.rs +@@ -27,6 +27,7 @@ use anyhow::Context as _; + use assets::Assets; + use futures::{channel::mpsc, select_biased, StreamExt}; + use outline_panel::OutlinePanel; ++use mega_panel::MegaPanel; + use project::Item; + use project_panel::ProjectPanel; + use quick_action_bar::QuickActionBar; +@@ -238,6 +239,7 @@ pub fn initialize_workspace( + + let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); + let outline_panel = OutlinePanel::load(workspace_handle.clone(), cx.clone()); ++ let mega_panel = MegaPanel::load(workspace_handle.clone(), cx.clone()); + let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone()); + let channels_panel = + collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone()); +@@ -250,6 +252,7 @@ pub fn initialize_workspace( + + let ( + project_panel, ++ mega_panel, + outline_panel, + terminal_panel, + assistant_panel, +@@ -258,6 +261,7 @@ pub fn initialize_workspace( + notification_panel, + ) = futures::try_join!( + project_panel, ++ mega_panel, + outline_panel, + terminal_panel, + assistant_panel, +@@ -269,6 +273,7 @@ pub fn initialize_workspace( + workspace_handle.update(&mut cx, |workspace, cx| { + workspace.add_panel(assistant_panel, cx); + workspace.add_panel(project_panel, cx); ++ workspace.add_panel(mega_panel, cx); + workspace.add_panel(outline_panel, cx); + workspace.add_panel(terminal_panel, cx); + workspace.add_panel(channels_panel, cx); +@@ -467,6 +472,13 @@ pub fn initialize_workspace( + workspace.toggle_panel_focus::(cx); + }, + ) ++ .register_action( ++ |workspace: &mut Workspace, ++ _: &mega_panel::ToggleFocus, ++ cx: &mut ViewContext| { ++ workspace.toggle_panel_focus::(cx); ++ }, ++ ) + .register_action( + |workspace: &mut Workspace, + _: &outline_panel::ToggleFocus, +-- +2.43.0 + diff --git a/scripts/zed-intergration/patches/0001-feat-basic-support-for-working-with-mega.patch b/scripts/zed-intergration/patches/0001-feat-basic-support-for-working-with-mega.patch deleted file mode 100644 index a9e27c6e1..000000000 --- a/scripts/zed-intergration/patches/0001-feat-basic-support-for-working-with-mega.patch +++ /dev/null @@ -1,407 +0,0 @@ -From afb90731865925957bc817414907f8070a5c3b6a Mon Sep 17 00:00:00 2001 -From: Neon -Date: Sun, 8 Sep 2024 20:19:20 +0800 -Subject: [PATCH] feat: basic support for working with mega - ---- - Cargo.lock | 9 ++ - Cargo.toml | 2 + - crates/mega_delegator/Cargo.toml | 13 +++ - crates/mega_delegator/src/mega_delegator.rs | 114 ++++++++++++++++++++ - crates/project_panel/Cargo.toml | 1 + - crates/project_panel/src/project_panel.rs | 111 ++++++++++++++----- - 6 files changed, 223 insertions(+), 27 deletions(-) - create mode 100644 crates/mega_delegator/Cargo.toml - create mode 100644 crates/mega_delegator/src/mega_delegator.rs - -diff --git a/Cargo.lock b/Cargo.lock -index 818c258aef..8a3597c041 100644 ---- a/Cargo.lock -+++ b/Cargo.lock -@@ -6534,6 +6534,14 @@ dependencies = [ - "objc", - ] - -+[[package]] -+name = "mega_delegator" -+version = "0.1.0" -+dependencies = [ -+ "isahc", -+ "lazy_static", -+] -+ - [[package]] - name = "memchr" - version = "2.7.4" -@@ -8105,6 +8113,7 @@ dependencies = [ - "git", - "gpui", - "language", -+ "mega_delegator", - "menu", - "pretty_assertions", - "project", -diff --git a/Cargo.toml b/Cargo.toml -index 740aaa9c1a..9e37dc9522 100644 ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -60,6 +60,7 @@ members = [ - "crates/markdown", - "crates/markdown_preview", - "crates/media", -+ "crates/mega_delegator", - "crates/menu", - "crates/multi_buffer", - "crates/node_runtime", -@@ -229,6 +230,7 @@ lsp = { path = "crates/lsp" } - markdown = { path = "crates/markdown" } - markdown_preview = { path = "crates/markdown_preview" } - media = { path = "crates/media" } -+mega_delegator ={ path = "crates/mega_delegator" } - menu = { path = "crates/menu" } - multi_buffer = { path = "crates/multi_buffer" } - node_runtime = { path = "crates/node_runtime" } -diff --git a/crates/mega_delegator/Cargo.toml b/crates/mega_delegator/Cargo.toml -new file mode 100644 -index 0000000000..3d423e81ae ---- /dev/null -+++ b/crates/mega_delegator/Cargo.toml -@@ -0,0 +1,13 @@ -+[package] -+name = "mega_delegator" -+version = "0.1.0" -+edition = "2021" -+publish = false -+license = "GPL-3.0-or-later" -+ -+[lib] -+path = "src/mega_delegator.rs" -+ -+[dependencies] -+lazy_static.workspace = true -+isahc.workspace = true -diff --git a/crates/mega_delegator/src/mega_delegator.rs b/crates/mega_delegator/src/mega_delegator.rs -new file mode 100644 -index 0000000000..fd6188c204 ---- /dev/null -+++ b/crates/mega_delegator/src/mega_delegator.rs -@@ -0,0 +1,114 @@ -+use std::{cell::RefCell, fs::File, process::{Child, Command, Stdio}, time::Duration}; -+ -+use isahc::config::Configurable; -+ -+// In this module, we made these assumption to mega: -+// 1. running on port 8000 -+// 2. -+ -+pub enum MegaEvent { -+ MegaStart, -+ MegaStarted, -+ MegaStop, -+} -+ -+#[derive(Debug, PartialEq)] -+pub enum MegaStatus { -+ MegaIdle, -+ MegaRunning, -+ MegaError, -+} -+ -+pub struct MegaDelegator{ -+ http_client: isahc::HttpClient, -+ holding: RefCell> -+} -+ -+impl MegaDelegator { -+ pub fn new() -> Self { -+ // TODO: In dev environment, asuuming mega and its config -+ // are right in the $PATH. -+ -+ let client = isahc::HttpClient::builder().connect_timeout(Duration::from_secs(1)).build().unwrap(); -+ -+ Self { -+ http_client: client, -+ holding: RefCell::from(None) -+ } -+ } -+ -+ pub fn status(&self) -> MegaStatus { -+ match *self.holding.borrow() { -+ Some(_) => { -+ match self.http_client.get("localhost:8000/api/v1/mono/status") { -+ Ok(_) => MegaStatus::MegaRunning, -+ Err(_) => MegaStatus::MegaIdle -+ } -+ } -+ None => MegaStatus::MegaError -+ } -+ } -+ -+ pub fn start(&self) -> &Self { -+ match self.status() { -+ MegaStatus::MegaRunning -+ | MegaStatus::MegaIdle => return self, -+ _ => {} -+ } -+ -+ let output_file = File::create("output.txt").expect("Unable to create file"); -+ let mega_root = match std::env::var("MEGA_ROOT") { -+ Ok(s) => s, -+ Err(_) => String::new() -+ }; -+ -+ let child = Command::new(format!("{}/target/debug/mega", mega_root)) -+ .args([ -+ "--config", -+ format!("{}/mega/config.toml", mega_root).as_str(), -+ "service", "http" -+ ]) -+ .stdout(Stdio::from(output_file.try_clone().expect("Failed to clone file"))) -+ .stderr(Stdio::from(output_file)) -+ .spawn() -+ .unwrap(); -+ -+ *self.holding.borrow_mut() = Some(child); -+ -+ self -+ } -+ -+ pub fn stop(&self) -> &Self { -+ if let Some(mut child) = self.holding.take() { -+ // There may be so many unexpected errors, -+ // so we simply ignore and leave. -+ if let Ok(()) = child.kill() { -+ let _ = child.wait(); -+ } -+ } -+ -+ *self.holding.borrow_mut() = None; -+ self -+ } -+ -+ pub fn get_fuse_path(&self) -> &str { -+ "/tmp" -+ } -+} -+ -+#[cfg(test)] -+mod test { -+ use std::{thread::sleep, time::Duration}; -+ -+ use crate::{MegaDelegator, MegaStatus}; -+ -+ #[test] -+ fn test_mega_start() { -+ let delegator = MegaDelegator::new(); -+ delegator.start(); -+ -+ sleep(Duration::from_secs(10)); -+ -+ assert_eq!(delegator.status(), MegaStatus::MegaRunning); -+ } -+} -diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml -index 5e11b60477..d6705f91b9 100644 ---- a/crates/project_panel/Cargo.toml -+++ b/crates/project_panel/Cargo.toml -@@ -36,6 +36,7 @@ util.workspace = true - client.workspace = true - worktree.workspace = true - workspace.workspace = true -+mega_delegator.workspace = true - - [dev-dependencies] - client = { workspace = true, features = ["test-support"] } -diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs -index d9202fab07..598924e86e 100644 ---- a/crates/project_panel/src/project_panel.rs -+++ b/crates/project_panel/src/project_panel.rs -@@ -1,6 +1,7 @@ - mod project_panel_settings; - mod scrollbar; - use client::{ErrorCode, ErrorExt}; -+use mega_delegator::{MegaDelegator, MegaEvent}; - use scrollbar::ProjectPanelScrollbar; - use settings::{Settings, SettingsStore}; - -@@ -34,6 +35,7 @@ use std::{ - ops::Range, - path::{Path, PathBuf}, - rc::Rc, -+ str::FromStr, - sync::Arc, - time::Duration, - }; -@@ -74,6 +76,7 @@ pub struct ProjectPanel { - show_scrollbar: bool, - scrollbar_drag_thumb_offset: Rc>>, - hide_scrollbar_task: Option>, -+ mega_delegator: MegaDelegator, - } - - #[derive(Clone, Debug)] -@@ -148,6 +151,7 @@ actions!( - UnfoldDirectory, - FoldDirectory, - SelectParent, -+ ToggleMega - ] - ); - -@@ -290,6 +294,7 @@ impl ProjectPanel { - show_scrollbar: !Self::should_autohide_scrollbar(cx), - hide_scrollbar_task: None, - scrollbar_drag_thumb_offset: Default::default(), -+ mega_delegator: MegaDelegator::new(), - }; - this.update_visible_entries(None, cx); - -@@ -2392,6 +2397,7 @@ impl Render for ProjectPanel { - fn render(&mut self, cx: &mut gpui::ViewContext) -> impl IntoElement { - let has_worktree = self.visible_entries.len() != 0; - let project = self.project.read(cx); -+ let ui_font = ThemeSettings::get_global(cx).ui_font.clone(); - - if has_worktree { - let items_count = self -@@ -2400,7 +2406,7 @@ impl Render for ProjectPanel { - .map(|(_, worktree_entries, _)| worktree_entries.len()) - .sum(); - -- h_flex() -+ v_flex() - .id("project-panel") - .group("project-panel") - .size_full() -@@ -2481,29 +2487,68 @@ impl Render for ProjectPanel { - ) - .track_focus(&self.focus_handle) - .child( -- uniform_list(cx.view().clone(), "entries", items_count, { -- |this, range, cx| { -- let mut items = Vec::new(); -- this.for_each_visible_entry(range, cx, |id, details, cx| { -- items.push(this.render_entry(id, details, cx)); -- }); -- items -- } -- }) -- .size_full() -- .with_sizing_behavior(ListSizingBehavior::Infer) -- .track_scroll(self.scroll_handle.clone()), -+ h_flex() -+ .m_1() -+ .border_2() -+ .border_color(cx.theme().colors().border) -+ .rounded_md() -+ .font(ui_font) -+ .child( -+ Button::new("stop_mega_button", "Stop Mega") -+ .icon(IconName::Stop) -+ .full_width() -+ .style(ButtonStyle::Filled) -+ .tooltip(move |cx| { -+ Tooltip::text("Stop Mega and unmount FUSE directories", cx) -+ }) -+ .on_click(cx.listener(|this, _, cx| { -+ this.mega_delegator.stop(); -+ -+ if let Some(entry_id) = this.last_worktree_root_id { -+ let project = this.project.read(cx); -+ -+ let worktree_id = if let Some(worktree) = -+ project.worktree_for_entry(entry_id, cx) -+ { -+ worktree.read(cx).id() -+ } else { -+ return; -+ }; -+ -+ this.project.update(cx, |project, cx| { -+ project.remove_worktree(worktree_id, cx) -+ }); -+ } -+ })), -+ ), -+ ) -+ .child( -+ h_flex() -+ .child( -+ uniform_list(cx.view().clone(), "entries", items_count, { -+ |this, range, cx| { -+ let mut items = Vec::new(); -+ this.for_each_visible_entry(range, cx, |id, details, cx| { -+ items.push(this.render_entry(id, details, cx)); -+ }); -+ items -+ } -+ }) -+ .size_full() -+ .with_sizing_behavior(ListSizingBehavior::Infer) -+ .track_scroll(self.scroll_handle.clone()), -+ ) -+ .children(self.render_scrollbar(items_count, cx)) -+ .children(self.context_menu.as_ref().map(|(menu, position, _)| { -+ deferred( -+ anchored() -+ .position(*position) -+ .anchor(gpui::AnchorCorner::TopLeft) -+ .child(menu.clone()), -+ ) -+ .with_priority(1) -+ })), - ) -- .children(self.render_scrollbar(items_count, cx)) -- .children(self.context_menu.as_ref().map(|(menu, position, _)| { -- deferred( -- anchored() -- .position(*position) -- .anchor(gpui::AnchorCorner::TopLeft) -- .child(menu.clone()), -- ) -- .with_priority(1) -- })) - } else { - v_flex() - .id("empty-project_panel") -@@ -2511,14 +2556,24 @@ impl Render for ProjectPanel { - .p_4() - .track_focus(&self.focus_handle) - .child( -- Button::new("open_project", "Open a project") -+ Button::new("start_mega", "Start Mega") - .style(ButtonStyle::Filled) - .full_width() -+ .icon(IconName::ArrowRight) - .key_binding(KeyBinding::for_action(&workspace::Open, cx)) - .on_click(cx.listener(|this, _, cx| { -- this.workspace -- .update(cx, |workspace, cx| workspace.open(&workspace::Open, cx)) -- .log_err(); -+ if let Some(task) = this -+ .workspace -+ .update(cx, |workspace, cx| { -+ let path = this.mega_delegator.start().get_fuse_path(); -+ let buf = PathBuf::from_str(path).unwrap(); -+ println!("Open workspace {}", path); -+ workspace.open_workspace_for_paths(true, vec![buf], cx) -+ }) -+ .log_err() -+ { -+ task.detach_and_log_err(cx); -+ } - })), - ) - .drag_over::(|style, _, cx| { -@@ -2580,6 +2635,8 @@ impl EventEmitter for ProjectPanel {} - - impl EventEmitter for ProjectPanel {} - -+impl EventEmitter for ProjectPanel {} -+ - impl Panel for ProjectPanel { - fn position(&self, cx: &WindowContext) -> DockPosition { - match ProjectPanelSettings::get_global(cx).dock { --- -2.46.0 - 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 new file mode 100644 index 000000000..a3990194b --- /dev/null +++ b/scripts/zed-intergration/patches/0002-mega-integrate-basic-panel-code-with-zed.patch @@ -0,0 +1,581 @@ +From afca3aee776df6674d0c6d17858d944c64ec56ae Mon Sep 17 00:00:00 2001 +From: Neon +Date: Mon, 21 Oct 2024 09:18:40 +0800 +Subject: [PATCH 02/10] mega: integrate basic panel code with zed + +--- + Cargo.lock | 7 +- + crates/mega/Cargo.toml | 18 ++ + crates/mega/src/fuse.rs | 0 + crates/mega/src/mega.rs | 52 +++++ + crates/mega_panel/Cargo.toml | 24 +++ + crates/mega_panel/src/mega_panel.rs | 195 +++++++++++++++++++ + crates/mega_panel/src/mega_panel_settings.rs | 41 +--- + crates/workspace/Cargo.toml | 1 + + crates/workspace/src/workspace.rs | 10 +- + crates/zed/Cargo.toml | 1 + + crates/zed/src/main.rs | 6 +- + 11 files changed, 315 insertions(+), 40 deletions(-) + create mode 100644 crates/mega/Cargo.toml + create mode 100644 crates/mega/src/fuse.rs + create mode 100644 crates/mega/src/mega.rs + create mode 100644 crates/mega_panel/Cargo.toml + create mode 100644 crates/mega_panel/src/mega_panel.rs + +diff --git a/Cargo.lock b/Cargo.lock +index d39aae09ee..6503eb5359 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -6888,6 +6888,7 @@ dependencies = [ + name = "mega" + version = "0.1.0" + dependencies = [ ++ "gpui", + "reqwest 0.12.8", + "serde", + ] +@@ -6897,12 +6898,14 @@ name = "mega_panel" + version = "0.1.0" + dependencies = [ + "anyhow", +- "editor", ++ "db", + "file_icons", + "gpui", ++ "mega", + "schemars", + "serde", + "settings", ++ "util", + "workspace", + ] + +@@ -14314,6 +14317,7 @@ dependencies = [ + "itertools 0.13.0", + "language", + "log", ++ "mega", + "node_runtime", + "parking_lot", + "postage", +@@ -14653,6 +14657,7 @@ dependencies = [ + "libc", + "log", + "markdown_preview", ++ "mega", + "mega_panel", + "menu", + "mimalloc", +diff --git a/crates/mega/Cargo.toml b/crates/mega/Cargo.toml +new file mode 100644 +index 0000000000..dec6093220 +--- /dev/null ++++ b/crates/mega/Cargo.toml +@@ -0,0 +1,18 @@ ++[package] ++name = "mega" ++version = "0.1.0" ++edition = "2021" ++publish = false ++license = "GPL-3.0-or-later" ++ ++[lints] ++workspace = true ++ ++[lib] ++path = 'src/mega.rs' ++ ++[dependencies] ++gpui.workspace = true ++ ++reqwest.workspace = true ++serde.workspace = true +diff --git a/crates/mega/src/fuse.rs b/crates/mega/src/fuse.rs +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +new file mode 100644 +index 0000000000..4b51004907 +--- /dev/null ++++ b/crates/mega/src/mega.rs +@@ -0,0 +1,52 @@ ++// This crate delegate mega and its fuse daemon. ++// The following requirements should be met: ++// ++// TODO: ++// 1. Only one daemon on this machine. ++// 2. At least one daemon on this machine when zed startup. ++// 3. Complete docs. ++ ++use std::sync::Arc; ++use gpui::{AppContext, Context, EventEmitter, Model, ModelContext}; ++ ++mod delegate; ++mod fuse; ++ ++pub fn init(cx: &mut AppContext) { ++ // let reservation = cx.reserve_model(); ++ // cx.insert_model(reservation, |cx| { ++ // cx.new_model(|_cx| { Mega::new() }) ++ // }); ++} ++ ++#[derive(Clone, Debug, PartialEq)] ++pub enum Event {} ++pub struct Mega {} ++ ++pub struct MegaFuse {} ++ ++impl EventEmitter for Mega {} ++ ++ ++impl Mega { ++ pub fn init_settings(cx: &mut AppContext) { ++ ++ } ++ ++ pub fn init(cx: &mut AppContext) { ++ // let reservation = cx.reserve_model(); ++ // cx.insert_model(reservation, |cx| { ++ // cx.new_model(|_cx| { Mega::new() }) ++ // }); ++ } ++ ++ pub fn new(cx: &mut AppContext) -> Self { ++ Mega {} ++ } ++ ++ pub fn toggle_mega(&self) { todo!() } ++ ++ pub fn toggle_fuse(&self) { todo!() } ++ ++ pub fn checkout_path(&self) { todo!() } ++} +diff --git a/crates/mega_panel/Cargo.toml b/crates/mega_panel/Cargo.toml +new file mode 100644 +index 0000000000..9a8ae896a2 +--- /dev/null ++++ b/crates/mega_panel/Cargo.toml +@@ -0,0 +1,24 @@ ++[package] ++name = "mega_panel" ++version = "0.1.0" ++edition = "2021" ++publish = false ++license = "GPL-3.0-or-later" ++[lib] ++path = 'src/mega_panel.rs' ++ ++[lints] ++workspace = true ++ ++[dependencies] ++mega.workspace = true ++workspace.workspace = true ++gpui.workspace = true ++file_icons.workspace = true ++settings.workspace = true ++db.workspace = true ++util.workspace = true ++ ++anyhow.workspace = true ++serde.workspace = true ++schemars.workspace = true +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +new file mode 100644 +index 0000000000..7e38d88752 +--- /dev/null ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -0,0 +1,195 @@ ++use anyhow::{anyhow, Context}; ++use db::kvp::KEY_VALUE_STORE; ++use file_icons::FileIcons; ++use gpui::{actions, Action, AppContext, AssetSource, AsyncWindowContext, Entity, EventEmitter, FocusHandle, FocusableView, IntoElement, Model, Pixels, Render, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext}; ++use gpui::private::serde_derive::{Deserialize, Serialize}; ++use gpui::private::serde_json; ++use mega::{Mega, MegaFuse}; ++use settings::{Settings, SettingsStore}; ++use util::{ResultExt, TryFutureExt}; ++use workspace::dock::{DockPosition, Panel, PanelEvent, PanelId}; ++use workspace::ui::IconName; ++use workspace::{Pane, Workspace}; ++use crate::mega_panel_settings::MegaPanelSettings; ++ ++mod mega_panel_settings; ++ ++const MEGA_PANEL_KEY: &str = "MegaPanel"; ++ ++actions!( ++ mega_panel, ++ [ ++ Open, ++ ToggleFocus, ++ ToggleFuseMount, ++ ] ++); ++ ++pub struct MegaPanel { ++ mega: WeakModel, ++ workspace: WeakView, ++ focus_handle: FocusHandle, ++ pending_serialization: Task>, ++ width: Option, ++} ++ ++#[derive(Serialize, Deserialize)] ++struct SerializedMegaPanel { ++ width: Option, ++} ++ ++#[derive(Debug)] ++pub enum Event { ++ Focus, ++} ++ ++pub fn init_settings(cx: &mut AppContext) { ++ MegaPanelSettings::register(cx); ++} ++ ++pub fn init(assets: impl AssetSource, cx: &mut AppContext) { ++ init_settings(cx); ++ file_icons::init(assets, cx); ++ ++ cx.observe_new_views(|workspace: &mut Workspace, _| { ++ workspace.register_action(|workspace, _: &ToggleFocus, cx| { ++ workspace.toggle_panel_focus::(cx); ++ }); ++ }) ++ .detach(); ++} ++ ++impl EventEmitter for MegaPanel {} ++ ++impl EventEmitter for MegaPanel {} ++ ++impl Render for MegaPanel { ++ fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { ++ todo!(); ++ "" ++ } ++} ++ ++impl FocusableView for MegaPanel { ++ fn focus_handle(&self, _cx: &AppContext) -> FocusHandle { ++ self.focus_handle.clone() ++ } ++} ++ ++impl Panel for MegaPanel { ++ fn persistent_name() -> &'static str { ++ todo!() ++ } ++ ++ fn position(&self, cx: &WindowContext) -> DockPosition { ++ todo!() ++ } ++ ++ fn position_is_valid(&self, position: DockPosition) -> bool { ++ todo!() ++ } ++ ++ fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext) { ++ todo!() ++ } ++ ++ fn size(&self, cx: &WindowContext) -> Pixels { ++ todo!() ++ } ++ ++ fn set_size(&mut self, size: Option, cx: &mut ViewContext) { ++ todo!() ++ } ++ ++ fn icon(&self, cx: &WindowContext) -> Option { ++ todo!() ++ } ++ ++ fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str> { ++ todo!() ++ } ++ ++ fn toggle_action(&self) -> Box { ++ todo!() ++ } ++} ++ ++impl MegaPanel { ++ pub async fn load( ++ workspace: WeakView, ++ mut cx: AsyncWindowContext, ++ ) -> anyhow::Result> { ++ let serialized_panel = cx ++ .background_executor() ++ .spawn(async move { KEY_VALUE_STORE.read_kvp(MEGA_PANEL_KEY) }) ++ .await ++ .map_err(|e| anyhow!("Failed to load mega panel: {}", e)) ++ .context("loading mega panel") ++ .log_err() ++ .flatten() ++ .map(|panel| serde_json::from_str::(&panel)) ++ .transpose() ++ .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 ++ } ++ ) ++ } ++ ++ 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(); ++ ++ cx.subscribe(mega, |this, mega, event, cx| { ++ // TODO: listen for user operations ++ }).detach(); ++ ++ Self { ++ mega: mega.downgrade(), ++ workspace: workspace.weak_handle(), ++ focus_handle, ++ pending_serialization: Task::ready(None), ++ width: None, ++ ++ } ++ }); ++ ++ mega_panel ++ } ++ ++ fn serialize(&mut self, cx: &mut ViewContext) { ++ let width = self.width; ++ self.pending_serialization = cx.background_executor().spawn( ++ async move { ++ KEY_VALUE_STORE ++ .write_kvp( ++ MEGA_PANEL_KEY.into(), ++ serde_json::to_string(&SerializedMegaPanel { width })?, ++ ) ++ .await?; ++ anyhow::Ok(()) ++ } ++ .log_err(), ++ ); ++ } ++ ++ fn focus_in(&mut self, cx: &mut ViewContext) { ++ if !self.focus_handle.contains_focused(cx) { ++ cx.emit(Event::Focus); ++ } ++ } ++} +\ No newline at end of file +diff --git a/crates/mega_panel/src/mega_panel_settings.rs b/crates/mega_panel/src/mega_panel_settings.rs +index 1ca1149b01..bfd4b337d0 100644 +--- a/crates/mega_panel/src/mega_panel_settings.rs ++++ b/crates/mega_panel/src/mega_panel_settings.rs +@@ -15,59 +15,26 @@ pub struct MegaPanelSettings { + pub button: bool, + pub default_width: Pixels, + pub dock: MegaPanelDockPosition, +- pub file_icons: bool, +- pub folder_icons: bool, +- pub git_status: bool, +- pub indent_size: f32, +- pub auto_reveal_entries: bool, +- pub auto_fold_dirs: bool, + } + + #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] + pub struct MegaPanelSettingsContent { +- /// Whether to show the outline panel button in the status bar. ++ /// Whether to show the mega panel button in the status bar. + /// + /// Default: true + pub button: Option, +- /// Customize default width (in pixels) taken by outline panel ++ /// Customize default width (in pixels) taken by mega panel + /// + /// Default: 240 + pub default_width: Option, +- /// The position of outline panel ++ /// The position of mega panel + /// + /// Default: left + pub dock: Option, +- /// Whether to show file icons in the outline panel. +- /// +- /// Default: true +- pub file_icons: Option, +- /// Whether to show folder icons or chevrons for directories in the outline panel. +- /// +- /// Default: true +- pub folder_icons: Option, +- /// Whether to show the git status in the outline panel. +- /// +- /// Default: true +- pub git_status: Option, +- /// Amount of indentation (in pixels) for nested items. +- /// +- /// Default: 20 +- pub indent_size: Option, +- /// Whether to reveal it in the outline panel automatically, +- /// when a corresponding project entry becomes active. +- /// Gitignored entries are never auto revealed. +- /// +- /// Default: true +- pub auto_reveal_entries: Option, +- /// Whether to fold directories automatically +- /// when directory has only one directory inside. +- /// +- /// Default: true +- pub auto_fold_dirs: Option, + } + + impl Settings for MegaPanelSettings { +- const KEY: Option<&'static str> = Some("outline_panel"); ++ const KEY: Option<&'static str> = Some("mega_panel"); + + type FileContent = MegaPanelSettingsContent; + +diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml +index 47f6c138c8..83686bb100 100644 +--- a/crates/workspace/Cargo.toml ++++ b/crates/workspace/Cargo.toml +@@ -45,6 +45,7 @@ http_client.workspace = true + itertools.workspace = true + language.workspace = true + log.workspace = true ++mega.workspace = true + node_runtime.workspace = true + parking_lot.workspace = true + postage.workspace = true +diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs +index ec4079ba9f..af4f47e5d3 100644 +--- a/crates/workspace/src/workspace.rs ++++ b/crates/workspace/src/workspace.rs +@@ -97,6 +97,7 @@ use ui::{ + }; + use util::{maybe, ResultExt, TryFutureExt}; + use uuid::Uuid; ++use mega::Mega; + pub use workspace_settings::{ + AutosaveSetting, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings, + }; +@@ -559,6 +560,7 @@ pub struct AppState { + pub build_window_options: fn(Option, &mut AppContext) -> WindowOptions, + pub node_runtime: NodeRuntime, + pub session: Model, ++ pub mega: Model, + } + + struct GlobalAppState(Weak); +@@ -609,7 +611,8 @@ impl AppState { + let session = cx.new_model(|cx| AppSession::new(Session::test(), cx)); + let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); + let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); +- ++ let mega = cx.new_model(|cx| { Mega::new(cx) }); ++ + theme::init(theme::LoadThemes::JustBase, cx); + client::init(&client, cx); + crate::init_settings(cx); +@@ -623,6 +626,7 @@ impl AppState { + node_runtime: NodeRuntime::unavailable(), + build_window_options: |_, _| Default::default(), + session, ++ mega, + }) + } + } +@@ -1266,6 +1270,8 @@ impl Workspace { + pub fn project(&self) -> &Model { + &self.project + } ++ ++ pub fn mega(&self) -> &Model { &self.app_state.mega } + + pub fn recent_navigation_history( + &self, +@@ -4426,6 +4432,7 @@ impl Workspace { + + let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); + let session = cx.new_model(|cx| AppSession::new(Session::test(), cx)); ++ let mega = cx.new_model(|cx| Mega::new(cx) ); + cx.activate_window(); + let app_state = Arc::new(AppState { + languages: project.read(cx).languages().clone(), +@@ -4436,6 +4443,7 @@ impl Workspace { + build_window_options: |_, _| Default::default(), + node_runtime: NodeRuntime::unavailable(), + session, ++ mega, + }); + let workspace = Self::new(Default::default(), project, app_state, cx); + workspace.active_pane.update(cx, |pane, cx| pane.focus(cx)); +diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml +index 81243cf62f..abc36527ec 100644 +--- a/crates/zed/Cargo.toml ++++ b/crates/zed/Cargo.toml +@@ -66,6 +66,7 @@ languages = { workspace = true, features = ["load-grammars"] } + libc.workspace = true + log.workspace = true + markdown_preview.workspace = true ++mega.workspace = true + mega_panel.workspace = true + menu.workspace = true + mimalloc = { version = "0.1", optional = true } +diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs +index 25baf74c68..3e3094ecb0 100644 +--- a/crates/zed/src/main.rs ++++ b/crates/zed/src/main.rs +@@ -53,6 +53,7 @@ use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings}; + use time::UtcOffset; + use util::{maybe, parse_env_output, ResultExt, TryFutureExt}; + use uuid::Uuid; ++use mega::Mega; + use welcome::{show_welcome_view, BaseKeymap, FIRST_OPEN}; + use workspace::{ + notifications::{simple_message_notification::MessageNotification, NotificationId}, +@@ -521,6 +522,7 @@ fn main() { + Client::set_global(client.clone(), cx); + + zed::init(cx); ++ mega::init(cx); + project::Project::init(&client, cx); + client::init(&client, cx); + language::init(cx); +@@ -548,7 +550,8 @@ fn main() { + } + } + let app_session = cx.new_model(|cx| AppSession::new(session, cx)); +- ++ let mega = cx.new_model(|cx| Mega::new(cx)); ++ + let app_state = Arc::new(AppState { + languages: languages.clone(), + client: client.clone(), +@@ -558,6 +561,7 @@ fn main() { + workspace_store, + node_runtime: node_runtime.clone(), + session: app_session, ++ mega, + }); + AppState::set_global(Arc::downgrade(&app_state), cx); + +-- +2.43.0 + 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 new file mode 100644 index 000000000..e0aeca8b9 --- /dev/null +++ b/scripts/zed-intergration/patches/0003-add-mega-panel-trait-implementation.patch @@ -0,0 +1,162 @@ +From a6636b00e11cfa284e2e152cb2ac0a6d92508a34 Mon Sep 17 00:00:00 2001 +From: Neon +Date: Mon, 21 Oct 2024 09:33:24 +0800 +Subject: [PATCH 03/10] add: mega `panel` trait implementation + +--- + Cargo.lock | 1 + + crates/mega_panel/Cargo.toml | 1 + + crates/mega_panel/src/mega_panel.rs | 41 +++++++++++++++++------ + crates/project_panel/src/project_panel.rs | 8 ++--- + 4 files changed, 37 insertions(+), 14 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 6503eb5359..80c72872e4 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -6900,6 +6900,7 @@ dependencies = [ + "anyhow", + "db", + "file_icons", ++ "fs", + "gpui", + "mega", + "schemars", +diff --git a/crates/mega_panel/Cargo.toml b/crates/mega_panel/Cargo.toml +index 9a8ae896a2..d139d51f0d 100644 +--- a/crates/mega_panel/Cargo.toml ++++ b/crates/mega_panel/Cargo.toml +@@ -17,6 +17,7 @@ gpui.workspace = true + file_icons.workspace = true + settings.workspace = true + db.workspace = true ++fs.workspace = true + util.workspace = true + + anyhow.workspace = true +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index 7e38d88752..e10ef55f0a 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -1,6 +1,8 @@ ++use std::sync::Arc; + use anyhow::{anyhow, Context}; + use db::kvp::KEY_VALUE_STORE; + use file_icons::FileIcons; ++use fs::Fs; + use gpui::{actions, Action, AppContext, AssetSource, AsyncWindowContext, Entity, EventEmitter, FocusHandle, FocusableView, IntoElement, Model, Pixels, Render, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext}; + use gpui::private::serde_derive::{Deserialize, Serialize}; + use gpui::private::serde_json; +@@ -10,7 +12,7 @@ use util::{ResultExt, TryFutureExt}; + use workspace::dock::{DockPosition, Panel, PanelEvent, PanelId}; + use workspace::ui::IconName; + use workspace::{Pane, Workspace}; +-use crate::mega_panel_settings::MegaPanelSettings; ++use crate::mega_panel_settings::{MegaPanelDockPosition, MegaPanelSettings}; + + mod mega_panel_settings; + +@@ -29,6 +31,7 @@ pub struct MegaPanel { + mega: WeakModel, + workspace: WeakView, + focus_handle: FocusHandle, ++ fs: Arc, + pending_serialization: Task>, + width: Option, + } +@@ -78,39 +81,57 @@ impl FocusableView for MegaPanel { + + impl Panel for MegaPanel { + fn persistent_name() -> &'static str { +- todo!() ++ "Mega Panel" + } + + fn position(&self, cx: &WindowContext) -> DockPosition { +- todo!() ++ match MegaPanelSettings::get_global(cx).dock { ++ MegaPanelDockPosition::Left => DockPosition::Left, ++ MegaPanelDockPosition::Right => DockPosition::Right, ++ } + } + + fn position_is_valid(&self, position: DockPosition) -> bool { +- todo!() ++ matches!(position, DockPosition::Left | DockPosition::Right) + } + + fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext) { +- todo!() ++ settings::update_settings_file::( ++ self.fs.clone(), ++ cx, ++ move |settings, _| { ++ let dock = match position { ++ DockPosition::Left | DockPosition::Bottom => MegaPanelDockPosition::Left, ++ DockPosition::Right => MegaPanelDockPosition::Right, ++ }; ++ settings.dock = Some(dock); ++ }, ++ ); + } + + fn size(&self, cx: &WindowContext) -> Pixels { +- todo!() ++ self.width ++ .unwrap_or_else(|| MegaPanelSettings::get_global(cx).default_width) + } + + fn set_size(&mut self, size: Option, cx: &mut ViewContext) { +- todo!() ++ self.width = size; ++ self.serialize(cx); ++ cx.notify(); + } + + fn icon(&self, cx: &WindowContext) -> Option { +- todo!() ++ MegaPanelSettings::get_global(cx) ++ .button ++ .then_some(IconName::FileGit) + } + + fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str> { +- todo!() ++ Some("Mega Panel") + } + + fn toggle_action(&self) -> Box { +- todo!() ++ Box::new(ToggleFocus) + } + } + +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index 08a0ef4b40..c4fb39f587 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -3060,6 +3060,10 @@ impl EventEmitter for ProjectPanel {} + impl EventEmitter for ProjectPanel {} + + impl Panel for ProjectPanel { ++ fn persistent_name() -> &'static str { ++ "Project Panel" ++ } ++ + fn position(&self, cx: &WindowContext) -> DockPosition { + match ProjectPanelSettings::get_global(cx).dock { + ProjectPanelDockPosition::Left => DockPosition::Left, +@@ -3110,10 +3114,6 @@ impl Panel for ProjectPanel { + Box::new(ToggleFocus) + } + +- fn persistent_name() -> &'static str { +- "Project Panel" +- } +- + fn starts_open(&self, cx: &WindowContext) -> bool { + let project = &self.project.read(cx); + project.dev_server_project_id().is_some() +-- +2.43.0 + 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 new file mode 100644 index 000000000..f743cc1d0 --- /dev/null +++ b/scripts/zed-intergration/patches/0004-fix-make-mega_panel-runnable.patch @@ -0,0 +1,87 @@ +From 5e676b9b1669d078a25bcee9a0d1b87743862a30 Mon Sep 17 00:00:00 2001 +From: Neon +Date: Mon, 21 Oct 2024 10:00:36 +0800 +Subject: [PATCH 04/10] fix: make mega_panel runnable + +--- + assets/settings/default.json | 8 ++++++++ + crates/mega_panel/src/mega_panel.rs | 8 ++++---- + crates/zed/src/main.rs | 1 + + 3 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/assets/settings/default.json b/assets/settings/default.json +index 4bde5ee174..147f80a12b 100644 +--- a/assets/settings/default.json ++++ b/assets/settings/default.json +@@ -371,6 +371,14 @@ + "show": null + } + }, ++ "mega_panel": { ++ // Whether to show the mega panel button in the status bar ++ "button": true, ++ // Default width of the mega panel. ++ "default_width": 240, ++ // Where to dock the mega panel. Can be 'left' or 'right'. ++ "dock": "left", ++ }, + "outline_panel": { + // Whether to show the outline panel button in the status bar + "button": true, +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index e10ef55f0a..03c41f06ad 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -10,7 +10,7 @@ use mega::{Mega, MegaFuse}; + use settings::{Settings, SettingsStore}; + use util::{ResultExt, TryFutureExt}; + use workspace::dock::{DockPosition, Panel, PanelEvent, PanelId}; +-use workspace::ui::IconName; ++use workspace::ui::{v_flex, IconName}; + use workspace::{Pane, Workspace}; + use crate::mega_panel_settings::{MegaPanelDockPosition, MegaPanelSettings}; + +@@ -52,6 +52,7 @@ pub fn init_settings(cx: &mut AppContext) { + + pub fn init(assets: impl AssetSource, cx: &mut AppContext) { + init_settings(cx); ++ println!("Mega settings should be registered"); + file_icons::init(assets, cx); + + cx.observe_new_views(|workspace: &mut Workspace, _| { +@@ -68,8 +69,7 @@ impl EventEmitter for MegaPanel {} + + impl Render for MegaPanel { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { +- todo!(); +- "" ++ v_flex() + } + } + +@@ -183,9 +183,9 @@ impl MegaPanel { + mega: mega.downgrade(), + workspace: workspace.weak_handle(), + focus_handle, ++ fs: workspace.app_state().fs.clone(), + pending_serialization: Task::ready(None), + width: None, +- + } + }); + +diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs +index 3e3094ecb0..005d086f48 100644 +--- a/crates/zed/src/main.rs ++++ b/crates/zed/src/main.rs +@@ -257,6 +257,7 @@ fn init_ui( + outline::init(cx); + project_symbols::init(cx); + project_panel::init(Assets, cx); ++ mega_panel::init(Assets, cx); + outline_panel::init(Assets, cx); + tasks_ui::init(cx); + snippets_ui::init(cx); +-- +2.43.0 + 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 new file mode 100644 index 000000000..5bea9ecc0 --- /dev/null +++ b/scripts/zed-intergration/patches/0005-mega_panel-very-basic-info-render-support.patch @@ -0,0 +1,185 @@ +From e04d4b01af7991ed4b40fcd9553f20e67f2ec53f Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 22 Oct 2024 16:15:01 +0800 +Subject: [PATCH 05/10] mega_panel: very basic info render support + +--- + Cargo.lock | 4 +- + crates/mega_panel/Cargo.toml | 2 + + crates/mega_panel/src/mega_panel.rs | 61 +++++++++++++++++++++++++---- + 3 files changed, 58 insertions(+), 9 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 80c72872e4..0474f53d0a 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -6889,7 +6889,7 @@ name = "mega" + version = "0.1.0" + dependencies = [ + "gpui", +- "reqwest 0.12.8", ++ "reqwest_client", + "serde", + ] + +@@ -6906,8 +6906,10 @@ dependencies = [ + "schemars", + "serde", + "settings", ++ "text", + "util", + "workspace", ++ "worktree", + ] + + [[package]] +diff --git a/crates/mega_panel/Cargo.toml b/crates/mega_panel/Cargo.toml +index d139d51f0d..2238a405c1 100644 +--- a/crates/mega_panel/Cargo.toml ++++ b/crates/mega_panel/Cargo.toml +@@ -13,11 +13,13 @@ workspace = true + [dependencies] + mega.workspace = true + workspace.workspace = true ++worktree.workspace = true + gpui.workspace = true + file_icons.workspace = true + settings.workspace = true + db.workspace = true + fs.workspace = true ++text.workspace = true + util.workspace = true + + anyhow.workspace = true +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index 03c41f06ad..b055d76fcf 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -3,15 +3,17 @@ use anyhow::{anyhow, Context}; + use db::kvp::KEY_VALUE_STORE; + use file_icons::FileIcons; + use fs::Fs; +-use gpui::{actions, Action, AppContext, AssetSource, AsyncWindowContext, Entity, EventEmitter, FocusHandle, FocusableView, IntoElement, Model, Pixels, Render, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext}; ++use gpui::{actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AnyElement, AppContext, AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, HighlightStyle, InteractiveElement, IntoElement, KeyContext, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, SharedString, Stateful, Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext}; + use gpui::private::serde_derive::{Deserialize, Serialize}; + use gpui::private::serde_json; + use mega::{Mega, MegaFuse}; + use settings::{Settings, SettingsStore}; ++use text::BufferId; + use util::{ResultExt, TryFutureExt}; + use workspace::dock::{DockPosition, Panel, PanelEvent, PanelId}; +-use workspace::ui::{v_flex, IconName}; ++use workspace::ui::{v_flex, IconName, Label, LabelCommon, LabelSize}; + use workspace::{Pane, Workspace}; ++use worktree::{Entry, ProjectEntryId, WorktreeId}; + use crate::mega_panel_settings::{MegaPanelDockPosition, MegaPanelSettings}; + + mod mega_panel_settings; +@@ -21,18 +23,18 @@ const MEGA_PANEL_KEY: &str = "MegaPanel"; + actions!( + mega_panel, + [ +- Open, + ToggleFocus, + ToggleFuseMount, ++ CheckoutPath, + ] + ); + + pub struct MegaPanel { +- mega: WeakModel, ++ mega_handle: WeakModel, + workspace: WeakView, + focus_handle: FocusHandle, + fs: Arc, +- pending_serialization: Task>, ++ pending_serialization: Task>, // TODO check how to use it + width: Option, + } + +@@ -41,6 +43,12 @@ struct SerializedMegaPanel { + width: Option, + } + ++#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] ++enum MegaEntry { ++ Dir(WorktreeId, ProjectEntryId), ++ File(WorktreeId, BufferId), ++} ++ + #[derive(Debug)] + pub enum Event { + Focus, +@@ -52,7 +60,6 @@ pub fn init_settings(cx: &mut AppContext) { + + pub fn init(assets: impl AssetSource, cx: &mut AppContext) { + init_settings(cx); +- println!("Mega settings should be registered"); + file_icons::init(assets, cx); + + cx.observe_new_views(|workspace: &mut Workspace, _| { +@@ -69,7 +76,27 @@ impl EventEmitter for MegaPanel {} + + impl Render for MegaPanel { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { +- v_flex() ++ let mega_panel = v_flex() ++ .id("mega_panel") ++ .size_full() ++ .relative() ++ .on_action(cx.listener(Self::toggle_fuse_mount)) ++ .on_action(cx.listener(Self::checkout_path)) ++ .track_focus(&self.focus_handle) ++ .gap_6() ++ .p_4() ++ .child( ++ Label::new("Mega Control Panel") ++ .single_line() ++ .weight(FontWeight::BOLD) ++ .size(LabelSize::Large) ++ ) ++ .gap_4() ++ .p_4() ++ .child(self.render_status_panel(cx)) ++ .child(self.render_control_panel(cx)); ++ ++ mega_panel + } + } + +@@ -180,7 +207,7 @@ impl MegaPanel { + }).detach(); + + Self { +- mega: mega.downgrade(), ++ mega_handle: mega.downgrade(), + workspace: workspace.weak_handle(), + focus_handle, + fs: workspace.app_state().fs.clone(), +@@ -213,4 +240,22 @@ impl MegaPanel { + cx.emit(Event::Focus); + } + } ++ ++ pub fn toggle_fuse_mount(&mut self, _: &ToggleFuseMount, cx: &mut ViewContext) { ++ // let mega = self.mega_handle.upgrade() ++ // .unwrap_or_else() ++ todo!() ++ } ++ ++ pub fn checkout_path(&mut self, _: &CheckoutPath, cx: &mut ViewContext) { ++ todo!() ++ } ++ ++ fn render_status_panel(&mut self, cx: &mut ViewContext) -> Div { ++ v_flex().child(Label::new("I am a status panel")) ++ } ++ ++ fn render_control_panel(&mut self, cx: &mut ViewContext) -> Div { ++ v_flex().child(Label::new("I am a control panel")) ++ } + } +\ No newline at end of file +-- +2.43.0 + 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 new file mode 100644 index 000000000..da1f1f21f --- /dev/null +++ b/scripts/zed-intergration/patches/0006-mega-basic-rest-api-support.patch @@ -0,0 +1,228 @@ +From 8b57a9f693c95e92ee5299ff30b6cd791c08da71 Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 22 Oct 2024 19:12:08 +0800 +Subject: [PATCH 06/10] mega: basic rest api support + +--- + Cargo.lock | 1 + + crates/mega/Cargo.toml | 3 +- + crates/mega/src/delegate.rs | 58 ++++++++++++++++++++ + crates/mega/src/mega.rs | 84 ++++++++++++++++++++++++++--- + crates/mega_panel/src/mega_panel.rs | 1 + + 5 files changed, 139 insertions(+), 8 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 0474f53d0a..1ffec64d85 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -6888,6 +6888,7 @@ dependencies = [ + name = "mega" + version = "0.1.0" + dependencies = [ ++ "bytes 0.4.12", + "gpui", + "reqwest_client", + "serde", +diff --git a/crates/mega/Cargo.toml b/crates/mega/Cargo.toml +index dec6093220..1572c73b90 100644 +--- a/crates/mega/Cargo.toml ++++ b/crates/mega/Cargo.toml +@@ -13,6 +13,7 @@ path = 'src/mega.rs' + + [dependencies] + gpui.workspace = true ++reqwest_client.workspace = true + +-reqwest.workspace = true + serde.workspace = true ++bytes = "0.4.12" +diff --git a/crates/mega/src/delegate.rs b/crates/mega/src/delegate.rs +index e69de29bb2..c767060048 100644 +--- a/crates/mega/src/delegate.rs ++++ b/crates/mega/src/delegate.rs +@@ -0,0 +1,58 @@ ++use serde::{Deserialize, Serialize}; ++ ++#[derive(Debug, Deserialize, Serialize)] ++pub(crate) struct MountRequest { ++ pub(crate) path: String, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct MountResponse { ++ status: String, ++ mount: MountInfo, ++ message: String, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct MountInfo { ++ hash: String, ++ path: String, ++ inode: u64, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct MountsResponse { ++ status: String, ++ mounts: Vec, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct UmountRequest { ++ path: Option, ++ inode: Option, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct UmountResponse { ++ status: String, ++ message: String, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct ConfigResponse { ++ status: String, ++ config: ConfigInfo, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct ConfigInfo { ++ mega_url: String, ++ mount_path: String, ++ store_path: String, ++} ++ ++#[derive(Debug, Deserialize, Serialize)] ++struct ConfigRequest { ++ mega_url: Option, ++ mount_path: Option, ++ store_path: Option, ++} +\ No newline at end of file +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 4b51004907..f24e2d9e5d 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -6,8 +6,10 @@ + // 2. At least one daemon on this machine when zed startup. + // 3. Complete docs. + +-use std::sync::Arc; +-use gpui::{AppContext, Context, EventEmitter, Model, ModelContext}; ++use gpui::http_client::{AsyncBody, HttpClient}; ++use gpui::{AppContext, Context, EventEmitter, WindowContext}; ++use reqwest_client::ReqwestClient; ++use serde::Serialize; + + mod delegate; + mod fuse; +@@ -21,7 +23,10 @@ pub fn init(cx: &mut AppContext) { + + #[derive(Clone, Debug, PartialEq)] + pub enum Event {} +-pub struct Mega {} ++pub struct Mega { ++ mega_running: bool, ++ fuse_running: bool, ++} + + pub struct MegaFuse {} + +@@ -41,12 +46,77 @@ impl Mega { + } + + pub fn new(cx: &mut AppContext) -> Self { +- Mega {} ++ Mega { ++ fuse_running: false, ++ mega_running: false, ++ } + } + +- pub fn toggle_mega(&self) { todo!() } ++ pub fn toggle_mega(&self, cx: &mut WindowContext) { todo!() } + +- pub fn toggle_fuse(&self) { todo!() } ++ pub fn toggle_fuse(&self, cx: &mut WindowContext) { todo!() } ++ ++ pub fn toggle_mount(&self, cx: &mut WindowContext) { ++ // let req_body = delegate::MountRequest { ++ // path: "".parse().unwrap() ++ // }; ++ ++ cx.spawn(|_cx| async { ++ let client = ReqwestClient::new(); ++ let req = client.get( ++ "localhost:2725/api/fs/mount", ++ AsyncBody::empty(), ++ false ++ ).await; ++ }).detach(); ++ } + +- pub fn checkout_path(&self) { todo!() } ++ pub fn checkout_path(&self, cx: &mut WindowContext) { ++ cx.spawn(|_cx| async { ++ let client = ReqwestClient::new(); ++ let req = client.get( ++ "localhost:2725/api/fs/mount", ++ AsyncBody::empty(), ++ false ++ ).await; ++ }).detach(); ++ } ++ ++ pub fn get_fuse_config(&self, cx: &mut WindowContext) { ++ cx.spawn(|_cx| async { ++ let client = ReqwestClient::new(); ++ let req = client.get( ++ "localhost:2725/api/fs/mount", ++ AsyncBody::empty(), ++ false ++ ).await; ++ }).detach(); ++ } ++ ++ pub fn set_fuse_config(&self, cx: &mut WindowContext) { ++ cx.spawn(|_cx| async { ++ let client = ReqwestClient::new(); ++ let req = client.post_json( ++ "localhost:2725/api/config", ++ AsyncBody::empty(), ++ ).await; ++ }).detach(); ++ } ++ ++ pub fn get_fuse_mpoint(&self, cx: &mut WindowContext) { ++ cx.spawn(|_cx| async { ++ let client = ReqwestClient::new(); ++ let req = client.get( ++ "localhost:2725/api/config", ++ AsyncBody::empty(), ++ false ++ ).await; ++ }).detach(); ++ } ++ ++} ++ ++#[cfg(test)] ++mod test { ++ + } +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index b055d76fcf..76ac003f31 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -244,6 +244,7 @@ impl MegaPanel { + pub fn toggle_fuse_mount(&mut self, _: &ToggleFuseMount, cx: &mut ViewContext) { + // let mega = self.mega_handle.upgrade() + // .unwrap_or_else() ++ + todo!() + } + +-- +2.43.0 + 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 new file mode 100644 index 000000000..963b15cd2 --- /dev/null +++ b/scripts/zed-intergration/patches/0007-mega_panel-control-pad-and-information-display.patch @@ -0,0 +1,350 @@ +From 0ae6bfb40025aca376878982579a322eaa231143 Mon Sep 17 00:00:00 2001 +From: Neon +Date: Sat, 26 Oct 2024 22:02:19 +0800 +Subject: [PATCH 07/10] mega_panel: control pad and information display + +--- + crates/mega/src/mega.rs | 56 ++++++++--- + crates/mega_panel/src/mega_panel.rs | 150 +++++++++++++++++++++------- + 2 files changed, 154 insertions(+), 52 deletions(-) + +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index f24e2d9e5d..4bf4aa27d6 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -6,8 +6,9 @@ + // 2. At least one daemon on this machine when zed startup. + // 3. Complete docs. + ++use std::path::{Path, PathBuf}; + use gpui::http_client::{AsyncBody, HttpClient}; +-use gpui::{AppContext, Context, EventEmitter, WindowContext}; ++use gpui::{AppContext, Context, EntityId, EventEmitter, ModelContext, WindowContext}; + use reqwest_client::ReqwestClient; + use serde::Serialize; + +@@ -22,10 +23,18 @@ pub fn init(cx: &mut AppContext) { + } + + #[derive(Clone, Debug, PartialEq)] +-pub enum Event {} ++pub enum Event { ++ MegaRunning(bool), ++ FuseRunning(bool), ++ FuseMounted(bool), ++} + pub struct Mega { + mega_running: bool, + fuse_running: bool, ++ fuse_mounted: bool, ++ ++ checkout_path: Option, ++ panel_id: Option, + } + + pub struct MegaFuse {} +@@ -49,19 +58,36 @@ impl Mega { + Mega { + fuse_running: false, + mega_running: false, ++ fuse_mounted: false, ++ checkout_path: None, ++ panel_id: None, + } +- } ++ } + +- pub fn toggle_mega(&self, cx: &mut WindowContext) { todo!() } ++ pub fn update_status(&mut self, cx: &mut ModelContext) { ++ if let None = self.panel_id { ++ return; ++ } ++ ++ cx.notify(); ++ } + +- pub fn toggle_fuse(&self, cx: &mut WindowContext) { todo!() } ++ pub fn status(&self) -> (bool, bool, bool) { ++ (self.mega_running, self.fuse_running, self.fuse_mounted) ++ } ++ ++ pub fn toggle_mega(&self, cx: &mut ModelContext) { todo!() } ++ ++ pub fn toggle_fuse(&self, cx: &mut ModelContext) { ++ ++ } + +- pub fn toggle_mount(&self, cx: &mut WindowContext) { ++ pub fn toggle_mount(&self, cx: &mut ModelContext) { + // let req_body = delegate::MountRequest { + // path: "".parse().unwrap() + // }; + +- cx.spawn(|_cx| async { ++ cx.spawn(|_this, _cx| async { + let client = ReqwestClient::new(); + let req = client.get( + "localhost:2725/api/fs/mount", +@@ -71,8 +97,8 @@ impl Mega { + }).detach(); + } + +- pub fn checkout_path(&self, cx: &mut WindowContext) { +- cx.spawn(|_cx| async { ++ pub fn checkout_path(&self, cx: &mut ModelContext) { ++ cx.spawn(|_this, _cx| async { + let client = ReqwestClient::new(); + let req = client.get( + "localhost:2725/api/fs/mount", +@@ -82,8 +108,8 @@ impl Mega { + }).detach(); + } + +- pub fn get_fuse_config(&self, cx: &mut WindowContext) { +- cx.spawn(|_cx| async { ++ pub fn get_fuse_config(&self, cx: &mut ModelContext) { ++ cx.spawn(|_this, _cx| async { + let client = ReqwestClient::new(); + let req = client.get( + "localhost:2725/api/fs/mount", +@@ -93,8 +119,8 @@ impl Mega { + }).detach(); + } + +- pub fn set_fuse_config(&self, cx: &mut WindowContext) { +- cx.spawn(|_cx| async { ++ pub fn set_fuse_config(&self, cx: &mut ModelContext) { ++ cx.spawn(|_this, _cx| async { + let client = ReqwestClient::new(); + let req = client.post_json( + "localhost:2725/api/config", +@@ -103,8 +129,8 @@ impl Mega { + }).detach(); + } + +- pub fn get_fuse_mpoint(&self, cx: &mut WindowContext) { +- cx.spawn(|_cx| async { ++ pub fn get_fuse_mpoint(&self, cx: &mut ModelContext) { ++ cx.spawn(|_this, _cx| async { + let client = ReqwestClient::new(); + let req = client.get( + "localhost:2725/api/config", +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index 76ac003f31..80a26cf4c2 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -1,20 +1,19 @@ +-use std::sync::Arc; ++use crate::mega_panel_settings::{MegaPanelDockPosition, MegaPanelSettings}; + use anyhow::{anyhow, Context}; + use db::kvp::KEY_VALUE_STORE; +-use file_icons::FileIcons; + use fs::Fs; +-use gpui::{actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AnyElement, AppContext, AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, HighlightStyle, InteractiveElement, IntoElement, KeyContext, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, SharedString, Stateful, Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext}; + use gpui::private::serde_derive::{Deserialize, Serialize}; + use gpui::private::serde_json; +-use mega::{Mega, MegaFuse}; +-use settings::{Settings, SettingsStore}; ++use gpui::{actions, div, Action, AppContext, AssetSource, AsyncWindowContext, Div, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, Hsla, InteractiveElement, IntoElement, Model, ParentElement, Pixels, PromptLevel, Render, SharedString, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, 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, PanelId}; +-use workspace::ui::{v_flex, IconName, Label, LabelCommon, LabelSize}; +-use workspace::{Pane, Workspace}; +-use worktree::{Entry, ProjectEntryId, WorktreeId}; +-use crate::mega_panel_settings::{MegaPanelDockPosition, MegaPanelSettings}; ++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::Workspace; ++use worktree::{ProjectEntryId, WorktreeId}; + + mod mega_panel_settings; + +@@ -30,9 +29,10 @@ actions!( + ); + + pub struct MegaPanel { +- mega_handle: WeakModel, ++ mega_handle: Model, + workspace: WeakView, + focus_handle: FocusHandle, ++ scroll_handle: UniformListScrollHandle, + fs: Arc, + pending_serialization: Task>, // TODO check how to use it + width: Option, +@@ -86,16 +86,17 @@ impl Render for MegaPanel { + .gap_6() + .p_4() + .child( +- Label::new("Mega Control Panel") +- .single_line() +- .weight(FontWeight::BOLD) +- .size(LabelSize::Large) ++ h_flex().justify_center().child( ++ Label::new("Mega Control Panel") ++ .single_line() ++ .weight(FontWeight::BOLD) ++ .size(LabelSize::Large)) + ) +- .gap_4() +- .p_4() +- .child(self.render_status_panel(cx)) +- .child(self.render_control_panel(cx)); +- ++ .child(horizontal_separator(cx)) ++ .child(self.render_status(cx)) ++ .child(horizontal_separator(cx)) ++ .child(self.render_buttons(cx)); ++ + mega_panel + } + } +@@ -191,25 +192,26 @@ impl MegaPanel { + }); + } + panel +- } ++ }, + ) + } + + 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(); +- ++ + cx.subscribe(mega, |this, mega, event, cx| { +- // TODO: listen for user operations ++ // TODO: listen for mega events + }).detach(); +- ++ + Self { +- mega_handle: mega.downgrade(), ++ mega_handle: mega.clone(), + workspace: workspace.weak_handle(), + focus_handle, ++ scroll_handle: UniformListScrollHandle::new(), + fs: workspace.app_state().fs.clone(), + pending_serialization: Task::ready(None), + width: None, +@@ -240,23 +242,97 @@ impl MegaPanel { + cx.emit(Event::Focus); + } + } +- ++ + pub fn toggle_fuse_mount(&mut self, _: &ToggleFuseMount, cx: &mut ViewContext) { + // let mega = self.mega_handle.upgrade() + // .unwrap_or_else() +- +- todo!() ++ ++ self.warn_unimplemented(cx); + } +- ++ + pub fn checkout_path(&mut self, _: &CheckoutPath, cx: &mut ViewContext) { +- todo!() ++ self.warn_unimplemented(cx); + } +- +- fn render_status_panel(&mut self, cx: &mut ViewContext) -> Div { +- v_flex().child(Label::new("I am a status panel")) ++ ++ fn render_status(&mut self, cx: &mut ViewContext) -> Div { ++ let ( ++ mega_running, ++ fuse_running, ++ fuse_mounted ++ ) = self.mega_handle.read(cx).status(); ++ ++ v_flex() ++ .gap_1() ++ .children([ ++ self.status_unit(cx, "Mega Backend:", mega_running), ++ self.status_unit(cx, "Scorpio Backend:", fuse_running), ++ self.status_unit(cx, "Fuse Mounted:", fuse_mounted), ++ ]) + } +- +- fn render_control_panel(&mut self, cx: &mut ViewContext) -> Div { +- v_flex().child(Label::new("I am a control panel")) ++ ++ fn render_buttons(&mut self, cx: &mut ViewContext) -> impl IntoElement { ++ ++ fn encap_btn(btn: Button) -> Div { ++ div() ++ .m_1() ++ .border_1() ++ .child(btn) ++ } ++ ++ v_flex() ++ .id("mega-control-pad") ++ .size_full() ++ .children([ ++ encap_btn(Button::new("btn_toggle_scorpio", "Toggle Scorpio") ++ .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)); ++ })) ++ ), ++ 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)); ++ })) ++ ), ++ encap_btn(Button::new("btn_checkout", "Checkout Path") ++ .full_width() ++ .icon(IconName::Check) ++ .icon_position(IconPosition::Start) ++ .on_click(cx.listener(|this, _, cx| { ++ // TODO: should get the path here ++ this.mega_handle.update(cx, |mega, cx| mega.checkout_path(cx)); ++ })) ++ ), ++ ]) + } ++ ++ fn status_unit(&self, cx: &mut ViewContext, name: &'static str, state: bool) -> Stateful
{ ++ let unit_id = ElementId::from(SharedString::from(format!("status_{}", name.clone()))); ++ 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"); ++ let _ = cx.prompt(PromptLevel::Warning, "Unimplemented", Some(&message), &["Got it"]); ++ } ++} ++ ++fn horizontal_separator(cx: &mut WindowContext) -> Div { ++ div().mx_2().border_primary(cx).border_t_1() + } +\ No newline at end of file +-- +2.43.0 + 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 new file mode 100644 index 000000000..9e3a072ae --- /dev/null +++ b/scripts/zed-intergration/patches/0008-mega-feat-toggle-fuse-mount.patch @@ -0,0 +1,579 @@ +From 8f8d3a30a45fa4f3b2359060d63a51f065c327d9 Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 29 Oct 2024 18:50:39 +0800 +Subject: [PATCH 08/10] mega: feat toggle fuse mount + +--- + Cargo.lock | 6 +- + assets/settings/default.json | 12 +++ + crates/mega/Cargo.toml | 5 +- + crates/mega/src/mega.rs | 116 +++++++++++++--------- + crates/mega/src/mega_settings.rs | 50 ++++++++++ + crates/mega_panel/src/mega_panel.rs | 66 +++++++++--- + crates/project_panel/Cargo.toml | 1 + + crates/project_panel/src/project_panel.rs | 31 ++++++ + crates/zed/src/main.rs | 3 +- + 9 files changed, 225 insertions(+), 65 deletions(-) + create mode 100644 crates/mega/src/mega_settings.rs + +diff --git a/Cargo.lock b/Cargo.lock +index 1ffec64d85..c25acea09d 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -6888,10 +6888,13 @@ dependencies = [ + name = "mega" + version = "0.1.0" + dependencies = [ +- "bytes 0.4.12", ++ "anyhow", ++ "bytes 1.7.2", + "gpui", + "reqwest_client", ++ "schemars", + "serde", ++ "settings", + ] + + [[package]] +@@ -8521,6 +8524,7 @@ dependencies = [ + "gpui", + "indexmap 1.9.3", + "language", ++ "mega", + "menu", + "pretty_assertions", + "project", +diff --git a/assets/settings/default.json b/assets/settings/default.json +index 147f80a12b..6ea0fff594 100644 +--- a/assets/settings/default.json ++++ b/assets/settings/default.json +@@ -379,6 +379,18 @@ + // Where to dock the mega panel. Can be 'left' or 'right'. + "dock": "left", + }, ++ "mega": { ++ // Url to communicate with mega ++ "mega_url": "http://localhost:8000", ++ // Url to communicate with fuse ++ "fuse_url": "http://localhost:2725", ++ // Default mount point for fuse ++ "mount_point": "/home/neon/projects", ++ // Path for mega executable ++ "mega_executable": "mega", ++ // Path for fuse executable ++ "fuse_executable": "scorpio" ++ }, + "outline_panel": { + // Whether to show the outline panel button in the status bar + "button": true, +diff --git a/crates/mega/Cargo.toml b/crates/mega/Cargo.toml +index 1572c73b90..658ed87476 100644 +--- a/crates/mega/Cargo.toml ++++ b/crates/mega/Cargo.toml +@@ -14,6 +14,9 @@ path = 'src/mega.rs' + [dependencies] + gpui.workspace = true + reqwest_client.workspace = true ++settings.workspace = true + + serde.workspace = true +-bytes = "0.4.12" ++bytes.workspace = true ++schemars.workspace = true ++anyhow.workspace = true +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 4bf4aa27d6..84314de815 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -2,39 +2,46 @@ + // The following requirements should be met: + // + // TODO: +-// 1. Only one daemon on this machine. ++// 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. ++// 4. Add settings for this module + + use std::path::{Path, PathBuf}; ++use std::sync::Arc; + use gpui::http_client::{AsyncBody, HttpClient}; +-use gpui::{AppContext, Context, EntityId, EventEmitter, ModelContext, WindowContext}; ++use gpui::{AppContext, Context, EntityId, EventEmitter, ModelContext, SharedString, WindowContext}; + use reqwest_client::ReqwestClient; + use serde::Serialize; ++use settings::Settings; ++use crate::mega_settings::MegaSettings; + + mod delegate; + mod fuse; ++mod mega_settings; + + pub fn init(cx: &mut AppContext) { +- // let reservation = cx.reserve_model(); +- // cx.insert_model(reservation, |cx| { +- // cx.new_model(|_cx| { Mega::new() }) +- // }); ++ Mega::init(cx); + } + + #[derive(Clone, Debug, PartialEq)] + pub enum Event { + MegaRunning(bool), + FuseRunning(bool), +- FuseMounted(bool), ++ FuseMounted(Option), ++ FuseCheckout(Option), + } + pub struct Mega { + mega_running: bool, + fuse_running: bool, + fuse_mounted: bool, + ++ mount_point: Option, + checkout_path: Option, +- panel_id: Option, ++ ++ mega_url: String, ++ fuse_url: String, + } + + pub struct MegaFuse {} +@@ -43,69 +50,93 @@ impl EventEmitter for Mega {} + + + impl Mega { +- pub fn init_settings(cx: &mut AppContext) { +- +- } ++ pub fn init_settings(cx: &mut AppContext) { MegaSettings::register(cx); } + + pub fn init(cx: &mut AppContext) { +- // let reservation = cx.reserve_model(); +- // cx.insert_model(reservation, |cx| { +- // cx.new_model(|_cx| { Mega::new() }) +- // }); ++ Self::init_settings(cx); + } + + pub fn new(cx: &mut AppContext) -> Self { ++ let mount_point = PathBuf::from(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(); ++ + Mega { + fuse_running: false, + mega_running: false, + fuse_mounted: false, ++ ++ mount_point: None, + checkout_path: None, +- panel_id: None, ++ ++ mega_url, ++ fuse_url, + } + } +- ++ + pub fn update_status(&mut self, cx: &mut ModelContext) { +- if let None = self.panel_id { +- return; +- } ++ ++ + + cx.notify(); + } +- ++ + pub fn status(&self) -> (bool, bool, bool) { + (self.mega_running, self.fuse_running, self.fuse_mounted) + } + +- pub fn toggle_mega(&self, cx: &mut ModelContext) { todo!() } ++ pub fn toggle_mega(&mut self, cx: &mut ModelContext) { ++ self.mega_running = !self.mega_running; ++ cx.emit(Event::MegaRunning(self.mega_running)); ++ } + +- pub fn toggle_fuse(&self, cx: &mut ModelContext) { +- ++ pub fn toggle_fuse(&mut self, cx: &mut ModelContext) { ++ self.fuse_running = !self.fuse_running; ++ cx.emit(Event::FuseRunning(self.fuse_running)); + } + +- pub fn toggle_mount(&self, cx: &mut ModelContext) { ++ pub fn toggle_mount(&mut self, cx: &mut ModelContext) { + // let req_body = delegate::MountRequest { + // path: "".parse().unwrap() + // }; +- +- cx.spawn(|_this, _cx| async { ++ ++ cx.spawn(|this, mut cx| async move { + let client = ReqwestClient::new(); + let req = client.get( + "localhost:2725/api/fs/mount", + AsyncBody::empty(), + false + ).await; ++ ++ if let Some(mega) = this.upgrade() { ++ let _ = mega.update(&mut cx, |this, cx| { ++ if this.fuse_mounted { ++ this.fuse_mounted = false; ++ } else { ++ // FIXME just pretending that we've got something from fuse response ++ this.fuse_mounted = true; ++ this.mount_point = Some(PathBuf::from("/home/neon/projects")); ++ } ++ cx.emit(Event::FuseMounted(this.mount_point.clone())); ++ }); ++ } + }).detach(); + } + +- pub fn checkout_path(&self, cx: &mut ModelContext) { +- cx.spawn(|_this, _cx| async { +- let client = ReqwestClient::new(); +- let req = client.get( +- "localhost:2725/api/fs/mount", +- AsyncBody::empty(), +- false +- ).await; +- }).detach(); ++ pub fn checkout_path(&mut self, cx: &mut ModelContext) { ++ // for now, we assume there's only one path being checkout at a time. ++ if self.checkout_path.is_none() { ++ cx.spawn(|_this, _cx| async { ++ let client = ReqwestClient::new(); ++ let req = client.get( ++ "localhost:2725/api/fs/mount", ++ AsyncBody::empty(), ++ false ++ ).await; ++ }).detach(); ++ } ++ ++ + } + + pub fn get_fuse_config(&self, cx: &mut ModelContext) { +@@ -128,18 +159,7 @@ impl Mega { + ).await; + }).detach(); + } +- +- pub fn get_fuse_mpoint(&self, cx: &mut ModelContext) { +- cx.spawn(|_this, _cx| async { +- let client = ReqwestClient::new(); +- let req = client.get( +- "localhost:2725/api/config", +- AsyncBody::empty(), +- false +- ).await; +- }).detach(); +- } +- ++ + } + + #[cfg(test)] +diff --git a/crates/mega/src/mega_settings.rs b/crates/mega/src/mega_settings.rs +new file mode 100644 +index 0000000000..82721208a1 +--- /dev/null ++++ b/crates/mega/src/mega_settings.rs +@@ -0,0 +1,50 @@ ++use std::path::PathBuf; ++use schemars::JsonSchema; ++use gpui::private::serde_derive::{Deserialize, Serialize}; ++use settings::{Settings, SettingsSources}; ++ ++#[derive(Default, Deserialize, Debug, Clone, PartialEq)] ++pub struct MegaSettings { ++ pub mega_url: String, ++ pub fuse_url: String, ++ pub mount_point: PathBuf, ++ pub mega_executable: PathBuf, ++ pub fuse_executable: PathBuf, ++} ++ ++#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] ++pub struct MegaSettingsContent { ++ /// Url to communicate with mega ++ /// ++ /// Default: http://localhost:8000 ++ pub mega_url: String, ++ /// Url to communicate with fuse ++ /// ++ /// Default: http://localhost:2725 ++ pub fuse_url: String, ++ /// Default mount point for fuse ++ /// ++ /// Default: "/" (for now) ++ pub mount_point: PathBuf, ++ /// Path for mega executable ++ /// ++ /// Default: "mega" (for now) ++ pub mega_executable: PathBuf, ++ /// Path for fuse executable ++ /// ++ /// Default: "scorpio" (for now) ++ pub fuse_executable: PathBuf, ++} ++ ++impl Settings for MegaSettings { ++ const KEY: Option<&'static str> = Some("mega"); ++ ++ type FileContent = MegaSettingsContent; ++ ++ fn load( ++ sources: SettingsSources, ++ _: &mut gpui::AppContext, ++ ) -> anyhow::Result { ++ sources.json_merge() ++ } ++} +\ 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 80a26cf4c2..82e6047244 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -1,10 +1,11 @@ ++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, FocusHandle, FocusableView, FontWeight, Hsla, InteractiveElement, IntoElement, Model, ParentElement, 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, PathPromptOptions, Pixels, PromptLevel, Render, SharedString, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext}; + use mega::Mega; + use settings::Settings; + use std::sync::Arc; +@@ -154,7 +155,7 @@ impl Panel for MegaPanel { + .then_some(IconName::FileGit) + } + +- fn icon_tooltip(&self, cx: &WindowContext) -> Option<&'static str> { ++ fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { + Some("Mega Panel") + } + +@@ -203,6 +204,7 @@ impl MegaPanel { + let focus_handle = cx.focus_handle(); + cx.on_focus(&focus_handle, Self::focus_in).detach(); + ++ #[allow(unused)] + cx.subscribe(mega, |this, mega, event, cx| { + // TODO: listen for mega events + }).detach(); +@@ -243,16 +245,22 @@ impl MegaPanel { + } + } + +- pub fn toggle_fuse_mount(&mut self, _: &ToggleFuseMount, cx: &mut ViewContext) { +- // let mega = self.mega_handle.upgrade() +- // .unwrap_or_else() +- +- self.warn_unimplemented(cx); +- } +- + 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) { ++ // if let Some(workspace) = self.workspace.upgrade() { ++ // workspace.model.update(cx, |this, mx| { ++ // ++ // }); ++ // } ++ ++ self.mega_handle.update(cx, |this, cx | { ++ this.toggle_mount(cx); ++ ++ }); ++ } + + fn render_status(&mut self, cx: &mut ViewContext) -> Div { + let ( +@@ -283,12 +291,22 @@ impl MegaPanel { + .id("mega-control-pad") + .size_full() + .children([ ++ encap_btn(Button::new("btn_toggle_mega", "Toggle Mega") ++ .full_width() ++ .icon(IconName::Plus) ++ .icon_position(IconPosition::Start) ++ .on_click(cx.listener(|this, _, cx| { ++ this.mega_handle.update(cx, |mega, cx| mega.toggle_mega(cx)); ++ this.warn_unimplemented(cx); ++ })) ++ ), + encap_btn(Button::new("btn_toggle_scorpio", "Toggle Scorpio") + .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)); ++ this.warn_unimplemented(cx); + })) + ), + encap_btn(Button::new("btn_toggle_mount", "Toggle Mount") +@@ -297,6 +315,9 @@ 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_checkout", "Checkout Path") +@@ -304,15 +325,34 @@ impl MegaPanel { + .icon(IconName::Check) + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { +- // TODO: should get the path here +- this.mega_handle.update(cx, |mega, cx| mega.checkout_path(cx)); ++ this.warn_unimplemented(cx); ++ // TODO: should read the path here ++ let options = PathPromptOptions { ++ files: true, ++ directories: true, ++ multiple: false, ++ }; ++ ++ let abs_path = cx.prompt_for_paths(options); ++ // if let Some(workspace_view) = this.workspace.upgrade() { ++ // let mut workspace = workspace_view.read(cx); ++ // workspace.open_workspace_for_paths(false, vec![], cx); ++ // } ++ cx.spawn(|this, mut cx| async move { ++ let Ok(Ok(Some(result))) = abs_path.await else { ++ return; ++ }; ++ ++ ++ }).detach(); ++ // mega.update(cx, |mega, cx| mega.checkout_path(cx)); + })) + ), + ]) + } + + fn status_unit(&self, cx: &mut ViewContext, name: &'static str, state: bool) -> Stateful
{ +- let unit_id = ElementId::from(SharedString::from(format!("status_{}", name.clone()))); ++ let unit_id = ElementId::from(SharedString::from(format!("status_{}", name))); + div() + .text_ui(cx) + .id(unit_id) +@@ -328,7 +368,7 @@ impl MegaPanel { + } + + fn warn_unimplemented(&self, cx: &mut ViewContext) { +- let message = String::from("This operation is not implemented yet"); ++ 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"]); + } + } +diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml +index 11c7364e58..cc9e0ba8f6 100644 +--- a/crates/project_panel/Cargo.toml ++++ b/crates/project_panel/Cargo.toml +@@ -21,6 +21,7 @@ file_icons.workspace = true + indexmap.workspace = true + git.workspace = true + gpui.workspace = true ++mega.workspace = true + menu.workspace = true + pretty_assertions.workspace = true + project.workspace = true +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index c4fb39f587..34f3bf0f54 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -43,6 +43,7 @@ use std::{ + sync::Arc, + time::Duration, + }; ++use db::write_and_log; + use theme::ThemeSettings; + use ui::{prelude::*, v_flex, ContextMenu, Icon, KeyBinding, Label, ListItem, Tooltip}; + use util::{maybe, ResultExt, TryFutureExt}; +@@ -223,6 +224,7 @@ struct DraggedProjectEntryView { + impl ProjectPanel { + fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> View { + let project = workspace.project().clone(); ++ let mega = workspace.mega().clone(); + let project_panel = cx.new_view(|cx: &mut ViewContext| { + let focus_handle = cx.focus_handle(); + cx.on_focus(&focus_handle, Self::focus_in).detach(); +@@ -282,6 +284,34 @@ impl ProjectPanel { + ) + .detach(); + ++ cx.subscribe(&mega, |this, mega, mega_event, cx| match mega_event { ++ mega::Event::FuseMounted(Some(path)) => { ++ let path = path.to_owned(); ++ this.workspace ++ .update(cx, |workspace, cx| { ++ 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) ++ }) ++ .log_err() ++ { ++ task.await.log_err(); ++ } ++ }) ++ .detach() ++ }) ++ .log_err(); ++ ++ ++ } ++ mega::Event::FuseCheckout(path) => { ++ // FIXME: impl it. ++ println!("Fuse Checkout NOT implemented in project for now!"); ++ } ++ _ => {} ++ }).detach(); ++ + cx.observe_global::(|_, cx| { + cx.notify(); + }) +@@ -491,6 +521,7 @@ impl ProjectPanel { + entry_id, + }); + ++ // FIXME add fuse dir specific behaviors + if let Some((worktree, entry)) = self.selected_sub_entry(cx) { + let auto_fold_dirs = ProjectPanelSettings::get_global(cx).auto_fold_dirs; + let is_root = Some(entry) == worktree.root_entry(); +diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs +index 005d086f48..1e5e9a6ac7 100644 +--- a/crates/zed/src/main.rs ++++ b/crates/zed/src/main.rs +@@ -523,10 +523,9 @@ fn main() { + Client::set_global(client.clone(), cx); + + zed::init(cx); +- mega::init(cx); + project::Project::init(&client, cx); + client::init(&client, cx); +- language::init(cx); ++ mega::init(cx); + let telemetry = client.telemetry(); + telemetry.start( + system_id.as_ref().map(|id| id.to_string()), +-- +2.43.0 + 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 new file mode 100644 index 000000000..81b5ddfba --- /dev/null +++ b/scripts/zed-intergration/patches/0009-Fix-dont-restore-previous-workspace.patch @@ -0,0 +1,254 @@ +From 69f92bb2dbb1b2facf7b60cb6c6e1c0d211c4f4d Mon Sep 17 00:00:00 2001 +From: Neon +Date: Tue, 29 Oct 2024 21:08:56 +0800 +Subject: [PATCH 09/10] Fix: dont restore previous workspace + +--- + crates/project/src/project.rs | 106 ++++++++++++++++++++++ + crates/project_panel/src/project_panel.rs | 5 +- + crates/zed/src/main.rs | 85 +++++++++-------- + 3 files changed, 155 insertions(+), 41 deletions(-) + +diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs +index 6e731e9cba..6795d005d7 100644 +--- a/crates/project/src/project.rs ++++ b/crates/project/src/project.rs +@@ -576,6 +576,112 @@ impl Project { + TaskStore::init(Some(&client)); + } + ++ pub fn local_new ( ++ client: Arc, ++ node: NodeRuntime, ++ user_store: Model, ++ languages: Arc, ++ fs: Arc, ++ env: Option>, ++ cx: &mut AppContext, ++ ) -> Model { ++ cx.new_model(|cx: &mut ModelContext| { ++ let (tx, rx) = mpsc::unbounded(); ++ cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx)) ++ .detach(); ++ let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx); ++ let worktree_store = cx.new_model(|_| WorktreeStore::local(false, fs.clone())); ++ cx.subscribe(&worktree_store, Self::on_worktree_store_event) ++ .detach(); ++ ++ let buffer_store = cx.new_model(|cx| BufferStore::local(worktree_store.clone(), cx)); ++ cx.subscribe(&buffer_store, Self::on_buffer_store_event) ++ .detach(); ++ ++ let prettier_store = cx.new_model(|cx| { ++ PrettierStore::new( ++ node.clone(), ++ fs.clone(), ++ languages.clone(), ++ worktree_store.clone(), ++ cx, ++ ) ++ }); ++ ++ let environment = ProjectEnvironment::new(&worktree_store, env, cx); ++ ++ let task_store = cx.new_model(|cx| { ++ TaskStore::local( ++ fs.clone(), ++ buffer_store.downgrade(), ++ worktree_store.clone(), ++ environment.clone(), ++ cx, ++ ) ++ }); ++ ++ let settings_observer = cx.new_model(|cx| { ++ SettingsObserver::new_local( ++ fs.clone(), ++ worktree_store.clone(), ++ task_store.clone(), ++ cx, ++ ) ++ }); ++ cx.subscribe(&settings_observer, Self::on_settings_observer_event) ++ .detach(); ++ ++ let lsp_store = cx.new_model(|cx| { ++ LspStore::new_local( ++ buffer_store.clone(), ++ worktree_store.clone(), ++ prettier_store.clone(), ++ environment.clone(), ++ languages.clone(), ++ client.http_client(), ++ fs.clone(), ++ cx, ++ ) ++ }); ++ cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach(); ++ ++ Self { ++ buffer_ordered_messages_tx: tx, ++ collaborators: Default::default(), ++ worktree_store, ++ buffer_store, ++ lsp_store, ++ join_project_response_message_id: 0, ++ client_state: ProjectClientState::Local, ++ client_subscriptions: Vec::new(), ++ _subscriptions: vec![cx.on_release(Self::release)], ++ active_entry: None, ++ snippets, ++ languages, ++ client, ++ task_store, ++ user_store, ++ settings_observer, ++ fs, ++ ssh_client: None, ++ buffers_needing_diff: Default::default(), ++ git_diff_debouncer: DebouncedDelay::new(), ++ terminals: Terminals { ++ local_handles: Vec::new(), ++ }, ++ node: Some(node), ++ hosted_project_id: None, ++ dev_server_project_id: None, ++ search_history: Self::new_search_history(), ++ environment, ++ remotely_created_models: Default::default(), ++ ++ search_included_history: Self::new_search_history(), ++ search_excluded_history: Self::new_search_history(), ++ } ++ }) ++ } ++ + pub fn local( + client: Arc, + node: NodeRuntime, +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index 34f3bf0f54..8b16829834 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -286,6 +286,7 @@ impl ProjectPanel { + + cx.subscribe(&mega, |this, mega, mega_event, cx| match mega_event { + mega::Event::FuseMounted(Some(path)) => { ++ + let path = path.to_owned(); + this.workspace + .update(cx, |workspace, cx| { +@@ -302,8 +303,8 @@ impl ProjectPanel { + .detach() + }) + .log_err(); +- +- ++ ++ + } + mega::Event::FuseCheckout(path) => { + // FIXME: impl it. +diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs +index 1e5e9a6ac7..9df417f3e5 100644 +--- a/crates/zed/src/main.rs ++++ b/crates/zed/src/main.rs +@@ -867,49 +867,56 @@ async fn installation_id() -> Result { + Ok(IdType::New(installation_id)) + } + ++/// **Mega Fuse Integration:** ++/// ++/// This function is used for detect if there is an existing workspace opened ++/// in the last run. ++/// ++/// Whenever the case is, create an empty workspace ++/// because fuse dir cannot be warrantied to be available at zed startup. + async fn restore_or_create_workspace( + app_state: Arc, + cx: &mut AsyncAppContext, + ) -> Result<()> { +- if let Some(locations) = restorable_workspace_locations(cx, &app_state).await { +- for location in locations { +- match location { +- SerializedWorkspaceLocation::Local(location, _) => { +- let task = cx.update(|cx| { +- workspace::open_paths( +- location.paths().as_ref(), +- app_state.clone(), +- workspace::OpenOptions::default(), +- cx, +- ) +- })?; +- task.await?; +- } +- SerializedWorkspaceLocation::Ssh(ssh_project) => { +- let connection_options = SshConnectionOptions { +- host: ssh_project.host.clone(), +- username: ssh_project.user.clone(), +- port: ssh_project.port, +- password: None, +- }; +- let app_state = app_state.clone(); +- cx.spawn(move |mut cx| async move { +- recent_projects::open_ssh_project( +- connection_options, +- ssh_project.paths.into_iter().map(PathBuf::from).collect(), +- app_state, +- workspace::OpenOptions::default(), +- &mut cx, +- ) +- .await +- .log_err(); +- }) +- .detach(); +- } +- SerializedWorkspaceLocation::DevServer(_) => {} +- } +- } +- } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { ++ // if let Some(locations) = restorable_workspace_locations(cx, &app_state).await { ++ // for location in locations { ++ // match location { ++ // SerializedWorkspaceLocation::Local(location, _) => { ++ // let task = cx.update(|cx| { ++ // workspace::open_paths( ++ // location.paths().as_ref(), ++ // app_state.clone(), ++ // workspace::OpenOptions::default(), ++ // cx, ++ // ) ++ // })?; ++ // task.await?; ++ // } ++ // SerializedWorkspaceLocation::Ssh(ssh_project) => { ++ // let connection_options = SshConnectionOptions { ++ // host: ssh_project.host.clone(), ++ // username: ssh_project.user.clone(), ++ // port: ssh_project.port, ++ // password: None, ++ // }; ++ // let app_state = app_state.clone(); ++ // cx.spawn(move |mut cx| async move { ++ // recent_projects::open_ssh_project( ++ // connection_options, ++ // ssh_project.paths.into_iter().map(PathBuf::from).collect(), ++ // app_state, ++ // workspace::OpenOptions::default(), ++ // &mut cx, ++ // ) ++ // .await ++ // .log_err(); ++ // }) ++ // .detach(); ++ // } ++ // SerializedWorkspaceLocation::DevServer(_) => {} ++ // } ++ // } ++ if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { + cx.update(|cx| show_welcome_view(app_state, cx))?.await?; + } else { + cx.update(|cx| { +-- +2.43.0 + 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 new file mode 100644 index 000000000..5498ebc3d --- /dev/null +++ b/scripts/zed-intergration/patches/0010-Fix-checkout-in-project_panel.patch @@ -0,0 +1,242 @@ +From f2551513e804e8f43f6fac39d58befffd9dcda39 Mon Sep 17 00:00:00 2001 +From: Neon +Date: Wed, 30 Oct 2024 10:53:59 +0800 +Subject: [PATCH 10/10] Fix checkout in project_panel + +--- + crates/mega/src/mega.rs | 18 +++++++++- + crates/mega_panel/src/mega_panel.rs | 28 +++++++-------- + crates/project_panel/src/project_panel.rs | 44 +++++++++++++++++------ + 3 files changed, 63 insertions(+), 27 deletions(-) + +diff --git a/crates/mega/src/mega.rs b/crates/mega/src/mega.rs +index 84314de815..c27c40d7f4 100644 +--- a/crates/mega/src/mega.rs ++++ b/crates/mega/src/mega.rs +@@ -123,7 +123,7 @@ impl Mega { + }).detach(); + } + +- pub fn checkout_path(&mut self, cx: &mut ModelContext) { ++ pub fn checkout_path(&mut self, cx: &mut ModelContext, mut path: PathBuf) { + // for now, we assume there's only one path being checkout at a time. + if self.checkout_path.is_none() { + cx.spawn(|_this, _cx| async { +@@ -137,6 +137,22 @@ impl Mega { + } + + ++ } ++ ++ pub fn checkout_multi_path(&mut self, cx: &mut ModelContext, mut path: Vec) { ++ // for now, we assume there's only one path being checkout at a time. ++ if self.checkout_path.is_none() { ++ cx.spawn(|_this, _cx| async { ++ let client = ReqwestClient::new(); ++ let req = client.get( ++ "localhost:2725/api/fs/mount", ++ AsyncBody::empty(), ++ false ++ ).await; ++ }).detach(); ++ } ++ ++ + } + + pub fn get_fuse_config(&self, cx: &mut ModelContext) { +diff --git a/crates/mega_panel/src/mega_panel.rs b/crates/mega_panel/src/mega_panel.rs +index 82e6047244..8b141b57fd 100644 +--- a/crates/mega_panel/src/mega_panel.rs ++++ b/crates/mega_panel/src/mega_panel.rs +@@ -5,7 +5,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, 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, Flatten, FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, ParentElement, PathPromptOptions, Pixels, PromptLevel, Render, SharedString, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext}; + use mega::Mega; + use settings::Settings; + use std::sync::Arc; +@@ -257,6 +257,7 @@ impl MegaPanel { + // } + + self.mega_handle.update(cx, |this, cx | { ++ // FIXME when there's already a visible worktree, this call will create a new window. + this.toggle_mount(cx); + + }); +@@ -315,8 +316,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); + })) + ), +@@ -326,7 +325,6 @@ impl MegaPanel { + .icon_position(IconPosition::Start) + .on_click(cx.listener(|this, _, cx| { + this.warn_unimplemented(cx); +- // TODO: should read the path here + let options = PathPromptOptions { + files: true, + directories: true, +@@ -334,18 +332,18 @@ impl MegaPanel { + }; + + let abs_path = cx.prompt_for_paths(options); +- // if let Some(workspace_view) = this.workspace.upgrade() { +- // let mut workspace = workspace_view.read(cx); +- // workspace.open_workspace_for_paths(false, vec![], cx); +- // } +- cx.spawn(|this, mut cx| async move { +- let Ok(Ok(Some(result))) = abs_path.await else { +- return; +- }; +- ++ ++ // 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(); +- // mega.update(cx, |mega, cx| mega.checkout_path(cx)); ++ }) .detach(); ++ + })) + ), + ]) +diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs +index 8b16829834..d69d682b8f 100644 +--- a/crates/project_panel/src/project_panel.rs ++++ b/crates/project_panel/src/project_panel.rs +@@ -44,6 +44,7 @@ use std::{ + 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}; +@@ -58,7 +59,9 @@ 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, + scroll_handle: UniformListScrollHandle, + focus_handle: FocusHandle, +@@ -67,6 +70,7 @@ 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>, +@@ -164,6 +168,7 @@ actions!( + UnfoldDirectory, + FoldDirectory, + SelectParent, ++ CheckoutPath, + ] + ); + +@@ -303,8 +308,8 @@ impl ProjectPanel { + .detach() + }) + .log_err(); +- +- ++ ++ this.focus_in(cx); + } + mega::Event::FuseCheckout(path) => { + // FIXME: impl it. +@@ -330,11 +335,13 @@ impl ProjectPanel { + + let mut this = Self { + project: project.clone(), ++ mega: mega.clone(), + fs: workspace.app_state().fs.clone(), + scroll_handle: UniformListScrollHandle::new(), + 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(), +@@ -541,7 +548,10 @@ impl ProjectPanel { + menu.action("Search Inside", Box::new(NewSearchInDirectory)) + }) + } else { +- menu.action("New File", Box::new(NewFile)) ++ 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| { +@@ -1324,6 +1334,15 @@ impl ProjectPanel { + } + } + ++ fn checkout_specific_path(&mut self, _: &CheckoutPath, cx: &mut ViewContext) { ++ if let Some((worktree, entry)) = self.selected_entry_handle(cx) { ++ let path = entry.path.clone(); ++ self.mega.update(cx, |this, cx| { ++ this.checkout_path(cx, path.to_path_buf()) ++ }); ++ } ++ } ++ + fn autoscroll(&mut self, cx: &mut ViewContext) { + if let Some((_, _, index)) = self.selection.and_then(|s| self.index_for_selection(s)) { + self.scroll_handle.scroll_to_item(index); +@@ -2937,6 +2956,7 @@ impl Render for ProjectPanel { + .on_action(cx.listener(Self::new_search_in_directory)) + .on_action(cx.listener(Self::unfold_directory)) + .on_action(cx.listener(Self::fold_directory)) ++ .on_action(cx.listener(Self::checkout_specific_path)) + .when(!project.is_read_only(cx), |el| { + el.on_action(cx.listener(Self::new_file)) + .on_action(cx.listener(Self::new_directory)) +@@ -3023,14 +3043,16 @@ impl Render for ProjectPanel { + .p_4() + .track_focus(&self.focus_handle) + .child( +- Button::new("open_project", "Open a project") +- .full_width() +- .key_binding(KeyBinding::for_action(&workspace::Open, cx)) +- .on_click(cx.listener(|this, _, cx| { +- this.workspace +- .update(cx, |workspace, cx| workspace.open(&workspace::Open, cx)) +- .log_err(); +- })), ++ // Button::new("open_project", "Open a project") ++ // .full_width() ++ // .key_binding(KeyBinding::for_action(&workspace::Open, cx)) ++ // .on_click(cx.listener(|this, _, cx| { ++ // this.workspace ++ // .update(cx, |workspace, cx| workspace.open(&workspace::Open, cx)) ++ // .log_err(); ++ // })), ++ 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 +