diff --git a/Cargo.lock b/Cargo.lock index 5b49bcd0d8..53c931e49f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10761,6 +10761,7 @@ dependencies = [ "async-tar", "async-trait", "backtrace", + "call2", "chrono", "cli", "client2", @@ -10790,6 +10791,7 @@ dependencies = [ "num_cpus", "parking_lot 0.11.2", "postage", + "project2", "rand 0.8.5", "regex", "rpc", diff --git a/crates/call2/src/call2.rs b/crates/call2/src/call2.rs index b1e6802089..1a514164ba 100644 --- a/crates/call2/src/call2.rs +++ b/crates/call2/src/call2.rs @@ -17,13 +17,14 @@ use gpui2::{ }; use postage::watch; use project2::Project; +use settings2::Settings; use std::sync::Arc; pub use participant::ParticipantLocation; pub use room::Room; pub fn init(client: Arc, user_store: Handle, cx: &mut AppContext) { - settings2::register::(cx); + CallSettings::register(cx); let active_call = cx.entity(|cx| ActiveCall::new(client, user_store, cx)); cx.set_global(active_call); @@ -105,7 +106,7 @@ impl ActiveCall { }; this.update(&mut cx, |this, _| { *this.incoming_call.0.borrow_mut() = Some(call); - }); + })?; Ok(proto::Ack {}) } @@ -124,7 +125,7 @@ impl ActiveCall { { incoming_call.take(); } - }); + })?; Ok(()) } @@ -150,7 +151,7 @@ impl ActiveCall { }; let invite = if let Some(room) = room { - cx.spawn(|_, mut cx| async move { + cx.spawn(move |_, mut cx| async move { let room = room.await.map_err(|err| anyhow!("{:?}", err))?; let initial_project_id = if let Some(initial_project) = initial_project { @@ -173,7 +174,7 @@ impl ActiveCall { let client = self.client.clone(); let user_store = self.user_store.clone(); let room = cx - .spawn(|this, mut cx| async move { + .spawn(move |this, mut cx| async move { let create_room = async { let room = cx .update(|cx| { @@ -205,10 +206,10 @@ impl ActiveCall { }) }; - cx.spawn(|this, mut cx| async move { + cx.spawn(move |this, mut cx| async move { let result = invite.await; if result.is_ok() { - this.update(&mut cx, |this, cx| this.report_call_event("invite", cx)); + this.update(&mut cx, |this, cx| this.report_call_event("invite", cx))?; } else { // TODO: Resport collaboration error } @@ -216,7 +217,7 @@ impl ActiveCall { this.update(&mut cx, |this, cx| { this.pending_invites.remove(&called_user_id); cx.notify(); - }); + })?; result }) } @@ -267,7 +268,7 @@ impl ActiveCall { .await?; this.update(&mut cx, |this, cx| { this.report_call_event("accept incoming", cx) - }); + })?; Ok(()) }) } @@ -307,7 +308,7 @@ impl ActiveCall { .await?; this.update(&mut cx, |this, cx| { this.report_call_event("join channel", cx) - }); + })?; Ok(room) }) } @@ -434,7 +435,7 @@ pub fn report_call_event_for_room( cx: &AppContext, ) { let telemetry = client.telemetry(); - let telemetry_settings = *settings2::get::(cx); + let telemetry_settings = *TelemetrySettings::get_global(cx); let event = ClickhouseEvent::Call { operation, room_id: Some(room_id), @@ -452,7 +453,8 @@ pub fn report_call_event_for_channel( let room = ActiveCall::global(cx).read(cx).room(); let telemetry = client.telemetry(); - let telemetry_settings = *settings2::get::(cx); + + let telemetry_settings = *TelemetrySettings::get_global(cx); let event = ClickhouseEvent::Call { operation, diff --git a/crates/call2/src/call_settings.rs b/crates/call2/src/call_settings.rs index 4cec8c50a0..c83ed73980 100644 --- a/crates/call2/src/call_settings.rs +++ b/crates/call2/src/call_settings.rs @@ -1,6 +1,8 @@ +use anyhow::Result; +use gpui2::AppContext; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings2::Setting; +use settings2::Settings; #[derive(Deserialize, Debug)] pub struct CallSettings { @@ -12,7 +14,7 @@ pub struct CallSettingsContent { pub mute_on_join: Option, } -impl Setting for CallSettings { +impl Settings for CallSettings { const KEY: Option<&'static str> = Some("calls"); type FileContent = CallSettingsContent; @@ -20,8 +22,11 @@ impl Setting for CallSettings { fn load( default_value: &Self::FileContent, user_values: &[&Self::FileContent], - _: &gpui2::AppContext, - ) -> anyhow::Result { + _cx: &mut AppContext, + ) -> Result + where + Self: Sized, + { Self::load_via_json_merge(default_value, user_values) } } diff --git a/crates/call2/src/room.rs b/crates/call2/src/room.rs index 6dea8adf7f..091ff17029 100644 --- a/crates/call2/src/room.rs +++ b/crates/call2/src/room.rs @@ -22,6 +22,7 @@ use live_kit_client::{ }; use postage::{sink::Sink, stream::Stream, watch}; use project2::Project; +use settings2::Settings; use std::{future::Future, mem, sync::Arc, time::Duration}; use util::{post_inc, ResultExt, TryFutureExt}; @@ -72,7 +73,7 @@ pub struct Room { user_store: Handle, follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec>, client_subscriptions: Vec, - subscriptions: Vec, + _subscriptions: Vec, room_update_completed_tx: watch::Sender>, room_update_completed_rx: watch::Receiver>, pending_room_update: Option>, @@ -193,8 +194,10 @@ impl Room { None }; - let maintain_connection = - cx.spawn(|this, cx| Self::maintain_connection(this, client.clone(), cx).log_err()); + let maintain_connection = cx.spawn({ + let client = client.clone(); + move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err() + }); Audio::play_sound(Sound::Joined, cx); @@ -215,7 +218,7 @@ impl Room { client_subscriptions: vec![ client.add_message_handler(cx.weak_handle(), Self::handle_room_updated) ], - subscriptions: vec![ + _subscriptions: vec![ cx.on_release(Self::released), cx.on_app_quit(Self::app_will_quit), ], @@ -237,7 +240,7 @@ impl Room { user_store: Handle, cx: &mut AppContext, ) -> Task>> { - cx.spawn(|mut cx| async move { + cx.spawn(move |mut cx| async move { let response = client.request(proto::CreateRoom {}).await?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room = cx.entity(|cx| { @@ -281,7 +284,7 @@ impl Room { user_store: Handle, cx: &mut AppContext, ) -> Task>> { - cx.spawn(|cx| async move { + cx.spawn(move |cx| async move { Self::from_join_response( client.request(proto::JoinChannel { channel_id }).await?, client, @@ -298,7 +301,7 @@ impl Room { cx: &mut AppContext, ) -> Task>> { let id = call.room_id; - cx.spawn(|cx| async move { + cx.spawn(move |cx| async move { Self::from_join_response( client.request(proto::JoinRoom { id }).await?, client, @@ -332,7 +335,7 @@ impl Room { } pub fn mute_on_join(cx: &AppContext) -> bool { - settings2::get::(cx).mute_on_join || client2::IMPERSONATE_LOGIN.is_some() + CallSettings::get_global(cx).mute_on_join || client2::IMPERSONATE_LOGIN.is_some() } fn from_join_response( @@ -356,7 +359,7 @@ impl Room { room.leave_when_empty = room.channel_id.is_none(); room.apply_room_update(room_proto, cx)?; anyhow::Ok(()) - })?; + })??; Ok(room) } @@ -1067,7 +1070,7 @@ impl Room { let client = self.client.clone(); let room_id = self.id; self.pending_call_count += 1; - cx.spawn(|this, mut cx| async move { + cx.spawn(move |this, mut cx| async move { let result = client .request(proto::Call { room_id, @@ -1096,7 +1099,7 @@ impl Room { let client = self.client.clone(); let user_store = self.user_store.clone(); cx.emit(Event::RemoteProjectJoined { project_id: id }); - cx.spawn(|this, mut cx| async move { + cx.spawn(move |this, mut cx| async move { let project = Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?; @@ -1132,7 +1135,7 @@ impl Room { project.update(&mut cx, |project, cx| { project.shared(response.project_id, cx) - })?; + })??; // If the user's location is in this project, it changes from UnsharedProject to SharedProject. this.update(&mut cx, |this, cx| { @@ -1192,7 +1195,7 @@ impl Room { }; cx.notify(); - cx.executor().spawn_on_main(|| async move { + cx.executor().spawn_on_main(move || async move { client .request(proto::UpdateParticipantLocation { room_id, @@ -1258,7 +1261,7 @@ impl Room { return Task::ready(Err(anyhow!("live-kit was not initialized"))); }; - cx.spawn(|this, mut cx| async move { + cx.spawn(move |this, mut cx| async move { let publish_track = async { let track = LocalAudioTrack::create(); this.upgrade() @@ -1340,7 +1343,7 @@ impl Room { return Task::ready(Err(anyhow!("live-kit was not initialized"))); }; - cx.spawn(|this, mut cx| async move { + cx.spawn(move |this, mut cx| async move { let publish_track = async { let displays = displays.await?; let display = displays @@ -1449,9 +1452,10 @@ impl Room { .room .remote_audio_track_publications(&participant.user.id.to_string()) { + let deafened = live_kit.deafened; tasks.push( cx.executor() - .spawn_on_main(|| track.set_enabled(!live_kit.deafened)), + .spawn_on_main(move || track.set_enabled(!deafened)), ); } } diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index d8d0277440..de9838c23d 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -150,6 +150,10 @@ pub struct Room { _delegate: RoomDelegate, } +// SAFETY: LiveKit objects are thread-safe: https://github.com/livekit/client-sdk-swift#thread-safety +unsafe impl Send for Room {} +unsafe impl Sync for Room {} + impl Room { pub fn new() -> Arc { Arc::new_cyclic(|weak_room| { diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index 93081c5e31..a49a17a3b6 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -58,7 +58,7 @@ use project_settings::{LspSettings, ProjectSettings}; use rand::prelude::*; use search::SearchQuery; use serde::Serialize; -use settings2::{SettingsStore, Settings}; +use settings2::{Settings, SettingsStore}; use sha2::{Digest, Sha256}; use similar::{ChangeTag, TextDiff}; use smol::channel::{Receiver, Sender}; diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 06804253b8..3ebdd6936a 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -19,7 +19,7 @@ path = "src/main.rs" # activity_indicator = { path = "../activity_indicator" } # auto_update = { path = "../auto_update" } # breadcrumbs = { path = "../breadcrumbs" } -# call = { path = "../call" } +call2 = { path = "../call2" } # channel = { path = "../channel" } cli = { path = "../cli" } # collab_ui = { path = "../collab_ui" } @@ -52,7 +52,7 @@ node_runtime = { path = "../node_runtime" } # assistant = { path = "../assistant" } # outline = { path = "../outline" } # plugin_runtime = { path = "../plugin_runtime",optional = true } -# project = { path = "../project" } +project2 = { path = "../project2" } # project_panel = { path = "../project_panel" } # project_symbols = { path = "../project_symbols" } # quick_action_bar = { path = "../quick_action_bar" } @@ -140,14 +140,14 @@ urlencoding = "2.1.2" uuid.workspace = true [dev-dependencies] -# call = { path = "../call", features = ["test-support"] } +call2 = { path = "../call2", features = ["test-support"] } # client = { path = "../client", features = ["test-support"] } # editor = { path = "../editor", features = ["test-support"] } # gpui = { path = "../gpui", features = ["test-support"] } gpui2 = { path = "../gpui2", features = ["test-support"] } -# language = { path = "../language", features = ["test-support"] } +language2 = { path = "../language2", features = ["test-support"] } # lsp = { path = "../lsp", features = ["test-support"] } -# project = { path = "../project", features = ["test-support"] } +project2 = { path = "../project2", features = ["test-support"] } # rpc = { path = "../rpc", features = ["test-support"] } # settings = { path = "../settings", features = ["test-support"] } # text = { path = "../text", features = ["test-support"] } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index d7c01ed1dd..0df2fbd61a 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -2,16 +2,17 @@ #![allow(non_snake_case)] use crate::open_listener::{OpenListener, OpenRequest}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Context as _, Result}; use backtrace::Backtrace; use cli::{ ipc::{self, IpcSender}, CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME, }; +use client2::UserStore; use db2::kvp::KEY_VALUE_STORE; use fs::RealFs; use futures::{channel::mpsc, SinkExt, StreamExt}; -use gpui2::{App, AppContext, AsyncAppContext, SemanticVersion, Task}; +use gpui2::{App, AppContext, AsyncAppContext, Context, SemanticVersion, Task}; use isahc::{prelude::Configurable, Request}; use language2::LanguageRegistry; use log::LevelFilter; @@ -119,8 +120,8 @@ fn main() { let languages = Arc::new(languages); let node_runtime = RealNodeRuntime::new(http.clone()); - languages2::init(languages.clone(), node_runtime.clone(), cx); - // let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); + language2::init(cx); + let user_store = cx.entity(|cx| UserStore::new(client.clone(), http.clone(), cx)); // let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx)); cx.set_global(client.clone()); @@ -167,7 +168,7 @@ fn main() { // client.telemetry().start(installation_id, session_id, cx); // todo!("app_state") - let app_state = Arc::new(AppState); + let app_state = Arc::new(AppState { client, user_store }); // let app_state = Arc::new(AppState { // languages, // client: client.clone(), diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index 21afc5bfdf..d78908dfb5 100644 --- a/crates/zed2/src/zed2.rs +++ b/crates/zed2/src/zed2.rs @@ -3,7 +3,8 @@ mod only_instance; mod open_listener; pub use assets::*; -use gpui2::AsyncAppContext; +use client2::{Client, UserStore}; +use gpui2::{AsyncAppContext, Handle}; pub use only_instance::*; pub use open_listener::*; @@ -44,7 +45,10 @@ pub fn connect_to_cli( Ok((async_request_rx, response_tx)) } -pub struct AppState; +pub struct AppState { + pub client: Arc, + pub user_store: Handle, +} pub async fn handle_cli_connection( (mut requests, _responses): (mpsc::Receiver, IpcSender),