Skip to content

Commit

Permalink
WIP: remoting (#10085)
Browse files Browse the repository at this point in the history
Release Notes:

- Added private alpha support for remote development. Please reach out to hi@zed.dev if you'd like to be part of shaping this feature.
  • Loading branch information
ConradIrwin committed Apr 11, 2024
1 parent ea44190 commit f6c85b2
Show file tree
Hide file tree
Showing 54 changed files with 4,108 additions and 750 deletions.
168 changes: 111 additions & 57 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ members = [
"crates/google_ai",
"crates/gpui",
"crates/gpui_macros",
"crates/headless",
"crates/image_viewer",
"crates/install_cli",
"crates/journal",
Expand Down Expand Up @@ -164,6 +165,7 @@ go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui" }
gpui_macros = { path = "crates/gpui_macros" }
headless = { path = "crates/headless" }
install_cli = { path = "crates/install_cli" }
image_viewer = { path = "crates/image_viewer" }
journal = { path = "crates/journal" }
Expand Down Expand Up @@ -242,6 +244,7 @@ chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.4", features = ["derive"] }
clickhouse = { version = "0.11.6" }
ctor = "0.2.6"
ctrlc = "3.4.4"
core-foundation = { version = "0.9.3" }
core-foundation-sys = "0.8.6"
derive_more = "0.99.17"
Expand Down
5 changes: 5 additions & 0 deletions assets/icons/server.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/trash.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion crates/call/src/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,7 @@ impl Room {
cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move {
let project =
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?;

this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| {
Expand Down
4 changes: 3 additions & 1 deletion crates/channel/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ pub use channel_chat::{
mentions_to_proto, ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId,
MessageParams,
};
pub use channel_store::{Channel, ChannelEvent, ChannelMembership, ChannelStore};
pub use channel_store::{
Channel, ChannelEvent, ChannelMembership, ChannelStore, DevServer, RemoteProject,
};

#[cfg(test)]
mod channel_store_tests;
Expand Down
205 changes: 201 additions & 4 deletions crates/channel/src/channel_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ mod channel_index;
use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};
use anyhow::{anyhow, Result};
use channel_index::ChannelIndex;
use client::{ChannelId, Client, ClientSettings, ProjectId, Subscription, User, UserId, UserStore};
use client::{
ChannelId, Client, ClientSettings, DevServerId, ProjectId, RemoteProjectId, Subscription, User,
UserId, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{
Expand All @@ -12,7 +15,7 @@ use gpui::{
};
use language::Capability;
use rpc::{
proto::{self, ChannelRole, ChannelVisibility},
proto::{self, ChannelRole, ChannelVisibility, DevServerStatus},
TypedEnvelope,
};
use settings::Settings;
Expand Down Expand Up @@ -40,7 +43,6 @@ pub struct HostedProject {
name: SharedString,
_visibility: proto::ChannelVisibility,
}

impl From<proto::HostedProject> for HostedProject {
fn from(project: proto::HostedProject) -> Self {
Self {
Expand All @@ -52,12 +54,56 @@ impl From<proto::HostedProject> for HostedProject {
}
}

#[derive(Debug, Clone)]
pub struct RemoteProject {
pub id: RemoteProjectId,
pub project_id: Option<ProjectId>,
pub channel_id: ChannelId,
pub name: SharedString,
pub path: SharedString,
pub dev_server_id: DevServerId,
}

impl From<proto::RemoteProject> for RemoteProject {
fn from(project: proto::RemoteProject) -> Self {
Self {
id: RemoteProjectId(project.id),
project_id: project.project_id.map(|id| ProjectId(id)),
channel_id: ChannelId(project.channel_id),
name: project.name.into(),
path: project.path.into(),
dev_server_id: DevServerId(project.dev_server_id),
}
}
}

#[derive(Debug, Clone)]
pub struct DevServer {
pub id: DevServerId,
pub channel_id: ChannelId,
pub name: SharedString,
pub status: DevServerStatus,
}

impl From<proto::DevServer> for DevServer {
fn from(dev_server: proto::DevServer) -> Self {
Self {
id: DevServerId(dev_server.dev_server_id),
channel_id: ChannelId(dev_server.channel_id),
status: dev_server.status(),
name: dev_server.name.into(),
}
}
}

pub struct ChannelStore {
pub channel_index: ChannelIndex,
channel_invitations: Vec<Arc<Channel>>,
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
channel_states: HashMap<ChannelId, ChannelState>,
hosted_projects: HashMap<ProjectId, HostedProject>,
remote_projects: HashMap<RemoteProjectId, RemoteProject>,
dev_servers: HashMap<DevServerId, DevServer>,

outgoing_invites: HashSet<(ChannelId, UserId)>,
update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,
Expand Down Expand Up @@ -87,6 +133,8 @@ pub struct ChannelState {
observed_chat_message: Option<u64>,
role: Option<ChannelRole>,
projects: HashSet<ProjectId>,
dev_servers: HashSet<DevServerId>,
remote_projects: HashSet<RemoteProjectId>,
}

impl Channel {
Expand Down Expand Up @@ -217,6 +265,8 @@ impl ChannelStore {
channel_index: ChannelIndex::default(),
channel_participants: Default::default(),
hosted_projects: Default::default(),
remote_projects: Default::default(),
dev_servers: Default::default(),
outgoing_invites: Default::default(),
opened_buffers: Default::default(),
opened_chats: Default::default(),
Expand Down Expand Up @@ -316,6 +366,40 @@ impl ChannelStore {
projects
}

pub fn dev_servers_for_id(&self, channel_id: ChannelId) -> Vec<DevServer> {
let mut dev_servers: Vec<DevServer> = self
.channel_states
.get(&channel_id)
.map(|state| state.dev_servers.clone())
.unwrap_or_default()
.into_iter()
.flat_map(|id| self.dev_servers.get(&id).cloned())
.collect();
dev_servers.sort_by_key(|s| (s.name.clone(), s.id));
dev_servers
}

pub fn find_dev_server_by_id(&self, id: DevServerId) -> Option<&DevServer> {
self.dev_servers.get(&id)
}

pub fn find_remote_project_by_id(&self, id: RemoteProjectId) -> Option<&RemoteProject> {
self.remote_projects.get(&id)
}

pub fn remote_projects_for_id(&self, channel_id: ChannelId) -> Vec<RemoteProject> {
let mut remote_projects: Vec<RemoteProject> = self
.channel_states
.get(&channel_id)
.map(|state| state.remote_projects.clone())
.unwrap_or_default()
.into_iter()
.flat_map(|id| self.remote_projects.get(&id).cloned())
.collect();
remote_projects.sort_by_key(|p| (p.name.clone(), p.id));
remote_projects
}

pub fn has_open_channel_buffer(&self, channel_id: ChannelId, _cx: &AppContext) -> bool {
if let Some(buffer) = self.opened_buffers.get(&channel_id) {
if let OpenedModelHandle::Open(buffer) = buffer {
Expand Down Expand Up @@ -818,6 +902,45 @@ impl ChannelStore {
})
}

pub fn create_remote_project(
&mut self,
channel_id: ChannelId,
dev_server_id: DevServerId,
name: String,
path: String,
cx: &mut ModelContext<Self>,
) -> Task<Result<proto::CreateRemoteProjectResponse>> {
let client = self.client.clone();
cx.background_executor().spawn(async move {
client
.request(proto::CreateRemoteProject {
channel_id: channel_id.0,
dev_server_id: dev_server_id.0,
name,
path,
})
.await
})
}

pub fn create_dev_server(
&mut self,
channel_id: ChannelId,
name: String,
cx: &mut ModelContext<Self>,
) -> Task<Result<proto::CreateDevServerResponse>> {
let client = self.client.clone();
cx.background_executor().spawn(async move {
let result = client
.request(proto::CreateDevServer {
channel_id: channel_id.0,
name,
})
.await?;
Ok(result)
})
}

pub fn get_channel_member_details(
&self,
channel_id: ChannelId,
Expand Down Expand Up @@ -1098,7 +1221,11 @@ impl ChannelStore {
|| !payload.latest_channel_message_ids.is_empty()
|| !payload.latest_channel_buffer_versions.is_empty()
|| !payload.hosted_projects.is_empty()
|| !payload.deleted_hosted_projects.is_empty();
|| !payload.deleted_hosted_projects.is_empty()
|| !payload.dev_servers.is_empty()
|| !payload.deleted_dev_servers.is_empty()
|| !payload.remote_projects.is_empty()
|| !payload.deleted_remote_projects.is_empty();

if channels_changed {
if !payload.delete_channels.is_empty() {
Expand Down Expand Up @@ -1186,6 +1313,60 @@ impl ChannelStore {
.remove_hosted_project(old_project.project_id);
}
}

for remote_project in payload.remote_projects {
let remote_project: RemoteProject = remote_project.into();
if let Some(old_remote_project) = self
.remote_projects
.insert(remote_project.id, remote_project.clone())
{
self.channel_states
.entry(old_remote_project.channel_id)
.or_default()
.remove_remote_project(old_remote_project.id);
}
self.channel_states
.entry(remote_project.channel_id)
.or_default()
.add_remote_project(remote_project.id);
}

for remote_project_id in payload.deleted_remote_projects {
let remote_project_id = RemoteProjectId(remote_project_id);

if let Some(old_project) = self.remote_projects.remove(&remote_project_id) {
self.channel_states
.entry(old_project.channel_id)
.or_default()
.remove_remote_project(old_project.id);
}
}

for dev_server in payload.dev_servers {
let dev_server: DevServer = dev_server.into();
if let Some(old_server) = self.dev_servers.insert(dev_server.id, dev_server.clone())
{
self.channel_states
.entry(old_server.channel_id)
.or_default()
.remove_dev_server(old_server.id);
}
self.channel_states
.entry(dev_server.channel_id)
.or_default()
.add_dev_server(dev_server.id);
}

for dev_server_id in payload.deleted_dev_servers {
let dev_server_id = DevServerId(dev_server_id);

if let Some(old_server) = self.dev_servers.remove(&dev_server_id) {
self.channel_states
.entry(old_server.channel_id)
.or_default()
.remove_dev_server(old_server.id);
}
}
}

cx.notify();
Expand Down Expand Up @@ -1300,4 +1481,20 @@ impl ChannelState {
fn remove_hosted_project(&mut self, project_id: ProjectId) {
self.projects.remove(&project_id);
}

fn add_remote_project(&mut self, remote_project_id: RemoteProjectId) {
self.remote_projects.insert(remote_project_id);
}

fn remove_remote_project(&mut self, remote_project_id: RemoteProjectId) {
self.remote_projects.remove(&remote_project_id);
}

fn add_dev_server(&mut self, dev_server_id: DevServerId) {
self.dev_servers.insert(dev_server_id);
}

fn remove_dev_server(&mut self, dev_server_id: DevServerId) {
self.dev_servers.remove(&dev_server_id);
}
}
3 changes: 2 additions & 1 deletion crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,9 @@ impl Client {
read_credentials_from_keychain(cx).await.is_some()
}

pub fn set_dev_server_token(&self, token: DevServerToken) {
pub fn set_dev_server_token(&self, token: DevServerToken) -> &Self {
self.state.write().credentials = Some(Credentials::DevServer { token });
self
}

#[async_recursion(?Send)]
Expand Down
6 changes: 6 additions & 0 deletions crates/client/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ impl std::fmt::Display for ChannelId {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct ProjectId(pub u64);

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct DevServerId(pub u64);

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct RemoteProjectId(pub u64);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantIndex(pub u32);

Expand Down
2 changes: 1 addition & 1 deletion crates/collab/.env.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
DATABASE_URL = "postgres://postgres@localhost/zed"
# DATABASE_URL = "sqlite:////home/zed/.config/zed/db.sqlite3?mode=rwc"
# DATABASE_URL = "sqlite:////root/0/zed/db.sqlite3?mode=rwc"
DATABASE_MAX_CONNECTIONS = 5
HTTP_PORT = 8080
API_TOKEN = "secret"
Expand Down
5 changes: 3 additions & 2 deletions crates/collab/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ tokio.workspace = true
toml.workspace = true
tower = "0.4"
tower-http = { workspace = true, features = ["trace"] }
tracing = "0.1.34"
tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json", "registry", "tracing-log"] }
tracing = "0.1.40"
tracing-subscriber = { git = "https://github.com/tokio-rs/tracing", rev = "tracing-subscriber-0.3.18", features = ["env-filter", "json", "registry", "tracing-log"] } # workaround for https://github.com/tokio-rs/tracing/issues/2927
util.workspace = true
uuid.workspace = true

Expand Down Expand Up @@ -102,3 +102,4 @@ theme.workspace = true
unindent.workspace = true
util.workspace = true
workspace = { workspace = true, features = ["test-support"] }
headless.workspace = true

0 comments on commit f6c85b2

Please sign in to comment.