Detailed changes
@@ -2550,7 +2550,6 @@ dependencies = [
"ctor",
"dashmap 6.0.1",
"derive_more",
- "dev_server_projects",
"editor",
"env_logger",
"envy",
@@ -2561,7 +2560,6 @@ dependencies = [
"git_hosting_providers",
"google_ai",
"gpui",
- "headless",
"hex",
"http_client",
"hyper 0.14.30",
@@ -3476,18 +3474,6 @@ dependencies = [
"syn 1.0.109",
]
-[[package]]
-name = "dev_server_projects"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "client",
- "gpui",
- "rpc",
- "serde",
- "serde_json",
-]
-
[[package]]
name = "diagnostics"
version = "0.1.0"
@@ -5274,28 +5260,6 @@ dependencies = [
"http 0.2.12",
]
-[[package]]
-name = "headless"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "client",
- "extension",
- "fs",
- "futures 0.3.30",
- "gpui",
- "language",
- "log",
- "node_runtime",
- "postage",
- "project",
- "proto",
- "settings",
- "shellexpand 2.1.2",
- "signal-hook",
- "util",
-]
-
[[package]]
name = "heck"
version = "0.3.3"
@@ -8443,7 +8407,6 @@ dependencies = [
"client",
"clock",
"collections",
- "dev_server_projects",
"env_logger",
"fs",
"futures 0.3.30",
@@ -8981,8 +8944,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"auto_update",
- "client",
- "dev_server_projects",
"editor",
"file_finder",
"futures 0.3.30",
@@ -8999,14 +8960,12 @@ dependencies = [
"project",
"release_channel",
"remote",
- "rpc",
"schemars",
"serde",
"serde_json",
"settings",
"smol",
"task",
- "terminal_view",
"theme",
"ui",
"util",
@@ -11912,7 +11871,6 @@ dependencies = [
"client",
"collections",
"command_palette",
- "dev_server_projects",
"editor",
"extensions_ui",
"feature_flags",
@@ -14309,7 +14267,6 @@ dependencies = [
"collections",
"db",
"derive_more",
- "dev_server_projects",
"env_logger",
"fs",
"futures 0.3.30",
@@ -14628,7 +14585,6 @@ dependencies = [
"command_palette_hooks",
"copilot",
"db",
- "dev_server_projects",
"diagnostics",
"editor",
"env_logger",
@@ -14644,7 +14600,6 @@ dependencies = [
"git_hosting_providers",
"go_to_line",
"gpui",
- "headless",
"http_client",
"image_viewer",
"inline_completion_button",
@@ -23,7 +23,6 @@ members = [
"crates/context_servers",
"crates/copilot",
"crates/db",
- "crates/dev_server_projects",
"crates/diagnostics",
"crates/docs_preprocessor",
"crates/editor",
@@ -45,7 +44,6 @@ members = [
"crates/google_ai",
"crates/gpui",
"crates/gpui_macros",
- "crates/headless",
"crates/html_to_markdown",
"crates/http_client",
"crates/image_viewer",
@@ -201,7 +199,6 @@ command_palette_hooks = { path = "crates/command_palette_hooks" }
context_servers = { path = "crates/context_servers" }
copilot = { path = "crates/copilot" }
db = { path = "crates/db" }
-dev_server_projects = { path = "crates/dev_server_projects" }
diagnostics = { path = "crates/diagnostics" }
editor = { path = "crates/editor" }
extension = { path = "crates/extension" }
@@ -219,7 +216,6 @@ go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui", default-features = false, features = ["http_client"]}
gpui_macros = { path = "crates/gpui_macros" }
-headless = { path = "crates/headless" }
html_to_markdown = { path = "crates/html_to_markdown" }
http_client = { path = "crates/http_client" }
image_viewer = { path = "crates/image_viewer" }
@@ -963,7 +963,7 @@ impl AssistantPanel {
fn new_context(&mut self, cx: &mut ViewContext<Self>) -> Option<View<ContextEditor>> {
let project = self.project.read(cx);
- if project.is_via_collab() && project.dev_server_project_id().is_none() {
+ if project.is_via_collab() {
let task = self
.context_store
.update(cx, |store, cx| store.create_remote_context(cx));
@@ -1194,26 +1194,15 @@ impl Room {
project: Model<Project>,
cx: &mut ModelContext<Self>,
) -> Task<Result<u64>> {
- let request = if let Some(dev_server_project_id) = project.read(cx).dev_server_project_id()
- {
- self.client.request(proto::ShareProject {
- room_id: self.id(),
- worktrees: vec![],
- dev_server_project_id: Some(dev_server_project_id.0),
- is_ssh_project: false,
- })
- } else {
- if let Some(project_id) = project.read(cx).remote_id() {
- return Task::ready(Ok(project_id));
- }
+ if let Some(project_id) = project.read(cx).remote_id() {
+ return Task::ready(Ok(project_id));
+ }
- self.client.request(proto::ShareProject {
- room_id: self.id(),
- worktrees: project.read(cx).worktree_metadata_protos(cx),
- dev_server_project_id: None,
- is_ssh_project: project.read(cx).is_via_ssh(),
- })
- };
+ let request = self.client.request(proto::ShareProject {
+ room_id: self.id(),
+ worktrees: project.read(cx).worktree_metadata_protos(cx),
+ is_ssh_project: project.read(cx).is_via_ssh(),
+ });
cx.spawn(|this, mut cx| async move {
let response = request.await?;
@@ -15,7 +15,6 @@ pub enum CliRequest {
urls: Vec<String>,
wait: bool,
open_new_workspace: Option<bool>,
- dev_server_token: Option<String>,
env: Option<HashMap<String, String>>,
},
}
@@ -151,6 +151,12 @@ fn main() -> Result<()> {
}
}
+ if let Some(_) = args.dev_server_token {
+ return Err(anyhow::anyhow!(
+ "Dev servers were removed in v0.157.x please upgrade to SSH remoting: https://zed.dev/docs/remote-development"
+ ))?;
+ }
+
let sender: JoinHandle<anyhow::Result<()>> = thread::spawn({
let exit_status = exit_status.clone();
move || {
@@ -162,7 +168,6 @@ fn main() -> Result<()> {
urls,
wait: args.wait,
open_new_workspace,
- dev_server_token: args.dev_server_token,
env,
})?;
@@ -30,7 +30,6 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
use socks::connect_socks_proxy_stream;
-use std::fmt;
use std::pin::Pin;
use std::{
any::TypeId,
@@ -54,15 +53,6 @@ pub use rpc::*;
pub use telemetry_events::Event;
pub use user::*;
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct DevServerToken(pub String);
-
-impl fmt::Display for DevServerToken {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
static ZED_SERVER_URL: LazyLock<Option<String>> =
LazyLock::new(|| std::env::var("ZED_SERVER_URL").ok());
static ZED_RPC_URL: LazyLock<Option<String>> = LazyLock::new(|| std::env::var("ZED_RPC_URL").ok());
@@ -304,20 +294,14 @@ struct ClientState {
}
#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum Credentials {
- DevServer { token: DevServerToken },
- User { user_id: u64, access_token: String },
+pub struct Credentials {
+ pub user_id: u64,
+ pub access_token: String,
}
impl Credentials {
pub fn authorization_header(&self) -> String {
- match self {
- Credentials::DevServer { token } => format!("dev-server-token {}", token),
- Credentials::User {
- user_id,
- access_token,
- } => format!("{} {}", user_id, access_token),
- }
+ format!("{} {}", self.user_id, self.access_token)
}
}
@@ -600,11 +584,11 @@ impl Client {
}
pub fn user_id(&self) -> Option<u64> {
- if let Some(Credentials::User { user_id, .. }) = self.state.read().credentials.as_ref() {
- Some(*user_id)
- } else {
- None
- }
+ self.state
+ .read()
+ .credentials
+ .as_ref()
+ .map(|credentials| credentials.user_id)
}
pub fn peer_id(&self) -> Option<PeerId> {
@@ -793,11 +777,6 @@ impl Client {
.is_some()
}
- pub fn set_dev_server_token(&self, token: DevServerToken) -> &Self {
- self.state.write().credentials = Some(Credentials::DevServer { token });
- self
- }
-
#[async_recursion(?Send)]
pub async fn authenticate_and_connect(
self: &Arc<Self>,
@@ -848,9 +827,7 @@ impl Client {
}
}
let credentials = credentials.unwrap();
- if let Credentials::User { user_id, .. } = &credentials {
- self.set_id(*user_id);
- }
+ self.set_id(credentials.user_id);
if was_disconnected {
self.set_status(Status::Connecting, cx);
@@ -866,9 +843,8 @@ impl Client {
Ok(conn) => {
self.state.write().credentials = Some(credentials.clone());
if !read_from_provider && IMPERSONATE_LOGIN.is_none() {
- if let Credentials::User{user_id, access_token} = credentials {
- self.credentials_provider.write_credentials(user_id, access_token, cx).await.log_err();
- }
+ self.credentials_provider.write_credentials(credentials.user_id, credentials.access_token, cx).await.log_err();
+
}
futures::select_biased! {
@@ -1301,7 +1277,7 @@ impl Client {
.decrypt_string(&access_token)
.context("failed to decrypt access token")?;
- Ok(Credentials::User {
+ Ok(Credentials {
user_id: user_id.parse()?,
access_token,
})
@@ -1422,7 +1398,7 @@ impl Client {
// Use the admin API token to authenticate as the impersonated user.
api_token.insert_str(0, "ADMIN_TOKEN:");
- Ok(Credentials::User {
+ Ok(Credentials {
user_id: response.user.id,
access_token: api_token,
})
@@ -1667,7 +1643,7 @@ impl CredentialsProvider for DevelopmentCredentialsProvider {
let credentials: DevelopmentCredentials = serde_json::from_slice(&json).log_err()?;
- Some(Credentials::User {
+ Some(Credentials {
user_id: credentials.user_id,
access_token: credentials.access_token,
})
@@ -1721,7 +1697,7 @@ impl CredentialsProvider for KeychainCredentialsProvider {
.await
.log_err()??;
- Some(Credentials::User {
+ Some(Credentials {
user_id: user_id.parse().ok()?,
access_token: String::from_utf8(access_token).ok()?,
})
@@ -1855,7 +1831,7 @@ mod tests {
// Time out when client tries to connect.
client.override_authenticate(move |cx| {
cx.background_executor().spawn(async move {
- Ok(Credentials::User {
+ Ok(Credentials {
user_id,
access_token: "token".into(),
})
@@ -49,7 +49,7 @@ impl FakeServer {
let mut state = state.lock();
state.auth_count += 1;
let access_token = state.access_token.to_string();
- Ok(Credentials::User {
+ Ok(Credentials {
user_id: client_user_id,
access_token,
})
@@ -73,7 +73,7 @@ impl FakeServer {
}
if credentials
- != (Credentials::User {
+ != (Credentials {
user_id: client_user_id,
access_token: state.lock().access_token.to_string(),
})
@@ -28,9 +28,6 @@ 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, serde::Serialize, serde::Deserialize,
)]
@@ -86,7 +86,6 @@ client = { workspace = true, features = ["test-support"] }
collab_ui = { workspace = true, features = ["test-support"] }
collections = { workspace = true, features = ["test-support"] }
ctor.workspace = true
-dev_server_projects.workspace = true
editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
file_finder.workspace = true
@@ -94,7 +93,6 @@ fs = { workspace = true, features = ["test-support"] }
git = { workspace = true, features = ["test-support"] }
git_hosting_providers.workspace = true
gpui = { workspace = true, features = ["test-support"] }
-headless.workspace = true
hyper.workspace = true
indoc.workspace = true
language = { workspace = true, features = ["test-support"] }
@@ -1,5 +1,5 @@
use crate::{
- db::{self, dev_server, AccessTokenId, Database, DevServerId, UserId},
+ db::{self, AccessTokenId, Database, UserId},
rpc::Principal,
AppState, Error, Result,
};
@@ -44,19 +44,10 @@ pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl Into
let first = auth_header.next().unwrap_or("");
if first == "dev-server-token" {
- let dev_server_token = auth_header.next().ok_or_else(|| {
- Error::http(
- StatusCode::BAD_REQUEST,
- "missing dev-server-token token in authorization header".to_string(),
- )
- })?;
- let dev_server = verify_dev_server_token(dev_server_token, &state.db)
- .await
- .map_err(|e| Error::http(StatusCode::UNAUTHORIZED, format!("{}", e)))?;
-
- req.extensions_mut()
- .insert(Principal::DevServer(dev_server));
- return Ok::<_, Error>(next.run(req).await);
+ Err(Error::http(
+ StatusCode::UNAUTHORIZED,
+ "Dev servers were removed in Zed 0.157 please upgrade to SSH remoting".to_string(),
+ ))?;
}
let user_id = UserId(first.parse().map_err(|_| {
@@ -240,41 +231,6 @@ pub async fn verify_access_token(
})
}
-pub fn generate_dev_server_token(id: usize, access_token: String) -> String {
- format!("{}.{}", id, access_token)
-}
-
-pub async fn verify_dev_server_token(
- dev_server_token: &str,
- db: &Arc<Database>,
-) -> anyhow::Result<dev_server::Model> {
- let (id, token) = split_dev_server_token(dev_server_token)?;
- let token_hash = hash_access_token(token);
- let server = db.get_dev_server(id).await?;
-
- if server
- .hashed_token
- .as_bytes()
- .ct_eq(token_hash.as_ref())
- .into()
- {
- Ok(server)
- } else {
- Err(anyhow!("wrong token for dev server"))
- }
-}
-
-// a dev_server_token has the format <id>.<base64>. This is to make them
-// relatively easy to copy/paste around.
-pub fn split_dev_server_token(dev_server_token: &str) -> anyhow::Result<(DevServerId, &str)> {
- let mut parts = dev_server_token.splitn(2, '.');
- let id = DevServerId(parts.next().unwrap_or_default().parse()?);
- let token = parts
- .next()
- .ok_or_else(|| anyhow!("invalid dev server token format"))?;
- Ok((id, token))
-}
-
#[cfg(test)]
mod test {
use rand::thread_rng;
@@ -726,7 +726,6 @@ pub struct Project {
pub collaborators: Vec<ProjectCollaborator>,
pub worktrees: BTreeMap<u64, Worktree>,
pub language_servers: Vec<proto::LanguageServer>,
- pub dev_server_project_id: Option<DevServerProjectId>,
}
pub struct ProjectCollaborator {
@@ -79,7 +79,6 @@ id_type!(ChannelChatParticipantId);
id_type!(ChannelId);
id_type!(ChannelMemberId);
id_type!(ContactId);
-id_type!(DevServerId);
id_type!(ExtensionId);
id_type!(FlagId);
id_type!(FollowerId);
@@ -89,7 +88,6 @@ id_type!(NotificationId);
id_type!(NotificationKindId);
id_type!(ProjectCollaboratorId);
id_type!(ProjectId);
-id_type!(DevServerProjectId);
id_type!(ReplicaId);
id_type!(RoomId);
id_type!(RoomParticipantId);
@@ -277,12 +275,6 @@ impl From<ChannelVisibility> for i32 {
}
}
-#[derive(Copy, Clone, Debug, Serialize, PartialEq)]
-pub enum PrincipalId {
- UserId(UserId),
- DevServerId(DevServerId),
-}
-
/// Indicate whether a [Buffer] has permissions to edit.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Capability {
@@ -8,8 +8,6 @@ pub mod buffers;
pub mod channels;
pub mod contacts;
pub mod contributors;
-pub mod dev_server_projects;
-pub mod dev_servers;
pub mod embeddings;
pub mod extensions;
pub mod hosted_projects;
@@ -1,365 +1 @@
-use anyhow::anyhow;
-use rpc::{
- proto::{self},
- ConnectionId,
-};
-use sea_orm::{
- ActiveModelTrait, ActiveValue, ColumnTrait, Condition, DatabaseTransaction, EntityTrait,
- IntoActiveModel, ModelTrait, QueryFilter,
-};
-use crate::db::ProjectId;
-
-use super::{
- dev_server, dev_server_project, project, project_collaborator, worktree, Database, DevServerId,
- DevServerProjectId, RejoinedProject, ResharedProject, ServerId, UserId,
-};
-
-impl Database {
- pub async fn get_dev_server_project(
- &self,
- dev_server_project_id: DevServerProjectId,
- ) -> crate::Result<dev_server_project::Model> {
- self.transaction(|tx| async move {
- Ok(
- dev_server_project::Entity::find_by_id(dev_server_project_id)
- .one(&*tx)
- .await?
- .ok_or_else(|| {
- anyhow!("no dev server project with id {}", dev_server_project_id)
- })?,
- )
- })
- .await
- }
-
- pub async fn get_projects_for_dev_server(
- &self,
- dev_server_id: DevServerId,
- ) -> crate::Result<Vec<proto::DevServerProject>> {
- self.transaction(|tx| async move {
- self.get_projects_for_dev_server_internal(dev_server_id, &tx)
- .await
- })
- .await
- }
-
- pub async fn get_projects_for_dev_server_internal(
- &self,
- dev_server_id: DevServerId,
- tx: &DatabaseTransaction,
- ) -> crate::Result<Vec<proto::DevServerProject>> {
- let servers = dev_server_project::Entity::find()
- .filter(dev_server_project::Column::DevServerId.eq(dev_server_id))
- .find_also_related(project::Entity)
- .all(tx)
- .await?;
- Ok(servers
- .into_iter()
- .map(|(dev_server_project, project)| dev_server_project.to_proto(project))
- .collect())
- }
-
- pub async fn dev_server_project_ids_for_user(
- &self,
- user_id: UserId,
- tx: &DatabaseTransaction,
- ) -> crate::Result<Vec<DevServerProjectId>> {
- let dev_servers = dev_server::Entity::find()
- .filter(dev_server::Column::UserId.eq(user_id))
- .find_with_related(dev_server_project::Entity)
- .all(tx)
- .await?;
-
- Ok(dev_servers
- .into_iter()
- .flat_map(|(_, projects)| projects.into_iter().map(|p| p.id))
- .collect())
- }
-
- pub async fn owner_for_dev_server_project(
- &self,
- dev_server_project_id: DevServerProjectId,
- tx: &DatabaseTransaction,
- ) -> crate::Result<UserId> {
- let dev_server = dev_server_project::Entity::find_by_id(dev_server_project_id)
- .find_also_related(dev_server::Entity)
- .one(tx)
- .await?
- .and_then(|(_, dev_server)| dev_server)
- .ok_or_else(|| anyhow!("no dev server project"))?;
-
- Ok(dev_server.user_id)
- }
-
- pub async fn get_stale_dev_server_projects(
- &self,
- connection: ConnectionId,
- ) -> crate::Result<Vec<ProjectId>> {
- self.transaction(|tx| async move {
- let projects = project::Entity::find()
- .filter(
- Condition::all()
- .add(project::Column::HostConnectionId.eq(connection.id))
- .add(project::Column::HostConnectionServerId.eq(connection.owner_id)),
- )
- .all(&*tx)
- .await?;
-
- Ok(projects.into_iter().map(|p| p.id).collect())
- })
- .await
- }
-
- pub async fn create_dev_server_project(
- &self,
- dev_server_id: DevServerId,
- path: &str,
- user_id: UserId,
- ) -> crate::Result<(dev_server_project::Model, proto::DevServerProjectsUpdate)> {
- self.transaction(|tx| async move {
- let dev_server = dev_server::Entity::find_by_id(dev_server_id)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("no dev server with id {}", dev_server_id))?;
- if dev_server.user_id != user_id {
- return Err(anyhow!("not your dev server"))?;
- }
-
- let project = dev_server_project::Entity::insert(dev_server_project::ActiveModel {
- id: ActiveValue::NotSet,
- dev_server_id: ActiveValue::Set(dev_server_id),
- paths: ActiveValue::Set(dev_server_project::JSONPaths(vec![path.to_string()])),
- })
- .exec_with_returning(&*tx)
- .await?;
-
- let status = self
- .dev_server_projects_update_internal(user_id, &tx)
- .await?;
-
- Ok((project, status))
- })
- .await
- }
-
- pub async fn update_dev_server_project(
- &self,
- id: DevServerProjectId,
- paths: &[String],
- user_id: UserId,
- ) -> crate::Result<(dev_server_project::Model, proto::DevServerProjectsUpdate)> {
- self.transaction(move |tx| async move {
- let paths = paths.to_owned();
- let Some((project, Some(dev_server))) = dev_server_project::Entity::find_by_id(id)
- .find_also_related(dev_server::Entity)
- .one(&*tx)
- .await?
- else {
- return Err(anyhow!("no such dev server project"))?;
- };
-
- if dev_server.user_id != user_id {
- return Err(anyhow!("not your dev server"))?;
- }
- let mut project = project.into_active_model();
- project.paths = ActiveValue::Set(dev_server_project::JSONPaths(paths));
- let project = project.update(&*tx).await?;
-
- let status = self
- .dev_server_projects_update_internal(user_id, &tx)
- .await?;
-
- Ok((project, status))
- })
- .await
- }
-
- pub async fn delete_dev_server_project(
- &self,
- dev_server_project_id: DevServerProjectId,
- dev_server_id: DevServerId,
- user_id: UserId,
- ) -> crate::Result<(Vec<proto::DevServerProject>, proto::DevServerProjectsUpdate)> {
- self.transaction(|tx| async move {
- project::Entity::delete_many()
- .filter(project::Column::DevServerProjectId.eq(dev_server_project_id))
- .exec(&*tx)
- .await?;
- let result = dev_server_project::Entity::delete_by_id(dev_server_project_id)
- .exec(&*tx)
- .await?;
- if result.rows_affected != 1 {
- return Err(anyhow!(
- "no dev server project with id {}",
- dev_server_project_id
- ))?;
- }
-
- let status = self
- .dev_server_projects_update_internal(user_id, &tx)
- .await?;
-
- let projects = self
- .get_projects_for_dev_server_internal(dev_server_id, &tx)
- .await?;
- Ok((projects, status))
- })
- .await
- }
-
- pub async fn share_dev_server_project(
- &self,
- dev_server_project_id: DevServerProjectId,
- dev_server_id: DevServerId,
- connection: ConnectionId,
- worktrees: &[proto::WorktreeMetadata],
- ) -> crate::Result<(
- proto::DevServerProject,
- UserId,
- proto::DevServerProjectsUpdate,
- )> {
- self.transaction(|tx| async move {
- let dev_server = dev_server::Entity::find_by_id(dev_server_id)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("no dev server with id {}", dev_server_id))?;
-
- let dev_server_project = dev_server_project::Entity::find_by_id(dev_server_project_id)
- .one(&*tx)
- .await?
- .ok_or_else(|| {
- anyhow!("no dev server project with id {}", dev_server_project_id)
- })?;
-
- if dev_server_project.dev_server_id != dev_server_id {
- return Err(anyhow!("dev server project shared from wrong server"))?;
- }
-
- let project = project::ActiveModel {
- room_id: ActiveValue::Set(None),
- host_user_id: ActiveValue::Set(None),
- host_connection_id: ActiveValue::set(Some(connection.id as i32)),
- host_connection_server_id: ActiveValue::set(Some(ServerId(
- connection.owner_id as i32,
- ))),
- id: ActiveValue::NotSet,
- hosted_project_id: ActiveValue::Set(None),
- dev_server_project_id: ActiveValue::Set(Some(dev_server_project_id)),
- }
- .insert(&*tx)
- .await?;
-
- if !worktrees.is_empty() {
- worktree::Entity::insert_many(worktrees.iter().map(|worktree| {
- worktree::ActiveModel {
- id: ActiveValue::set(worktree.id as i64),
- project_id: ActiveValue::set(project.id),
- abs_path: ActiveValue::set(worktree.abs_path.clone()),
- root_name: ActiveValue::set(worktree.root_name.clone()),
- visible: ActiveValue::set(worktree.visible),
- scan_id: ActiveValue::set(0),
- completed_scan_id: ActiveValue::set(0),
- }
- }))
- .exec(&*tx)
- .await?;
- }
-
- let status = self
- .dev_server_projects_update_internal(dev_server.user_id, &tx)
- .await?;
-
- Ok((
- dev_server_project.to_proto(Some(project)),
- dev_server.user_id,
- status,
- ))
- })
- .await
- }
-
- pub async fn reshare_dev_server_projects(
- &self,
- reshared_projects: &Vec<proto::UpdateProject>,
- dev_server_id: DevServerId,
- connection: ConnectionId,
- ) -> crate::Result<Vec<ResharedProject>> {
- self.transaction(|tx| async move {
- let mut ret = Vec::new();
- for reshared_project in reshared_projects {
- let project_id = ProjectId::from_proto(reshared_project.project_id);
- let (project, dev_server_project) = project::Entity::find_by_id(project_id)
- .find_also_related(dev_server_project::Entity)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("project does not exist"))?;
-
- if dev_server_project.map(|rp| rp.dev_server_id) != Some(dev_server_id) {
- return Err(anyhow!("dev server project reshared from wrong server"))?;
- }
-
- let Ok(old_connection_id) = project.host_connection() else {
- return Err(anyhow!("dev server project was not shared"))?;
- };
-
- project::Entity::update(project::ActiveModel {
- id: ActiveValue::set(project_id),
- host_connection_id: ActiveValue::set(Some(connection.id as i32)),
- host_connection_server_id: ActiveValue::set(Some(ServerId(
- connection.owner_id as i32,
- ))),
- ..Default::default()
- })
- .exec(&*tx)
- .await?;
-
- let collaborators = project
- .find_related(project_collaborator::Entity)
- .all(&*tx)
- .await?;
-
- self.update_project_worktrees(project_id, &reshared_project.worktrees, &tx)
- .await?;
-
- ret.push(super::ResharedProject {
- id: project_id,
- old_connection_id,
- collaborators: collaborators
- .iter()
- .map(|collaborator| super::ProjectCollaborator {
- connection_id: collaborator.connection(),
- user_id: collaborator.user_id,
- replica_id: collaborator.replica_id,
- is_host: collaborator.is_host,
- })
- .collect(),
- worktrees: reshared_project.worktrees.clone(),
- });
- }
- Ok(ret)
- })
- .await
- }
-
- pub async fn rejoin_dev_server_projects(
- &self,
- rejoined_projects: &Vec<proto::RejoinProject>,
- user_id: UserId,
- connection_id: ConnectionId,
- ) -> crate::Result<Vec<RejoinedProject>> {
- self.transaction(|tx| async move {
- let mut ret = Vec::new();
- for rejoined_project in rejoined_projects {
- if let Some(project) = self
- .rejoin_project_internal(&tx, rejoined_project, user_id, connection_id)
- .await?
- {
- ret.push(project);
- }
- }
- Ok(ret)
- })
- .await
- }
-}
@@ -1,222 +1 @@
-use rpc::proto;
-use sea_orm::{
- ActiveValue, ColumnTrait, DatabaseTransaction, EntityTrait, IntoActiveModel, QueryFilter,
-};
-use super::{dev_server, dev_server_project, Database, DevServerId, UserId};
-
-impl Database {
- pub async fn get_dev_server(
- &self,
- dev_server_id: DevServerId,
- ) -> crate::Result<dev_server::Model> {
- self.transaction(|tx| async move {
- Ok(dev_server::Entity::find_by_id(dev_server_id)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow::anyhow!("no dev server with id {}", dev_server_id))?)
- })
- .await
- }
-
- pub async fn get_dev_server_for_user(
- &self,
- dev_server_id: DevServerId,
- user_id: UserId,
- ) -> crate::Result<dev_server::Model> {
- self.transaction(|tx| async move {
- let server = dev_server::Entity::find_by_id(dev_server_id)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow::anyhow!("no dev server with id {}", dev_server_id))?;
- if server.user_id != user_id {
- return Err(anyhow::anyhow!(
- "dev server {} is not owned by user {}",
- dev_server_id,
- user_id
- ))?;
- }
- Ok(server)
- })
- .await
- }
-
- pub async fn get_dev_servers(&self, user_id: UserId) -> crate::Result<Vec<dev_server::Model>> {
- self.transaction(|tx| async move {
- Ok(dev_server::Entity::find()
- .filter(dev_server::Column::UserId.eq(user_id))
- .all(&*tx)
- .await?)
- })
- .await
- }
-
- pub async fn dev_server_projects_update(
- &self,
- user_id: UserId,
- ) -> crate::Result<proto::DevServerProjectsUpdate> {
- self.transaction(|tx| async move {
- self.dev_server_projects_update_internal(user_id, &tx).await
- })
- .await
- }
-
- pub async fn dev_server_projects_update_internal(
- &self,
- user_id: UserId,
- tx: &DatabaseTransaction,
- ) -> crate::Result<proto::DevServerProjectsUpdate> {
- let dev_servers = dev_server::Entity::find()
- .filter(dev_server::Column::UserId.eq(user_id))
- .all(tx)
- .await?;
-
- let dev_server_projects = dev_server_project::Entity::find()
- .filter(
- dev_server_project::Column::DevServerId
- .is_in(dev_servers.iter().map(|d| d.id).collect::<Vec<_>>()),
- )
- .find_also_related(super::project::Entity)
- .all(tx)
- .await?;
-
- Ok(proto::DevServerProjectsUpdate {
- dev_servers: dev_servers
- .into_iter()
- .map(|d| d.to_proto(proto::DevServerStatus::Offline))
- .collect(),
- dev_server_projects: dev_server_projects
- .into_iter()
- .map(|(dev_server_project, project)| dev_server_project.to_proto(project))
- .collect(),
- })
- }
-
- pub async fn create_dev_server(
- &self,
- name: &str,
- ssh_connection_string: Option<&str>,
- hashed_access_token: &str,
- user_id: UserId,
- ) -> crate::Result<(dev_server::Model, proto::DevServerProjectsUpdate)> {
- self.transaction(|tx| async move {
- if name.trim().is_empty() {
- return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
- }
-
- let dev_server = dev_server::Entity::insert(dev_server::ActiveModel {
- id: ActiveValue::NotSet,
- hashed_token: ActiveValue::Set(hashed_access_token.to_string()),
- name: ActiveValue::Set(name.trim().to_string()),
- user_id: ActiveValue::Set(user_id),
- ssh_connection_string: ActiveValue::Set(
- ssh_connection_string.map(ToOwned::to_owned),
- ),
- })
- .exec_with_returning(&*tx)
- .await?;
-
- let dev_server_projects = self
- .dev_server_projects_update_internal(user_id, &tx)
- .await?;
-
- Ok((dev_server, dev_server_projects))
- })
- .await
- }
-
- pub async fn update_dev_server_token(
- &self,
- id: DevServerId,
- hashed_token: &str,
- user_id: UserId,
- ) -> crate::Result<proto::DevServerProjectsUpdate> {
- self.transaction(|tx| async move {
- let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
- return Err(anyhow::anyhow!("no dev server with id {}", id))?;
- };
- if dev_server.user_id != user_id {
- return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
- }
-
- dev_server::Entity::update(dev_server::ActiveModel {
- hashed_token: ActiveValue::Set(hashed_token.to_string()),
- ..dev_server.clone().into_active_model()
- })
- .exec(&*tx)
- .await?;
-
- let dev_server_projects = self
- .dev_server_projects_update_internal(user_id, &tx)
- .await?;
-
- Ok(dev_server_projects)
- })
- .await
- }
-
- pub async fn rename_dev_server(
- &self,
- id: DevServerId,
- name: &str,
- ssh_connection_string: Option<&str>,
- user_id: UserId,
- ) -> crate::Result<proto::DevServerProjectsUpdate> {
- self.transaction(|tx| async move {
- let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
- return Err(anyhow::anyhow!("no dev server with id {}", id))?;
- };
- if dev_server.user_id != user_id || name.trim().is_empty() {
- return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
- }
-
- dev_server::Entity::update(dev_server::ActiveModel {
- name: ActiveValue::Set(name.trim().to_string()),
- ssh_connection_string: ActiveValue::Set(
- ssh_connection_string.map(ToOwned::to_owned),
- ),
- ..dev_server.clone().into_active_model()
- })
- .exec(&*tx)
- .await?;
-
- let dev_server_projects = self
- .dev_server_projects_update_internal(user_id, &tx)
- .await?;
-
- Ok(dev_server_projects)
- })
- .await
- }
-
- pub async fn delete_dev_server(
- &self,
- id: DevServerId,
- user_id: UserId,
- ) -> crate::Result<proto::DevServerProjectsUpdate> {
- self.transaction(|tx| async move {
- let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
- return Err(anyhow::anyhow!("no dev server with id {}", id))?;
- };
- if dev_server.user_id != user_id {
- return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
- }
-
- dev_server_project::Entity::delete_many()
- .filter(dev_server_project::Column::DevServerId.eq(id))
- .exec(&*tx)
- .await?;
-
- dev_server::Entity::delete(dev_server.into_active_model())
- .exec(&*tx)
- .await?;
-
- let dev_server_projects = self
- .dev_server_projects_update_internal(user_id, &tx)
- .await?;
-
- Ok(dev_server_projects)
- })
- .await
- }
-}
@@ -32,7 +32,6 @@ impl Database {
connection: ConnectionId,
worktrees: &[proto::WorktreeMetadata],
is_ssh_project: bool,
- dev_server_project_id: Option<DevServerProjectId>,
) -> Result<TransactionGuard<(ProjectId, proto::Room)>> {
self.room_transaction(room_id, |tx| async move {
let participant = room_participant::Entity::find()
@@ -61,38 +60,6 @@ impl Database {
return Err(anyhow!("guests cannot share projects"))?;
}
- if let Some(dev_server_project_id) = dev_server_project_id {
- let project = project::Entity::find()
- .filter(project::Column::DevServerProjectId.eq(Some(dev_server_project_id)))
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("no remote project"))?;
-
- let (_, dev_server) = dev_server_project::Entity::find_by_id(dev_server_project_id)
- .find_also_related(dev_server::Entity)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("no dev_server_project"))?;
-
- if !dev_server.is_some_and(|dev_server| dev_server.user_id == participant.user_id) {
- return Err(anyhow!("not your dev server"))?;
- }
-
- if project.room_id.is_some() {
- return Err(anyhow!("project already shared"))?;
- };
-
- let project = project::Entity::update(project::ActiveModel {
- room_id: ActiveValue::Set(Some(room_id)),
- ..project.into_active_model()
- })
- .exec(&*tx)
- .await?;
-
- let room = self.get_room(room_id, &tx).await?;
- return Ok((project.id, room));
- }
-
let project = project::ActiveModel {
room_id: ActiveValue::set(Some(participant.room_id)),
host_user_id: ActiveValue::set(Some(participant.user_id)),
@@ -102,7 +69,6 @@ impl Database {
))),
id: ActiveValue::NotSet,
hosted_project_id: ActiveValue::Set(None),
- dev_server_project_id: ActiveValue::Set(None),
}
.insert(&*tx)
.await?;
@@ -156,7 +122,6 @@ impl Database {
&self,
project_id: ProjectId,
connection: ConnectionId,
- user_id: Option<UserId>,
) -> Result<TransactionGuard<(bool, Option<proto::Room>, Vec<ConnectionId>)>> {
self.project_transaction(project_id, |tx| async move {
let guest_connection_ids = self.project_guest_connection_ids(project_id, &tx).await?;
@@ -172,25 +137,6 @@ impl Database {
if project.host_connection()? == connection {
return Ok((true, room, guest_connection_ids));
}
- if let Some(dev_server_project_id) = project.dev_server_project_id {
- if let Some(user_id) = user_id {
- if user_id
- != self
- .owner_for_dev_server_project(dev_server_project_id, &tx)
- .await?
- {
- Err(anyhow!("cannot unshare a project hosted by another user"))?
- }
- project::Entity::update(project::ActiveModel {
- room_id: ActiveValue::Set(None),
- ..project.into_active_model()
- })
- .exec(&*tx)
- .await?;
- return Ok((false, room, guest_connection_ids));
- }
- }
-
Err(anyhow!("cannot unshare a project hosted by another user"))?
})
.await
@@ -633,17 +579,6 @@ impl Database {
.await
}
- pub async fn find_dev_server_project(&self, id: DevServerProjectId) -> Result<project::Model> {
- self.transaction(|tx| async move {
- Ok(project::Entity::find()
- .filter(project::Column::DevServerProjectId.eq(id))
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("no such project"))?)
- })
- .await
- }
-
/// Adds the given connection to the specified project
/// in the current room.
pub async fn join_project(
@@ -654,13 +589,7 @@ impl Database {
) -> Result<TransactionGuard<(Project, ReplicaId)>> {
self.project_transaction(project_id, |tx| async move {
let (project, role) = self
- .access_project(
- project_id,
- connection,
- PrincipalId::UserId(user_id),
- Capability::ReadOnly,
- &tx,
- )
+ .access_project(project_id, connection, Capability::ReadOnly, &tx)
.await?;
self.join_project_internal(project, user_id, connection, role, &tx)
.await
@@ -851,7 +780,6 @@ impl Database {
worktree_id: None,
})
.collect(),
- dev_server_project_id: project.dev_server_project_id,
};
Ok((project, replica_id as ReplicaId))
}
@@ -1007,29 +935,14 @@ impl Database {
&self,
project_id: ProjectId,
connection_id: ConnectionId,
- principal_id: PrincipalId,
capability: Capability,
tx: &DatabaseTransaction,
) -> Result<(project::Model, ChannelRole)> {
- let (mut project, dev_server_project) = project::Entity::find_by_id(project_id)
- .find_also_related(dev_server_project::Entity)
+ let project = project::Entity::find_by_id(project_id)
.one(tx)
.await?
.ok_or_else(|| anyhow!("no such project"))?;
- let user_id = match principal_id {
- PrincipalId::DevServerId(_) => {
- if project
- .host_connection()
- .is_ok_and(|connection| connection == connection_id)
- {
- return Ok((project, ChannelRole::Admin));
- }
- return Err(anyhow!("not the project host"))?;
- }
- PrincipalId::UserId(user_id) => user_id,
- };
-
let role_from_room = if let Some(room_id) = project.room_id {
room_participant::Entity::find()
.filter(room_participant::Column::RoomId.eq(room_id))
@@ -1040,34 +953,8 @@ impl Database {
} else {
None
};
- let role_from_dev_server = if let Some(dev_server_project) = dev_server_project {
- let dev_server = dev_server::Entity::find_by_id(dev_server_project.dev_server_id)
- .one(tx)
- .await?
- .ok_or_else(|| anyhow!("no such channel"))?;
- if user_id == dev_server.user_id {
- // If the user left the room "uncleanly" they may rejoin the
- // remote project before leave_room runs. IN that case kick
- // the project out of the room pre-emptively.
- if role_from_room.is_none() {
- project = project::Entity::update(project::ActiveModel {
- room_id: ActiveValue::Set(None),
- ..project.into_active_model()
- })
- .exec(tx)
- .await?;
- }
- Some(ChannelRole::Admin)
- } else {
- None
- }
- } else {
- None
- };
- let role = role_from_dev_server
- .or(role_from_room)
- .unwrap_or(ChannelRole::Banned);
+ let role = role_from_room.unwrap_or(ChannelRole::Banned);
match capability {
Capability::ReadWrite => {
@@ -1090,17 +977,10 @@ impl Database {
&self,
project_id: ProjectId,
connection_id: ConnectionId,
- user_id: UserId,
) -> Result<ConnectionId> {
self.project_transaction(project_id, |tx| async move {
let (project, _) = self
- .access_project(
- project_id,
- connection_id,
- PrincipalId::UserId(user_id),
- Capability::ReadOnly,
- &tx,
- )
+ .access_project(project_id, connection_id, Capability::ReadOnly, &tx)
.await?;
project.host_connection()
})
@@ -1113,17 +993,10 @@ impl Database {
&self,
project_id: ProjectId,
connection_id: ConnectionId,
- user_id: UserId,
) -> Result<ConnectionId> {
self.project_transaction(project_id, |tx| async move {
let (project, _) = self
- .access_project(
- project_id,
- connection_id,
- PrincipalId::UserId(user_id),
- Capability::ReadWrite,
- &tx,
- )
+ .access_project(project_id, connection_id, Capability::ReadWrite, &tx)
.await?;
project.host_connection()
})
@@ -1131,47 +1004,16 @@ impl Database {
.map(|guard| guard.into_inner())
}
- /// Returns the host connection for a request to join a shared project.
- pub async fn host_for_owner_project_request(
- &self,
- project_id: ProjectId,
- _connection_id: ConnectionId,
- user_id: UserId,
- ) -> Result<ConnectionId> {
- self.project_transaction(project_id, |tx| async move {
- let (project, dev_server_project) = project::Entity::find_by_id(project_id)
- .find_also_related(dev_server_project::Entity)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("no such project"))?;
-
- let Some(dev_server_project) = dev_server_project else {
- return Err(anyhow!("not a dev server project"))?;
- };
- let dev_server = dev_server::Entity::find_by_id(dev_server_project.dev_server_id)
- .one(&*tx)
- .await?
- .ok_or_else(|| anyhow!("no such dev server"))?;
- if dev_server.user_id != user_id {
- return Err(anyhow!("not your project"))?;
- }
- project.host_connection()
- })
- .await
- .map(|guard| guard.into_inner())
- }
-
pub async fn connections_for_buffer_update(
&self,
project_id: ProjectId,
- principal_id: PrincipalId,
connection_id: ConnectionId,
capability: Capability,
) -> Result<TransactionGuard<(ConnectionId, Vec<ConnectionId>)>> {
self.project_transaction(project_id, |tx| async move {
// Authorize
let (project, _) = self
- .access_project(project_id, connection_id, principal_id, capability, &tx)
+ .access_project(project_id, connection_id, capability, &tx)
.await?;
let host_connection_id = project.host_connection()?;
@@ -858,25 +858,6 @@ impl Database {
.all(&*tx)
.await?;
- // if any project in the room has a remote-project-id that belongs to a dev server that this user owns.
- let dev_server_projects_for_user = self
- .dev_server_project_ids_for_user(leaving_participant.user_id, &tx)
- .await?;
-
- let dev_server_projects_to_unshare = project::Entity::find()
- .filter(
- Condition::all()
- .add(project::Column::RoomId.eq(room_id))
- .add(
- project::Column::DevServerProjectId
- .is_in(dev_server_projects_for_user.clone()),
- ),
- )
- .all(&*tx)
- .await?
- .into_iter()
- .map(|project| project.id)
- .collect::<HashSet<_>>();
let mut left_projects = HashMap::default();
let mut collaborators = project_collaborator::Entity::find()
.filter(project_collaborator::Column::ProjectId.is_in(project_ids))
@@ -899,9 +880,7 @@ impl Database {
left_project.connection_ids.push(collaborator_connection_id);
}
- if (collaborator.is_host && collaborator.connection() == connection)
- || dev_server_projects_to_unshare.contains(&collaborator.project_id)
- {
+ if collaborator.is_host && collaborator.connection() == connection {
left_project.should_unshare = true;
}
}
@@ -944,17 +923,6 @@ impl Database {
.exec(&*tx)
.await?;
- if !dev_server_projects_to_unshare.is_empty() {
- project::Entity::update_many()
- .filter(project::Column::Id.is_in(dev_server_projects_to_unshare))
- .set(project::ActiveModel {
- room_id: ActiveValue::Set(None),
- ..Default::default()
- })
- .exec(&*tx)
- .await?;
- }
-
let (channel, room) = self.get_channel_room(room_id, &tx).await?;
let deleted = if room.participants.is_empty() {
let result = room::Entity::delete_by_id(room_id).exec(&*tx).await?;
@@ -1323,26 +1291,6 @@ impl Database {
project.worktree_root_names.push(db_worktree.root_name);
}
}
- } else if let Some(dev_server_project_id) = db_project.dev_server_project_id {
- let host = self
- .owner_for_dev_server_project(dev_server_project_id, tx)
- .await?;
- if let Some((_, participant)) = participants
- .iter_mut()
- .find(|(_, v)| v.user_id == host.to_proto())
- {
- participant.projects.push(proto::ParticipantProject {
- id: db_project.id.to_proto(),
- worktree_root_names: Default::default(),
- });
- let project = participant.projects.last_mut().unwrap();
-
- for db_worktree in db_worktrees {
- if db_worktree.visible {
- project.worktree_root_names.push(db_worktree.root_name);
- }
- }
- }
}
}
@@ -13,8 +13,6 @@ pub mod channel_message;
pub mod channel_message_mention;
pub mod contact;
pub mod contributor;
-pub mod dev_server;
-pub mod dev_server_project;
pub mod embedding;
pub mod extension;
pub mod extension_version;
@@ -1,39 +0,0 @@
-use crate::db::{DevServerId, UserId};
-use rpc::proto;
-use sea_orm::entity::prelude::*;
-
-#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
-#[sea_orm(table_name = "dev_servers")]
-pub struct Model {
- #[sea_orm(primary_key)]
- pub id: DevServerId,
- pub name: String,
- pub user_id: UserId,
- pub hashed_token: String,
- pub ssh_connection_string: Option<String>,
-}
-
-impl ActiveModelBehavior for ActiveModel {}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {
- #[sea_orm(has_many = "super::dev_server_project::Entity")]
- RemoteProject,
-}
-
-impl Related<super::dev_server_project::Entity> for Entity {
- fn to() -> RelationDef {
- Relation::RemoteProject.def()
- }
-}
-
-impl Model {
- pub fn to_proto(&self, status: proto::DevServerStatus) -> proto::DevServer {
- proto::DevServer {
- dev_server_id: self.id.to_proto(),
- name: self.name.clone(),
- status: status as i32,
- ssh_connection_string: self.ssh_connection_string.clone(),
- }
- }
-}
@@ -1,59 +0,0 @@
-use super::project;
-use crate::db::{DevServerId, DevServerProjectId};
-use rpc::proto;
-use sea_orm::{entity::prelude::*, FromJsonQueryResult};
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
-#[sea_orm(table_name = "dev_server_projects")]
-pub struct Model {
- #[sea_orm(primary_key)]
- pub id: DevServerProjectId,
- pub dev_server_id: DevServerId,
- pub paths: JSONPaths,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
-pub struct JSONPaths(pub Vec<String>);
-
-impl ActiveModelBehavior for ActiveModel {}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {
- #[sea_orm(has_one = "super::project::Entity")]
- Project,
- #[sea_orm(
- belongs_to = "super::dev_server::Entity",
- from = "Column::DevServerId",
- to = "super::dev_server::Column::Id"
- )]
- DevServer,
-}
-
-impl Related<super::project::Entity> for Entity {
- fn to() -> RelationDef {
- Relation::Project.def()
- }
-}
-
-impl Related<super::dev_server::Entity> for Entity {
- fn to() -> RelationDef {
- Relation::DevServer.def()
- }
-}
-
-impl Model {
- pub fn to_proto(&self, project: Option<project::Model>) -> proto::DevServerProject {
- proto::DevServerProject {
- id: self.id.to_proto(),
- project_id: project.map(|p| p.id.to_proto()),
- dev_server_id: self.dev_server_id.to_proto(),
- path: self.paths().first().cloned().unwrap_or_default(),
- paths: self.paths().clone(),
- }
- }
-
- pub fn paths(&self) -> &Vec<String> {
- &self.paths.0
- }
-}
@@ -1,4 +1,4 @@
-use crate::db::{DevServerProjectId, HostedProjectId, ProjectId, Result, RoomId, ServerId, UserId};
+use crate::db::{HostedProjectId, ProjectId, Result, RoomId, ServerId, UserId};
use anyhow::anyhow;
use rpc::ConnectionId;
use sea_orm::entity::prelude::*;
@@ -13,7 +13,6 @@ pub struct Model {
pub host_connection_id: Option<i32>,
pub host_connection_server_id: Option<ServerId>,
pub hosted_project_id: Option<HostedProjectId>,
- pub dev_server_project_id: Option<DevServerProjectId>,
}
impl Model {
@@ -57,12 +56,6 @@ pub enum Relation {
to = "super::hosted_project::Column::Id"
)]
HostedProject,
- #[sea_orm(
- belongs_to = "super::dev_server_project::Entity",
- from = "Column::DevServerProjectId",
- to = "super::dev_server_project::Column::Id"
- )]
- RemoteProject,
}
impl Related<super::user::Entity> for Entity {
@@ -101,10 +94,4 @@ impl Related<super::hosted_project::Entity> for Entity {
}
}
-impl Related<super::dev_server_project::Entity> for Entity {
- fn to() -> RelationDef {
- Relation::RemoteProject.def()
- }
-}
-
impl ActiveModelBehavior for ActiveModel {}
@@ -540,18 +540,18 @@ async fn test_project_count(db: &Arc<Database>) {
.unwrap();
assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0);
- db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, None)
+ db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false)
.await
.unwrap();
assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1);
- db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, None)
+ db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false)
.await
.unwrap();
assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
// Projects shared by admins aren't counted.
- db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], false, None)
+ db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], false)
.await
.unwrap();
assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2);
@@ -5,11 +5,10 @@ use crate::llm::LlmTokenClaims;
use crate::{
auth,
db::{
- self, dev_server, BufferId, Capability, Channel, ChannelId, ChannelRole, ChannelsForUser,
- CreatedChannelMessage, Database, DevServerId, DevServerProjectId, InviteMemberResult,
- MembershipUpdated, MessageId, NotificationId, PrincipalId, Project, ProjectId,
- RejoinedProject, RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId,
- ServerId, UpdatedChannelMessage, User, UserId,
+ self, BufferId, Capability, Channel, ChannelId, ChannelRole, ChannelsForUser,
+ CreatedChannelMessage, Database, InviteMemberResult, MembershipUpdated, MessageId,
+ NotificationId, Project, ProjectId, RejoinedProject, RemoveChannelMemberResult, ReplicaId,
+ RespondToChannelInvite, RoomId, ServerId, UpdatedChannelMessage, User, UserId,
},
executor::Executor,
AppState, Config, Error, RateLimit, Result,
@@ -42,10 +41,8 @@ use sha2::Digest;
use supermaven_api::{CreateExternalUserRequest, SupermavenAdminApi};
use futures::{
- channel::oneshot,
- future::{self, BoxFuture},
- stream::FuturesUnordered,
- FutureExt, SinkExt, StreamExt, TryStreamExt,
+ channel::oneshot, future::BoxFuture, stream::FuturesUnordered, FutureExt, SinkExt, StreamExt,
+ TryStreamExt,
};
use prometheus::{register_int_gauge, IntGauge};
use rpc::{
@@ -109,7 +106,6 @@ impl<R: RequestMessage> Response<R> {
pub enum Principal {
User(User),
Impersonated { user: User, admin: User },
- DevServer(dev_server::Model),
}
impl Principal {
@@ -124,9 +120,6 @@ impl Principal {
span.record("login", &user.github_login);
span.record("impersonator", &admin.github_login);
}
- Principal::DevServer(dev_server) => {
- span.record("dev_server_id", dev_server.id.0);
- }
}
}
}
@@ -167,27 +160,10 @@ impl Session {
}
}
- fn for_user(self) -> Option<UserSession> {
- UserSession::new(self)
- }
-
- fn for_dev_server(self) -> Option<DevServerSession> {
- DevServerSession::new(self)
- }
-
- fn user_id(&self) -> Option<UserId> {
- match &self.principal {
- Principal::User(user) => Some(user.id),
- Principal::Impersonated { user, .. } => Some(user.id),
- Principal::DevServer(_) => None,
- }
- }
-
fn is_staff(&self) -> bool {
match &self.principal {
Principal::User(user) => user.admin,
Principal::Impersonated { .. } => true,
- Principal::DevServer(_) => false,
}
}
@@ -199,9 +175,7 @@ impl Session {
return Ok(true);
}
- let Some(user_id) = self.user_id() else {
- return Ok(false);
- };
+ let user_id = self.user_id();
Ok(db.has_active_billing_subscription(user_id).await?)
}
@@ -217,18 +191,17 @@ impl Session {
}
}
- fn dev_server_id(&self) -> Option<DevServerId> {
+ fn user_id(&self) -> UserId {
match &self.principal {
- Principal::User(_) | Principal::Impersonated { .. } => None,
- Principal::DevServer(dev_server) => Some(dev_server.id),
+ Principal::User(user) => user.id,
+ Principal::Impersonated { user, .. } => user.id,
}
}
- fn principal_id(&self) -> PrincipalId {
+ pub fn email(&self) -> Option<String> {
match &self.principal {
- Principal::User(user) => PrincipalId::UserId(user.id),
- Principal::Impersonated { user, .. } => PrincipalId::UserId(user.id),
- Principal::DevServer(dev_server) => PrincipalId::DevServerId(dev_server.id),
+ Principal::User(user) => user.email_address.clone(),
+ Principal::Impersonated { user, .. } => user.email_address.clone(),
}
}
}
@@ -244,143 +217,11 @@ impl Debug for Session {
result.field("user", &user.github_login);
result.field("impersonator", &admin.github_login);
}
- Principal::DevServer(dev_server) => {
- result.field("dev_server", &dev_server.id);
- }
}
result.field("connection_id", &self.connection_id).finish()
}
}
-struct UserSession(Session);
-
-impl UserSession {
- pub fn new(s: Session) -> Option<Self> {
- s.user_id().map(|_| UserSession(s))
- }
- pub fn user_id(&self) -> UserId {
- self.0.user_id().unwrap()
- }
-
- pub fn email(&self) -> Option<String> {
- match &self.0.principal {
- Principal::User(user) => user.email_address.clone(),
- Principal::Impersonated { user, .. } => user.email_address.clone(),
- Principal::DevServer(..) => None,
- }
- }
-}
-
-impl Deref for UserSession {
- type Target = Session;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-impl DerefMut for UserSession {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-struct DevServerSession(Session);
-
-impl DevServerSession {
- pub fn new(s: Session) -> Option<Self> {
- s.dev_server_id().map(|_| DevServerSession(s))
- }
- pub fn dev_server_id(&self) -> DevServerId {
- self.0.dev_server_id().unwrap()
- }
-
- fn dev_server(&self) -> &dev_server::Model {
- match &self.0.principal {
- Principal::DevServer(dev_server) => dev_server,
- _ => unreachable!(),
- }
- }
-}
-
-impl Deref for DevServerSession {
- type Target = Session;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-impl DerefMut for DevServerSession {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-fn user_handler<M: RequestMessage, Fut>(
- handler: impl 'static + Send + Sync + Fn(M, Response<M>, UserSession) -> Fut,
-) -> impl 'static + Send + Sync + Fn(M, Response<M>, Session) -> BoxFuture<'static, Result<()>>
-where
- Fut: Send + Future<Output = Result<()>>,
-{
- let handler = Arc::new(handler);
- move |message, response, session| {
- let handler = handler.clone();
- Box::pin(async move {
- if let Some(user_session) = session.for_user() {
- Ok(handler(message, response, user_session).await?)
- } else {
- Err(Error::Internal(anyhow!(
- "must be a user to call {}",
- M::NAME
- )))
- }
- })
- }
-}
-
-fn dev_server_handler<M: RequestMessage, Fut>(
- handler: impl 'static + Send + Sync + Fn(M, Response<M>, DevServerSession) -> Fut,
-) -> impl 'static + Send + Sync + Fn(M, Response<M>, Session) -> BoxFuture<'static, Result<()>>
-where
- Fut: Send + Future<Output = Result<()>>,
-{
- let handler = Arc::new(handler);
- move |message, response, session| {
- let handler = handler.clone();
- Box::pin(async move {
- if let Some(dev_server_session) = session.for_dev_server() {
- Ok(handler(message, response, dev_server_session).await?)
- } else {
- Err(Error::Internal(anyhow!(
- "must be a dev server to call {}",
- M::NAME
- )))
- }
- })
- }
-}
-
-fn user_message_handler<M: EnvelopedMessage, InnertRetFut>(
- handler: impl 'static + Send + Sync + Fn(M, UserSession) -> InnertRetFut,
-) -> impl 'static + Send + Sync + Fn(M, Session) -> BoxFuture<'static, Result<()>>
-where
- InnertRetFut: Send + Future<Output = Result<()>>,
-{
- let handler = Arc::new(handler);
- move |message, session| {
- let handler = handler.clone();
- Box::pin(async move {
- if let Some(user_session) = session.for_user() {
- Ok(handler(message, user_session).await?)
- } else {
- Err(Error::Internal(anyhow!(
- "must be a user to call {}",
- M::NAME
- )))
- }
- })
- }
-}
-
struct DbHandle(Arc<Database>);
impl Deref for DbHandle {
@@ -434,141 +275,64 @@ impl Server {
server
.add_request_handler(ping)
- .add_request_handler(user_handler(create_room))
- .add_request_handler(user_handler(join_room))
- .add_request_handler(user_handler(rejoin_room))
- .add_request_handler(user_handler(leave_room))
- .add_request_handler(user_handler(set_room_participant_role))
- .add_request_handler(user_handler(call))
- .add_request_handler(user_handler(cancel_call))
- .add_message_handler(user_message_handler(decline_call))
- .add_request_handler(user_handler(update_participant_location))
- .add_request_handler(user_handler(share_project))
+ .add_request_handler(create_room)
+ .add_request_handler(join_room)
+ .add_request_handler(rejoin_room)
+ .add_request_handler(leave_room)
+ .add_request_handler(set_room_participant_role)
+ .add_request_handler(call)
+ .add_request_handler(cancel_call)
+ .add_message_handler(decline_call)
+ .add_request_handler(update_participant_location)
+ .add_request_handler(share_project)
.add_message_handler(unshare_project)
- .add_request_handler(user_handler(join_project))
- .add_request_handler(user_handler(join_hosted_project))
- .add_request_handler(user_handler(rejoin_dev_server_projects))
- .add_request_handler(user_handler(create_dev_server_project))
- .add_request_handler(user_handler(update_dev_server_project))
- .add_request_handler(user_handler(delete_dev_server_project))
- .add_request_handler(user_handler(create_dev_server))
- .add_request_handler(user_handler(regenerate_dev_server_token))
- .add_request_handler(user_handler(rename_dev_server))
- .add_request_handler(user_handler(delete_dev_server))
- .add_request_handler(user_handler(list_remote_directory))
- .add_request_handler(dev_server_handler(share_dev_server_project))
- .add_request_handler(dev_server_handler(shutdown_dev_server))
- .add_request_handler(dev_server_handler(reconnect_dev_server))
- .add_message_handler(user_message_handler(leave_project))
+ .add_request_handler(join_project)
+ .add_request_handler(join_hosted_project)
+ .add_message_handler(leave_project)
.add_request_handler(update_project)
.add_request_handler(update_worktree)
.add_message_handler(start_language_server)
.add_message_handler(update_language_server)
.add_message_handler(update_diagnostic_summary)
.add_message_handler(update_worktree_settings)
- .add_request_handler(user_handler(
- forward_project_request_for_owner::<proto::TaskContextForLocation>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::GetHover>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::GetDefinition>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::GetTypeDefinition>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::GetReferences>,
- ))
- .add_request_handler(user_handler(forward_find_search_candidates_request))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::GetDocumentHighlights>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::GetProjectSymbols>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::OpenBufferForSymbol>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::OpenBufferById>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::SynchronizeBuffers>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::InlayHints>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::ResolveInlayHint>,
- ))
- .add_request_handler(user_handler(
- forward_read_only_project_request::<proto::OpenBufferByPath>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::GetCompletions>,
- ))
- .add_request_handler(user_handler(
+ .add_request_handler(forward_read_only_project_request::<proto::GetHover>)
+ .add_request_handler(forward_read_only_project_request::<proto::GetDefinition>)
+ .add_request_handler(forward_read_only_project_request::<proto::GetTypeDefinition>)
+ .add_request_handler(forward_read_only_project_request::<proto::GetReferences>)
+ .add_request_handler(forward_find_search_candidates_request)
+ .add_request_handler(forward_read_only_project_request::<proto::GetDocumentHighlights>)
+ .add_request_handler(forward_read_only_project_request::<proto::GetProjectSymbols>)
+ .add_request_handler(forward_read_only_project_request::<proto::OpenBufferForSymbol>)
+ .add_request_handler(forward_read_only_project_request::<proto::OpenBufferById>)
+ .add_request_handler(forward_read_only_project_request::<proto::SynchronizeBuffers>)
+ .add_request_handler(forward_read_only_project_request::<proto::InlayHints>)
+ .add_request_handler(forward_read_only_project_request::<proto::ResolveInlayHint>)
+ .add_request_handler(forward_read_only_project_request::<proto::OpenBufferByPath>)
+ .add_request_handler(forward_mutating_project_request::<proto::GetCompletions>)
+ .add_request_handler(
forward_mutating_project_request::<proto::ApplyCompletionAdditionalEdits>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::OpenNewBuffer>,
- ))
- .add_request_handler(user_handler(
+ )
+ .add_request_handler(forward_mutating_project_request::<proto::OpenNewBuffer>)
+ .add_request_handler(
forward_mutating_project_request::<proto::ResolveCompletionDocumentation>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::GetCodeActions>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::ApplyCodeAction>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::PrepareRename>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::PerformRename>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::ReloadBuffers>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::FormatBuffers>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::CreateProjectEntry>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::RenameProjectEntry>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::CopyProjectEntry>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::DeleteProjectEntry>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::ExpandProjectEntry>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::OnTypeFormatting>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::SaveBuffer>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::BlameBuffer>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::MultiLspQuery>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::RestartLanguageServers>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::LinkedEditingRange>,
- ))
+ )
+ .add_request_handler(forward_mutating_project_request::<proto::GetCodeActions>)
+ .add_request_handler(forward_mutating_project_request::<proto::ApplyCodeAction>)
+ .add_request_handler(forward_mutating_project_request::<proto::PrepareRename>)
+ .add_request_handler(forward_mutating_project_request::<proto::PerformRename>)
+ .add_request_handler(forward_mutating_project_request::<proto::ReloadBuffers>)
+ .add_request_handler(forward_mutating_project_request::<proto::FormatBuffers>)
+ .add_request_handler(forward_mutating_project_request::<proto::CreateProjectEntry>)
+ .add_request_handler(forward_mutating_project_request::<proto::RenameProjectEntry>)
+ .add_request_handler(forward_mutating_project_request::<proto::CopyProjectEntry>)
+ .add_request_handler(forward_mutating_project_request::<proto::DeleteProjectEntry>)
+ .add_request_handler(forward_mutating_project_request::<proto::ExpandProjectEntry>)
+ .add_request_handler(forward_mutating_project_request::<proto::OnTypeFormatting>)
+ .add_request_handler(forward_mutating_project_request::<proto::SaveBuffer>)
+ .add_request_handler(forward_mutating_project_request::<proto::BlameBuffer>)
+ .add_request_handler(forward_mutating_project_request::<proto::MultiLspQuery>)
+ .add_request_handler(forward_mutating_project_request::<proto::RestartLanguageServers>)
+ .add_request_handler(forward_mutating_project_request::<proto::LinkedEditingRange>)
.add_message_handler(create_buffer_for_peer)
.add_request_handler(update_buffer)
.add_message_handler(broadcast_project_message_from_host::<proto::RefreshInlayHints>)
@@ -577,53 +341,47 @@ impl Server {
.add_message_handler(broadcast_project_message_from_host::<proto::BufferSaved>)
.add_message_handler(broadcast_project_message_from_host::<proto::UpdateDiffBase>)
.add_request_handler(get_users)
- .add_request_handler(user_handler(fuzzy_search_users))
- .add_request_handler(user_handler(request_contact))
- .add_request_handler(user_handler(remove_contact))
- .add_request_handler(user_handler(respond_to_contact_request))
+ .add_request_handler(fuzzy_search_users)
+ .add_request_handler(request_contact)
+ .add_request_handler(remove_contact)
+ .add_request_handler(respond_to_contact_request)
.add_message_handler(subscribe_to_channels)
- .add_request_handler(user_handler(create_channel))
- .add_request_handler(user_handler(delete_channel))
- .add_request_handler(user_handler(invite_channel_member))
- .add_request_handler(user_handler(remove_channel_member))
- .add_request_handler(user_handler(set_channel_member_role))
- .add_request_handler(user_handler(set_channel_visibility))
- .add_request_handler(user_handler(rename_channel))
- .add_request_handler(user_handler(join_channel_buffer))
- .add_request_handler(user_handler(leave_channel_buffer))
- .add_message_handler(user_message_handler(update_channel_buffer))
- .add_request_handler(user_handler(rejoin_channel_buffers))
- .add_request_handler(user_handler(get_channel_members))
- .add_request_handler(user_handler(respond_to_channel_invite))
- .add_request_handler(user_handler(join_channel))
- .add_request_handler(user_handler(join_channel_chat))
- .add_message_handler(user_message_handler(leave_channel_chat))
- .add_request_handler(user_handler(send_channel_message))
- .add_request_handler(user_handler(remove_channel_message))
- .add_request_handler(user_handler(update_channel_message))
- .add_request_handler(user_handler(get_channel_messages))
- .add_request_handler(user_handler(get_channel_messages_by_id))
- .add_request_handler(user_handler(get_notifications))
- .add_request_handler(user_handler(mark_notification_as_read))
- .add_request_handler(user_handler(move_channel))
- .add_request_handler(user_handler(follow))
- .add_message_handler(user_message_handler(unfollow))
- .add_message_handler(user_message_handler(update_followers))
- .add_request_handler(user_handler(get_private_user_info))
- .add_request_handler(user_handler(get_llm_api_token))
- .add_request_handler(user_handler(accept_terms_of_service))
- .add_message_handler(user_message_handler(acknowledge_channel_message))
- .add_message_handler(user_message_handler(acknowledge_buffer_version))
- .add_request_handler(user_handler(get_supermaven_api_key))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::OpenContext>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::CreateContext>,
- ))
- .add_request_handler(user_handler(
- forward_mutating_project_request::<proto::SynchronizeContexts>,
- ))
+ .add_request_handler(create_channel)
+ .add_request_handler(delete_channel)
+ .add_request_handler(invite_channel_member)
+ .add_request_handler(remove_channel_member)
+ .add_request_handler(set_channel_member_role)
+ .add_request_handler(set_channel_visibility)
+ .add_request_handler(rename_channel)
+ .add_request_handler(join_channel_buffer)
+ .add_request_handler(leave_channel_buffer)
+ .add_message_handler(update_channel_buffer)
+ .add_request_handler(rejoin_channel_buffers)
+ .add_request_handler(get_channel_members)
+ .add_request_handler(respond_to_channel_invite)
+ .add_request_handler(join_channel)
+ .add_request_handler(join_channel_chat)
+ .add_message_handler(leave_channel_chat)
+ .add_request_handler(send_channel_message)
+ .add_request_handler(remove_channel_message)
+ .add_request_handler(update_channel_message)
+ .add_request_handler(get_channel_messages)
+ .add_request_handler(get_channel_messages_by_id)
+ .add_request_handler(get_notifications)
+ .add_request_handler(mark_notification_as_read)
+ .add_request_handler(move_channel)
+ .add_request_handler(follow)
+ .add_message_handler(unfollow)
+ .add_message_handler(update_followers)
+ .add_request_handler(get_private_user_info)
+ .add_request_handler(get_llm_api_token)
+ .add_request_handler(accept_terms_of_service)
+ .add_message_handler(acknowledge_channel_message)
+ .add_message_handler(acknowledge_buffer_version)
+ .add_request_handler(get_supermaven_api_key)
+ .add_request_handler(forward_mutating_project_request::<proto::OpenContext>)
+ .add_request_handler(forward_mutating_project_request::<proto::CreateContext>)
+ .add_request_handler(forward_mutating_project_request::<proto::SynchronizeContexts>)
.add_message_handler(broadcast_project_message_from_host::<proto::AdvertiseContexts>)
.add_message_handler(update_context)
.add_request_handler({
@@ -636,21 +394,17 @@ impl Server {
}
}
})
- .add_request_handler({
- user_handler(move |request, response, session| {
- get_cached_embeddings(request, response, session)
- })
- })
+ .add_request_handler(get_cached_embeddings)
.add_request_handler({
let app_state = app_state.clone();
- user_handler(move |request, response, session| {
+ move |request, response, session| {
compute_embeddings(
request,
response,
session,
app_state.config.openai_api_key.clone(),
)
- })
+ }
});
Arc::new(server)
@@ -936,7 +690,6 @@ impl Server {
user_id=field::Empty,
login=field::Empty,
impersonator=field::Empty,
- dev_server_id=field::Empty,
geoip_country_code=field::Empty
);
principal.update_span(&span);
@@ -1031,7 +784,6 @@ impl Server {
user_id=field::Empty,
login=field::Empty,
impersonator=field::Empty,
- dev_server_id=field::Empty
);
principal.update_span(&span);
let span_enter = span.enter();
@@ -1100,11 +852,7 @@ impl Server {
update_user_plan(user.id, session).await?;
- let (contacts, dev_server_projects) = future::try_join(
- self.app_state.db.get_contacts(user.id),
- self.app_state.db.dev_server_projects_update(user.id),
- )
- .await?;
+ let contacts = self.app_state.db.get_contacts(user.id).await?;
{
let mut pool = self.connection_pool.lock();
@@ -1119,8 +867,6 @@ impl Server {
subscribe_user_to_channels(user.id, session).await?;
}
- send_dev_server_projects_update(user.id, dev_server_projects, session).await;
-
if let Some(incoming_call) =
self.app_state.db.incoming_call_for_user(user.id).await?
{
@@ -1129,39 +875,6 @@ impl Server {
update_user_contacts(user.id, session).await?;
}
- Principal::DevServer(dev_server) => {
- {
- let mut pool = self.connection_pool.lock();
- if let Some(stale_connection_id) = pool.dev_server_connection_id(dev_server.id)
- {
- self.peer.send(
- stale_connection_id,
- proto::ShutdownDevServer {
- reason: Some(
- "another dev server connected with the same token".to_string(),
- ),
- },
- )?;
- pool.remove_connection(stale_connection_id)?;
- };
- pool.add_dev_server(connection_id, dev_server.id, zed_version);
- }
-
- let projects = self
- .app_state
- .db
- .get_projects_for_dev_server(dev_server.id)
- .await?;
- self.peer
- .send(connection_id, proto::DevServerInstructions { projects })?;
-
- let status = self
- .app_state
- .db
- .dev_server_projects_update(dev_server.user_id)
- .await?;
- send_dev_server_projects_update(dev_server.user_id, status, session).await;
- }
}
Ok(())
@@ -1452,33 +1165,25 @@ async fn connection_lost(
futures::select_biased! {
_ = executor.sleep(RECONNECT_TIMEOUT).fuse() => {
- match &session.principal {
- Principal::User(_) | Principal::Impersonated{ user: _, admin:_ } => {
- let session = session.for_user().unwrap();
-
- log::info!("connection lost, removing all resources for user:{}, connection:{:?}", session.user_id(), session.connection_id);
- leave_room_for_session(&session, session.connection_id).await.trace_err();
- leave_channel_buffers_for_session(&session)
- .await
- .trace_err();
-
- if !session
- .connection_pool()
- .await
- .is_user_online(session.user_id())
- {
- let db = session.db().await;
- if let Some(room) = db.decline_call(None, session.user_id()).await.trace_err().flatten() {
- room_updated(&room, &session.peer);
- }
- }
- update_user_contacts(session.user_id(), &session).await?;
- },
- Principal::DevServer(_) => {
- lost_dev_server_connection(&session.for_dev_server().unwrap()).await?;
- },
- }
+ log::info!("connection lost, removing all resources for user:{}, connection:{:?}", session.user_id(), session.connection_id);
+ leave_room_for_session(&session, session.connection_id).await.trace_err();
+ leave_channel_buffers_for_session(&session)
+ .await
+ .trace_err();
+
+ if !session
+ .connection_pool()
+ .await
+ .is_user_online(session.user_id())
+ {
+ let db = session.db().await;
+ if let Some(room) = db.decline_call(None, session.user_id()).await.trace_err().flatten() {
+ room_updated(&room, &session.peer);
+ }
+ }
+
+ update_user_contacts(session.user_id(), &session).await?;
},
_ = teardown.changed().fuse() => {}
}
@@ -1496,7 +1201,7 @@ async fn ping(_: proto::Ping, response: Response<proto::Ping>, _session: Session
async fn create_room(
_request: proto::CreateRoom,
response: Response<proto::CreateRoom>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let live_kit_room = nanoid::nanoid!(30);
@@ -1536,7 +1241,7 @@ async fn create_room(
async fn join_room(
request: proto::JoinRoom,
response: Response<proto::JoinRoom>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let room_id = RoomId::from_proto(request.id);
@@ -1603,7 +1308,7 @@ async fn join_room(
async fn rejoin_room(
request: proto::RejoinRoom,
response: Response<proto::RejoinRoom>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let room;
let channel;
@@ -1693,7 +1398,7 @@ async fn rejoin_room(
fn notify_rejoined_projects(
rejoined_projects: &mut Vec<RejoinedProject>,
- session: &UserSession,
+ session: &Session,
) -> Result<()> {
for project in rejoined_projects.iter() {
for collaborator in &project.collaborators {
@@ -1778,7 +1483,7 @@ fn notify_rejoined_projects(
async fn leave_room(
_: proto::LeaveRoom,
response: Response<proto::LeaveRoom>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
leave_room_for_session(&session, session.connection_id).await?;
response.send(proto::Ack {})?;
@@ -1789,7 +1494,7 @@ async fn leave_room(
async fn set_room_participant_role(
request: proto::SetRoomParticipantRole,
response: Response<proto::SetRoomParticipantRole>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let user_id = UserId::from_proto(request.user_id);
let role = ChannelRole::from(request.role());
@@ -1837,7 +1542,7 @@ async fn set_room_participant_role(
async fn call(
request: proto::Call,
response: Response<proto::Call>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let room_id = RoomId::from_proto(request.room_id);
let calling_user_id = session.user_id();
@@ -1906,7 +1611,7 @@ async fn call(
async fn cancel_call(
request: proto::CancelCall,
response: Response<proto::CancelCall>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let called_user_id = UserId::from_proto(request.called_user_id);
let room_id = RoomId::from_proto(request.room_id);
@@ -1941,7 +1646,7 @@ async fn cancel_call(
}
/// Decline an incoming call.
-async fn decline_call(message: proto::DeclineCall, session: UserSession) -> Result<()> {
+async fn decline_call(message: proto::DeclineCall, session: Session) -> Result<()> {
let room_id = RoomId::from_proto(message.room_id);
{
let room = session
@@ -1976,7 +1681,7 @@ async fn decline_call(message: proto::DeclineCall, session: UserSession) -> Resu
async fn update_participant_location(
request: proto::UpdateParticipantLocation,
response: Response<proto::UpdateParticipantLocation>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let room_id = RoomId::from_proto(request.room_id);
let location = request
@@ -1997,7 +1702,7 @@ async fn update_participant_location(
async fn share_project(
request: proto::ShareProject,
response: Response<proto::ShareProject>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let (project_id, room) = &*session
.db()
@@ -2007,9 +1712,6 @@ async fn share_project(
session.connection_id,
&request.worktrees,
request.is_ssh_project,
- request
- .dev_server_project_id
- .map(DevServerProjectId::from_proto),
)
.await?;
response.send(proto::ShareProjectResponse {
@@ -2023,26 +1725,19 @@ async fn share_project(
/// Unshare a project from the room.
async fn unshare_project(message: proto::UnshareProject, session: Session) -> Result<()> {
let project_id = ProjectId::from_proto(message.project_id);
- unshare_project_internal(
- project_id,
- session.connection_id,
- session.user_id(),
- &session,
- )
- .await
+ unshare_project_internal(project_id, session.connection_id, &session).await
}
async fn unshare_project_internal(
project_id: ProjectId,
connection_id: ConnectionId,
- user_id: Option<UserId>,
session: &Session,
) -> Result<()> {
let delete = {
let room_guard = session
.db()
.await
- .unshare_project(project_id, connection_id, user_id)
+ .unshare_project(project_id, connection_id)
.await?;
let (delete, room, guest_connection_ids) = &*room_guard;
@@ -2071,38 +1766,11 @@ async fn unshare_project_internal(
Ok(())
}
-/// DevServer makes a project available online
-async fn share_dev_server_project(
- request: proto::ShareDevServerProject,
- response: Response<proto::ShareDevServerProject>,
- session: DevServerSession,
-) -> Result<()> {
- let (dev_server_project, user_id, status) = session
- .db()
- .await
- .share_dev_server_project(
- DevServerProjectId::from_proto(request.dev_server_project_id),
- session.dev_server_id(),
- session.connection_id,
- &request.worktrees,
- )
- .await?;
- let Some(project_id) = dev_server_project.project_id else {
- return Err(anyhow!("failed to share remote project"))?;
- };
-
- send_dev_server_projects_update(user_id, status, &session).await;
-
- response.send(proto::ShareProjectResponse { project_id })?;
-
- Ok(())
-}
-
/// Join someone elses shared project.
async fn join_project(
request: proto::JoinProject,
response: Response<proto::JoinProject>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let project_id = ProjectId::from_proto(request.project_id);
@@ -2133,7 +1801,7 @@ impl JoinProjectInternalResponse for Response<proto::JoinHostedProject> {
fn join_project_internal(
response: impl JoinProjectInternalResponse,
- session: UserSession,
+ session: Session,
project: &mut Project,
replica_id: &ReplicaId,
) -> Result<()> {
@@ -2184,9 +1852,6 @@ fn join_project_internal(
collaborators: collaborators.clone(),
language_servers: project.language_servers.clone(),
role: project.role.into(),
- dev_server_project_id: project
- .dev_server_project_id
- .map(|dev_server_project_id| dev_server_project_id.0 as u64),
})?;
for (worktree_id, worktree) in mem::take(&mut project.worktrees) {
@@ -2252,7 +1917,7 @@ fn join_project_internal(
}
/// Leave someone elses shared project.
-async fn leave_project(request: proto::LeaveProject, session: UserSession) -> Result<()> {
+async fn leave_project(request: proto::LeaveProject, session: Session) -> Result<()> {
let sender_id = session.connection_id;
let project_id = ProjectId::from_proto(request.project_id);
let db = session.db().await;
@@ -2279,7 +1944,7 @@ async fn leave_project(request: proto::LeaveProject, session: UserSession) -> Re
async fn join_hosted_project(
request: proto::JoinHostedProject,
response: Response<proto::JoinHostedProject>,
- session: UserSession,
+ session: Session,
) -> Result<()> {
let (mut project, replica_id) = session
.db()
@@ -2294,481 +1959,6 @@ async fn join_hosted_project(
join_project_internal(response, session, &mut project, &replica_id)
}
-async fn list_remote_directory(
- request: proto::ListRemoteDirectory,
- response: Response<proto::ListRemoteDirectory>,
- session: UserSession,
-) -> Result<()> {
- let dev_server_id = DevServerId(request.dev_server_id as i32);
- let dev_server_connection_id = session
- .connection_pool()
- .await
- .online_dev_server_connection_id(dev_server_id)?;
-
- session
- .db()
- .await
- .get_dev_server_for_user(dev_server_id, session.user_id())
- .await?;
-
- response.send(
- session
- .peer
- .forward_request(session.connection_id, dev_server_connection_id, request)
- .await?,
- )?;
- Ok(())
-}
-
-async fn update_dev_server_project(
- request: proto::UpdateDevServerProject,
- response: Response<proto::UpdateDevServerProject>,
- session: UserSession,
-) -> Result<()> {
- let dev_server_project_id = DevServerProjectId(request.dev_server_project_id as i32);
-
- let (dev_server_project, update) = session
- .db()
- .await
- .update_dev_server_project(dev_server_project_id, &request.paths, session.user_id())
- .await?;
-
- let projects = session
- .db()
- .await
- .get_projects_for_dev_server(dev_server_project.dev_server_id)
- .await?;
-
- let dev_server_connection_id = session
- .connection_pool()
- .await
- .online_dev_server_connection_id(dev_server_project.dev_server_id)?;
-
- session.peer.send(
- dev_server_connection_id,
- proto::DevServerInstructions { projects },
- )?;
-
- send_dev_server_projects_update(session.user_id(), update, &session).await;
-
- response.send(proto::Ack {})
-}
-
-async fn create_dev_server_project(
- request: proto::CreateDevServerProject,
- response: Response<proto::CreateDevServerProject>,
- session: UserSession,
-) -> Result<()> {
- let dev_server_id = DevServerId(request.dev_server_id as i32);
- let dev_server_connection_id = session
- .connection_pool()
- .await
- .dev_server_connection_id(dev_server_id);
- let Some(dev_server_connection_id) = dev_server_connection_id else {
- Err(ErrorCode::DevServerOffline
- .message("Cannot create a remote project when the dev server is offline".to_string())
- .anyhow())?
- };
-
- let path = request.path.clone();
- //Check that the path exists on the dev server
- session
- .peer
- .forward_request(
- session.connection_id,
- dev_server_connection_id,
- proto::ValidateDevServerProjectRequest { path: path.clone() },
- )
- .await?;
-
- let (dev_server_project, update) = session
- .db()
- .await
- .create_dev_server_project(
- DevServerId(request.dev_server_id as i32),
- &request.path,
- session.user_id(),
- )
- .await?;
-
- let projects = session
- .db()
- .await
- .get_projects_for_dev_server(dev_server_project.dev_server_id)
- .await?;
-
- session.peer.send(
- dev_server_connection_id,
- proto::DevServerInstructions { projects },
- )?;
-
- send_dev_server_projects_update(session.user_id(), update, &session).await;
-
- response.send(proto::CreateDevServerProjectResponse {
- dev_server_project: Some(dev_server_project.to_proto(None)),
- })?;
- Ok(())
-}
-
-async fn create_dev_server(
- request: proto::CreateDevServer,
- response: Response<proto::CreateDevServer>,
- session: UserSession,
-) -> Result<()> {
- let access_token = auth::random_token();
- let hashed_access_token = auth::hash_access_token(&access_token);
-
- if request.name.is_empty() {
- return Err(proto::ErrorCode::Forbidden
- .message("Dev server name cannot be empty".to_string())
- .anyhow())?;
- }
-
- let (dev_server, status) = session
- .db()
- .await
- .create_dev_server(
- &request.name,
- request.ssh_connection_string.as_deref(),
- &hashed_access_token,
- session.user_id(),
- )
- .await?;
-
- send_dev_server_projects_update(session.user_id(), status, &session).await;
-
- response.send(proto::CreateDevServerResponse {
- dev_server_id: dev_server.id.0 as u64,
- access_token: auth::generate_dev_server_token(dev_server.id.0 as usize, access_token),
- name: request.name,
- })?;
- Ok(())
-}
-
-async fn regenerate_dev_server_token(
- request: proto::RegenerateDevServerToken,
- response: Response<proto::RegenerateDevServerToken>,
- session: UserSession,
-) -> Result<()> {
- let dev_server_id = DevServerId(request.dev_server_id as i32);
- let access_token = auth::random_token();
- let hashed_access_token = auth::hash_access_token(&access_token);
-
- let connection_id = session
- .connection_pool()
- .await
- .dev_server_connection_id(dev_server_id);
- if let Some(connection_id) = connection_id {
- shutdown_dev_server_internal(dev_server_id, connection_id, &session).await?;
- session.peer.send(
- connection_id,
- proto::ShutdownDevServer {
- reason: Some("dev server token was regenerated".to_string()),
- },
- )?;
- let _ = remove_dev_server_connection(dev_server_id, &session).await;
- }
-
- let status = session
- .db()
- .await
- .update_dev_server_token(dev_server_id, &hashed_access_token, session.user_id())
- .await?;
-
- send_dev_server_projects_update(session.user_id(), status, &session).await;
-
- response.send(proto::RegenerateDevServerTokenResponse {
- dev_server_id: dev_server_id.to_proto(),
- access_token: auth::generate_dev_server_token(dev_server_id.0 as usize, access_token),
- })?;
- Ok(())
-}
-
-async fn rename_dev_server(
- request: proto::RenameDevServer,
- response: Response<proto::RenameDevServer>,
- session: UserSession,
-) -> Result<()> {
- if request.name.trim().is_empty() {
- return Err(proto::ErrorCode::Forbidden
- .message("Dev server name cannot be empty".to_string())
- .anyhow())?;
- }
-
- let dev_server_id = DevServerId(request.dev_server_id as i32);
- let dev_server = session.db().await.get_dev_server(dev_server_id).await?;
- if dev_server.user_id != session.user_id() {
- return Err(anyhow!(ErrorCode::Forbidden))?;
- }
-
- let status = session
- .db()
- .await
- .rename_dev_server(
- dev_server_id,
- &request.name,
- request.ssh_connection_string.as_deref(),
- session.user_id(),
- )
- .await?;
-
- send_dev_server_projects_update(session.user_id(), status, &session).await;
-
- response.send(proto::Ack {})?;
- Ok(())
-}
-
-async fn delete_dev_server(
- request: proto::DeleteDevServer,
- response: Response<proto::DeleteDevServer>,
- session: UserSession,
-) -> Result<()> {
- let dev_server_id = DevServerId(request.dev_server_id as i32);
- let dev_server = session.db().await.get_dev_server(dev_server_id).await?;
- if dev_server.user_id != session.user_id() {
- return Err(anyhow!(ErrorCode::Forbidden))?;
- }
-
- let connection_id = session
- .connection_pool()
- .await
- .dev_server_connection_id(dev_server_id);
- if let Some(connection_id) = connection_id {
- shutdown_dev_server_internal(dev_server_id, connection_id, &session).await?;
- session.peer.send(
- connection_id,
- proto::ShutdownDevServer {
- reason: Some("dev server was deleted".to_string()),
- },
- )?;
- let _ = remove_dev_server_connection(dev_server_id, &session).await;
- }
-
- let status = session
- .db()
- .await
- .delete_dev_server(dev_server_id, session.user_id())
- .await?;
-
- send_dev_server_projects_update(session.user_id(), status, &session).await;
-
- response.send(proto::Ack {})?;
- Ok(())
-}
-
-async fn delete_dev_server_project(
- request: proto::DeleteDevServerProject,
- response: Response<proto::DeleteDevServerProject>,
- session: UserSession,
-) -> Result<()> {
- let dev_server_project_id = DevServerProjectId(request.dev_server_project_id as i32);
- let dev_server_project = session
- .db()
- .await
- .get_dev_server_project(dev_server_project_id)
- .await?;
-
- let dev_server = session
- .db()
- .await
- .get_dev_server(dev_server_project.dev_server_id)
- .await?;
- if dev_server.user_id != session.user_id() {
- return Err(anyhow!(ErrorCode::Forbidden))?;
- }
-
- let dev_server_connection_id = session
- .connection_pool()
- .await
- .dev_server_connection_id(dev_server.id);
-
- if let Some(dev_server_connection_id) = dev_server_connection_id {
- let project = session
- .db()
- .await
- .find_dev_server_project(dev_server_project_id)
- .await;
- if let Ok(project) = project {
- unshare_project_internal(
- project.id,
- dev_server_connection_id,
- Some(session.user_id()),
- &session,
- )
- .await?;
- }
- }
-
- let (projects, status) = session
- .db()
- .await
- .delete_dev_server_project(dev_server_project_id, dev_server.id, session.user_id())
- .await?;
-
- if let Some(dev_server_connection_id) = dev_server_connection_id {
- session.peer.send(
- dev_server_connection_id,
- proto::DevServerInstructions { projects },
- )?;
- }
-
- send_dev_server_projects_update(session.user_id(), status, &session).await;
-
- response.send(proto::Ack {})?;
- Ok(())
-}
-
-async fn rejoin_dev_server_projects(
- request: proto::RejoinRemoteProjects,
- response: Response<proto::RejoinRemoteProjects>,
- session: UserSession,
-) -> Result<()> {
- let mut rejoined_projects = {
- let db = session.db().await;
- db.rejoin_dev_server_projects(
- &request.rejoined_projects,
- session.user_id(),
- session.0.connection_id,
- )
- .await?
- };
- response.send(proto::RejoinRemoteProjectsResponse {
- rejoined_projects: rejoined_projects
- .iter()
- .map(|project| project.to_proto())
- .collect(),
- })?;
- notify_rejoined_projects(&mut rejoined_projects, &session)
-}
-
-async fn reconnect_dev_server(
- request: proto::ReconnectDevServer,
- response: Response<proto::ReconnectDevServer>,
- session: DevServerSession,
-) -> Result<()> {
- let reshared_projects = {
- let db = session.db().await;
- db.reshare_dev_server_projects(
- &request.reshared_projects,
- session.dev_server_id(),
- session.0.connection_id,
- )
- .await?
- };
-
- for project in &reshared_projects {
- for collaborator in &project.collaborators {
- session
- .peer
- .send(
- collaborator.connection_id,
- proto::UpdateProjectCollaborator {
- project_id: project.id.to_proto(),
- old_peer_id: Some(project.old_connection_id.into()),
- new_peer_id: Some(session.connection_id.into()),
- },
- )
- .trace_err();
- }
-
- broadcast(
- Some(session.connection_id),
- project
- .collaborators
- .iter()
- .map(|collaborator| collaborator.connection_id),
- |connection_id| {
- session.peer.forward_send(
- session.connection_id,
- connection_id,
- proto::UpdateProject {
- project_id: project.id.to_proto(),
- worktrees: project.worktrees.clone(),
- },
- )
- },
- );
- }
-
- response.send(proto::ReconnectDevServerResponse {
- reshared_projects: reshared_projects
- .iter()
- .map(|project| proto::ResharedProject {
- id: project.id.to_proto(),
- collaborators: project
- .collaborators
- .iter()
- .map(|collaborator| collaborator.to_proto())
- .collect(),
- })
- .collect(),
- })?;
-
- Ok(())
-}
-
-async fn shutdown_dev_server(
- _: proto::ShutdownDevServer,
- response: Response<proto::ShutdownDevServer>,
- session: DevServerSession,
-) -> Result<()> {
- response.send(proto::Ack {})?;
- shutdown_dev_server_internal(session.dev_server_id(), session.connection_id, &session).await?;
- remove_dev_server_connection(session.dev_server_id(), &session).await
-}
-
-async fn shutdown_dev_server_internal(
- dev_server_id: DevServerId,
- connection_id: ConnectionId,
- session: &Session,
-) -> Result<()> {
- let (dev_server_projects, dev_server) = {
- let db = session.db().await;
- let dev_server_projects = db.get_projects_for_dev_server(dev_server_id).await?;
- let dev_server = db.get_dev_server(dev_server_id).await?;
- (dev_server_projects, dev_server)
- };
-
- for project_id in dev_server_projects.iter().filter_map(|p| p.project_id) {
- unshare_project_internal(
- ProjectId::from_proto(project_id),
- connection_id,
- None,
- session,
- )
- .await?;
- }
-
- session
- .connection_pool()
- .await
- .set_dev_server_offline(dev_server_id);
-
- let status = session
- .db()
- .await
- .dev_server_projects_update(dev_server.user_id)
- .await?;
- send_dev_server_projects_update(dev_server.user_id, status, session).await;
-
- Ok(())
-}
-
-async fn remove_dev_server_connection(dev_server_id: DevServerId, session: &Session) -> Result<()> {
- let dev_server_connection = session
- .connection_pool()
- .await
- .dev_server_connection_id(dev_server_id);
-
- if let Some(dev_server_connection) = dev_server_connection {
- session
- .connection_pool()
- .await
- .remove_connection(dev_server_connection)?;
- }
- Ok(())
-}
-
/// Updates other participants with changes to the project
async fn update_project(
request: proto::UpdateProject,
@@ -1,7 +1,7 @@
-use crate::db::{ChannelId, ChannelRole, DevServerId, PrincipalId, UserId};
+use crate::db::{ChannelId, ChannelRole, UserId};
use anyhow::{anyhow, Result};
use collections::{BTreeMap, HashMap, HashSet};
-use rpc::{proto, ConnectionId};
+use rpc::ConnectionId;
use semantic_version::SemanticVersion;
use serde::Serialize;
use std::fmt;
@@ -11,9 +11,7 @@ use tracing::instrument;
pub struct ConnectionPool {
connections: BTreeMap<ConnectionId, Connection>,
connected_users: BTreeMap<UserId, ConnectedPrincipal>,
- connected_dev_servers: BTreeMap<DevServerId, ConnectionId>,
channels: ChannelPool,
- offline_dev_servers: HashSet<DevServerId>,
}
#[derive(Default, Serialize)]
@@ -32,13 +30,13 @@ impl fmt::Display for ZedVersion {
impl ZedVersion {
pub fn can_collaborate(&self) -> bool {
- self.0 >= SemanticVersion::new(0, 151, 0)
+ self.0 >= SemanticVersion::new(0, 157, 0)
}
}
#[derive(Serialize)]
pub struct Connection {
- pub principal_id: PrincipalId,
+ pub user_id: UserId,
pub admin: bool,
pub zed_version: ZedVersion,
}
@@ -47,7 +45,6 @@ impl ConnectionPool {
pub fn reset(&mut self) {
self.connections.clear();
self.connected_users.clear();
- self.connected_dev_servers.clear();
self.channels.clear();
}
@@ -66,7 +63,7 @@ impl ConnectionPool {
self.connections.insert(
connection_id,
Connection {
- principal_id: PrincipalId::UserId(user_id),
+ user_id,
admin,
zed_version,
},
@@ -75,25 +72,6 @@ impl ConnectionPool {
connected_user.connection_ids.insert(connection_id);
}
- pub fn add_dev_server(
- &mut self,
- connection_id: ConnectionId,
- dev_server_id: DevServerId,
- zed_version: ZedVersion,
- ) {
- self.connections.insert(
- connection_id,
- Connection {
- principal_id: PrincipalId::DevServerId(dev_server_id),
- admin: false,
- zed_version,
- },
- );
-
- self.connected_dev_servers
- .insert(dev_server_id, connection_id);
- }
-
#[instrument(skip(self))]
pub fn remove_connection(&mut self, connection_id: ConnectionId) -> Result<()> {
let connection = self
@@ -101,28 +79,18 @@ impl ConnectionPool {
.get_mut(&connection_id)
.ok_or_else(|| anyhow!("no such connection"))?;
- match connection.principal_id {
- PrincipalId::UserId(user_id) => {
- let connected_user = self.connected_users.get_mut(&user_id).unwrap();
- connected_user.connection_ids.remove(&connection_id);
- if connected_user.connection_ids.is_empty() {
- self.connected_users.remove(&user_id);
- self.channels.remove_user(&user_id);
- }
- }
- PrincipalId::DevServerId(dev_server_id) => {
- self.connected_dev_servers.remove(&dev_server_id);
- self.offline_dev_servers.remove(&dev_server_id);
- }
- }
+ let user_id = connection.user_id;
+
+ let connected_user = self.connected_users.get_mut(&user_id).unwrap();
+ connected_user.connection_ids.remove(&connection_id);
+ if connected_user.connection_ids.is_empty() {
+ self.connected_users.remove(&user_id);
+ self.channels.remove_user(&user_id);
+ };
self.connections.remove(&connection_id).unwrap();
Ok(())
}
- pub fn set_dev_server_offline(&mut self, dev_server_id: DevServerId) {
- self.offline_dev_servers.insert(dev_server_id);
- }
-
pub fn connections(&self) -> impl Iterator<Item = &Connection> {
self.connections.values()
}
@@ -147,42 +115,6 @@ impl ConnectionPool {
.copied()
}
- pub fn dev_server_status(&self, dev_server_id: DevServerId) -> proto::DevServerStatus {
- if self.dev_server_connection_id(dev_server_id).is_some()
- && !self.offline_dev_servers.contains(&dev_server_id)
- {
- proto::DevServerStatus::Online
- } else {
- proto::DevServerStatus::Offline
- }
- }
-
- pub fn dev_server_connection_id(&self, dev_server_id: DevServerId) -> Option<ConnectionId> {
- self.connected_dev_servers.get(&dev_server_id).copied()
- }
-
- pub fn online_dev_server_connection_id(
- &self,
- dev_server_id: DevServerId,
- ) -> Result<ConnectionId> {
- match self.connected_dev_servers.get(&dev_server_id) {
- Some(cid) => Ok(*cid),
- None => Err(anyhow!(proto::ErrorCode::DevServerOffline)),
- }
- }
-
- pub fn dev_server_connection_id_supporting(
- &self,
- dev_server_id: DevServerId,
- required: ZedVersion,
- ) -> Result<ConnectionId> {
- match self.connected_dev_servers.get(&dev_server_id) {
- Some(cid) if self.connections[cid].zed_version >= required => Ok(*cid),
- Some(_) => Err(anyhow!(proto::ErrorCode::RemoteUpgradeRequired)),
- None => Err(anyhow!(proto::ErrorCode::DevServerOffline)),
- }
- }
-
pub fn channel_user_ids(
&self,
channel_id: ChannelId,
@@ -227,39 +159,22 @@ impl ConnectionPool {
#[cfg(test)]
pub fn check_invariants(&self) {
for (connection_id, connection) in &self.connections {
- match &connection.principal_id {
- PrincipalId::UserId(user_id) => {
- assert!(self
- .connected_users
- .get(user_id)
- .unwrap()
- .connection_ids
- .contains(connection_id));
- }
- PrincipalId::DevServerId(dev_server_id) => {
- assert_eq!(
- self.connected_dev_servers.get(dev_server_id).unwrap(),
- connection_id
- );
- }
- }
+ assert!(self
+ .connected_users
+ .get(&connection.user_id)
+ .unwrap()
+ .connection_ids
+ .contains(connection_id));
}
for (user_id, state) in &self.connected_users {
for connection_id in &state.connection_ids {
assert_eq!(
- self.connections.get(connection_id).unwrap().principal_id,
- PrincipalId::UserId(*user_id)
+ self.connections.get(connection_id).unwrap().user_id,
+ *user_id
);
}
}
-
- for (dev_server_id, connection_id) in &self.connected_dev_servers {
- assert_eq!(
- self.connections.get(connection_id).unwrap().principal_id,
- PrincipalId::DevServerId(*dev_server_id)
- );
- }
}
}
@@ -8,7 +8,6 @@ mod channel_buffer_tests;
mod channel_guest_tests;
mod channel_message_tests;
mod channel_tests;
-mod dev_server_tests;
mod editor_tests;
mod following_tests;
mod integration_tests;
@@ -1,643 +0,0 @@
-use std::{path::Path, sync::Arc};
-
-use call::ActiveCall;
-use editor::Editor;
-use fs::Fs;
-use gpui::{TestAppContext, VisualTestContext, WindowHandle};
-use rpc::{proto::DevServerStatus, ErrorCode, ErrorExt};
-use serde_json::json;
-use workspace::{AppState, Workspace};
-
-use crate::tests::{following_tests::join_channel, TestServer};
-
-use super::TestClient;
-
-#[gpui::test]
-async fn test_dev_server(cx: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
- let (server, client) = TestServer::start1(cx).await;
-
- let store = cx.update(|cx| dev_server_projects::Store::global(cx).clone());
-
- let resp = store
- .update(cx, |store, cx| {
- store.create_dev_server("server-1".to_string(), None, cx)
- })
- .await
- .unwrap();
-
- store.update(cx, |store, _| {
- assert_eq!(store.dev_servers().len(), 1);
- assert_eq!(store.dev_servers()[0].name, "server-1");
- assert_eq!(store.dev_servers()[0].status, DevServerStatus::Offline);
- });
-
- let dev_server = server.create_dev_server(resp.access_token, cx2).await;
- cx.executor().run_until_parked();
- store.update(cx, |store, _| {
- assert_eq!(store.dev_servers()[0].status, DevServerStatus::Online);
- });
-
- dev_server
- .fs()
- .insert_tree(
- "/remote",
- json!({
- "1.txt": "remote\nremote\nremote",
- "2.js": "function two() { return 2; }",
- "3.rs": "mod test",
- }),
- )
- .await;
-
- store
- .update(cx, |store, cx| {
- store.create_dev_server_project(
- client::DevServerId(resp.dev_server_id),
- "/remote".to_string(),
- cx,
- )
- })
- .await
- .unwrap();
-
- cx.executor().run_until_parked();
-
- let remote_workspace = store
- .update(cx, |store, cx| {
- let projects = store.dev_server_projects();
- assert_eq!(projects.len(), 1);
- assert_eq!(projects[0].paths, vec!["/remote"]);
- workspace::join_dev_server_project(
- projects[0].id,
- projects[0].project_id.unwrap(),
- client.app_state.clone(),
- None,
- cx,
- )
- })
- .await
- .unwrap();
-
- cx.executor().run_until_parked();
-
- let cx = VisualTestContext::from_window(remote_workspace.into(), cx).as_mut();
- cx.simulate_keystrokes("cmd-p 1 enter");
-
- let editor = remote_workspace
- .update(cx, |ws, cx| {
- ws.active_item_as::<Editor>(cx).unwrap().clone()
- })
- .unwrap();
- editor.update(cx, |ed, cx| {
- assert_eq!(ed.text(cx).to_string(), "remote\nremote\nremote");
- });
- cx.simulate_input("wow!");
- cx.simulate_keystrokes("cmd-s");
-
- let content = dev_server
- .fs()
- .load(Path::new("/remote/1.txt"))
- .await
- .unwrap();
- assert_eq!(content, "wow!remote\nremote\nremote\n");
-}
-
-#[gpui::test]
-async fn test_dev_server_env_files(
- cx1: &mut gpui::TestAppContext,
- cx2: &mut gpui::TestAppContext,
- cx3: &mut gpui::TestAppContext,
-) {
- let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await;
-
- let (_dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await;
-
- cx1.executor().run_until_parked();
-
- let cx1 = VisualTestContext::from_window(remote_workspace.into(), cx1).as_mut();
- cx1.simulate_keystrokes("cmd-p . e enter");
-
- let editor = remote_workspace
- .update(cx1, |ws, cx| {
- ws.active_item_as::<Editor>(cx).unwrap().clone()
- })
- .unwrap();
- editor.update(cx1, |ed, cx| {
- assert_eq!(ed.text(cx).to_string(), "SECRET");
- });
-
- cx1.update(|cx| {
- workspace::join_channel(
- channel_id,
- client1.app_state.clone(),
- Some(remote_workspace),
- cx,
- )
- })
- .await
- .unwrap();
- cx1.executor().run_until_parked();
-
- remote_workspace
- .update(cx1, |ws, cx| {
- assert!(ws.project().read(cx).is_shared());
- })
- .unwrap();
-
- join_channel(channel_id, &client2, cx2).await.unwrap();
- cx2.executor().run_until_parked();
-
- let (workspace2, cx2) = client2.active_workspace(cx2);
- let editor = workspace2.update(cx2, |ws, cx| {
- ws.active_item_as::<Editor>(cx).unwrap().clone()
- });
- // TODO: it'd be nice to hide .env files from other people
- editor.update(cx2, |ed, cx| {
- assert_eq!(ed.text(cx).to_string(), "SECRET");
- });
-}
-
-async fn create_dev_server_project(
- server: &TestServer,
- client_app_state: Arc<AppState>,
- cx: &mut TestAppContext,
- cx_devserver: &mut TestAppContext,
-) -> (TestClient, WindowHandle<Workspace>) {
- let store = cx.update(|cx| dev_server_projects::Store::global(cx).clone());
-
- let resp = store
- .update(cx, |store, cx| {
- store.create_dev_server("server-1".to_string(), None, cx)
- })
- .await
- .unwrap();
- let dev_server = server
- .create_dev_server(resp.access_token, cx_devserver)
- .await;
-
- cx.executor().run_until_parked();
-
- dev_server
- .fs()
- .insert_tree(
- "/remote",
- json!({
- "1.txt": "remote\nremote\nremote",
- ".env": "SECRET",
- }),
- )
- .await;
-
- store
- .update(cx, |store, cx| {
- store.create_dev_server_project(
- client::DevServerId(resp.dev_server_id),
- "/remote".to_string(),
- cx,
- )
- })
- .await
- .unwrap();
-
- cx.executor().run_until_parked();
-
- let workspace = store
- .update(cx, |store, cx| {
- let projects = store.dev_server_projects();
- assert_eq!(projects.len(), 1);
- assert_eq!(projects[0].paths, vec!["/remote"]);
- workspace::join_dev_server_project(
- projects[0].id,
- projects[0].project_id.unwrap(),
- client_app_state,
- None,
- cx,
- )
- })
- .await
- .unwrap();
-
- cx.executor().run_until_parked();
-
- (dev_server, workspace)
-}
-
-#[gpui::test]
-async fn test_dev_server_leave_room(
- cx1: &mut gpui::TestAppContext,
- cx2: &mut gpui::TestAppContext,
- cx3: &mut gpui::TestAppContext,
-) {
- let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await;
-
- let (_dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await;
-
- cx1.update(|cx| {
- workspace::join_channel(
- channel_id,
- client1.app_state.clone(),
- Some(remote_workspace),
- cx,
- )
- })
- .await
- .unwrap();
- cx1.executor().run_until_parked();
-
- remote_workspace
- .update(cx1, |ws, cx| {
- assert!(ws.project().read(cx).is_shared());
- })
- .unwrap();
-
- join_channel(channel_id, &client2, cx2).await.unwrap();
- cx2.executor().run_until_parked();
-
- cx1.update(|cx| ActiveCall::global(cx).update(cx, |active_call, cx| active_call.hang_up(cx)))
- .await
- .unwrap();
-
- cx1.executor().run_until_parked();
-
- let (workspace, cx2) = client2.active_workspace(cx2);
- cx2.update(|cx| assert!(workspace.read(cx).project().read(cx).is_disconnected(cx)));
-}
-
-#[gpui::test]
-async fn test_dev_server_delete(
- cx1: &mut gpui::TestAppContext,
- cx2: &mut gpui::TestAppContext,
- cx3: &mut gpui::TestAppContext,
-) {
- let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await;
-
- let (_dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await;
-
- cx1.update(|cx| {
- workspace::join_channel(
- channel_id,
- client1.app_state.clone(),
- Some(remote_workspace),
- cx,
- )
- })
- .await
- .unwrap();
- cx1.executor().run_until_parked();
-
- remote_workspace
- .update(cx1, |ws, cx| {
- assert!(ws.project().read(cx).is_shared());
- })
- .unwrap();
-
- join_channel(channel_id, &client2, cx2).await.unwrap();
- cx2.executor().run_until_parked();
-
- cx1.update(|cx| {
- dev_server_projects::Store::global(cx).update(cx, |store, cx| {
- store.delete_dev_server_project(store.dev_server_projects().first().unwrap().id, cx)
- })
- })
- .await
- .unwrap();
-
- cx1.executor().run_until_parked();
-
- let (workspace, cx2) = client2.active_workspace(cx2);
- cx2.update(|cx| assert!(workspace.read(cx).project().read(cx).is_disconnected(cx)));
-
- cx1.update(|cx| {
- dev_server_projects::Store::global(cx).update(cx, |store, _| {
- assert_eq!(store.dev_server_projects().len(), 0);
- })
- })
-}
-
-#[gpui::test]
-async fn test_dev_server_rename(
- cx1: &mut gpui::TestAppContext,
- cx2: &mut gpui::TestAppContext,
- cx3: &mut gpui::TestAppContext,
-) {
- let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await;
-
- let (_dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await;
-
- cx1.update(|cx| {
- workspace::join_channel(
- channel_id,
- client1.app_state.clone(),
- Some(remote_workspace),
- cx,
- )
- })
- .await
- .unwrap();
- cx1.executor().run_until_parked();
-
- remote_workspace
- .update(cx1, |ws, cx| {
- assert!(ws.project().read(cx).is_shared());
- })
- .unwrap();
-
- join_channel(channel_id, &client2, cx2).await.unwrap();
- cx2.executor().run_until_parked();
-
- cx1.update(|cx| {
- dev_server_projects::Store::global(cx).update(cx, |store, cx| {
- store.rename_dev_server(
- store.dev_servers().first().unwrap().id,
- "name-edited".to_string(),
- None,
- cx,
- )
- })
- })
- .await
- .unwrap();
-
- cx1.executor().run_until_parked();
-
- cx1.update(|cx| {
- dev_server_projects::Store::global(cx).update(cx, |store, _| {
- assert_eq!(store.dev_servers().first().unwrap().name, "name-edited");
- })
- })
-}
-
-#[gpui::test]
-async fn test_dev_server_refresh_access_token(
- cx1: &mut gpui::TestAppContext,
- cx2: &mut gpui::TestAppContext,
- cx3: &mut gpui::TestAppContext,
- cx4: &mut gpui::TestAppContext,
-) {
- let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await;
-
- let (_dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await;
-
- cx1.update(|cx| {
- workspace::join_channel(
- channel_id,
- client1.app_state.clone(),
- Some(remote_workspace),
- cx,
- )
- })
- .await
- .unwrap();
- cx1.executor().run_until_parked();
-
- remote_workspace
- .update(cx1, |ws, cx| {
- assert!(ws.project().read(cx).is_shared());
- })
- .unwrap();
-
- join_channel(channel_id, &client2, cx2).await.unwrap();
- cx2.executor().run_until_parked();
-
- // Regenerate the access token
- let new_token_response = cx1
- .update(|cx| {
- dev_server_projects::Store::global(cx).update(cx, |store, cx| {
- store.regenerate_dev_server_token(store.dev_servers().first().unwrap().id, cx)
- })
- })
- .await
- .unwrap();
-
- cx1.executor().run_until_parked();
-
- // Assert that the other client was disconnected
- let (workspace, cx2) = client2.active_workspace(cx2);
- cx2.update(|cx| assert!(workspace.read(cx).project().read(cx).is_disconnected(cx)));
-
- // Assert that the owner of the dev server does not see the dev server as online anymore
- let (workspace, cx1) = client1.active_workspace(cx1);
- cx1.update(|cx| {
- assert!(workspace.read(cx).project().read(cx).is_disconnected(cx));
- dev_server_projects::Store::global(cx).update(cx, |store, _| {
- assert_eq!(
- store.dev_servers().first().unwrap().status,
- DevServerStatus::Offline
- );
- })
- });
-
- // Reconnect the dev server with the new token
- let _dev_server = server
- .create_dev_server(new_token_response.access_token, cx4)
- .await;
-
- cx1.executor().run_until_parked();
-
- // Assert that the dev server is online again
- cx1.update(|cx| {
- dev_server_projects::Store::global(cx).update(cx, |store, _| {
- assert_eq!(store.dev_servers().len(), 1);
- assert_eq!(
- store.dev_servers().first().unwrap().status,
- DevServerStatus::Online
- );
- })
- });
-}
-
-#[gpui::test]
-async fn test_dev_server_reconnect(
- cx1: &mut gpui::TestAppContext,
- cx2: &mut gpui::TestAppContext,
- cx3: &mut gpui::TestAppContext,
-) {
- let (mut server, client1) = TestServer::start1(cx1).await;
- let channel_id = server
- .make_channel("test", None, (&client1, cx1), &mut [])
- .await;
-
- let (_dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await;
-
- cx1.update(|cx| {
- workspace::join_channel(
- channel_id,
- client1.app_state.clone(),
- Some(remote_workspace),
- cx,
- )
- })
- .await
- .unwrap();
- cx1.executor().run_until_parked();
-
- remote_workspace
- .update(cx1, |ws, cx| {
- assert!(ws.project().read(cx).is_shared());
- })
- .unwrap();
-
- drop(client1);
-
- let client2 = server.create_client(cx2, "user_a").await;
-
- let store = cx2.update(|cx| dev_server_projects::Store::global(cx).clone());
-
- store
- .update(cx2, |store, cx| {
- let projects = store.dev_server_projects();
- workspace::join_dev_server_project(
- projects[0].id,
- projects[0].project_id.unwrap(),
- client2.app_state.clone(),
- None,
- cx,
- )
- })
- .await
- .unwrap();
-}
-
-#[gpui::test]
-async fn test_dev_server_restart(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
- let (server, client1) = TestServer::start1(cx1).await;
-
- let (_dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await;
- let cx = VisualTestContext::from_window(remote_workspace.into(), cx1).as_mut();
-
- server.reset().await;
- cx.run_until_parked();
-
- cx.simulate_keystrokes("cmd-p 1 enter");
- remote_workspace
- .update(cx, |ws, cx| {
- ws.active_item_as::<Editor>(cx)
- .unwrap()
- .update(cx, |ed, cx| {
- assert_eq!(ed.text(cx).to_string(), "remote\nremote\nremote");
- })
- })
- .unwrap();
-}
-
-#[gpui::test]
-async fn test_create_dev_server_project_path_validation(
- cx1: &mut gpui::TestAppContext,
- cx2: &mut gpui::TestAppContext,
- cx3: &mut gpui::TestAppContext,
-) {
- let (server, client1) = TestServer::start1(cx1).await;
- let _channel_id = server
- .make_channel("test", None, (&client1, cx1), &mut [])
- .await;
-
- // Creating a project with a path that does exist should not fail
- let (_dev_server, _) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await;
-
- cx1.executor().run_until_parked();
-
- let store = cx1.update(|cx| dev_server_projects::Store::global(cx).clone());
-
- let resp = store
- .update(cx1, |store, cx| {
- store.create_dev_server("server-2".to_string(), None, cx)
- })
- .await
- .unwrap();
-
- cx1.executor().run_until_parked();
-
- let _dev_server = server.create_dev_server(resp.access_token, cx3).await;
-
- cx1.executor().run_until_parked();
-
- // Creating a remote project with a path that does not exist should fail
- let result = store
- .update(cx1, |store, cx| {
- store.create_dev_server_project(
- client::DevServerId(resp.dev_server_id),
- "/notfound".to_string(),
- cx,
- )
- })
- .await;
-
- cx1.executor().run_until_parked();
-
- let error = result.unwrap_err();
- assert!(matches!(
- error.error_code(),
- ErrorCode::DevServerProjectPathDoesNotExist
- ));
-}
-
-#[gpui::test]
-async fn test_save_as_remote(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
- let (server, client1) = TestServer::start1(cx1).await;
-
- // Creating a project with a path that does exist should not fail
- let (dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await;
-
- let mut cx = VisualTestContext::from_window(remote_workspace.into(), cx1);
-
- cx.simulate_keystrokes("cmd-p 1 enter");
- cx.simulate_keystrokes("cmd-shift-s");
- cx.simulate_input("2.txt");
- cx.simulate_keystrokes("enter");
-
- cx.executor().run_until_parked();
-
- let title = remote_workspace
- .update(&mut cx, |ws, cx| {
- let active_item = ws.active_item(cx).unwrap();
- active_item.tab_description(0, cx).unwrap()
- })
- .unwrap();
-
- assert_eq!(title, "2.txt");
-
- let path = Path::new("/remote/2.txt");
- assert_eq!(
- dev_server.fs().load(path).await.unwrap(),
- "remote\nremote\nremote"
- );
-}
-
-#[gpui::test]
-async fn test_new_file_remote(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
- let (server, client1) = TestServer::start1(cx1).await;
-
- // Creating a project with a path that does exist should not fail
- let (dev_server, remote_workspace) =
- create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await;
-
- let mut cx = VisualTestContext::from_window(remote_workspace.into(), cx1);
-
- cx.simulate_keystrokes("cmd-n");
- cx.simulate_input("new!");
- cx.simulate_keystrokes("cmd-shift-s");
- cx.simulate_input("2.txt");
- cx.simulate_keystrokes("enter");
-
- cx.executor().run_until_parked();
-
- let title = remote_workspace
- .update(&mut cx, |ws, cx| {
- ws.active_item(cx).unwrap().tab_description(0, cx).unwrap()
- })
- .unwrap();
-
- assert_eq!(title, "2.txt");
-
- let path = Path::new("/remote/2.txt");
- assert_eq!(dev_server.fs().load(path).await.unwrap(), "new!");
-}
@@ -1,5 +1,4 @@
use crate::{
- auth::split_dev_server_token,
db::{tests::TestDb, NewUserParams, UserId},
executor::Executor,
rpc::{Principal, Server, ZedVersion, CLEANUP_TIMEOUT, RECONNECT_TIMEOUT},
@@ -204,7 +203,7 @@ impl TestServer {
.override_authenticate(move |cx| {
cx.spawn(|_| async move {
let access_token = "the-token".to_string();
- Ok(Credentials::User {
+ Ok(Credentials {
user_id: user_id.to_proto(),
access_token,
})
@@ -213,7 +212,7 @@ impl TestServer {
.override_establish_connection(move |credentials, cx| {
assert_eq!(
credentials,
- &Credentials::User {
+ &Credentials {
user_id: user_id.0 as u64,
access_token: "the-token".into()
}
@@ -297,7 +296,6 @@ impl TestServer {
collab_ui::init(&app_state, cx);
file_finder::init(cx);
menu::init();
- dev_server_projects::init(client.clone(), cx);
settings::KeymapFile::load_asset(os_keymap, cx).unwrap();
language_model::LanguageModelRegistry::test(cx);
assistant::context_store::init(&client.clone().into());
@@ -319,135 +317,6 @@ impl TestServer {
client
}
- pub async fn create_dev_server(
- &self,
- access_token: String,
- cx: &mut TestAppContext,
- ) -> TestClient {
- cx.update(|cx| {
- if cx.has_global::<SettingsStore>() {
- panic!("Same cx used to create two test clients")
- }
- let settings = SettingsStore::test(cx);
- cx.set_global(settings);
- release_channel::init(SemanticVersion::default(), cx);
- client::init_settings(cx);
- });
- let (dev_server_id, _) = split_dev_server_token(&access_token).unwrap();
-
- let clock = Arc::new(FakeSystemClock::default());
- let http = FakeHttpClient::with_404_response();
- let mut client = cx.update(|cx| Client::new(clock, http.clone(), cx));
- let server = self.server.clone();
- let db = self.app_state.db.clone();
- let connection_killers = self.connection_killers.clone();
- let forbid_connections = self.forbid_connections.clone();
- Arc::get_mut(&mut client)
- .unwrap()
- .set_id(1)
- .set_dev_server_token(client::DevServerToken(access_token.clone()))
- .override_establish_connection(move |credentials, cx| {
- assert_eq!(
- credentials,
- &Credentials::DevServer {
- token: client::DevServerToken(access_token.to_string())
- }
- );
-
- let server = server.clone();
- let db = db.clone();
- let connection_killers = connection_killers.clone();
- let forbid_connections = forbid_connections.clone();
- cx.spawn(move |cx| async move {
- if forbid_connections.load(SeqCst) {
- Err(EstablishConnectionError::other(anyhow!(
- "server is forbidding connections"
- )))
- } else {
- let (client_conn, server_conn, killed) =
- Connection::in_memory(cx.background_executor().clone());
- let (connection_id_tx, connection_id_rx) = oneshot::channel();
- let dev_server = db
- .get_dev_server(dev_server_id)
- .await
- .expect("retrieving dev_server failed");
- cx.background_executor()
- .spawn(server.handle_connection(
- server_conn,
- "dev-server".to_string(),
- Principal::DevServer(dev_server),
- ZedVersion(SemanticVersion::new(1, 0, 0)),
- None,
- Some(connection_id_tx),
- Executor::Deterministic(cx.background_executor().clone()),
- ))
- .detach();
- let connection_id = connection_id_rx.await.map_err(|e| {
- EstablishConnectionError::Other(anyhow!(
- "{} (is server shutting down?)",
- e
- ))
- })?;
- connection_killers
- .lock()
- .insert(connection_id.into(), killed);
- Ok(client_conn)
- }
- })
- });
-
- let fs = FakeFs::new(cx.executor());
- 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 language_registry = Arc::new(LanguageRegistry::test(cx.executor()));
- let session = cx.new_model(|cx| AppSession::new(Session::test(), cx));
- let app_state = Arc::new(workspace::AppState {
- client: client.clone(),
- user_store: user_store.clone(),
- workspace_store,
- languages: language_registry,
- fs: fs.clone(),
- build_window_options: |_, _| Default::default(),
- node_runtime: NodeRuntime::unavailable(),
- session,
- });
-
- cx.update(|cx| {
- theme::init(theme::LoadThemes::JustBase, cx);
- Project::init(&client, cx);
- client::init(&client, cx);
- language::init(cx);
- editor::init(cx);
- workspace::init(app_state.clone(), cx);
- call::init(client.clone(), user_store.clone(), cx);
- channel::init(&client, user_store.clone(), cx);
- notifications::init(client.clone(), user_store, cx);
- collab_ui::init(&app_state, cx);
- file_finder::init(cx);
- menu::init();
- headless::init(
- client.clone(),
- headless::AppState {
- languages: app_state.languages.clone(),
- user_store: app_state.user_store.clone(),
- fs: fs.clone(),
- node_runtime: app_state.node_runtime.clone(),
- },
- cx,
- )
- })
- .await
- .unwrap();
-
- TestClient {
- app_state,
- username: "dev-server".to_string(),
- channel_store: cx.read(ChannelStore::global).clone(),
- notification_store: cx.read(NotificationStore::global).clone(),
- state: Default::default(),
- }
- }
-
pub fn disconnect_client(&self, peer_id: PeerId) {
self.connection_killers
.lock()
@@ -1,23 +0,0 @@
-[package]
-name = "dev_server_projects"
-version = "0.1.0"
-edition = "2021"
-publish = false
-license = "GPL-3.0-or-later"
-
-[lints]
-workspace = true
-
-[lib]
-path = "src/dev_server_projects.rs"
-doctest = false
-
-[dependencies]
-anyhow.workspace = true
-gpui.workspace = true
-serde.workspace = true
-client.workspace = true
-rpc.workspace = true
-
-[dev-dependencies]
-serde_json.workspace = true
@@ -1 +0,0 @@
-../../LICENSE-GPL
@@ -1,249 +1 @@
-use anyhow::Result;
-use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ModelContext, SharedString, Task};
-use rpc::{
- proto::{self, DevServerStatus},
- TypedEnvelope,
-};
-use std::{collections::HashMap, sync::Arc};
-use client::{Client, ProjectId};
-pub use client::{DevServerId, DevServerProjectId};
-
-pub struct Store {
- dev_server_projects: HashMap<DevServerProjectId, DevServerProject>,
- dev_servers: HashMap<DevServerId, DevServer>,
- _subscriptions: Vec<client::Subscription>,
- client: Arc<Client>,
-}
-
-#[derive(Debug, Clone)]
-pub struct DevServerProject {
- pub id: DevServerProjectId,
- pub project_id: Option<ProjectId>,
- pub paths: Vec<SharedString>,
- pub dev_server_id: DevServerId,
-}
-
-impl From<proto::DevServerProject> for DevServerProject {
- fn from(project: proto::DevServerProject) -> Self {
- Self {
- id: DevServerProjectId(project.id),
- project_id: project.project_id.map(ProjectId),
- paths: project.paths.into_iter().map(|path| path.into()).collect(),
- dev_server_id: DevServerId(project.dev_server_id),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct DevServer {
- pub id: DevServerId,
- pub name: SharedString,
- pub ssh_connection_string: Option<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),
- status: dev_server.status(),
- name: dev_server.name.into(),
- ssh_connection_string: dev_server.ssh_connection_string.map(|s| s.into()),
- }
- }
-}
-
-struct GlobalStore(Model<Store>);
-
-impl Global for GlobalStore {}
-
-pub fn init(client: Arc<Client>, cx: &mut AppContext) {
- let store = cx.new_model(|cx| Store::new(client, cx));
- cx.set_global(GlobalStore(store));
-}
-
-impl Store {
- pub fn global(cx: &AppContext) -> Model<Store> {
- cx.global::<GlobalStore>().0.clone()
- }
-
- pub fn new(client: Arc<Client>, cx: &ModelContext<Self>) -> Self {
- Self {
- dev_server_projects: Default::default(),
- dev_servers: Default::default(),
- _subscriptions: vec![client
- .add_message_handler(cx.weak_model(), Self::handle_dev_server_projects_update)],
- client,
- }
- }
-
- pub fn projects_for_server(&self, id: DevServerId) -> Vec<DevServerProject> {
- let mut projects: Vec<DevServerProject> = self
- .dev_server_projects
- .values()
- .filter(|project| project.dev_server_id == id)
- .cloned()
- .collect();
- projects.sort_by_key(|p| (p.paths.clone(), p.id));
- projects
- }
-
- pub fn dev_servers(&self) -> Vec<DevServer> {
- let mut dev_servers: Vec<DevServer> = self.dev_servers.values().cloned().collect();
- dev_servers.sort_by_key(|d| (d.status == DevServerStatus::Offline, d.name.clone(), d.id));
- dev_servers
- }
-
- pub fn dev_server(&self, id: DevServerId) -> Option<&DevServer> {
- self.dev_servers.get(&id)
- }
-
- pub fn dev_server_status(&self, id: DevServerId) -> DevServerStatus {
- self.dev_server(id)
- .map(|server| server.status)
- .unwrap_or(DevServerStatus::Offline)
- }
-
- pub fn dev_server_projects(&self) -> Vec<DevServerProject> {
- let mut projects: Vec<DevServerProject> =
- self.dev_server_projects.values().cloned().collect();
- projects.sort_by_key(|p| (p.paths.clone(), p.id));
- projects
- }
-
- pub fn dev_server_project(&self, id: DevServerProjectId) -> Option<&DevServerProject> {
- self.dev_server_projects.get(&id)
- }
-
- pub fn dev_server_for_project(&self, id: DevServerProjectId) -> Option<&DevServer> {
- self.dev_server_project(id)
- .and_then(|project| self.dev_server(project.dev_server_id))
- }
-
- async fn handle_dev_server_projects_update(
- this: Model<Self>,
- envelope: TypedEnvelope<proto::DevServerProjectsUpdate>,
- mut cx: AsyncAppContext,
- ) -> Result<()> {
- this.update(&mut cx, |this, cx| {
- this.dev_servers = envelope
- .payload
- .dev_servers
- .into_iter()
- .map(|dev_server| (DevServerId(dev_server.dev_server_id), dev_server.into()))
- .collect();
- this.dev_server_projects = envelope
- .payload
- .dev_server_projects
- .into_iter()
- .map(|project| (DevServerProjectId(project.id), project.into()))
- .collect();
-
- cx.notify();
- })?;
- Ok(())
- }
-
- pub fn create_dev_server_project(
- &mut self,
- dev_server_id: DevServerId,
- path: String,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<proto::CreateDevServerProjectResponse>> {
- let client = self.client.clone();
- cx.background_executor().spawn(async move {
- client
- .request(proto::CreateDevServerProject {
- dev_server_id: dev_server_id.0,
- path,
- })
- .await
- })
- }
-
- pub fn create_dev_server(
- &mut self,
- name: String,
- ssh_connection_string: Option<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 {
- name,
- ssh_connection_string,
- })
- .await?;
- Ok(result)
- })
- }
-
- pub fn rename_dev_server(
- &mut self,
- dev_server_id: DevServerId,
- name: String,
- ssh_connection_string: Option<String>,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<()>> {
- let client = self.client.clone();
- cx.background_executor().spawn(async move {
- client
- .request(proto::RenameDevServer {
- dev_server_id: dev_server_id.0,
- name,
- ssh_connection_string,
- })
- .await?;
- Ok(())
- })
- }
-
- pub fn regenerate_dev_server_token(
- &mut self,
- dev_server_id: DevServerId,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<proto::RegenerateDevServerTokenResponse>> {
- let client = self.client.clone();
- cx.background_executor().spawn(async move {
- client
- .request(proto::RegenerateDevServerToken {
- dev_server_id: dev_server_id.0,
- })
- .await
- })
- }
-
- pub fn delete_dev_server(
- &mut self,
- id: DevServerId,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<()>> {
- let client = self.client.clone();
- cx.background_executor().spawn(async move {
- client
- .request(proto::DeleteDevServer {
- dev_server_id: id.0,
- })
- .await?;
- Ok(())
- })
- }
-
- pub fn delete_dev_server_project(
- &mut self,
- id: DevServerProjectId,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<()>> {
- let client = self.client.clone();
- cx.background_executor().spawn(async move {
- client
- .request(proto::DeleteDevServerProject {
- dev_server_project_id: id.0,
- })
- .await?;
- Ok(())
- })
- }
-}
@@ -1,37 +0,0 @@
-[package]
-name = "headless"
-version = "0.1.0"
-edition = "2021"
-publish = false
-license = "GPL-3.0-or-later"
-
-[lints]
-workspace = true
-
-[lib]
-path = "src/headless.rs"
-doctest = false
-
-[dependencies]
-anyhow.workspace = true
-client.workspace = true
-extension.workspace = true
-signal-hook.workspace = true
-gpui.workspace = true
-log.workspace = true
-util.workspace = true
-node_runtime.workspace = true
-language.workspace = true
-project.workspace = true
-proto.workspace = true
-fs.workspace = true
-futures.workspace = true
-settings.workspace = true
-shellexpand.workspace = true
-postage.workspace = true
-
-[dev-dependencies]
-client = { workspace = true, features = ["test-support"] }
-fs = { workspace = true, features = ["test-support"] }
-gpui = { workspace = true, features = ["test-support"] }
-util = { workspace = true, features = ["test-support"] }
@@ -1 +0,0 @@
-../../LICENSE-GPL
@@ -1,397 +0,0 @@
-use anyhow::{anyhow, Result};
-use client::DevServerProjectId;
-use client::{user::UserStore, Client, ClientSettings};
-use extension::ExtensionStore;
-use fs::Fs;
-use futures::{Future, StreamExt};
-use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ModelContext, Task, WeakModel};
-use language::LanguageRegistry;
-use node_runtime::NodeRuntime;
-use postage::stream::Stream;
-use project::Project;
-use proto::{self, ErrorCode, TypedEnvelope};
-use settings::{Settings, SettingsStore};
-use std::path::Path;
-use std::{collections::HashMap, sync::Arc};
-use util::{ResultExt, TryFutureExt};
-
-pub struct DevServer {
- client: Arc<Client>,
- app_state: AppState,
- remote_shutdown: bool,
- projects: HashMap<DevServerProjectId, Model<Project>>,
- _subscriptions: Vec<client::Subscription>,
- _maintain_connection: Task<Option<()>>,
-}
-
-pub struct AppState {
- pub node_runtime: NodeRuntime,
- pub user_store: Model<UserStore>,
- pub languages: Arc<LanguageRegistry>,
- pub fs: Arc<dyn Fs>,
-}
-
-struct GlobalDevServer(Model<DevServer>);
-
-impl Global for GlobalDevServer {}
-
-pub fn init(client: Arc<Client>, app_state: AppState, cx: &mut AppContext) -> Task<Result<()>> {
- let dev_server = cx.new_model(|cx| DevServer::new(client.clone(), app_state, cx));
- cx.set_global(GlobalDevServer(dev_server.clone()));
-
- #[cfg(not(target_os = "windows"))]
- {
- use signal_hook::consts::{SIGINT, SIGTERM};
- use signal_hook::iterator::Signals;
- // Set up a handler when the dev server is shut down
- // with ctrl-c or kill
- let (tx, rx) = futures::channel::oneshot::channel();
- let mut signals = Signals::new([SIGTERM, SIGINT]).unwrap();
- std::thread::spawn({
- move || {
- if let Some(sig) = signals.forever().next() {
- tx.send(sig).log_err();
- }
- }
- });
- cx.spawn(|cx| async move {
- if let Ok(sig) = rx.await {
- log::info!("received signal {sig:?}");
- cx.update(|cx| cx.quit()).log_err();
- }
- })
- .detach();
- }
-
- let server_url = ClientSettings::get_global(cx).server_url.clone();
- cx.spawn(|cx| async move {
- client
- .authenticate_and_connect(false, &cx)
- .await
- .map_err(|e| anyhow!("Error connecting to '{}': {}", server_url, e))
- })
-}
-
-impl DevServer {
- pub fn global(cx: &AppContext) -> Model<DevServer> {
- cx.global::<GlobalDevServer>().0.clone()
- }
-
- pub fn new(client: Arc<Client>, app_state: AppState, cx: &mut ModelContext<Self>) -> Self {
- cx.on_app_quit(Self::app_will_quit).detach();
-
- let maintain_connection = cx.spawn({
- let client = client.clone();
- move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err()
- });
-
- cx.observe_global::<SettingsStore>(|_, cx| {
- ExtensionStore::global(cx).update(cx, |store, cx| store.auto_install_extensions(cx))
- })
- .detach();
-
- DevServer {
- _subscriptions: vec![
- client.add_message_handler(cx.weak_model(), Self::handle_dev_server_instructions),
- client.add_request_handler(
- cx.weak_model(),
- Self::handle_validate_dev_server_project_request,
- ),
- client.add_request_handler(cx.weak_model(), Self::handle_list_remote_directory),
- client.add_message_handler(cx.weak_model(), Self::handle_shutdown),
- ],
- _maintain_connection: maintain_connection,
- projects: Default::default(),
- remote_shutdown: false,
- app_state,
- client,
- }
- }
-
- fn app_will_quit(&mut self, _: &mut ModelContext<Self>) -> impl Future<Output = ()> {
- let request = if self.remote_shutdown {
- None
- } else {
- Some(
- self.client
- .request(proto::ShutdownDevServer { reason: None }),
- )
- };
- async move {
- if let Some(request) = request {
- request.await.log_err();
- }
- }
- }
-
- async fn handle_dev_server_instructions(
- this: Model<Self>,
- envelope: TypedEnvelope<proto::DevServerInstructions>,
- mut cx: AsyncAppContext,
- ) -> Result<()> {
- let (added_projects, retained_projects, removed_projects_ids) =
- this.read_with(&mut cx, |this, _| {
- let removed_projects = this
- .projects
- .keys()
- .filter(|dev_server_project_id| {
- !envelope
- .payload
- .projects
- .iter()
- .any(|p| p.id == dev_server_project_id.0)
- })
- .cloned()
- .collect::<Vec<_>>();
-
- let mut added_projects = vec![];
- let mut retained_projects = vec![];
-
- for project in envelope.payload.projects.iter() {
- if this.projects.contains_key(&DevServerProjectId(project.id)) {
- retained_projects.push(project.clone());
- } else {
- added_projects.push(project.clone());
- }
- }
-
- (added_projects, retained_projects, removed_projects)
- })?;
-
- for dev_server_project in added_projects {
- DevServer::share_project(this.clone(), &dev_server_project, &mut cx).await?;
- }
-
- for dev_server_project in retained_projects {
- DevServer::update_project(this.clone(), &dev_server_project, &mut cx).await?;
- }
-
- this.update(&mut cx, |this, cx| {
- for old_project_id in &removed_projects_ids {
- this.unshare_project(old_project_id, cx)?;
- }
- Ok::<(), anyhow::Error>(())
- })??;
- Ok(())
- }
-
- async fn handle_validate_dev_server_project_request(
- this: Model<Self>,
- envelope: TypedEnvelope<proto::ValidateDevServerProjectRequest>,
- cx: AsyncAppContext,
- ) -> Result<proto::Ack> {
- let expanded = shellexpand::tilde(&envelope.payload.path).to_string();
- let path = std::path::Path::new(&expanded);
- let fs = cx.read_model(&this, |this, _| this.app_state.fs.clone())?;
-
- let path_exists = fs.metadata(path).await.is_ok_and(|result| result.is_some());
- if !path_exists {
- return Err(anyhow!(ErrorCode::DevServerProjectPathDoesNotExist))?;
- }
-
- Ok(proto::Ack {})
- }
-
- async fn handle_list_remote_directory(
- this: Model<Self>,
- envelope: TypedEnvelope<proto::ListRemoteDirectory>,
- cx: AsyncAppContext,
- ) -> Result<proto::ListRemoteDirectoryResponse> {
- let expanded = shellexpand::tilde(&envelope.payload.path).to_string();
- let fs = cx.read_model(&this, |this, _| this.app_state.fs.clone())?;
-
- let mut entries = Vec::new();
- let mut response = fs.read_dir(Path::new(&expanded)).await?;
- while let Some(path) = response.next().await {
- if let Some(file_name) = path?.file_name() {
- entries.push(file_name.to_string_lossy().to_string());
- }
- }
- Ok(proto::ListRemoteDirectoryResponse { entries })
- }
-
- async fn handle_shutdown(
- this: Model<Self>,
- _envelope: TypedEnvelope<proto::ShutdownDevServer>,
- mut cx: AsyncAppContext,
- ) -> Result<()> {
- this.update(&mut cx, |this, cx| {
- this.remote_shutdown = true;
- cx.quit();
- })
- }
-
- fn unshare_project(
- &mut self,
- dev_server_project_id: &DevServerProjectId,
- cx: &mut ModelContext<Self>,
- ) -> Result<()> {
- if let Some(project) = self.projects.remove(dev_server_project_id) {
- project.update(cx, |project, cx| project.unshare(cx))?;
- }
- Ok(())
- }
-
- async fn share_project(
- this: Model<Self>,
- dev_server_project: &proto::DevServerProject,
- cx: &mut AsyncAppContext,
- ) -> Result<()> {
- let (client, project) = this.update(cx, |this, cx| {
- let project = Project::local(
- this.client.clone(),
- this.app_state.node_runtime.clone(),
- this.app_state.user_store.clone(),
- this.app_state.languages.clone(),
- this.app_state.fs.clone(),
- None,
- cx,
- );
-
- (this.client.clone(), project)
- })?;
-
- for path in &dev_server_project.paths {
- let path = shellexpand::tilde(path).to_string();
-
- let (worktree, _) = project
- .update(cx, |project, cx| {
- project.find_or_create_worktree(&path, true, cx)
- })?
- .await?;
-
- worktree.update(cx, |worktree, cx| {
- worktree.as_local_mut().unwrap().share_private_files(cx)
- })?;
- }
-
- let worktrees =
- project.read_with(cx, |project, cx| project.worktree_metadata_protos(cx))?;
-
- let response = client
- .request(proto::ShareDevServerProject {
- dev_server_project_id: dev_server_project.id,
- worktrees,
- })
- .await?;
-
- let project_id = response.project_id;
- project.update(cx, |project, cx| project.shared(project_id, cx))??;
- this.update(cx, |this, _| {
- this.projects
- .insert(DevServerProjectId(dev_server_project.id), project);
- })?;
- Ok(())
- }
-
- async fn update_project(
- this: Model<Self>,
- dev_server_project: &proto::DevServerProject,
- cx: &mut AsyncAppContext,
- ) -> Result<()> {
- let tasks = this.update(cx, |this, cx| {
- let Some(project) = this
- .projects
- .get(&DevServerProjectId(dev_server_project.id))
- else {
- return vec![];
- };
-
- let mut to_delete = vec![];
- let mut tasks = vec![];
-
- project.update(cx, |project, cx| {
- for worktree in project.visible_worktrees(cx) {
- let mut delete = true;
- for config in dev_server_project.paths.iter() {
- if worktree.read(cx).abs_path().to_string_lossy()
- == shellexpand::tilde(config)
- {
- delete = false;
- }
- }
- if delete {
- to_delete.push(worktree.read(cx).id())
- }
- }
-
- for worktree_id in to_delete {
- project.remove_worktree(worktree_id, cx)
- }
-
- for config in dev_server_project.paths.iter() {
- tasks.push(project.find_or_create_worktree(
- shellexpand::tilde(config).to_string(),
- true,
- cx,
- ));
- }
-
- tasks
- })
- })?;
- futures::future::join_all(tasks).await;
- Ok(())
- }
-
- async fn maintain_connection(
- this: WeakModel<Self>,
- client: Arc<Client>,
- mut cx: AsyncAppContext,
- ) -> Result<()> {
- let mut client_status = client.status();
-
- let _ = client_status.try_recv();
- let current_status = *client_status.borrow();
- if current_status.is_connected() {
- // wait for first disconnect
- client_status.recv().await;
- }
-
- loop {
- let Some(current_status) = client_status.recv().await else {
- return Ok(());
- };
- let Some(this) = this.upgrade() else {
- return Ok(());
- };
-
- if !current_status.is_connected() {
- continue;
- }
-
- this.update(&mut cx, |this, cx| this.rejoin(cx))?.await?;
- }
- }
-
- fn rejoin(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
- let mut projects: HashMap<u64, Model<Project>> = HashMap::default();
- let request = self.client.request(proto::ReconnectDevServer {
- reshared_projects: self
- .projects
- .iter()
- .flat_map(|(_, handle)| {
- let project = handle.read(cx);
- let project_id = project.remote_id()?;
- projects.insert(project_id, handle.clone());
- Some(proto::UpdateProject {
- project_id,
- worktrees: project.worktree_metadata_protos(cx),
- })
- })
- .collect(),
- });
- cx.spawn(|_, mut cx| async move {
- let response = request.await?;
-
- for reshared_project in response.reshared_projects {
- if let Some(project) = projects.get(&reshared_project.id) {
- project.update(&mut cx, |project, cx| {
- project.reshared(reshared_project, cx).log_err();
- })?;
- }
- }
- Ok(())
- })
- }
-}
@@ -30,7 +30,6 @@ async-trait.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
-dev_server_projects.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
@@ -25,8 +25,7 @@ mod yarn;
use anyhow::{anyhow, Context as _, Result};
use buffer_store::{BufferStore, BufferStoreEvent};
use client::{
- proto, Client, Collaborator, DevServerProjectId, PendingEntitySubscription, ProjectId,
- TypedEnvelope, UserStore,
+ proto, Client, Collaborator, PendingEntitySubscription, ProjectId, TypedEnvelope, UserStore,
};
use clock::ReplicaId;
use collections::{BTreeSet, HashMap, HashSet};
@@ -156,7 +155,6 @@ pub struct Project {
terminals: Terminals,
node: Option<NodeRuntime>,
hosted_project_id: Option<ProjectId>,
- dev_server_project_id: Option<client::DevServerProjectId>,
search_history: SearchHistory,
search_included_history: SearchHistory,
search_excluded_history: SearchHistory,
@@ -217,7 +215,6 @@ enum ProjectClientState {
capability: Capability,
remote_id: u64,
replica_id: ReplicaId,
- in_room: bool,
},
}
@@ -675,7 +672,6 @@ impl Project {
},
node: Some(node),
hosted_project_id: None,
- dev_server_project_id: None,
search_history: Self::new_search_history(),
environment,
remotely_created_models: Default::default(),
@@ -705,7 +701,7 @@ impl Project {
let ssh_proto = ssh.read(cx).proto_client();
let worktree_store =
- cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), 0, None));
+ cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), 0));
cx.subscribe(&worktree_store, Self::on_worktree_store_event)
.detach();
@@ -794,7 +790,6 @@ impl Project {
},
node: Some(node),
hosted_project_id: None,
- dev_server_project_id: None,
search_history: Self::new_search_history(),
environment,
remotely_created_models: Default::default(),
@@ -898,15 +893,7 @@ impl Project {
let role = response.payload.role();
let worktree_store = cx.new_model(|_| {
- WorktreeStore::remote(
- true,
- client.clone().into(),
- response.payload.project_id,
- response
- .payload
- .dev_server_project_id
- .map(DevServerProjectId),
- )
+ WorktreeStore::remote(true, client.clone().into(), response.payload.project_id)
})?;
let buffer_store = cx.new_model(|cx| {
BufferStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
@@ -992,7 +979,6 @@ impl Project {
capability: Capability::ReadWrite,
remote_id,
replica_id,
- in_room: response.payload.dev_server_project_id.is_none(),
},
buffers_needing_diff: Default::default(),
git_diff_debouncer: DebouncedDelay::new(),
@@ -1001,10 +987,6 @@ impl Project {
},
node: None,
hosted_project_id: None,
- dev_server_project_id: response
- .payload
- .dev_server_project_id
- .map(DevServerProjectId),
search_history: Self::new_search_history(),
search_included_history: Self::new_search_history(),
search_excluded_history: Self::new_search_history(),
@@ -1305,39 +1287,23 @@ impl Project {
self.hosted_project_id
}
- pub fn dev_server_project_id(&self) -> Option<DevServerProjectId> {
- self.dev_server_project_id
- }
-
- pub fn supports_terminal(&self, cx: &AppContext) -> bool {
+ pub fn supports_terminal(&self, _cx: &AppContext) -> bool {
if self.is_local() {
return true;
}
if self.is_via_ssh() {
return true;
}
- let Some(id) = self.dev_server_project_id else {
- return false;
- };
- let Some(server) = dev_server_projects::Store::global(cx)
- .read(cx)
- .dev_server_for_project(id)
- else {
- return false;
- };
- server.ssh_connection_string.is_some()
+
+ return false;
}
pub fn ssh_connection_string(&self, cx: &AppContext) -> Option<SharedString> {
if let Some(ssh_state) = &self.ssh_client {
return Some(ssh_state.read(cx).connection_string().into());
}
- let dev_server_id = self.dev_server_project_id()?;
- dev_server_projects::Store::global(cx)
- .read(cx)
- .dev_server_for_project(dev_server_id)?
- .ssh_connection_string
- .clone()
+
+ return None;
}
pub fn ssh_connection_state(&self, cx: &AppContext) -> Option<remote::ConnectionState> {
@@ -1549,17 +1515,9 @@ impl Project {
pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext<Self>) -> Result<()> {
if !matches!(self.client_state, ProjectClientState::Local) {
- if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
- if *in_room || self.dev_server_project_id.is_none() {
- return Err(anyhow!("project was already shared"));
- } else {
- *in_room = true;
- return Ok(());
- }
- } else {
- return Err(anyhow!("project was already shared"));
- }
+ return Err(anyhow!("project was already shared"));
}
+
self.client_subscriptions.extend([
self.client
.subscribe_to_entity(project_id)?
@@ -1657,14 +1615,7 @@ impl Project {
fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> {
if self.is_via_collab() {
- if self.dev_server_project_id().is_some() {
- if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
- *in_room = false
- }
- return Ok(());
- } else {
- return Err(anyhow!("attempted to unshare a remote project"));
- }
+ return Err(anyhow!("attempted to unshare a remote project"));
}
if let ProjectClientState::Shared { remote_id, .. } = self.client_state {
@@ -2265,29 +2216,6 @@ impl Project {
}
fn on_worktree_released(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
- if let Some(dev_server_project_id) = self.dev_server_project_id {
- let paths: Vec<String> = self
- .visible_worktrees(cx)
- .filter_map(|worktree| {
- if worktree.read(cx).id() == id_to_remove {
- None
- } else {
- Some(worktree.read(cx).abs_path().to_string_lossy().to_string())
- }
- })
- .collect();
- if !paths.is_empty() {
- let request = self.client.request(proto::UpdateDevServerProject {
- dev_server_project_id: dev_server_project_id.0,
- paths,
- });
- cx.background_executor()
- .spawn(request)
- .detach_and_log_err(cx);
- }
- return;
- }
-
if let Some(ssh) = &self.ssh_client {
ssh.read(cx)
.proto_client()
@@ -3152,7 +3080,7 @@ impl Project {
match &self.client_state {
ProjectClientState::Shared { .. } => true,
ProjectClientState::Local => false,
- ProjectClientState::Remote { in_room, .. } => *in_room,
+ ProjectClientState::Remote { .. } => true,
}
}
@@ -3279,20 +3207,6 @@ impl Project {
let response = response.await?;
Ok(response.entries.into_iter().map(PathBuf::from).collect())
})
- } else if let Some(dev_server) = self.dev_server_project_id().and_then(|id| {
- dev_server_projects::Store::global(cx)
- .read(cx)
- .dev_server_for_project(id)
- }) {
- let request = proto::ListRemoteDirectory {
- dev_server_id: dev_server.id.0,
- path: query,
- };
- let response = self.client.request(request);
- cx.background_executor().spawn(async move {
- let response = response.await?;
- Ok(response.entries.into_iter().map(PathBuf::from).collect())
- })
} else {
Task::ready(Err(anyhow!("cannot list directory in remote project")))
}
@@ -37,11 +37,8 @@ pub enum TerminalKind {
/// SshCommand describes how to connect to a remote server
#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SshCommand {
- /// DevServers give a string from the user
- DevServer(String),
- /// Direct ssh has a list of arguments to pass to ssh
- Direct(Vec<String>),
+pub struct SshCommand {
+ arguments: Vec<String>,
}
impl Project {
@@ -73,19 +70,12 @@ impl Project {
if let Some(args) = ssh_client.ssh_args() {
return Some((
ssh_client.connection_options().host.clone(),
- SshCommand::Direct(args),
+ SshCommand { arguments: args },
));
}
}
- let dev_server_project_id = self.dev_server_project_id()?;
- let projects_store = dev_server_projects::Store::global(cx).read(cx);
- let ssh_command = projects_store
- .dev_server_for_project(dev_server_project_id)?
- .ssh_connection_string
- .as_ref()?
- .to_string();
- Some(("".to_string(), SshCommand::DevServer(ssh_command)))
+ return None;
}
pub fn create_terminal(
@@ -399,14 +389,8 @@ pub fn wrap_for_ssh(
};
let shell_invocation = format!("sh -c {}", shlex::try_quote(&commands).unwrap());
- let (program, mut args) = match ssh_command {
- SshCommand::DevServer(ssh_command) => {
- let mut args = shlex::split(ssh_command).unwrap_or_default();
- let program = args.drain(0..1).next().unwrap_or("ssh".to_string());
- (program, args)
- }
- SshCommand::Direct(ssh_args) => ("ssh".to_string(), ssh_args.clone()),
- };
+ let program = "ssh".to_string();
+ let mut args = ssh_command.arguments.clone();
args.push("-t".to_string());
args.push(shell_invocation);
@@ -1,11 +1,9 @@
use std::{
- cell::RefCell,
path::{Path, PathBuf},
sync::{atomic::AtomicUsize, Arc},
};
use anyhow::{anyhow, Context as _, Result};
-use client::DevServerProjectId;
use collections::{HashMap, HashSet};
use fs::Fs;
use futures::{
@@ -41,7 +39,6 @@ enum WorktreeStoreState {
fs: Arc<dyn Fs>,
},
Remote {
- dev_server_project_id: Option<DevServerProjectId>,
upstream_client: AnyProtoClient,
upstream_project_id: u64,
},
@@ -94,7 +91,6 @@ impl WorktreeStore {
retain_worktrees: bool,
upstream_client: AnyProtoClient,
upstream_project_id: u64,
- dev_server_project_id: Option<DevServerProjectId>,
) -> Self {
Self {
next_entry_id: Default::default(),
@@ -106,7 +102,6 @@ impl WorktreeStore {
state: WorktreeStoreState::Remote {
upstream_client,
upstream_project_id,
- dev_server_project_id,
},
}
}
@@ -196,18 +191,9 @@ impl WorktreeStore {
if !self.loading_worktrees.contains_key(&path) {
let task = match &self.state {
WorktreeStoreState::Remote {
- upstream_client,
- dev_server_project_id,
- ..
+ upstream_client, ..
} => {
- if let Some(dev_server_project_id) = dev_server_project_id {
- self.create_dev_server_worktree(
- upstream_client.clone(),
- *dev_server_project_id,
- abs_path,
- cx,
- )
- } else if upstream_client.is_via_collab() {
+ if upstream_client.is_via_collab() {
Task::ready(Err(Arc::new(anyhow!("cannot create worktrees via collab"))))
} else {
self.create_ssh_worktree(upstream_client.clone(), abs_path, visible, cx)
@@ -322,51 +308,6 @@ impl WorktreeStore {
})
}
- fn create_dev_server_worktree(
- &mut self,
- client: AnyProtoClient,
- dev_server_project_id: DevServerProjectId,
- abs_path: impl AsRef<Path>,
- cx: &mut ModelContext<Self>,
- ) -> Task<Result<Model<Worktree>, Arc<anyhow::Error>>> {
- let path: Arc<Path> = abs_path.as_ref().into();
- let mut paths: Vec<String> = self
- .visible_worktrees(cx)
- .map(|worktree| worktree.read(cx).abs_path().to_string_lossy().to_string())
- .collect();
- paths.push(path.to_string_lossy().to_string());
- let request = client.request(proto::UpdateDevServerProject {
- dev_server_project_id: dev_server_project_id.0,
- paths,
- });
-
- let abs_path = abs_path.as_ref().to_path_buf();
- cx.spawn(move |project, cx| async move {
- let (tx, rx) = futures::channel::oneshot::channel();
- let tx = RefCell::new(Some(tx));
- let Some(project) = project.upgrade() else {
- return Err(anyhow!("project dropped"))?;
- };
- let observer = cx.update(|cx| {
- cx.observe(&project, move |project, cx| {
- let abs_path = abs_path.clone();
- project.update(cx, |project, cx| {
- if let Some((worktree, _)) = project.find_worktree(&abs_path, cx) {
- if let Some(tx) = tx.borrow_mut().take() {
- tx.send(worktree).ok();
- }
- }
- })
- })
- })?;
-
- request.await?;
- let worktree = rx.await.map_err(|e| anyhow!(e))?;
- drop(observer);
- Ok(worktree)
- })
- }
-
pub fn add(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
let worktree_id = worktree.read(cx).id();
debug_assert!(self.worktrees().all(|w| w.read(cx).id() != worktree_id));
@@ -503,7 +503,7 @@ impl ProjectPanel {
let is_unfoldable = auto_fold_dirs && self.is_unfoldable(entry, worktree);
let worktree_id = worktree.id();
let is_read_only = project.is_read_only(cx);
- let is_remote = project.is_via_collab() && project.dev_server_project_id().is_none();
+ let is_remote = project.is_via_collab();
let is_local = project.is_local();
let context_menu = ContextMenu::build(cx, |menu, cx| {
@@ -3334,12 +3334,11 @@ impl Panel for ProjectPanel {
fn starts_open(&self, cx: &WindowContext) -> bool {
let project = &self.project.read(cx);
- project.dev_server_project_id().is_some()
- || project.visible_worktrees(cx).any(|tree| {
- tree.read(cx)
- .root_entry()
- .map_or(false, |entry| entry.is_dir())
- })
+ project.visible_worktrees(cx).any(|tree| {
+ tree.read(cx)
+ .root_entry()
+ .map_or(false, |entry| entry.is_dir())
+ })
}
}
@@ -217,33 +217,14 @@ message Envelope {
MultiLspQueryResponse multi_lsp_query_response = 176;
RestartLanguageServers restart_language_servers = 208;
- CreateDevServerProject create_dev_server_project = 177;
- CreateDevServerProjectResponse create_dev_server_project_response = 188;
- CreateDevServer create_dev_server = 178;
- CreateDevServerResponse create_dev_server_response = 179;
- ShutdownDevServer shutdown_dev_server = 180;
- DevServerInstructions dev_server_instructions = 181;
- ReconnectDevServer reconnect_dev_server = 182;
- ReconnectDevServerResponse reconnect_dev_server_response = 183;
-
- ShareDevServerProject share_dev_server_project = 184;
- JoinDevServerProject join_dev_server_project = 185;
RejoinRemoteProjects rejoin_remote_projects = 186;
RejoinRemoteProjectsResponse rejoin_remote_projects_response = 187;
- DevServerProjectsUpdate dev_server_projects_update = 193;
- ValidateDevServerProjectRequest validate_dev_server_project_request = 194;
- DeleteDevServer delete_dev_server = 195;
OpenNewBuffer open_new_buffer = 196;
- DeleteDevServerProject delete_dev_server_project = 197;
GetSupermavenApiKey get_supermaven_api_key = 198;
GetSupermavenApiKeyResponse get_supermaven_api_key_response = 199;
- RegenerateDevServerToken regenerate_dev_server_token = 200;
- RegenerateDevServerTokenResponse regenerate_dev_server_token_response = 201;
- RenameDevServer rename_dev_server = 202;
-
TaskContextForLocation task_context_for_location = 203;
TaskContext task_context = 204;
@@ -264,7 +245,6 @@ message Envelope {
ListRemoteDirectory list_remote_directory = 219;
ListRemoteDirectoryResponse list_remote_directory_response = 220;
- UpdateDevServerProject update_dev_server_project = 221;
AddWorktree add_worktree = 222;
AddWorktreeResponse add_worktree_response = 223;
@@ -304,10 +284,17 @@ message Envelope {
LanguageServerPromptResponse language_server_prompt_response = 269; // current max
}
+
reserved 87 to 88;
reserved 158 to 161;
reserved 166 to 169;
+ reserved 177 to 185;
+ reserved 188;
+ reserved 193 to 195;
+ reserved 197;
+ reserved 200 to 202;
reserved 205 to 206;
+ reserved 221;
reserved 224 to 229;
reserved 247 to 254;
}
@@ -342,12 +329,11 @@ enum ErrorCode {
WrongMoveTarget = 11;
UnsharedItem = 12;
NoSuchProject = 13;
- DevServerAlreadyOnline = 14;
- DevServerOffline = 15;
DevServerProjectPathDoesNotExist = 16;
RemoteUpgradeRequired = 17;
RateLimitExceeded = 18;
reserved 6;
+ reserved 14 to 15;
}
message EndStream {}
@@ -511,7 +497,7 @@ message LiveKitConnectionInfo {
message ShareProject {
uint64 room_id = 1;
repeated WorktreeMetadata worktrees = 2;
- optional uint64 dev_server_project_id = 3;
+ reserved 3;
bool is_ssh_project = 4;
}
@@ -536,19 +522,6 @@ message JoinHostedProject {
uint64 project_id = 1;
}
-message CreateDevServerProject {
- reserved 1;
- reserved 2;
- uint64 dev_server_id = 3;
- string path = 4;
-}
-message CreateDevServerProjectResponse {
- DevServerProject dev_server_project = 1;
-}
-
-message ValidateDevServerProjectRequest {
- string path = 1;
-}
message ListRemoteDirectory {
uint64 dev_server_id = 1;
@@ -559,77 +532,6 @@ message ListRemoteDirectoryResponse {
repeated string entries = 1;
}
-message UpdateDevServerProject {
- uint64 dev_server_project_id = 1;
- repeated string paths = 2;
-}
-
-message CreateDevServer {
- reserved 1;
- string name = 2;
- optional string ssh_connection_string = 3;
-}
-
-message RegenerateDevServerToken {
- uint64 dev_server_id = 1;
-}
-
-message RegenerateDevServerTokenResponse {
- uint64 dev_server_id = 1;
- string access_token = 2;
-}
-
-message CreateDevServerResponse {
- uint64 dev_server_id = 1;
- reserved 2;
- string access_token = 3;
- string name = 4;
-}
-
-message ShutdownDevServer {
- optional string reason = 1;
-}
-
-message RenameDevServer {
- uint64 dev_server_id = 1;
- string name = 2;
- optional string ssh_connection_string = 3;
-}
-
-message DeleteDevServer {
- uint64 dev_server_id = 1;
-}
-
-message DeleteDevServerProject {
- uint64 dev_server_project_id = 1;
-}
-
-message ReconnectDevServer {
- repeated UpdateProject reshared_projects = 1;
-}
-
-message ReconnectDevServerResponse {
- repeated ResharedProject reshared_projects = 1;
-}
-
-message DevServerInstructions {
- repeated DevServerProject projects = 1;
-}
-
-message DevServerProjectsUpdate {
- repeated DevServer dev_servers = 1;
- repeated DevServerProject dev_server_projects = 2;
-}
-
-message ShareDevServerProject {
- uint64 dev_server_project_id = 1;
- repeated WorktreeMetadata worktrees = 2;
-}
-
-message JoinDevServerProject {
- uint64 dev_server_project_id = 1;
-}
-
message JoinProjectResponse {
uint64 project_id = 5;
uint32 replica_id = 1;
@@ -637,7 +539,7 @@ message JoinProjectResponse {
repeated Collaborator collaborators = 3;
repeated LanguageServer language_servers = 4;
ChannelRole role = 6;
- optional uint64 dev_server_project_id = 7;
+ reserved 7;
}
message LeaveProject {
@@ -1429,29 +1331,6 @@ message HostedProject {
ChannelVisibility visibility = 4;
}
-message DevServerProject {
- uint64 id = 1;
- optional uint64 project_id = 2;
- reserved 3;
- reserved 4;
- uint64 dev_server_id = 5;
- string path = 6;
- repeated string paths = 7;
-}
-
-message DevServer {
- reserved 1;
- uint64 dev_server_id = 2;
- string name = 3;
- DevServerStatus status = 4;
- optional string ssh_connection_string = 5;
-}
-
-enum DevServerStatus {
- Offline = 0;
- Online = 1;
-}
-
message JoinChannel {
uint64 channel_id = 1;
}
@@ -318,30 +318,12 @@ messages!(
(SetRoomParticipantRole, Foreground),
(BlameBuffer, Foreground),
(BlameBufferResponse, Foreground),
- (CreateDevServerProject, Background),
- (CreateDevServerProjectResponse, Foreground),
- (CreateDevServer, Foreground),
- (CreateDevServerResponse, Foreground),
- (DevServerInstructions, Foreground),
- (ShutdownDevServer, Foreground),
- (ReconnectDevServer, Foreground),
- (ReconnectDevServerResponse, Foreground),
- (ShareDevServerProject, Foreground),
- (JoinDevServerProject, Foreground),
(RejoinRemoteProjects, Foreground),
(RejoinRemoteProjectsResponse, Foreground),
(MultiLspQuery, Background),
(MultiLspQueryResponse, Background),
- (DevServerProjectsUpdate, Foreground),
- (ValidateDevServerProjectRequest, Background),
(ListRemoteDirectory, Background),
(ListRemoteDirectoryResponse, Background),
- (UpdateDevServerProject, Background),
- (DeleteDevServer, Foreground),
- (DeleteDevServerProject, Foreground),
- (RegenerateDevServerToken, Foreground),
- (RegenerateDevServerTokenResponse, Foreground),
- (RenameDevServer, Foreground),
(OpenNewBuffer, Foreground),
(RestartLanguageServers, Foreground),
(LinkedEditingRange, Background),
@@ -419,7 +401,6 @@ request_messages!(
(GetTypeDefinition, GetTypeDefinitionResponse),
(LinkedEditingRange, LinkedEditingRangeResponse),
(ListRemoteDirectory, ListRemoteDirectoryResponse),
- (UpdateDevServerProject, Ack),
(GetUsers, UsersResponse),
(IncomingCall, Ack),
(InlayHints, InlayHintsResponse),
@@ -477,19 +458,8 @@ request_messages!(
(LspExtExpandMacro, LspExtExpandMacroResponse),
(SetRoomParticipantRole, Ack),
(BlameBuffer, BlameBufferResponse),
- (CreateDevServerProject, CreateDevServerProjectResponse),
- (CreateDevServer, CreateDevServerResponse),
- (ShutdownDevServer, Ack),
- (ShareDevServerProject, ShareProjectResponse),
- (JoinDevServerProject, JoinProjectResponse),
(RejoinRemoteProjects, RejoinRemoteProjectsResponse),
- (ReconnectDevServer, ReconnectDevServerResponse),
- (ValidateDevServerProjectRequest, Ack),
(MultiLspQuery, MultiLspQueryResponse),
- (DeleteDevServer, Ack),
- (DeleteDevServerProject, Ack),
- (RegenerateDevServerToken, RegenerateDevServerTokenResponse),
- (RenameDevServer, Ack),
(RestartLanguageServers, Ack),
(OpenContext, OpenContextResponse),
(CreateContext, CreateContextResponse),
@@ -16,7 +16,6 @@ doctest = false
anyhow.workspace = true
auto_update.workspace = true
release_channel.workspace = true
-client.workspace = true
editor.workspace = true
file_finder.workspace = true
futures.workspace = true
@@ -30,15 +29,12 @@ menu.workspace = true
ordered-float.workspace = true
picker.workspace = true
project.workspace = true
-dev_server_projects.workspace = true
remote.workspace = true
-rpc.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
smol.workspace = true
task.workspace = true
-terminal_view.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
@@ -1,6 +1,5 @@
use std::path::PathBuf;
-use dev_server_projects::DevServer;
use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, FocusableView, Render, WeakView};
use project::project_settings::ProjectSettings;
use remote::SshConnectionOptions;
@@ -12,14 +11,10 @@ use ui::{
};
use workspace::{notifications::DetachAndPromptErr, ModalView, OpenOptions, Workspace};
-use crate::{
- open_dev_server_project, open_ssh_project, remote_servers::reconnect_to_dev_server_project,
- RemoteServerProjects, SshSettings,
-};
+use crate::{open_ssh_project, SshSettings};
enum Host {
RemoteProject,
- DevServerProject(DevServer),
SshRemoteProject(SshConnectionOptions),
}
@@ -55,20 +50,9 @@ impl DisconnectedOverlay {
return;
}
let handle = cx.view().downgrade();
- let dev_server = project
- .read(cx)
- .dev_server_project_id()
- .and_then(|id| {
- dev_server_projects::Store::global(cx)
- .read(cx)
- .dev_server_for_project(id)
- })
- .cloned();
let ssh_connection_options = project.read(cx).ssh_connection_options(cx);
- let host = if let Some(dev_server) = dev_server {
- Host::DevServerProject(dev_server)
- } else if let Some(ssh_connection_options) = ssh_connection_options {
+ let host = if let Some(ssh_connection_options) = ssh_connection_options {
Host::SshRemoteProject(ssh_connection_options)
} else {
Host::RemoteProject
@@ -89,9 +73,6 @@ impl DisconnectedOverlay {
cx.emit(DismissEvent);
match &self.host {
- Host::DevServerProject(dev_server) => {
- self.reconnect_to_dev_server(dev_server.clone(), cx);
- }
Host::SshRemoteProject(ssh_connection_options) => {
self.reconnect_to_ssh_remote(ssh_connection_options.clone(), cx);
}
@@ -99,50 +80,6 @@ impl DisconnectedOverlay {
}
}
- fn reconnect_to_dev_server(&self, dev_server: DevServer, cx: &mut ViewContext<Self>) {
- let Some(workspace) = self.workspace.upgrade() else {
- return;
- };
- let Some(dev_server_project_id) = workspace
- .read(cx)
- .project()
- .read(cx)
- .dev_server_project_id()
- else {
- return;
- };
-
- if let Some(project_id) = dev_server_projects::Store::global(cx)
- .read(cx)
- .dev_server_project(dev_server_project_id)
- .and_then(|project| project.project_id)
- {
- return workspace.update(cx, move |_, cx| {
- open_dev_server_project(true, dev_server_project_id, project_id, cx)
- .detach_and_prompt_err("Failed to reconnect", cx, |_, _| None)
- });
- }
-
- if dev_server.ssh_connection_string.is_some() {
- let task = workspace.update(cx, |_, cx| {
- reconnect_to_dev_server_project(
- cx.view().clone(),
- dev_server,
- dev_server_project_id,
- true,
- cx,
- )
- });
-
- task.detach_and_prompt_err("Failed to reconnect", cx, |_, _| None);
- } else {
- return workspace.update(cx, |workspace, cx| {
- let handle = cx.view().downgrade();
- workspace.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, handle))
- });
- }
- }
-
fn reconnect_to_ssh_remote(
&self,
connection_options: SshConnectionOptions,
@@ -200,13 +137,10 @@ impl DisconnectedOverlay {
impl Render for DisconnectedOverlay {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
- let can_reconnect = matches!(
- self.host,
- Host::DevServerProject(_) | Host::SshRemoteProject(_)
- );
+ let can_reconnect = matches!(self.host, Host::SshRemoteProject(_));
let message = match &self.host {
- Host::RemoteProject | Host::DevServerProject(_) => {
+ Host::RemoteProject => {
"Your connection to the remote project has been lost.".to_string()
}
Host::SshRemoteProject(options) => {
@@ -4,7 +4,6 @@ mod ssh_connections;
use remote::SshConnectionOptions;
pub use ssh_connections::open_ssh_project;
-use client::{DevServerProjectId, ProjectId};
use disconnected_overlay::DisconnectedOverlay;
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
@@ -17,9 +16,7 @@ use picker::{
highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText},
Picker, PickerDelegate,
};
-use remote_servers::reconnect_to_dev_server_project;
pub use remote_servers::RemoteServerProjects;
-use rpc::proto::DevServerStatus;
use serde::Deserialize;
use settings::Settings;
pub use ssh_connections::SshSettings;
@@ -28,13 +25,12 @@ use std::{
sync::Arc,
};
use ui::{
- prelude::*, tooltip_container, ButtonLike, IconWithIndicator, Indicator, KeyBinding, ListItem,
- ListItemSpacing, Tooltip,
+ prelude::*, tooltip_container, ButtonLike, KeyBinding, ListItem, ListItemSpacing, Tooltip,
};
use util::{paths::PathExt, ResultExt};
use workspace::{
- AppState, CloseIntent, ModalView, OpenOptions, SerializedWorkspaceLocation, Workspace,
- WorkspaceId, WORKSPACE_DB,
+ CloseIntent, ModalView, OpenOptions, SerializedWorkspaceLocation, Workspace, WorkspaceId,
+ WORKSPACE_DB,
};
#[derive(PartialEq, Clone, Deserialize, Default)]
@@ -101,7 +97,7 @@ impl RecentProjects {
}
}
- fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+ fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) {
workspace.register_action(|workspace, open_recent: &OpenRecent, cx| {
let Some(recent_projects) = workspace.active_modal::<Self>(cx) else {
Self::open(workspace, open_recent.create_new_window, cx);
@@ -114,20 +110,6 @@ impl RecentProjects {
.update(cx, |picker, cx| picker.cycle_selection(cx))
});
});
- if workspace
- .project()
- .read(cx)
- .dev_server_project_id()
- .is_some()
- {
- workspace.register_action(|workspace, _: &workspace::Open, cx| {
- if workspace.active_modal::<Self>(cx).is_some() {
- cx.propagate();
- } else {
- Self::open(workspace, true, cx);
- }
- });
- }
}
pub fn open(
@@ -254,13 +236,6 @@ impl PickerDelegate for RecentProjectsDelegate {
.map(|(_, path)| path.compact().to_string_lossy().into_owned())
.collect::<Vec<_>>()
.join(""),
- SerializedWorkspaceLocation::DevServer(dev_server_project) => {
- format!(
- "{}{}",
- dev_server_project.dev_server_name,
- dev_server_project.paths.join("")
- )
- }
SerializedWorkspaceLocation::Ssh(ssh_project) => ssh_project
.ssh_urls()
.iter()
@@ -321,7 +296,10 @@ impl PickerDelegate for RecentProjectsDelegate {
cx.spawn(move |workspace, mut cx| async move {
let continue_replacing = workspace
.update(&mut cx, |workspace, cx| {
- workspace.prepare_to_close(CloseIntent::ReplaceWindow, cx)
+ workspace.prepare_to_close(
+ CloseIntent::ReplaceWindow,
+ cx,
+ )
})?
.await?;
if continue_replacing {
@@ -339,74 +317,56 @@ impl PickerDelegate for RecentProjectsDelegate {
workspace.open_workspace_for_paths(false, paths, cx)
}
}
- SerializedWorkspaceLocation::DevServer(dev_server_project) => {
- let store = dev_server_projects::Store::global(cx);
- let Some(project_id) = store.read(cx)
- .dev_server_project(dev_server_project.id)
- .and_then(|p| p.project_id)
- else {
- let server = store.read(cx).dev_server_for_project(dev_server_project.id);
- if server.is_some_and(|server| server.ssh_connection_string.is_some()) {
- return reconnect_to_dev_server_project(cx.view().clone(), server.unwrap().clone(), dev_server_project.id, replace_current_window, cx);
- } else {
- let dev_server_name = dev_server_project.dev_server_name.clone();
- return cx.spawn(|workspace, mut cx| async move {
- let response =
- cx.prompt(gpui::PromptLevel::Warning,
- "Dev Server is offline",
- Some(format!("Cannot connect to {}. To debug open the remote project settings.", dev_server_name).as_str()),
- &["Ok", "Open Settings"]
- ).await?;
- if response == 1 {
- workspace.update(&mut cx, |workspace, cx| {
- let handle = cx.view().downgrade();
- workspace.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, handle))
- })?;
- } else {
- workspace.update(&mut cx, |workspace, cx| {
- RecentProjects::open(workspace, true, cx);
- })?;
- }
- Ok(())
- })
- }
+ SerializedWorkspaceLocation::Ssh(ssh_project) => {
+ let app_state = workspace.app_state().clone();
+
+ let replace_window = if replace_current_window {
+ cx.window_handle().downcast::<Workspace>()
+ } else {
+ None
};
- open_dev_server_project(replace_current_window, dev_server_project.id, project_id, cx)
- }
- SerializedWorkspaceLocation::Ssh(ssh_project) => {
- let app_state = workspace.app_state().clone();
-
- let replace_window = if replace_current_window {
- cx.window_handle().downcast::<Workspace>()
- } else {
- None
- };
-
- let open_options = OpenOptions {
- replace_window,
- ..Default::default()
- };
-
- let args = SshSettings::get_global(cx).args_for(&ssh_project.host, ssh_project.port, &ssh_project.user);
- let nickname = SshSettings::get_global(cx).nickname_for(&ssh_project.host, ssh_project.port, &ssh_project.user);
- let connection_options = SshConnectionOptions {
- host: ssh_project.host.clone(),
- username: ssh_project.user.clone(),
- port: ssh_project.port,
- password: None,
- args,
- };
-
- let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
-
- cx.spawn(|_, mut cx| async move {
- open_ssh_project(connection_options, paths, app_state, open_options, nickname, &mut cx).await
- })
+
+ let open_options = OpenOptions {
+ replace_window,
+ ..Default::default()
+ };
+
+ let args = SshSettings::get_global(cx).args_for(
+ &ssh_project.host,
+ ssh_project.port,
+ &ssh_project.user,
+ );
+ let nickname = SshSettings::get_global(cx).nickname_for(
+ &ssh_project.host,
+ ssh_project.port,
+ &ssh_project.user,
+ );
+ let connection_options = SshConnectionOptions {
+ host: ssh_project.host.clone(),
+ username: ssh_project.user.clone(),
+ port: ssh_project.port,
+ password: None,
+ args,
+ };
+
+ let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
+
+ cx.spawn(|_, mut cx| async move {
+ open_ssh_project(
+ connection_options,
+ paths,
+ app_state,
+ open_options,
+ nickname,
+ &mut cx,
+ )
+ .await
+ })
+ }
}
}
- }
})
- .detach_and_log_err(cx);
+ .detach_and_log_err(cx);
cx.emit(DismissEvent);
}
}
@@ -431,20 +391,6 @@ impl PickerDelegate for RecentProjectsDelegate {
let (_, location) = self.workspaces.get(hit.candidate_id)?;
- let dev_server_status =
- if let SerializedWorkspaceLocation::DevServer(dev_server_project) = location {
- let store = dev_server_projects::Store::global(cx).read(cx);
- Some(
- store
- .dev_server_project(dev_server_project.id)
- .and_then(|p| store.dev_server(p.dev_server_id))
- .map(|s| s.status)
- .unwrap_or_default(),
- )
- } else {
- None
- };
-
let mut path_start_offset = 0;
let paths = match location {
SerializedWorkspaceLocation::Local(paths, order) => Arc::new(
@@ -457,13 +403,6 @@ impl PickerDelegate for RecentProjectsDelegate {
.collect(),
),
SerializedWorkspaceLocation::Ssh(ssh_project) => Arc::new(ssh_project.ssh_urls()),
- SerializedWorkspaceLocation::DevServer(dev_server_project) => {
- Arc::new(vec![PathBuf::from(format!(
- "{}:{}",
- dev_server_project.dev_server_name,
- dev_server_project.paths.join(", ")
- ))])
- }
};
let (match_labels, paths): (Vec<_>, Vec<_>) = paths
@@ -478,13 +417,7 @@ impl PickerDelegate for RecentProjectsDelegate {
.unzip();
let highlighted_match = HighlightedMatchWithPaths {
- match_label: HighlightedText::join(match_labels.into_iter().flatten(), ", ").color(
- if matches!(dev_server_status, Some(DevServerStatus::Offline)) {
- Color::Disabled
- } else {
- Color::Default
- },
- ),
+ match_label: HighlightedText::join(match_labels.into_iter().flatten(), ", "),
paths,
};
@@ -507,24 +440,6 @@ impl PickerDelegate for RecentProjectsDelegate {
SerializedWorkspaceLocation::Ssh(_) => Icon::new(IconName::Server)
.color(Color::Muted)
.into_any_element(),
- SerializedWorkspaceLocation::DevServer(_) => {
- let indicator_color = match dev_server_status {
- Some(DevServerStatus::Online) => Color::Created,
- Some(DevServerStatus::Offline) => Color::Hidden,
- _ => unreachable!(),
- };
- IconWithIndicator::new(
- Icon::new(IconName::Server).color(Color::Muted),
- Some(Indicator::dot()),
- )
- .indicator_color(indicator_color)
- .indicator_border_color(if selected {
- Some(cx.theme().colors().element_selected)
- } else {
- None
- })
- .into_any_element()
- }
})
})
.child({
@@ -597,59 +512,6 @@ impl PickerDelegate for RecentProjectsDelegate {
}
}
-fn open_dev_server_project(
- replace_current_window: bool,
- dev_server_project_id: DevServerProjectId,
- project_id: ProjectId,
- cx: &mut ViewContext<Workspace>,
-) -> Task<anyhow::Result<()>> {
- if let Some(app_state) = AppState::global(cx).upgrade() {
- let handle = if replace_current_window {
- cx.window_handle().downcast::<Workspace>()
- } else {
- None
- };
-
- if let Some(handle) = handle {
- cx.spawn(move |workspace, mut cx| async move {
- let continue_replacing = workspace
- .update(&mut cx, |workspace, cx| {
- workspace.prepare_to_close(CloseIntent::ReplaceWindow, cx)
- })?
- .await?;
- if continue_replacing {
- workspace
- .update(&mut cx, |_workspace, cx| {
- workspace::join_dev_server_project(
- dev_server_project_id,
- project_id,
- app_state,
- Some(handle),
- cx,
- )
- })?
- .await?;
- }
- Ok(())
- })
- } else {
- let task = workspace::join_dev_server_project(
- dev_server_project_id,
- project_id,
- app_state,
- None,
- cx,
- );
- cx.spawn(|_, _| async move {
- task.await?;
- Ok(())
- })
- }
- } else {
- Task::ready(Err(anyhow::anyhow!("App state not found")))
- }
-}
-
// Compute the highlighted text for the name and path
fn highlights_for_path(
path: &Path,
@@ -1,19 +1,12 @@
-use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
-use std::time::Duration;
-use anyhow::anyhow;
-use anyhow::Context;
-use anyhow::Result;
-use dev_server_projects::{DevServer, DevServerId, DevServerProjectId};
use editor::Editor;
use file_finder::OpenPathDelegate;
use futures::channel::oneshot;
use futures::future::Shared;
use futures::FutureExt;
use gpui::canvas;
-use gpui::AsyncWindowContext;
use gpui::ClipboardItem;
use gpui::Task;
use gpui::WeakView;
@@ -22,17 +15,10 @@ use gpui::{
PromptLevel, ScrollHandle, View, ViewContext,
};
use picker::Picker;
-use project::terminals::wrap_for_ssh;
-use project::terminals::SshCommand;
use project::Project;
use remote::SshConnectionOptions;
-use rpc::proto::DevServerStatus;
use settings::update_settings_file;
use settings::Settings;
-use task::HideStrategy;
-use task::RevealStrategy;
-use task::SpawnInTerminal;
-use terminal_view::terminal_panel::TerminalPanel;
use ui::{
prelude::*, IconButtonShape, List, ListItem, ListSeparator, Modal, ModalHeader, Scrollbar,
ScrollbarState, Section, Tooltip,
@@ -43,7 +29,6 @@ use workspace::OpenOptions;
use workspace::Toast;
use workspace::{notifications::DetachAndPromptErr, ModalView, Workspace};
-use crate::open_dev_server_project;
use crate::ssh_connections::connect_over_ssh;
use crate::ssh_connections::open_ssh_project;
use crate::ssh_connections::RemoteSettingsContent;
@@ -1319,146 +1304,3 @@ impl Render for RemoteServerProjects {
})
}
}
-
-pub fn reconnect_to_dev_server_project(
- workspace: View<Workspace>,
- dev_server: DevServer,
- dev_server_project_id: DevServerProjectId,
- replace_current_window: bool,
- cx: &mut WindowContext,
-) -> Task<Result<()>> {
- let store = dev_server_projects::Store::global(cx);
- let reconnect = reconnect_to_dev_server(workspace.clone(), dev_server, cx);
- cx.spawn(|mut cx| async move {
- reconnect.await?;
-
- cx.background_executor()
- .timer(Duration::from_millis(1000))
- .await;
-
- if let Some(project_id) = store.update(&mut cx, |store, _| {
- store
- .dev_server_project(dev_server_project_id)
- .and_then(|p| p.project_id)
- })? {
- workspace
- .update(&mut cx, move |_, cx| {
- open_dev_server_project(
- replace_current_window,
- dev_server_project_id,
- project_id,
- cx,
- )
- })?
- .await?;
- }
-
- Ok(())
- })
-}
-
-pub fn reconnect_to_dev_server(
- workspace: View<Workspace>,
- dev_server: DevServer,
- cx: &mut WindowContext,
-) -> Task<Result<()>> {
- let Some(ssh_connection_string) = dev_server.ssh_connection_string else {
- return Task::ready(Err(anyhow!("Can't reconnect, no ssh_connection_string")));
- };
- let dev_server_store = dev_server_projects::Store::global(cx);
- let get_access_token = dev_server_store.update(cx, |store, cx| {
- store.regenerate_dev_server_token(dev_server.id, cx)
- });
-
- cx.spawn(|mut cx| async move {
- let access_token = get_access_token.await?.access_token;
-
- spawn_ssh_task(
- workspace,
- dev_server_store,
- dev_server.id,
- ssh_connection_string.to_string(),
- access_token,
- &mut cx,
- )
- .await
- })
-}
-
-pub async fn spawn_ssh_task(
- workspace: View<Workspace>,
- dev_server_store: Model<dev_server_projects::Store>,
- dev_server_id: DevServerId,
- ssh_connection_string: String,
- access_token: String,
- cx: &mut AsyncWindowContext,
-) -> Result<()> {
- let terminal_panel = workspace
- .update(cx, |workspace, cx| workspace.panel::<TerminalPanel>(cx))
- .ok()
- .flatten()
- .with_context(|| anyhow!("No terminal panel"))?;
-
- let command = "sh".to_string();
- let args = vec![
- "-x".to_string(),
- "-c".to_string(),
- format!(
- r#"~/.local/bin/zed -v >/dev/stderr || (curl -f https://zed.dev/install.sh || wget -qO- https://zed.dev/install.sh) | sh && ZED_HEADLESS=1 ~/.local/bin/zed --dev-server-token {}"#,
- access_token
- ),
- ];
-
- let ssh_connection_string = ssh_connection_string.to_string();
- let (command, args) = wrap_for_ssh(
- &SshCommand::DevServer(ssh_connection_string.clone()),
- Some((&command, &args)),
- None,
- HashMap::default(),
- None,
- );
-
- let terminal = terminal_panel
- .update(cx, |terminal_panel, cx| {
- terminal_panel.spawn_in_new_terminal(
- SpawnInTerminal {
- id: task::TaskId("ssh-remote".into()),
- full_label: "Install zed over ssh".into(),
- label: "Install zed over ssh".into(),
- command,
- args,
- command_label: ssh_connection_string.clone(),
- cwd: None,
- use_new_terminal: true,
- allow_concurrent_runs: false,
- reveal: RevealStrategy::Always,
- hide: HideStrategy::Never,
- env: Default::default(),
- shell: Default::default(),
- },
- cx,
- )
- })?
- .await?;
-
- terminal
- .update(cx, |terminal, cx| terminal.wait_for_completed_task(cx))?
- .await;
-
- // There's a race-condition between the task completing successfully, and the server sending us the online status. Make it less likely we'll show the error state.
- if dev_server_store.update(cx, |this, _| this.dev_server_status(dev_server_id))?
- == DevServerStatus::Offline
- {
- cx.background_executor()
- .timer(Duration::from_millis(200))
- .await
- }
-
- if dev_server_store.update(cx, |this, _| this.dev_server_status(dev_server_id))?
- == DevServerStatus::Offline
- {
- return Err(anyhow!("couldn't reconnect"))?;
- }
-
- Ok(())
-}
@@ -33,7 +33,6 @@ auto_update.workspace = true
call.workspace = true
client.workspace = true
command_palette.workspace = true
-dev_server_projects.workspace = true
extensions_ui.workspace = true
feedback.workspace = true
feature_flags.workspace = true
@@ -285,8 +285,7 @@ impl TitleBar {
let room = room.read(cx);
let project = self.project.read(cx);
let is_local = project.is_local() || project.is_via_ssh();
- let is_dev_server_project = project.dev_server_project_id().is_some();
- let is_shared = (is_local || is_dev_server_project) && project.is_shared();
+ let is_shared = is_local && project.is_shared();
let is_muted = room.is_muted();
let is_deafened = room.is_deafened().unwrap_or(false);
let is_screen_sharing = room.is_screen_sharing();
@@ -299,7 +298,7 @@ impl TitleBar {
let mut children = Vec::new();
- if (is_local || is_dev_server_project) && can_share_projects {
+ if is_local && can_share_projects {
children.push(
Button::new(
"toggle_sharing",
@@ -20,7 +20,7 @@ use gpui::{
use project::{Project, RepositoryEntry};
use recent_projects::{OpenRemote, RecentProjects, SshSettings};
use remote::SshConnectionOptions;
-use rpc::proto::{self, DevServerStatus};
+use rpc::proto;
use settings::Settings;
use smallvec::SmallVec;
use std::sync::Arc;
@@ -334,39 +334,6 @@ impl TitleBar {
}
pub fn render_project_host(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
- if let Some(dev_server) =
- self.project
- .read(cx)
- .dev_server_project_id()
- .and_then(|dev_server_project_id| {
- dev_server_projects::Store::global(cx)
- .read(cx)
- .dev_server_for_project(dev_server_project_id)
- })
- {
- return Some(
- ButtonLike::new("dev_server_trigger")
- .child(Indicator::dot().color(
- if dev_server.status == DevServerStatus::Online {
- Color::Created
- } else {
- Color::Disabled
- },
- ))
- .child(
- Label::new(dev_server.name.clone())
- .size(LabelSize::Small)
- .line_height_style(LineHeightStyle::UiLabel),
- )
- .tooltip(move |cx| Tooltip::text("Project is hosted on a dev server", cx))
- .on_click(cx.listener(|this, _, cx| {
- if let Some(workspace) = this.workspace.upgrade() {
- recent_projects::RemoteServerProjects::open(workspace, cx)
- }
- }))
- .into_any_element(),
- );
- }
if self.project.read(cx).is_via_ssh() {
return self.render_ssh_project_host(cx);
}
@@ -49,7 +49,6 @@ node_runtime.workspace = true
parking_lot.workspace = true
postage.workspace = true
project.workspace = true
-dev_server_projects.workspace = true
task.workspace = true
release_channel.workspace = true
remote.workspace = true
@@ -24,9 +24,7 @@ use model::{
SerializedSshProject, SerializedWorkspace,
};
-use self::model::{
- DockStructure, LocalPathsOrder, SerializedDevServerProject, SerializedWorkspaceLocation,
-};
+use self::model::{DockStructure, LocalPathsOrder, SerializedWorkspaceLocation};
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) struct SerializedAxis(pub(crate) gpui::Axis);
@@ -460,89 +458,6 @@ impl WorkspaceDb {
})
}
- pub(crate) fn workspace_for_dev_server_project(
- &self,
- dev_server_project_id: DevServerProjectId,
- ) -> Option<SerializedWorkspace> {
- // Note that we re-assign the workspace_id here in case it's empty
- // and we've grabbed the most recent workspace
- let (
- workspace_id,
- dev_server_project_id,
- window_bounds,
- display,
- centered_layout,
- docks,
- window_id,
- ): (
- WorkspaceId,
- Option<u64>,
- Option<SerializedWindowBounds>,
- Option<Uuid>,
- Option<bool>,
- DockStructure,
- Option<u64>,
- ) = self
- .select_row_bound(sql! {
- SELECT
- workspace_id,
- dev_server_project_id,
- window_state,
- window_x,
- window_y,
- window_width,
- window_height,
- display,
- centered_layout,
- left_dock_visible,
- left_dock_active_panel,
- left_dock_zoom,
- right_dock_visible,
- right_dock_active_panel,
- right_dock_zoom,
- bottom_dock_visible,
- bottom_dock_active_panel,
- bottom_dock_zoom,
- window_id
- FROM workspaces
- WHERE dev_server_project_id = ?
- })
- .and_then(|mut prepared_statement| (prepared_statement)(dev_server_project_id.0))
- .context("No workspaces found")
- .warn_on_err()
- .flatten()?;
-
- let dev_server_project_id = dev_server_project_id?;
-
- let dev_server_project: SerializedDevServerProject = self
- .select_row_bound(sql! {
- SELECT id, path, dev_server_name
- FROM dev_server_projects
- WHERE id = ?
- })
- .and_then(|mut prepared_statement| (prepared_statement)(dev_server_project_id))
- .context("No remote project found")
- .warn_on_err()
- .flatten()?;
-
- let location = SerializedWorkspaceLocation::DevServer(dev_server_project);
-
- Some(SerializedWorkspace {
- id: workspace_id,
- location,
- center_group: self
- .get_center_pane_group(workspace_id)
- .context("Getting center group")
- .log_err()?,
- window_bounds,
- centered_layout: centered_layout.unwrap_or(false),
- display,
- docks,
- session_id: None,
- window_id,
- })
- }
-
pub(crate) fn workspace_for_ssh_project(
&self,
ssh_project: &SerializedSshProject,
@@ -659,61 +574,6 @@ impl WorkspaceDb {
prepared_query(args).context("Updating workspace")?;
}
- SerializedWorkspaceLocation::DevServer(dev_server_project) => {
- conn.exec_bound(sql!(
- DELETE FROM workspaces WHERE dev_server_project_id = ? AND workspace_id != ?
- ))?((dev_server_project.id.0, workspace.id))
- .context("clearing out old locations")?;
-
- conn.exec_bound(sql!(
- INSERT INTO dev_server_projects(
- id,
- path,
- dev_server_name
- ) VALUES (?1, ?2, ?3)
- ON CONFLICT DO
- UPDATE SET
- path = ?2,
- dev_server_name = ?3
- ))?(&dev_server_project)?;
-
- // Upsert
- conn.exec_bound(sql!(
- INSERT INTO workspaces(
- workspace_id,
- dev_server_project_id,
- left_dock_visible,
- left_dock_active_panel,
- left_dock_zoom,
- right_dock_visible,
- right_dock_active_panel,
- right_dock_zoom,
- bottom_dock_visible,
- bottom_dock_active_panel,
- bottom_dock_zoom,
- timestamp
- )
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, CURRENT_TIMESTAMP)
- ON CONFLICT DO
- UPDATE SET
- dev_server_project_id = ?2,
- left_dock_visible = ?3,
- left_dock_active_panel = ?4,
- left_dock_zoom = ?5,
- right_dock_visible = ?6,
- right_dock_active_panel = ?7,
- right_dock_zoom = ?8,
- bottom_dock_visible = ?9,
- bottom_dock_active_panel = ?10,
- bottom_dock_zoom = ?11,
- timestamp = CURRENT_TIMESTAMP
- ))?((
- workspace.id,
- dev_server_project.id.0,
- workspace.docks,
- ))
- .context("Updating workspace")?;
- },
SerializedWorkspaceLocation::Ssh(ssh_project) => {
conn.exec_bound(sql!(
DELETE FROM workspaces WHERE ssh_project_id = ? AND workspace_id != ?
@@ -824,11 +684,10 @@ impl WorkspaceDb {
}
query! {
- fn recent_workspaces() -> Result<Vec<(WorkspaceId, LocalPaths, LocalPathsOrder, Option<u64>, Option<u64>)>> {
- SELECT workspace_id, local_paths, local_paths_order, dev_server_project_id, ssh_project_id
+ fn recent_workspaces() -> Result<Vec<(WorkspaceId, LocalPaths, LocalPathsOrder, Option<u64>)>> {
+ SELECT workspace_id, local_paths, local_paths_order, ssh_project_id
FROM workspaces
WHERE local_paths IS NOT NULL
- OR dev_server_project_id IS NOT NULL
OR ssh_project_id IS NOT NULL
ORDER BY timestamp DESC
}
@@ -843,13 +702,6 @@ impl WorkspaceDb {
}
}
- query! {
- fn dev_server_projects() -> Result<Vec<SerializedDevServerProject>> {
- SELECT id, path, dev_server_name
- FROM dev_server_projects
- }
- }
-
query! {
fn ssh_projects() -> Result<Vec<SerializedSshProject>> {
SELECT id, host, port, paths, user
@@ -913,24 +765,9 @@ impl WorkspaceDb {
) -> Result<Vec<(WorkspaceId, SerializedWorkspaceLocation)>> {
let mut result = Vec::new();
let mut delete_tasks = Vec::new();
- let dev_server_projects = self.dev_server_projects()?;
let ssh_projects = self.ssh_projects()?;
- for (id, location, order, dev_server_project_id, ssh_project_id) in
- self.recent_workspaces()?
- {
- if let Some(dev_server_project_id) = dev_server_project_id.map(DevServerProjectId) {
- if let Some(dev_server_project) = dev_server_projects
- .iter()
- .find(|rp| rp.id == dev_server_project_id)
- {
- result.push((id, dev_server_project.clone().into()));
- } else {
- delete_tasks.push(self.delete_workspace_by_id(id));
- }
- continue;
- }
-
+ for (id, location, order, ssh_project_id) in self.recent_workspaces()? {
if let Some(ssh_project_id) = ssh_project_id.map(SshProjectId) {
if let Some(ssh_project) = ssh_projects.iter().find(|rp| rp.id == ssh_project_id) {
result.push((id, SerializedWorkspaceLocation::Ssh(ssh_project.clone())));
@@ -4,7 +4,6 @@ use crate::{
};
use anyhow::{Context, Result};
use async_recursion::async_recursion;
-use client::DevServerProjectId;
use db::sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
@@ -17,7 +16,6 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
-use ui::SharedString;
use util::ResultExt;
use uuid::Uuid;
@@ -92,13 +90,6 @@ impl Column for SerializedSshProject {
}
}
-#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
-pub struct SerializedDevServerProject {
- pub id: DevServerProjectId,
- pub dev_server_name: String,
- pub paths: Vec<SharedString>,
-}
-
#[derive(Debug, PartialEq, Clone)]
pub struct LocalPaths(Arc<Vec<PathBuf>>);
@@ -176,49 +167,10 @@ impl Column for LocalPathsOrder {
}
}
-impl From<SerializedDevServerProject> for SerializedWorkspaceLocation {
- fn from(dev_server_project: SerializedDevServerProject) -> Self {
- Self::DevServer(dev_server_project)
- }
-}
-
-impl StaticColumnCount for SerializedDevServerProject {}
-impl Bind for &SerializedDevServerProject {
- fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
- let next_index = statement.bind(&self.id.0, start_index)?;
- let next_index = statement.bind(&self.dev_server_name, next_index)?;
- let paths = serde_json::to_string(&self.paths)?;
- statement.bind(&paths, next_index)
- }
-}
-
-impl Column for SerializedDevServerProject {
- fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
- let id = statement.column_int64(start_index)?;
- let dev_server_name = statement.column_text(start_index + 1)?.to_string();
- let paths = statement.column_text(start_index + 2)?.to_string();
- let paths: Vec<SharedString> = if paths.starts_with('[') {
- serde_json::from_str(&paths).context("JSON deserialization of paths failed")?
- } else {
- vec![paths.into()]
- };
-
- Ok((
- Self {
- id: DevServerProjectId(id as u64),
- dev_server_name,
- paths,
- },
- start_index + 3,
- ))
- }
-}
-
#[derive(Debug, PartialEq, Clone)]
pub enum SerializedWorkspaceLocation {
Local(LocalPaths, LocalPathsOrder),
Ssh(SerializedSshProject),
- DevServer(SerializedDevServerProject),
}
impl SerializedWorkspaceLocation {
@@ -16,7 +16,7 @@ use anyhow::{anyhow, Context as _, Result};
use call::{call_settings::CallSettings, ActiveCall};
use client::{
proto::{self, ErrorCode, PanelId, PeerId},
- ChannelId, Client, DevServerProjectId, ErrorExt, ProjectId, Status, TypedEnvelope, UserStore,
+ ChannelId, Client, ErrorExt, ProjectId, Status, TypedEnvelope, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use derive_more::{Deref, DerefMut};
@@ -52,7 +52,7 @@ use notifications::{
pub use pane::*;
pub use pane_group::*;
pub use persistence::{
- model::{ItemId, LocalPaths, SerializedDevServerProject, SerializedWorkspaceLocation},
+ model::{ItemId, LocalPaths, SerializedWorkspaceLocation},
WorkspaceDb, DB as WORKSPACE_DB,
};
use persistence::{
@@ -97,7 +97,7 @@ use ui::{
IntoElement, ParentElement as _, Pixels, SharedString, Styled as _, ViewContext,
VisualContext as _, WindowContext,
};
-use util::{maybe, ResultExt, TryFutureExt};
+use util::{ResultExt, TryFutureExt};
use uuid::Uuid;
pub use workspace_settings::{
AutosaveSetting, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings,
@@ -2057,7 +2057,7 @@ impl Workspace {
fn add_folder_to_project(&mut self, _: &AddFolderToProject, cx: &mut ViewContext<Self>) {
let project = self.project.read(cx);
- if project.is_via_collab() && project.dev_server_project_id().is_none() {
+ if project.is_via_collab() {
self.show_error(
&anyhow!("You cannot add folders to someone else's project"),
cx,
@@ -4133,20 +4133,6 @@ impl Workspace {
} else {
None
}
- } else if let Some(dev_server_project_id) = self.project().read(cx).dev_server_project_id()
- {
- let store = dev_server_projects::Store::global(cx).read(cx);
- maybe!({
- let project = store.dev_server_project(dev_server_project_id)?;
- let dev_server = store.dev_server(project.dev_server_id)?;
-
- let dev_server_project = SerializedDevServerProject {
- id: dev_server_project_id,
- dev_server_name: dev_server.name.to_string(),
- paths: project.paths.to_vec(),
- };
- Some(SerializedWorkspaceLocation::DevServer(dev_server_project))
- })
} else {
None
};
@@ -5180,13 +5166,12 @@ async fn join_channel_internal(
if let Some(workspace) = requesting_window {
let project = workspace.update(cx, |workspace, cx| {
let project = workspace.project.read(cx);
- let is_dev_server = project.dev_server_project_id().is_some();
- if !is_dev_server && !CallSettings::get_global(cx).share_on_join {
+ if !CallSettings::get_global(cx).share_on_join {
return None;
}
- if (project.is_local() || project.is_via_ssh() || is_dev_server)
+ if (project.is_local() || project.is_via_ssh())
&& project.visible_worktrees(cx).any(|tree| {
tree.read(cx)
.root_entry()
@@ -5685,84 +5670,6 @@ fn serialize_ssh_project(
})
}
-pub fn join_dev_server_project(
- dev_server_project_id: DevServerProjectId,
- project_id: ProjectId,
- app_state: Arc<AppState>,
- window_to_replace: Option<WindowHandle<Workspace>>,
- cx: &mut AppContext,
-) -> Task<Result<WindowHandle<Workspace>>> {
- let windows = cx.windows();
- cx.spawn(|mut cx| async move {
- let existing_workspace = windows.into_iter().find_map(|window| {
- window.downcast::<Workspace>().and_then(|window| {
- window
- .update(&mut cx, |workspace, cx| {
- if workspace.project().read(cx).remote_id() == Some(project_id.0) {
- Some(window)
- } else {
- None
- }
- })
- .unwrap_or(None)
- })
- });
-
- let serialized_workspace: Option<SerializedWorkspace> =
- persistence::DB.workspace_for_dev_server_project(dev_server_project_id);
-
- let workspace = if let Some(existing_workspace) = existing_workspace {
- existing_workspace
- } else {
- let project = Project::remote(
- project_id.0,
- app_state.client.clone(),
- app_state.user_store.clone(),
- app_state.languages.clone(),
- app_state.fs.clone(),
- cx.clone(),
- )
- .await?;
-
- let workspace_id = if let Some(ref serialized_workspace) = serialized_workspace {
- serialized_workspace.id
- } else {
- persistence::DB.next_id().await?
- };
-
- if let Some(window_to_replace) = window_to_replace {
- cx.update_window(window_to_replace.into(), |_, cx| {
- cx.replace_root_view(|cx| {
- Workspace::new(Some(workspace_id), project, app_state.clone(), cx)
- });
- })?;
- window_to_replace
- } else {
- let window_bounds_override = window_bounds_env_override();
- cx.update(|cx| {
- let mut options = (app_state.build_window_options)(None, cx);
- options.window_bounds = window_bounds_override.map(WindowBounds::Windowed);
- cx.open_window(options, |cx| {
- cx.new_view(|cx| {
- Workspace::new(Some(workspace_id), project, app_state.clone(), cx)
- })
- })
- })??
- }
- };
-
- workspace
- .update(&mut cx, |_, cx| {
- cx.activate(true);
- cx.activate_window();
- open_items(serialized_workspace, vec![], app_state, cx)
- })?
- .await?;
-
- anyhow::Ok(workspace)
- })
-}
-
pub fn join_in_room_project(
project_id: u64,
follow_user_id: u64,
@@ -36,7 +36,6 @@ command_palette.workspace = true
command_palette_hooks.workspace = true
copilot.workspace = true
db.workspace = true
-dev_server_projects.workspace = true
diagnostics.workspace = true
editor.workspace = true
env_logger.workspace = true
@@ -52,7 +51,6 @@ git.workspace = true
git_hosting_providers.workspace = true
go_to_line.workspace = true
gpui = { workspace = true, features = ["wayland", "x11", "font-kit"] }
-headless.workspace = true
http_client.workspace = true
image_viewer.workspace = true
inline_completion_button.workspace = true
@@ -11,7 +11,7 @@ use assistant::PromptBuilder;
use chrono::Offset;
use clap::{command, Parser};
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
-use client::{parse_zed_link, Client, DevServerToken, ProxySettings, UserStore};
+use client::{parse_zed_link, Client, ProxySettings, UserStore};
use collab_ui::channel_view::ChannelView;
use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE};
use editor::Editor;
@@ -20,8 +20,8 @@ use fs::{Fs, RealFs};
use futures::{future, StreamExt};
use git::GitHostingProviderRegistry;
use gpui::{
- Action, App, AppContext, AsyncAppContext, Context, DismissEvent, Global, Task,
- UpdateGlobal as _, VisualContext,
+ Action, App, AppContext, AsyncAppContext, Context, DismissEvent, UpdateGlobal as _,
+ VisualContext,
};
use http_client::{read_proxy_from_env, Uri};
use language::LanguageRegistry;
@@ -136,43 +136,6 @@ fn fail_to_open_window(e: anyhow::Error, _cx: &mut AppContext) {
}
}
-enum AppMode {
- Headless(DevServerToken),
- Ui,
-}
-impl Global for AppMode {}
-
-fn init_headless(
- dev_server_token: DevServerToken,
- app_state: Arc<AppState>,
- cx: &mut AppContext,
-) -> Task<Result<()>> {
- match cx.try_global::<AppMode>() {
- Some(AppMode::Headless(token)) if token == &dev_server_token => return Task::ready(Ok(())),
- Some(_) => {
- return Task::ready(Err(anyhow!(
- "zed is already running. Use `kill {}` to stop it",
- process::id()
- )))
- }
- None => {
- cx.set_global(AppMode::Headless(dev_server_token.clone()));
- }
- };
- let client = app_state.client.clone();
- client.set_dev_server_token(dev_server_token);
- headless::init(
- client.clone(),
- headless::AppState {
- languages: app_state.languages.clone(),
- user_store: app_state.user_store.clone(),
- fs: app_state.fs.clone(),
- node_runtime: app_state.node_runtime.clone(),
- },
- cx,
- )
-}
-
// init_common is called for both headless and normal mode.
fn init_common(app_state: Arc<AppState>, cx: &mut AppContext) -> Arc<PromptBuilder> {
SystemAppearance::init(cx);
@@ -223,19 +186,6 @@ fn init_ui(
prompt_builder: Arc<PromptBuilder>,
cx: &mut AppContext,
) -> Result<()> {
- match cx.try_global::<AppMode>() {
- Some(AppMode::Headless(_)) => {
- return Err(anyhow!(
- "zed is already running in headless mode. Use `kill {}` to stop it",
- process::id()
- ))
- }
- Some(AppMode::Ui) => return Ok(()),
- None => {
- cx.set_global(AppMode::Ui);
- }
- };
-
load_embedded_fonts(cx);
#[cfg(target_os = "linux")]
@@ -252,7 +202,6 @@ fn init_ui(
go_to_line::init(cx);
file_finder::init(cx);
tab_switcher::init(cx);
- dev_server_projects::init(app_state.client.clone(), cx);
outline::init(cx);
project_symbols::init(cx);
project_panel::init(Assets, cx);
@@ -426,22 +375,15 @@ fn main() {
app.on_reopen(move |cx| {
if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade())
{
- let ui_has_launched = cx
- .try_global::<AppMode>()
- .map(|mode| matches!(mode, AppMode::Ui))
- .unwrap_or(false);
-
- if ui_has_launched {
- cx.spawn({
- let app_state = app_state.clone();
- |mut cx| async move {
- if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await {
- fail_to_open_window_async(e, &mut cx)
- }
+ cx.spawn({
+ let app_state = app_state.clone();
+ |mut cx| async move {
+ if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await {
+ fail_to_open_window_async(e, &mut cx)
}
- })
- .detach();
- }
+ }
+ })
+ .detach();
}
});
@@ -590,30 +532,16 @@ fn main() {
handle_open_request(request, app_state.clone(), prompt_builder.clone(), cx);
}
None => {
- if let Some(dev_server_token) = args.dev_server_token {
- let task =
- init_headless(DevServerToken(dev_server_token), app_state.clone(), cx);
- cx.spawn(|cx| async move {
- if let Err(e) = task.await {
- log::error!("{}", e);
- cx.update(|cx| cx.quit()).log_err();
- } else {
- log::info!("connected!");
- }
- })
- .detach();
- } else {
- init_ui(app_state.clone(), prompt_builder.clone(), cx).unwrap();
- cx.spawn({
- let app_state = app_state.clone();
- |mut cx| async move {
- if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await {
- fail_to_open_window_async(e, &mut cx)
- }
+ init_ui(app_state.clone(), prompt_builder.clone(), cx).unwrap();
+ cx.spawn({
+ let app_state = app_state.clone();
+ |mut cx| async move {
+ if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await {
+ fail_to_open_window_async(e, &mut cx)
}
- })
- .detach();
- }
+ }
+ })
+ .detach();
}
}
@@ -927,7 +855,6 @@ async fn restore_or_create_workspace(
})
.detach();
}
- SerializedWorkspaceLocation::DevServer(_) => {}
}
}
} else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
@@ -1,5 +1,5 @@
use crate::restorable_workspace_locations;
-use crate::{handle_open_request, init_headless, init_ui};
+use crate::{handle_open_request, init_ui};
use anyhow::{anyhow, Context, Result};
use assistant::PromptBuilder;
use cli::{ipc, IpcHandshake};
@@ -21,8 +21,8 @@ use remote::SshConnectionOptions;
use settings::Settings;
use std::path::{Path, PathBuf};
use std::sync::Arc;
+use std::thread;
use std::time::Duration;
-use std::{process, thread};
use util::paths::PathWithPosition;
use util::ResultExt;
use welcome::{show_welcome_view, FIRST_OPEN};
@@ -262,38 +262,9 @@ pub async fn handle_cli_connection(
paths,
wait,
open_new_workspace,
- dev_server_token,
+
env,
} => {
- if let Some(dev_server_token) = dev_server_token {
- match cx
- .update(|cx| {
- init_headless(client::DevServerToken(dev_server_token), app_state, cx)
- })
- .unwrap()
- .await
- {
- Ok(_) => {
- responses
- .send(CliResponse::Stdout {
- message: format!("zed (pid {}) connected!", process::id()),
- })
- .log_err();
- responses.send(CliResponse::Exit { status: 0 }).log_err();
- }
- Err(error) => {
- responses
- .send(CliResponse::Stderr {
- message: format!("{error}"),
- })
- .log_err();
- responses.send(CliResponse::Exit { status: 1 }).log_err();
- cx.update(|cx| cx.quit()).log_err();
- }
- }
- return;
- }
-
if !urls.is_empty() {
cx.update(|cx| {
match OpenRequest::parse(urls, cx) {
@@ -459,7 +430,6 @@ async fn open_workspaces(
// We don't set `errored` here, because for ssh projects, the
// error is displayed in the window.
}
- SerializedWorkspaceLocation::DevServer(_) => {}
}
}