diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 0ecd6082d63f576be7c4f3342aa679921600e873..c1b0dc191d07bb4cbb6e83ab3239260ca0e0edb1 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -287,6 +287,8 @@ impl Room { mut room: proto::Room, cx: &mut ModelContext, ) -> Result<()> { + // TODO: honor room version. + // Filter ourselves out from the room's participants. let local_participant_ix = room .participants diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 39bc2775a0fe9f6ecf615fd08c4625d9d30a4572..a12985b94bef8d71dd25db32845d82866ddd33e0 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1145,8 +1145,8 @@ where FROM projects, project_collaborators WHERE projects.room_id = $1 AND - projects.host_connection_id = $2 AND - projects.id = project_collaborators.project_id + projects.id = project_collaborators.project_id AND + project_collaborators.connection_id = $2 ", ) .bind(room_id) @@ -1370,9 +1370,9 @@ where pub async fn share_project( &self, + room_id: RoomId, user_id: UserId, connection_id: ConnectionId, - room_id: RoomId, worktrees: &[proto::WorktreeMetadata], ) -> Result<(ProjectId, proto::Room)> { test_support!(self, { @@ -1426,11 +1426,19 @@ where .await?; let room = self.commit_room_transaction(room_id, tx).await?; - dbg!(&room); Ok((project_id, room)) }) } + // pub async fn join_project( + // &self, + // user_id: UserId, + // connection_id: ConnectionId, + // project_id: ProjectId, + // ) -> Result<(Project, ReplicaId)> { + // todo!() + // } + pub async fn unshare_project(&self, project_id: ProjectId) -> Result<()> { todo!() // test_support!(self, { diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 3a4c2368e8060994482b464dfbe7081a080e3efc..b54f03ce53e0aa6200814f8db9f1fc67744b718a 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -30,9 +30,7 @@ use language::{ use live_kit_client::MacOSDisplay; use lsp::{self, FakeLanguageServer}; use parking_lot::Mutex; -use project::{ - search::SearchQuery, DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId, -}; +use project::{search::SearchQuery, DiagnosticSummary, Project, ProjectPath, WorktreeId}; use rand::prelude::*; use serde_json::json; use settings::{Formatter, Settings}; @@ -2280,7 +2278,6 @@ async fn test_leaving_project( project_id, client_b.client.clone(), client_b.user_store.clone(), - client_b.project_store.clone(), client_b.language_registry.clone(), FakeFs::new(cx.background()), cx, @@ -5792,11 +5789,9 @@ impl TestServer { let fs = FakeFs::new(cx.background()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); - let project_store = cx.add_model(|_| ProjectStore::new()); let app_state = Arc::new(workspace::AppState { client: client.clone(), user_store: user_store.clone(), - project_store: project_store.clone(), languages: Arc::new(LanguageRegistry::new(Task::ready(()))), themes: ThemeRegistry::new((), cx.font_cache()), fs: fs.clone(), @@ -5823,7 +5818,6 @@ impl TestServer { remote_projects: Default::default(), next_root_dir_id: 0, user_store, - project_store, fs, language_registry: Arc::new(LanguageRegistry::test()), buffers: Default::default(), @@ -5929,7 +5923,6 @@ struct TestClient { remote_projects: Vec>, next_root_dir_id: usize, pub user_store: ModelHandle, - pub project_store: ModelHandle, language_registry: Arc, fs: Arc, buffers: HashMap, HashSet>>, @@ -5999,7 +5992,6 @@ impl TestClient { Project::local( self.client.clone(), self.user_store.clone(), - self.project_store.clone(), self.language_registry.clone(), self.fs.clone(), cx, @@ -6027,7 +6019,6 @@ impl TestClient { host_project_id, self.client.clone(), self.user_store.clone(), - self.project_store.clone(), self.language_registry.clone(), FakeFs::new(cx.background()), cx, @@ -6157,7 +6148,6 @@ impl TestClient { remote_project_id, client.client.clone(), client.user_store.clone(), - client.project_store.clone(), client.language_registry.clone(), FakeFs::new(cx.background()), cx.to_async(), diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index d9c8c616f38ba6228c84949b45721a7021df0cc3..bed6ebf9cd649a10bb9bce2d931606e2a56ed281 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -151,7 +151,7 @@ impl Server { .add_message_handler(Server::unshare_project) .add_request_handler(Server::join_project) .add_message_handler(Server::leave_project) - .add_message_handler(Server::update_project) + .add_request_handler(Server::update_project) .add_request_handler(Server::update_worktree) .add_message_handler(Server::start_language_server) .add_message_handler(Server::update_language_server) @@ -861,9 +861,9 @@ impl Server { .app_state .db .share_project( + RoomId::from_proto(request.payload.room_id), request.sender_user_id, request.sender_connection_id, - RoomId::from_proto(request.payload.room_id), &request.payload.worktrees, ) .await @@ -1084,6 +1084,7 @@ impl Server { async fn update_project( self: Arc, request: Message, + response: Response, ) -> Result<()> { let project_id = ProjectId::from_proto(request.payload.project_id); { @@ -1108,6 +1109,7 @@ impl Server { }, ); self.room_updated(room); + response.send(proto::Ack {})?; }; Ok(()) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index f5f508ce5b167059cf8c3fbaebcb0e1d5e80b996..dc8a1716989e4b5ccec4ae476591b3f8f41339f3 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -43,7 +43,6 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { project_id, app_state.client.clone(), app_state.user_store.clone(), - app_state.project_store.clone(), app_state.languages.clone(), app_state.fs.clone(), cx.clone(), diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3c28f6b512e38f9f803398641b7b9676beec234f..d01571f44b1f7df78698420af0f19e282a4d8c55 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -70,10 +70,6 @@ pub trait Item: Entity { fn entry_id(&self, cx: &AppContext) -> Option; } -pub struct ProjectStore { - projects: Vec>, -} - // Language server state is stored across 3 collections: // language_servers => // a mapping from unique server id to LanguageServerState which can either be a task for a @@ -102,7 +98,6 @@ pub struct Project { next_entry_id: Arc, next_diagnostic_group_id: usize, user_store: ModelHandle, - project_store: ModelHandle, fs: Arc, client_state: Option, collaborators: HashMap, @@ -152,6 +147,8 @@ enum WorktreeHandle { enum ProjectClientState { Local { remote_id: u64, + metadata_changed: watch::Sender<()>, + _maintain_metadata: Task<()>, _detect_unshare: Task>, }, Remote { @@ -376,7 +373,7 @@ impl Project { client.add_model_message_handler(Self::handle_start_language_server); client.add_model_message_handler(Self::handle_update_language_server); client.add_model_message_handler(Self::handle_remove_collaborator); - client.add_model_message_handler(Self::handle_update_project); + client.add_model_message_handler(Self::handle_project_updated); client.add_model_message_handler(Self::handle_unshare_project); client.add_model_message_handler(Self::handle_create_buffer_for_peer); client.add_model_message_handler(Self::handle_update_buffer_file); @@ -412,46 +409,39 @@ impl Project { pub fn local( client: Arc, user_store: ModelHandle, - project_store: ModelHandle, languages: Arc, fs: Arc, cx: &mut MutableAppContext, ) -> ModelHandle { - cx.add_model(|cx: &mut ModelContext| { - let handle = cx.weak_handle(); - project_store.update(cx, |store, cx| store.add_project(handle, cx)); - - Self { - worktrees: Default::default(), - collaborators: Default::default(), - opened_buffers: Default::default(), - shared_buffers: Default::default(), - incomplete_buffers: Default::default(), - loading_buffers: Default::default(), - loading_local_worktrees: Default::default(), - buffer_snapshots: Default::default(), - client_state: None, - opened_buffer: watch::channel(), - client_subscriptions: Vec::new(), - _subscriptions: vec![cx.observe_global::(Self::on_settings_changed)], - _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), - active_entry: None, - languages, - client, - user_store, - project_store, - fs, - next_entry_id: Default::default(), - next_diagnostic_group_id: Default::default(), - language_servers: Default::default(), - language_server_ids: Default::default(), - language_server_statuses: Default::default(), - last_workspace_edits_by_language_server: Default::default(), - language_server_settings: Default::default(), - buffers_being_formatted: Default::default(), - next_language_server_id: 0, - nonce: StdRng::from_entropy().gen(), - } + cx.add_model(|cx: &mut ModelContext| Self { + worktrees: Default::default(), + collaborators: Default::default(), + opened_buffers: Default::default(), + shared_buffers: Default::default(), + incomplete_buffers: Default::default(), + loading_buffers: Default::default(), + loading_local_worktrees: Default::default(), + buffer_snapshots: Default::default(), + client_state: None, + opened_buffer: watch::channel(), + client_subscriptions: Vec::new(), + _subscriptions: vec![cx.observe_global::(Self::on_settings_changed)], + _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), + active_entry: None, + languages, + client, + user_store, + fs, + next_entry_id: Default::default(), + next_diagnostic_group_id: Default::default(), + language_servers: Default::default(), + language_server_ids: Default::default(), + language_server_statuses: Default::default(), + last_workspace_edits_by_language_server: Default::default(), + language_server_settings: Default::default(), + buffers_being_formatted: Default::default(), + next_language_server_id: 0, + nonce: StdRng::from_entropy().gen(), }) } @@ -459,7 +449,6 @@ impl Project { remote_id: u64, client: Arc, user_store: ModelHandle, - project_store: ModelHandle, languages: Arc, fs: Arc, mut cx: AsyncAppContext, @@ -482,9 +471,6 @@ impl Project { } let this = cx.add_model(|cx: &mut ModelContext| { - let handle = cx.weak_handle(); - project_store.update(cx, |store, cx| store.add_project(handle, cx)); - let mut this = Self { worktrees: Vec::new(), loading_buffers: Default::default(), @@ -497,7 +483,6 @@ impl Project { _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), languages, user_store: user_store.clone(), - project_store, fs, next_entry_id: Default::default(), next_diagnostic_group_id: Default::default(), @@ -593,9 +578,7 @@ impl Project { let http_client = client::test::FakeHttpClient::with_404_response(); let client = cx.update(|cx| client::Client::new(http_client.clone(), cx)); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); - let project_store = cx.add_model(|_| ProjectStore::new()); - let project = - cx.update(|cx| Project::local(client, user_store, project_store, languages, fs, cx)); + let project = cx.update(|cx| Project::local(client, user_store, languages, fs, cx)); for path in root_paths { let (tree, _) = project .update(cx, |project, cx| { @@ -676,10 +659,6 @@ impl Project { self.user_store.clone() } - pub fn project_store(&self) -> ModelHandle { - self.project_store.clone() - } - #[cfg(any(test, feature = "test-support"))] pub fn check_invariants(&self, cx: &AppContext) { if self.is_local() { @@ -752,51 +731,12 @@ impl Project { } fn metadata_changed(&mut self, cx: &mut ModelContext) { - if let Some(ProjectClientState::Local { remote_id, .. }) = &self.client_state { - let project_id = *remote_id; - // Broadcast worktrees only if the project is online. - let worktrees = self - .worktrees - .iter() - .filter_map(|worktree| { - worktree - .upgrade(cx) - .map(|worktree| worktree.read(cx).as_local().unwrap().metadata_proto()) - }) - .collect(); - self.client - .send(proto::UpdateProject { - project_id, - worktrees, - }) - .log_err(); - - let worktrees = self.visible_worktrees(cx).collect::>(); - let scans_complete = futures::future::join_all( - worktrees - .iter() - .filter_map(|worktree| Some(worktree.read(cx).as_local()?.scan_complete())), - ); - - let worktrees = worktrees.into_iter().map(|handle| handle.downgrade()); - - cx.spawn_weak(move |_, cx| async move { - scans_complete.await; - cx.read(|cx| { - for worktree in worktrees { - if let Some(worktree) = worktree - .upgrade(cx) - .and_then(|worktree| worktree.read(cx).as_local()) - { - worktree.send_extension_counts(project_id); - } - } - }) - }) - .detach(); + if let Some(ProjectClientState::Local { + metadata_changed, .. + }) = &mut self.client_state + { + *metadata_changed.borrow_mut() = (); } - - self.project_store.update(cx, |_, cx| cx.notify()); cx.notify(); } @@ -1092,8 +1032,32 @@ impl Project { cx.notify(); let mut status = self.client.status(); + let (metadata_changed_tx, mut metadata_changed_rx) = watch::channel(); self.client_state = Some(ProjectClientState::Local { remote_id: project_id, + metadata_changed: metadata_changed_tx, + _maintain_metadata: cx.spawn_weak(move |this, cx| async move { + while let Some(()) = metadata_changed_rx.next().await { + let Some(this) = this.upgrade(&cx) else { break }; + this.read_with(&cx, |this, cx| { + let worktrees = this + .worktrees + .iter() + .filter_map(|worktree| { + worktree.upgrade(cx).map(|worktree| { + worktree.read(cx).as_local().unwrap().metadata_proto() + }) + }) + .collect(); + this.client.request(proto::UpdateProject { + project_id, + worktrees, + }) + }) + .await + .log_err(); + } + }), _detect_unshare: cx.spawn_weak(move |this, mut cx| { async move { let is_connected = status.next().await.map_or(false, |s| s.is_connected()); @@ -1632,10 +1596,6 @@ impl Project { operations: vec![language::proto::serialize_operation(operation)], }); cx.background().spawn(request).detach_and_log_err(cx); - } else if let Some(project_id) = self.remote_id() { - let _ = self - .client - .send(proto::RegisterProjectActivity { project_id }); } } BufferEvent::Edited { .. } => { @@ -4573,9 +4533,9 @@ impl Project { }) } - async fn handle_update_project( + async fn handle_project_updated( this: ModelHandle, - envelope: TypedEnvelope, + envelope: TypedEnvelope, client: Arc, mut cx: AsyncAppContext, ) -> Result<()> { @@ -5832,48 +5792,6 @@ impl Project { } } -impl ProjectStore { - pub fn new() -> Self { - Self { - projects: Default::default(), - } - } - - pub fn projects<'a>( - &'a self, - cx: &'a AppContext, - ) -> impl 'a + Iterator> { - self.projects - .iter() - .filter_map(|project| project.upgrade(cx)) - } - - fn add_project(&mut self, project: WeakModelHandle, cx: &mut ModelContext) { - if let Err(ix) = self - .projects - .binary_search_by_key(&project.id(), WeakModelHandle::id) - { - self.projects.insert(ix, project); - } - cx.notify(); - } - - fn prune_projects(&mut self, cx: &mut ModelContext) { - let mut did_change = false; - self.projects.retain(|project| { - if project.is_upgradable(cx) { - true - } else { - did_change = true; - false - } - }); - if did_change { - cx.notify(); - } - } -} - impl WorktreeHandle { pub fn upgrade(&self, cx: &AppContext) -> Option> { match self { @@ -5952,16 +5870,10 @@ impl<'a> Iterator for PathMatchCandidateSetIter<'a> { } } -impl Entity for ProjectStore { - type Event = (); -} - impl Entity for Project { type Event = Event; - fn release(&mut self, cx: &mut gpui::MutableAppContext) { - self.project_store.update(cx, ProjectStore::prune_projects); - + fn release(&mut self, _: &mut gpui::MutableAppContext) { match &self.client_state { Some(ProjectClientState::Local { remote_id, .. }) => { self.client diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index db8fb8e3ff8e4cea90a33c805311d4032302a890..9e4ec3ffb9a236e8b9b13c871269833e225fa1b3 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1051,25 +1051,6 @@ impl LocalWorktree { pub fn is_shared(&self) -> bool { self.share.is_some() } - - pub fn send_extension_counts(&self, project_id: u64) { - let mut extensions = Vec::new(); - let mut counts = Vec::new(); - - for (extension, count) in self.extension_counts() { - extensions.push(extension.to_string_lossy().to_string()); - counts.push(*count as u32); - } - - self.client - .send(proto::UpdateWorktreeExtensions { - project_id, - worktree_id: self.id().to_proto(), - extensions, - counts, - }) - .log_err(); - } } impl RemoteWorktree { diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index a93c0b593fad871a7cf1b22ad16918e439765e14..94880ce9f56e80e5b677eafa65878295a5b424e7 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -48,9 +48,8 @@ message Envelope { OpenBufferForSymbolResponse open_buffer_for_symbol_response = 40; UpdateProject update_project = 41; - RegisterProjectActivity register_project_activity = 42; + ProjectUpdated project_updated = 42; UpdateWorktree update_worktree = 43; - UpdateWorktreeExtensions update_worktree_extensions = 44; CreateProjectEntry create_project_entry = 45; RenameProjectEntry rename_project_entry = 46; @@ -258,8 +257,10 @@ message UpdateProject { repeated WorktreeMetadata worktrees = 2; } -message RegisterProjectActivity { +message ProjectUpdated { uint64 project_id = 1; + repeated WorktreeMetadata worktrees = 2; + uint64 room_version = 3; } message JoinProject { diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 11bbaaf5ffcbdeff96906033b1cacae6f62e48f0..31f53564a8b9d99c7bba00de3de969f95cfc1498 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -140,12 +140,12 @@ messages!( (OpenBufferResponse, Background), (PerformRename, Background), (PerformRenameResponse, Background), + (Ping, Foreground), (PrepareRename, Background), (PrepareRenameResponse, Background), (ProjectEntryResponse, Foreground), + (ProjectUpdated, Foreground), (RemoveContact, Foreground), - (Ping, Foreground), - (RegisterProjectActivity, Foreground), (ReloadBuffers, Foreground), (ReloadBuffersResponse, Foreground), (RemoveProjectCollaborator, Foreground), @@ -175,7 +175,6 @@ messages!( (UpdateParticipantLocation, Foreground), (UpdateProject, Foreground), (UpdateWorktree, Foreground), - (UpdateWorktreeExtensions, Background), (UpdateDiffBase, Background), (GetPrivateUserInfo, Foreground), (GetPrivateUserInfoResponse, Foreground), @@ -231,6 +230,7 @@ request_messages!( (Test, Test), (UpdateBuffer, Ack), (UpdateParticipantLocation, Ack), + (UpdateProject, Ack), (UpdateWorktree, Ack), ); @@ -261,8 +261,8 @@ entity_messages!( OpenBufferByPath, OpenBufferForSymbol, PerformRename, + ProjectUpdated, PrepareRename, - RegisterProjectActivity, ReloadBuffers, RemoveProjectCollaborator, RenameProjectEntry, @@ -278,7 +278,6 @@ entity_messages!( UpdateLanguageServer, UpdateProject, UpdateWorktree, - UpdateWorktreeExtensions, UpdateDiffBase ); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 2dbf923484e64fca19cdc885d368737377a543d3..9db524ee9ba2b935d817ce64081d8ee374bb363a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -33,7 +33,7 @@ use log::{error, warn}; pub use pane::*; pub use pane_group::*; use postage::prelude::Stream; -use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; +use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; use searchable::SearchableItemHandle; use serde::Deserialize; use settings::{Autosave, DockAnchor, Settings}; @@ -337,7 +337,6 @@ pub struct AppState { pub themes: Arc, pub client: Arc, pub user_store: ModelHandle, - pub project_store: ModelHandle, pub fs: Arc, pub build_window_options: fn() -> WindowOptions<'static>, pub initialize_workspace: fn(&mut Workspace, &Arc, &mut ViewContext), @@ -1039,7 +1038,6 @@ impl AppState { let languages = Arc::new(LanguageRegistry::test()); let http_client = client::test::FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone(), cx); - let project_store = cx.add_model(|_| ProjectStore::new()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); let themes = ThemeRegistry::new((), cx.font_cache().clone()); Arc::new(Self { @@ -1048,7 +1046,6 @@ impl AppState { fs, languages, user_store, - project_store, initialize_workspace: |_, _, _| {}, build_window_options: Default::default, default_item_factory: |_, _| unimplemented!(), @@ -1301,7 +1298,6 @@ impl Workspace { Project::local( app_state.client.clone(), app_state.user_store.clone(), - app_state.project_store.clone(), app_state.languages.clone(), app_state.fs.clone(), cx, @@ -2965,7 +2961,6 @@ pub fn open_paths( let project = Project::local( app_state.client.clone(), app_state.user_store.clone(), - app_state.project_store.clone(), app_state.languages.clone(), app_state.fs.clone(), cx, @@ -2997,7 +2992,6 @@ fn open_new(app_state: &Arc, cx: &mut MutableAppContext) { Project::local( app_state.client.clone(), app_state.user_store.clone(), - app_state.project_store.clone(), app_state.languages.clone(), app_state.fs.clone(), cx, diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index e849632a2df38945fcf34bf8b5967491f19df9e9..5a7ee2dbaee735ebf1132242aba7ef7fa674424f 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -23,7 +23,7 @@ use isahc::{config::Configurable, Request}; use language::LanguageRegistry; use log::LevelFilter; use parking_lot::Mutex; -use project::{Fs, HomeDir, ProjectStore}; +use project::{Fs, HomeDir}; use serde_json::json; use settings::{ self, settings_file::SettingsFile, KeymapFileContent, Settings, SettingsFileContent, @@ -146,7 +146,6 @@ fn main() { }) .detach(); - let project_store = cx.add_model(|_| ProjectStore::new()); let db = cx.background().block(db); client.start_telemetry(db.clone()); client.report_event("start app", Default::default()); @@ -156,7 +155,6 @@ fn main() { themes, client: client.clone(), user_store, - project_store, fs, build_window_options, initialize_workspace,