From a7a4e2e3699659af9e8bbfd232faa3608aae97ea Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 21 Aug 2023 16:30:57 -0700 Subject: [PATCH] Add buffer integration test Rearrange channel crate structure Get channel buffer from database co-authored-by: Max --- Cargo.lock | 41 +++++++++ Cargo.toml | 1 + crates/call/Cargo.toml | 1 + crates/call/src/call.rs | 5 +- crates/channel/Cargo.toml | 51 +++++++++++ crates/channel/src/channel.rs | 7 ++ crates/channel/src/channel_buffer.rs | 80 ++++++++++++++++++ .../{client => channel}/src/channel_store.rs | 6 +- .../src/channel_store_tests.rs | 3 + crates/client/Cargo.toml | 1 + crates/client/src/client.rs | 5 -- crates/client/src/user.rs | 4 +- crates/collab/Cargo.toml | 1 + crates/collab/src/db.rs | 5 +- crates/collab/src/db/queries.rs | 3 - crates/collab/src/db/queries/channels.rs | 28 +++++++ crates/collab/src/db/tables/channel.rs | 3 +- crates/collab/src/db/{test_db.rs => tests.rs} | 10 ++- .../src/db/{queries => tests}/buffer_tests.rs | 0 crates/collab/src/db/{ => tests}/db_tests.rs | 31 ++++++- crates/collab/src/rpc.rs | 25 +++++- crates/collab/src/tests.rs | 7 +- .../collab/src/tests/channel_buffer_tests.rs | 84 +++++++++++++++++++ crates/collab/src/tests/channel_tests.rs | 3 +- crates/collab_ui/Cargo.toml | 1 + crates/collab_ui/src/collab_panel.rs | 6 +- .../src/collab_panel/channel_modal.rs | 3 +- crates/rpc/proto/zed.proto | 12 +++ crates/rpc/src/proto.rs | 5 +- crates/workspace/Cargo.toml | 1 + crates/workspace/src/workspace.rs | 3 +- crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 5 +- 33 files changed, 403 insertions(+), 39 deletions(-) create mode 100644 crates/channel/Cargo.toml create mode 100644 crates/channel/src/channel.rs create mode 100644 crates/channel/src/channel_buffer.rs rename crates/{client => channel}/src/channel_store.rs (99%) rename crates/{client => channel}/src/channel_store_tests.rs (98%) rename crates/collab/src/db/{test_db.rs => tests.rs} (95%) rename crates/collab/src/db/{queries => tests}/buffer_tests.rs (100%) rename crates/collab/src/db/{ => tests}/db_tests.rs (98%) create mode 100644 crates/collab/src/tests/channel_buffer_tests.rs diff --git a/Cargo.lock b/Cargo.lock index b10d8730fb..a40aa7d89c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1063,6 +1063,7 @@ dependencies = [ "anyhow", "async-broadcast", "audio", + "channel", "client", "collections", "fs", @@ -1190,6 +1191,41 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "channel" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "collections", + "db", + "futures 0.3.28", + "gpui", + "image", + "language", + "lazy_static", + "log", + "parking_lot 0.11.2", + "postage", + "rand 0.8.5", + "rpc", + "schemars", + "serde", + "serde_derive", + "settings", + "smol", + "staff_mode", + "sum_tree", + "tempfile", + "text", + "thiserror", + "time 0.3.24", + "tiny_http", + "url", + "util", + "uuid 1.4.1", +] + [[package]] name = "chrono" version = "0.4.26" @@ -1354,6 +1390,7 @@ dependencies = [ "staff_mode", "sum_tree", "tempfile", + "text", "thiserror", "time 0.3.24", "tiny_http", @@ -1418,6 +1455,7 @@ dependencies = [ "axum-extra", "base64 0.13.1", "call", + "channel", "clap 3.2.25", "client", "collections", @@ -1480,6 +1518,7 @@ dependencies = [ "anyhow", "auto_update", "call", + "channel", "client", "clock", "collections", @@ -9536,6 +9575,7 @@ dependencies = [ "async-recursion 1.0.4", "bincode", "call", + "channel", "client", "collections", "context_menu", @@ -9661,6 +9701,7 @@ dependencies = [ "backtrace", "breadcrumbs", "call", + "channel", "chrono", "cli", "client", diff --git a/Cargo.toml b/Cargo.toml index a35b3eea23..0fb8f0b6b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "crates/auto_update", "crates/breadcrumbs", "crates/call", + "crates/channel", "crates/cli", "crates/client", "crates/clock", diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml index eb448d8d8d..b4e94fe56c 100644 --- a/crates/call/Cargo.toml +++ b/crates/call/Cargo.toml @@ -20,6 +20,7 @@ test-support = [ [dependencies] audio = { path = "../audio" } +channel = { path = "../channel" } client = { path = "../client" } collections = { path = "../collections" } gpui = { path = "../gpui" } diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 5fef53fa81..5af094df05 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -7,9 +7,8 @@ use std::sync::Arc; use anyhow::{anyhow, Result}; use audio::Audio; use call_settings::CallSettings; -use client::{ - proto, ChannelId, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore, -}; +use channel::ChannelId; +use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore}; use collections::HashSet; use futures::{future::Shared, FutureExt}; use postage::watch; diff --git a/crates/channel/Cargo.toml b/crates/channel/Cargo.toml new file mode 100644 index 0000000000..0978462a1a --- /dev/null +++ b/crates/channel/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "channel" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/channel.rs" +doctest = false + +[features] +test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"] + +[dependencies] +client = { path = "../client" } +collections = { path = "../collections" } +db = { path = "../db" } +gpui = { path = "../gpui" } +util = { path = "../util" } +rpc = { path = "../rpc" } +text = { path = "../text" } +language = { path = "../language" } +settings = { path = "../settings" } +staff_mode = { path = "../staff_mode" } +sum_tree = { path = "../sum_tree" } + +anyhow.workspace = true +futures.workspace = true +image = "0.23" +lazy_static.workspace = true +log.workspace = true +parking_lot.workspace = true +postage.workspace = true +rand.workspace = true +schemars.workspace = true +smol.workspace = true +thiserror.workspace = true +time.workspace = true +tiny_http = "0.8" +uuid = { version = "1.1.2", features = ["v4"] } +url = "2.2" +serde.workspace = true +serde_derive.workspace = true +tempfile = "3" + +[dev-dependencies] +collections = { path = "../collections", features = ["test-support"] } +gpui = { path = "../gpui", features = ["test-support"] } +rpc = { path = "../rpc", features = ["test-support"] } +settings = { path = "../settings", features = ["test-support"] } +util = { path = "../util", features = ["test-support"] } diff --git a/crates/channel/src/channel.rs b/crates/channel/src/channel.rs new file mode 100644 index 0000000000..67c560a1fc --- /dev/null +++ b/crates/channel/src/channel.rs @@ -0,0 +1,7 @@ +mod channel_store; + +pub mod channel_buffer; +pub use channel_store::*; + +#[cfg(test)] +mod channel_store_tests; diff --git a/crates/channel/src/channel_buffer.rs b/crates/channel/src/channel_buffer.rs new file mode 100644 index 0000000000..10f59bce46 --- /dev/null +++ b/crates/channel/src/channel_buffer.rs @@ -0,0 +1,80 @@ +use crate::ChannelId; +use anyhow::Result; +use client::Client; +use gpui::{Entity, ModelContext, ModelHandle, Task}; +use rpc::proto::GetChannelBuffer; +use std::sync::Arc; + +// Open the channel document +// ChannelDocumentView { ChannelDocument, Editor } -> On clone, clones internal ChannelDocument handle, instantiates new editor +// Produces a view which is: (ChannelDocument, Editor), ChannelDocument manages subscriptions +// ChannelDocuments -> Buffers -> Editor with that buffer + +// ChannelDocuments { +// ChannleBuffers: HashMap> +// } + +pub struct ChannelBuffer { + channel_id: ChannelId, + buffer: Option>, + client: Arc, +} + +impl Entity for ChannelBuffer { + type Event = (); +} + +impl ChannelBuffer { + pub fn for_channel( + channel_id: ChannelId, + client: Arc, + cx: &mut ModelContext, + ) -> Self { + Self { + channel_id, + client, + buffer: None, + } + } + + fn on_buffer_update( + &mut self, + buffer: ModelHandle, + event: &language::Event, + cx: &mut ModelContext, + ) { + // + } + + pub fn buffer( + &mut self, + cx: &mut ModelContext, + ) -> Task>> { + if let Some(buffer) = &self.buffer { + Task::ready(Ok(buffer.clone())) + } else { + let channel_id = self.channel_id; + let client = self.client.clone(); + cx.spawn(|this, mut cx| async move { + let response = client.request(GetChannelBuffer { channel_id }).await?; + + let base_text = response.base_text; + let operations = response + .operations + .into_iter() + .map(language::proto::deserialize_operation) + .collect::, _>>()?; + + this.update(&mut cx, |this, cx| { + let buffer = cx.add_model(|cx| language::Buffer::new(0, base_text, cx)); + buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?; + + cx.subscribe(&buffer, Self::on_buffer_update).detach(); + + this.buffer = Some(buffer.clone()); + anyhow::Ok(buffer) + }) + }) + } + } +} diff --git a/crates/client/src/channel_store.rs b/crates/channel/src/channel_store.rs similarity index 99% rename from crates/client/src/channel_store.rs rename to crates/channel/src/channel_store.rs index 6352ac791e..b9b2c98acd 100644 --- a/crates/client/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -1,7 +1,8 @@ -use crate::Status; -use crate::{Client, Subscription, User, UserStore}; use anyhow::anyhow; use anyhow::Result; +use client::Status; +use client::UserId; +use client::{Client, Subscription, User, UserStore}; use collections::HashMap; use collections::HashSet; use futures::channel::mpsc; @@ -13,7 +14,6 @@ use std::sync::Arc; use util::ResultExt; pub type ChannelId = u64; -pub type UserId = u64; pub struct ChannelStore { channels_by_id: HashMap>, diff --git a/crates/client/src/channel_store_tests.rs b/crates/channel/src/channel_store_tests.rs similarity index 98% rename from crates/client/src/channel_store_tests.rs rename to crates/channel/src/channel_store_tests.rs index 51e819349e..18894b1f47 100644 --- a/crates/client/src/channel_store_tests.rs +++ b/crates/channel/src/channel_store_tests.rs @@ -1,4 +1,7 @@ use super::*; +use client::{Client, UserStore}; +use gpui::{AppContext, ModelHandle}; +use rpc::proto; use util::http::FakeHttpClient; #[gpui::test] diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 3ecc515986..64d8f02c8a 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -17,6 +17,7 @@ db = { path = "../db" } gpui = { path = "../gpui" } util = { path = "../util" } rpc = { path = "../rpc" } +text = { path = "../text" } settings = { path = "../settings" } staff_mode = { path = "../staff_mode" } sum_tree = { path = "../sum_tree" } diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 8ef3e32ea8..a32c415f7e 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -1,10 +1,6 @@ #[cfg(any(test, feature = "test-support"))] pub mod test; -#[cfg(test)] -mod channel_store_tests; - -pub mod channel_store; pub mod telemetry; pub mod user; @@ -48,7 +44,6 @@ use util::channel::ReleaseChannel; use util::http::HttpClient; use util::{ResultExt, TryFutureExt}; -pub use channel_store::*; pub use rpc::*; pub use telemetry::ClickhouseEvent; pub use user::*; diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index be11d1fb44..1dc384da17 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -10,9 +10,11 @@ use std::sync::{Arc, Weak}; use util::http::HttpClient; use util::TryFutureExt as _; +pub type UserId = u64; + #[derive(Default, Debug)] pub struct User { - pub id: u64, + pub id: UserId, pub github_login: String, pub avatar: Option>, } diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 49d17bdc63..fc78a03f67 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -64,6 +64,7 @@ collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } +channel = { path = "../channel" } editor = { path = "../editor", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } fs = { path = "../fs", features = ["test-support"] } diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 19915777dc..9c759f79a8 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1,7 +1,8 @@ #[cfg(test)] -mod db_tests; +pub mod tests; + #[cfg(test)] -pub mod test_db; +pub use tests::TestDb; mod ids; mod queries; diff --git a/crates/collab/src/db/queries.rs b/crates/collab/src/db/queries.rs index c4a1d57eb4..09a8f073b4 100644 --- a/crates/collab/src/db/queries.rs +++ b/crates/collab/src/db/queries.rs @@ -9,6 +9,3 @@ pub mod rooms; pub mod servers; pub mod signups; pub mod users; - -#[cfg(test)] -pub mod buffer_tests; diff --git a/crates/collab/src/db/queries/channels.rs b/crates/collab/src/db/queries/channels.rs index e3d3643a61..85a9304a2e 100644 --- a/crates/collab/src/db/queries/channels.rs +++ b/crates/collab/src/db/queries/channels.rs @@ -689,6 +689,34 @@ impl Database { }) .await } + + pub async fn get_or_create_buffer_for_channel( + &self, + channel_id: ChannelId, + ) -> Result { + self.transaction(|tx| async move { + let tx = tx; + let channel = channel::Entity::find_by_id(channel_id) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("invalid channel"))?; + + if let Some(id) = channel.main_buffer_id { + return Ok(id); + } else { + let buffer = buffer::ActiveModel::new().insert(&*tx).await?; + channel::ActiveModel { + id: ActiveValue::Unchanged(channel_id), + main_buffer_id: ActiveValue::Set(Some(buffer.id)), + ..Default::default() + } + .update(&*tx) + .await?; + Ok(buffer.id) + } + }) + .await + } } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] diff --git a/crates/collab/src/db/tables/channel.rs b/crates/collab/src/db/tables/channel.rs index f00b4ced62..444d5fa6d9 100644 --- a/crates/collab/src/db/tables/channel.rs +++ b/crates/collab/src/db/tables/channel.rs @@ -1,4 +1,4 @@ -use crate::db::ChannelId; +use crate::db::{BufferId, ChannelId}; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] @@ -7,6 +7,7 @@ pub struct Model { #[sea_orm(primary_key)] pub id: ChannelId, pub name: String, + pub main_buffer_id: Option, } impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/db/test_db.rs b/crates/collab/src/db/tests.rs similarity index 95% rename from crates/collab/src/db/test_db.rs rename to crates/collab/src/db/tests.rs index 71e352eb86..36a0888a62 100644 --- a/crates/collab/src/db/test_db.rs +++ b/crates/collab/src/db/tests.rs @@ -1,3 +1,6 @@ +mod buffer_tests; +mod db_tests; + use super::*; use gpui::executor::Background; use parking_lot::Mutex; @@ -96,7 +99,7 @@ macro_rules! test_both_dbs { ($test_name:ident, $postgres_test_name:ident, $sqlite_test_name:ident) => { #[gpui::test] async fn $postgres_test_name() { - let test_db = crate::db::test_db::TestDb::postgres( + let test_db = crate::db::TestDb::postgres( gpui::executor::Deterministic::new(0).build_background(), ); $test_name(test_db.db()).await; @@ -104,9 +107,8 @@ macro_rules! test_both_dbs { #[gpui::test] async fn $sqlite_test_name() { - let test_db = crate::db::test_db::TestDb::sqlite( - gpui::executor::Deterministic::new(0).build_background(), - ); + let test_db = + crate::db::TestDb::sqlite(gpui::executor::Deterministic::new(0).build_background()); $test_name(test_db.db()).await; } }; diff --git a/crates/collab/src/db/queries/buffer_tests.rs b/crates/collab/src/db/tests/buffer_tests.rs similarity index 100% rename from crates/collab/src/db/queries/buffer_tests.rs rename to crates/collab/src/db/tests/buffer_tests.rs diff --git a/crates/collab/src/db/db_tests.rs b/crates/collab/src/db/tests/db_tests.rs similarity index 98% rename from crates/collab/src/db/db_tests.rs rename to crates/collab/src/db/tests/db_tests.rs index 8c5dab77bd..0fffabc7c4 100644 --- a/crates/collab/src/db/db_tests.rs +++ b/crates/collab/src/db/tests/db_tests.rs @@ -3,7 +3,7 @@ use crate::test_both_dbs; use gpui::executor::{Background, Deterministic}; use pretty_assertions::{assert_eq, assert_ne}; use std::sync::Arc; -use test_db::TestDb; +use tests::TestDb; test_both_dbs!( test_get_users, @@ -1329,6 +1329,35 @@ async fn test_channel_renames(db: &Arc) { assert!(bad_name_rename.is_err()) } +test_both_dbs!( + test_get_or_create_channel_buffer, + test_get_or_create_channel_buffer_postgres, + test_get_or_create_channel_buffer_sqlite +); + +async fn test_get_or_create_channel_buffer(db: &Arc) { + let a_id = db + .create_user( + "user1@example.com", + false, + NewUserParams { + github_login: "user1".into(), + github_user_id: 5, + invite_count: 0, + }, + ) + .await + .unwrap() + .user_id; + + let zed_id = db.create_root_channel("zed", "1", a_id).await.unwrap(); + + let first_buffer_id = db.get_or_create_buffer_for_channel(zed_id).await.unwrap(); + let second_buffer_id = db.get_or_create_buffer_for_channel(zed_id).await.unwrap(); + + assert_eq!(first_buffer_id, second_buffer_id); +} + #[gpui::test] async fn test_multiple_signup_overwrite() { let test_db = TestDb::postgres(build_background_executor()); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 521aa3e7b4..22eb23ce8e 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -35,8 +35,8 @@ use lazy_static::lazy_static; use prometheus::{register_int_gauge, IntGauge}; use rpc::{ proto::{ - self, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, LiveKitConnectionInfo, - RequestMessage, + self, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, GetChannelBufferResponse, + LiveKitConnectionInfo, RequestMessage, }, Connection, ConnectionId, Peer, Receipt, TypedEnvelope, }; @@ -248,6 +248,7 @@ impl Server { .add_request_handler(remove_channel_member) .add_request_handler(set_channel_member_admin) .add_request_handler(rename_channel) + .add_request_handler(get_channel_buffer) .add_request_handler(get_channel_members) .add_request_handler(respond_to_channel_invite) .add_request_handler(join_channel) @@ -2478,6 +2479,26 @@ async fn join_channel( Ok(()) } +async fn get_channel_buffer( + request: proto::GetChannelBuffer, + response: Response, + session: Session, +) -> Result<()> { + let db = session.db().await; + let channel_id = ChannelId::from_proto(request.channel_id); + + let buffer_id = db.get_or_create_buffer_for_channel(channel_id).await?; + + let buffer = db.get_buffer(buffer_id).await?; + + response.send(GetChannelBufferResponse { + base_text: buffer.base_text, + operations: buffer.operations, + })?; + + Ok(()) +} + async fn update_diff_base(request: proto::UpdateDiffBase, session: Session) -> Result<()> { let project_id = ProjectId::from_proto(request.project_id); let project_connection_ids = session diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index c9f358ca5b..831bccbb72 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -1,14 +1,14 @@ use crate::{ - db::{test_db::TestDb, NewUserParams, UserId}, + db::{tests::TestDb, NewUserParams, UserId}, executor::Executor, rpc::{Server, CLEANUP_TIMEOUT}, AppState, }; use anyhow::anyhow; use call::{ActiveCall, Room}; +use channel::ChannelStore; use client::{ - self, proto::PeerId, ChannelStore, Client, Connection, Credentials, EstablishConnectionError, - UserStore, + self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore, }; use collections::{HashMap, HashSet}; use fs::FakeFs; @@ -31,6 +31,7 @@ use std::{ use util::http::FakeHttpClient; use workspace::Workspace; +mod channel_buffer_tests; mod channel_tests; mod integration_tests; mod randomized_integration_tests; diff --git a/crates/collab/src/tests/channel_buffer_tests.rs b/crates/collab/src/tests/channel_buffer_tests.rs new file mode 100644 index 0000000000..e7f662523e --- /dev/null +++ b/crates/collab/src/tests/channel_buffer_tests.rs @@ -0,0 +1,84 @@ +use crate::tests::TestServer; + +use channel::channel_buffer::ChannelBuffer; +use gpui::{executor::Deterministic, ModelHandle, TestAppContext}; +use std::{ops::Range, sync::Arc}; + +#[gpui::test] +async fn test_channel_buffers( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(&deterministic).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + + let zed_id = server + .make_channel("zed", (&client_a, cx_a), &mut [(&client_b, cx_b)]) + .await; + + let a_document = + cx_a.add_model(|cx| ChannelBuffer::for_channel(zed_id, client_a.client().to_owned(), cx)); + let channel_buffer_a = a_document + .update(cx_a, |doc, cx| doc.buffer(cx)) + .await + .unwrap(); + + edit_channel_buffer(&channel_buffer_a, cx_a, [(0..0, "hello world")]); + edit_channel_buffer(&channel_buffer_a, cx_a, [(5..5, ", cruel")]); + edit_channel_buffer(&channel_buffer_a, cx_a, [(0..5, "goodbye")]); + undo_channel_buffer(&channel_buffer_a, cx_a); + + assert_eq!( + channel_buffer_text(&channel_buffer_a, cx_a), + "hello, cruel world" + ); + + let b_document = + cx_b.add_model(|cx| ChannelBuffer::for_channel(zed_id, client_b.client().to_owned(), cx)); + let channel_buffer_b = b_document + .update(cx_b, |doc, cx| doc.buffer(cx)) + .await + .unwrap(); + + assert_eq!( + channel_buffer_text(&channel_buffer_b, cx_b), + "hello, cruel world" + ); + + edit_channel_buffer(&channel_buffer_b, cx_b, [(7..12, "beautiful")]); + + deterministic.run_until_parked(); + + assert_eq!( + channel_buffer_text(&channel_buffer_a, cx_a), + "hello, beautiful world" + ); + assert_eq!( + channel_buffer_text(&channel_buffer_b, cx_b), + "hello, beautiful world" + ); +} + +fn edit_channel_buffer( + channel_buffer: &ModelHandle, + cx: &mut TestAppContext, + edits: I, +) where + I: IntoIterator, &'static str)>, +{ + channel_buffer.update(cx, |buffer, cx| buffer.edit(edits, None, cx)); +} + +fn undo_channel_buffer(channel_buffer: &ModelHandle, cx: &mut TestAppContext) { + channel_buffer.update(cx, |buffer, cx| buffer.undo(cx)); +} + +fn channel_buffer_text( + channel_buffer: &ModelHandle, + cx: &mut TestAppContext, +) -> String { + channel_buffer.read_with(cx, |buffer, _| buffer.text()) +} diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index 06cf3607c0..41d2286772 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -3,7 +3,8 @@ use crate::{ tests::{room_participants, RoomParticipants, TestServer}, }; use call::ActiveCall; -use client::{ChannelId, ChannelMembership, ChannelStore, User}; +use channel::{ChannelId, ChannelMembership, ChannelStore}; +use client::User; use gpui::{executor::Deterministic, ModelHandle, TestAppContext}; use rpc::{proto, RECEIVE_TIMEOUT}; use std::sync::Arc; diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 471608c43e..e0177f6609 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -26,6 +26,7 @@ auto_update = { path = "../auto_update" } db = { path = "../db" } call = { path = "../call" } client = { path = "../client" } +channel = { path = "../channel" } clock = { path = "../clock" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 5623ada42d..ab692dd166 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -4,10 +4,8 @@ mod panel_settings; use anyhow::Result; use call::ActiveCall; -use client::{ - proto::PeerId, Channel, ChannelEvent, ChannelId, ChannelStore, Client, Contact, User, UserStore, -}; - +use channel::{Channel, ChannelEvent, ChannelId, ChannelStore}; +use client::{proto::PeerId, Client, Contact, User, UserStore}; use context_menu::{ContextMenu, ContextMenuItem}; use db::kvp::KEY_VALUE_STORE; use editor::{Cancel, Editor}; diff --git a/crates/collab_ui/src/collab_panel/channel_modal.rs b/crates/collab_ui/src/collab_panel/channel_modal.rs index 75ab40be85..0adf2806d7 100644 --- a/crates/collab_ui/src/collab_panel/channel_modal.rs +++ b/crates/collab_ui/src/collab_panel/channel_modal.rs @@ -1,4 +1,5 @@ -use client::{proto, ChannelId, ChannelMembership, ChannelStore, User, UserId, UserStore}; +use channel::{ChannelId, ChannelMembership, ChannelStore}; +use client::{proto, User, UserId, UserStore}; use context_menu::{ContextMenu, ContextMenuItem}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index caa5efd2cb..baeaae1876 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -142,6 +142,9 @@ message Envelope { GetChannelMembersResponse get_channel_members_response = 128; SetChannelMemberAdmin set_channel_member_admin = 129; RenameChannel rename_channel = 130; + + GetChannelBuffer get_channel_buffer = 131; + GetChannelBufferResponse get_channel_buffer_response = 132; } } @@ -948,6 +951,15 @@ message RenameChannel { string name = 2; } +message GetChannelBuffer { + uint64 channel_id = 1; +} + +message GetChannelBufferResponse { + string base_text = 1; + repeated Operation operations = 2; +} + message RespondToChannelInvite { uint64 channel_id = 1; bool accept = 2; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 92732b00b5..21a491b934 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -248,7 +248,9 @@ messages!( (GetPrivateUserInfo, Foreground), (GetPrivateUserInfoResponse, Foreground), (GetChannelMembers, Foreground), - (GetChannelMembersResponse, Foreground) + (GetChannelMembersResponse, Foreground), + (GetChannelBuffer, Foreground), + (GetChannelBufferResponse, Foreground) ); request_messages!( @@ -315,6 +317,7 @@ request_messages!( (UpdateParticipantLocation, Ack), (UpdateProject, Ack), (UpdateWorktree, Ack), + (GetChannelBuffer, GetChannelBufferResponse) ); entity_messages!( diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 8606be4944..e2dae07b8c 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -22,6 +22,7 @@ test-support = [ db = { path = "../db" } call = { path = "../call" } client = { path = "../client" } +channel = { path = "../channel" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } drag_and_drop = { path = "../drag_and_drop" } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 79b701e015..a8354472aa 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -12,9 +12,10 @@ mod workspace_settings; use anyhow::{anyhow, Context, Result}; use call::ActiveCall; +use channel::ChannelStore; use client::{ proto::{self, PeerId}, - ChannelStore, Client, TypedEnvelope, UserStore, + Client, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; use drag_and_drop::DragAndDrop; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index faa3ae6953..92900f84cb 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -21,6 +21,7 @@ activity_indicator = { path = "../activity_indicator" } auto_update = { path = "../auto_update" } breadcrumbs = { path = "../breadcrumbs" } call = { path = "../call" } +channel = { path = "../channel" } cli = { path = "../cli" } collab_ui = { path = "../collab_ui" } collections = { path = "../collections" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index caeaeceded..b905c1d37b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -3,13 +3,12 @@ use anyhow::{anyhow, Context, Result}; use backtrace::Backtrace; +use channel::ChannelStore; use cli::{ ipc::{self, IpcSender}, CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME, }; -use client::{ - self, ChannelStore, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN, -}; +use client::{self, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN}; use db::kvp::KEY_VALUE_STORE; use editor::{scroll::autoscroll::Autoscroll, Editor}; use futures::{