diff --git a/Cargo.lock b/Cargo.lock
index 693d27666967891fba73dff467a5fbb2760af4ed..8bd63c644cc90fb3293fa4645d3a3bb42b6c42ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1188,7 +1188,7 @@ dependencies = [
[[package]]
name = "collab"
-version = "0.6.1"
+version = "0.7.1"
dependencies = [
"anyhow",
"async-tungstenite",
@@ -1259,6 +1259,7 @@ dependencies = [
"collections",
"context_menu",
"editor",
+ "feedback",
"futures 0.3.25",
"fuzzy",
"gpui",
@@ -3018,6 +3019,17 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
+[[package]]
+name = "install_cli"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "gpui",
+ "log",
+ "smol",
+ "util",
+]
+
[[package]]
name = "instant"
version = "0.1.12"
@@ -3157,6 +3169,7 @@ dependencies = [
name = "journal"
version = "0.1.0"
dependencies = [
+ "anyhow",
"chrono",
"dirs 4.0.0",
"editor",
@@ -3285,6 +3298,22 @@ dependencies = [
"util",
]
+[[package]]
+name = "language_selector"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "editor",
+ "fuzzy",
+ "gpui",
+ "language",
+ "picker",
+ "project",
+ "settings",
+ "theme",
+ "workspace",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -8013,6 +8042,26 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
+[[package]]
+name = "welcome"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "db",
+ "editor",
+ "fuzzy",
+ "gpui",
+ "install_cli",
+ "log",
+ "picker",
+ "project",
+ "settings",
+ "theme",
+ "theme_selector",
+ "util",
+ "workspace",
+]
+
[[package]]
name = "wepoll-ffi"
version = "0.1.2"
@@ -8288,6 +8337,7 @@ dependencies = [
"futures 0.3.25",
"gpui",
"indoc",
+ "install_cli",
"language",
"lazy_static",
"log",
@@ -8359,7 +8409,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zed"
-version = "0.76.0"
+version = "0.77.0"
dependencies = [
"activity_indicator",
"anyhow",
@@ -8381,6 +8431,7 @@ dependencies = [
"command_palette",
"context_menu",
"ctor",
+ "db",
"diagnostics",
"easy-parallel",
"editor",
@@ -8396,9 +8447,11 @@ dependencies = [
"ignore",
"image",
"indexmap",
+ "install_cli",
"isahc",
"journal",
"language",
+ "language_selector",
"lazy_static",
"libc",
"log",
@@ -8460,6 +8513,7 @@ dependencies = [
"util",
"uuid 1.2.2",
"vim",
+ "welcome",
"workspace",
]
diff --git a/Cargo.toml b/Cargo.toml
index c74a76cccefe6c6de610b30c264a81e74b2654df..ab8bcd6de83d8f96853e4f4599bd3c51eced63d3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,8 +26,10 @@ members = [
"crates/go_to_line",
"crates/gpui",
"crates/gpui_macros",
+ "crates/install_cli",
"crates/journal",
"crates/language",
+ "crates/language_selector",
"crates/live_kit_client",
"crates/live_kit_server",
"crates/lsp",
@@ -58,6 +60,7 @@ members = [
"crates/util",
"crates/vim",
"crates/workspace",
+ "crates/welcome",
"crates/zed",
]
default-members = ["crates/zed"]
diff --git a/assets/icons/logo_96.svg b/assets/icons/logo_96.svg
new file mode 100644
index 0000000000000000000000000000000000000000..dc98bb8bc249bfb1decb1771b33470b324dde96f
--- /dev/null
+++ b/assets/icons/logo_96.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/speech_bubble_12.svg b/assets/icons/speech_bubble_12.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f5f330056a34f1261d31416b688bcc86dcdf8bf1
--- /dev/null
+++ b/assets/icons/speech_bubble_12.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/keymaps/atom.json b/assets/keymaps/atom.json
new file mode 100644
index 0000000000000000000000000000000000000000..766c46c133df9b617061694b9eb23ad8fecaceb8
--- /dev/null
+++ b/assets/keymaps/atom.json
@@ -0,0 +1,68 @@
+[
+ {
+ "bindings": {
+ "cmd-k cmd-p": "workspace::ActivatePreviousPane",
+ "cmd-k cmd-n": "workspace::ActivateNextPane"
+ }
+ },
+ {
+ "context": "Editor",
+ "bindings": {
+ "cmd-b": "editor::GoToDefinition",
+ "cmd-<": "editor::ScrollCursorCenter",
+ "cmd-g": [
+ "editor::SelectNext",
+ {
+ "replace_newest": true
+ }
+ ],
+ "ctrl-shift-down": "editor::AddSelectionBelow",
+ "ctrl-shift-up": "editor::AddSelectionAbove",
+ "cmd-shift-backspace": "editor::DeleteToBeginningOfLine"
+ }
+ },
+ {
+ "context": "Editor && mode == full",
+ "bindings": {
+ "cmd-r": "outline::Toggle"
+ }
+ },
+ {
+ "context": "BufferSearchBar",
+ "bindings": {
+ "cmd-f3": "search::SelectNextMatch",
+ "cmd-shift-f3": "search::SelectPrevMatch"
+ }
+ },
+ {
+ "context": "Workspace",
+ "bindings": {
+ "cmd-\\": "workspace::ToggleLeftSidebar",
+ "cmd-k cmd-b": "workspace::ToggleLeftSidebar",
+ "cmd-t": "file_finder::Toggle",
+ "cmd-shift-r": "project_symbols::Toggle"
+ }
+ },
+ {
+ "context": "Pane",
+ "bindings": {
+ "alt-cmd-/": "search::ToggleRegex",
+ "ctrl-0": "project_panel::ToggleFocus"
+ }
+ },
+ {
+ "context": "ProjectPanel",
+ "bindings": {
+ "ctrl-[": "project_panel::CollapseSelectedEntry",
+ "ctrl-b": "project_panel::CollapseSelectedEntry",
+ "h": "project_panel::CollapseSelectedEntry",
+ "ctrl-]": "project_panel::ExpandSelectedEntry",
+ "ctrl-f": "project_panel::ExpandSelectedEntry",
+ "ctrl-shift-c": "project_panel::CopyPath"
+ }
+ },
+ {
+ "context": "Dock",
+ "bindings": {}
+ }
+]
\ No newline at end of file
diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json
index cce65eda8abcadcb632d1b496c10b2dc283dc548..57f5075aca9a6678bb6a7231d8fdd5a3036c5aef 100644
--- a/assets/keymaps/default.json
+++ b/assets/keymaps/default.json
@@ -353,7 +353,8 @@
"cmd-shift-p": "command_palette::Toggle",
"cmd-shift-m": "diagnostics::Deploy",
"cmd-shift-e": "project_panel::ToggleFocus",
- "cmd-alt-s": "workspace::SaveAll"
+ "cmd-alt-s": "workspace::SaveAll",
+ "cmd-k m": "language_selector::Toggle"
}
},
// Bindings from Sublime Text
@@ -537,4 +538,4 @@
]
}
}
-]
\ No newline at end of file
+]
diff --git a/assets/keymaps/jetbrains.json b/assets/keymaps/jetbrains.json
new file mode 100644
index 0000000000000000000000000000000000000000..2e6e5e77e6abebd3d6c464895af5cdcaa9dc2075
--- /dev/null
+++ b/assets/keymaps/jetbrains.json
@@ -0,0 +1,78 @@
+[
+ {
+ "bindings": {
+ "cmd-shift-[": "pane::ActivatePrevItem",
+ "cmd-shift-]": "pane::ActivateNextItem"
+ }
+ },
+ {
+ "context": "Editor",
+ "bindings": {
+ "ctrl->": "zed::IncreaseBufferFontSize",
+ "ctrl-<": "zed::DecreaseBufferFontSize",
+ "cmd-d": "editor::DuplicateLine",
+ "cmd-pagedown": "editor::MovePageDown",
+ "cmd-pageup": "editor::MovePageUp",
+ "ctrl-alt-shift-b": "editor::SelectToPreviousWordStart",
+ "shift-enter": "editor::NewlineBelow",
+ "cmd--": "editor::Fold",
+ "cmd-=": "editor::UnfoldLines",
+ "alt-shift-g": "editor::SplitSelectionIntoLines",
+ "ctrl-g": [
+ "editor::SelectNext",
+ {
+ "replace_newest": false
+ }
+ ],
+ "cmd-/": [
+ "editor::ToggleComments",
+ {
+ "advance_downwards": true
+ }
+ ],
+ "shift-alt-up": "editor::MoveLineUp",
+ "shift-alt-down": "editor::MoveLineDown",
+ "cmd-[": "pane::GoBack",
+ "cmd-]": "pane::GoForward",
+ "alt-f7": "editor::FindAllReferences",
+ "cmd-alt-f7": "editor::FindAllReferences",
+ "cmd-b": "editor::GoToDefinition",
+ "cmd-alt-b": "editor::GoToDefinition",
+ "cmd-shift-b": "editor::GoToTypeDefinition",
+ "alt-enter": "editor::ToggleCodeActions",
+ "f2": "editor::GoToDiagnostic",
+ "cmd-f2": "editor::GoToPrevDiagnostic",
+ "ctrl-alt-shift-down": "editor::GoToHunk",
+ "ctrl-alt-shift-up": "editor::GoToPrevHunk",
+ "cmd-home": "editor::MoveToBeginning",
+ "cmd-end": "editor::MoveToEnd",
+ "cmd-shift-home": "editor::SelectToBeginning",
+ "cmd-shift-end": "editor::SelectToEnd"
+ }
+ },
+ {
+ "context": "Editor && mode == full",
+ "bindings": {
+ "cmd-f12": "outline::Toggle",
+ "cmd-7": "outline::Toggle",
+ "cmd-shift-o": "file_finder::Toggle",
+ "cmd-l": "go_to_line::Toggle"
+ }
+ },
+ {
+ "context": "Workspace",
+ "bindings": {
+ "cmd-shift-a": "command_palette::Toggle",
+ "cmd-alt-o": "project_symbols::Toggle",
+ "cmd-1": "workspace::ToggleLeftSidebar",
+ "cmd-6": "diagnostics::Deploy",
+ "alt-f12": "dock::FocusDock"
+ }
+ },
+ {
+ "context": "Dock",
+ "bindings": {
+ "alt-f12": "dock::HideDock"
+ }
+ }
+]
\ No newline at end of file
diff --git a/assets/keymaps/sublime_text.json b/assets/keymaps/sublime_text.json
new file mode 100644
index 0000000000000000000000000000000000000000..1d3dd887d7b4a57add7d5be95d34700e1be1143a
--- /dev/null
+++ b/assets/keymaps/sublime_text.json
@@ -0,0 +1,60 @@
+[
+ {
+ "bindings": {
+ "cmd-shift-[": "pane::ActivatePrevItem",
+ "cmd-shift-]": "pane::ActivateNextItem",
+ "ctrl-pagedown": "pane::ActivatePrevItem",
+ "ctrl-pageup": "pane::ActivateNextItem",
+ "ctrl-shift-tab": "pane::ActivateNextItem",
+ "ctrl-tab": "pane::ActivatePrevItem",
+ "cmd-+": "zed::IncreaseBufferFontSize"
+ }
+ },
+ {
+ "context": "Editor",
+ "bindings": {
+ "ctrl-shift-up": "editor::AddSelectionAbove",
+ "ctrl-shift-down": "editor::AddSelectionBelow",
+ "cmd-shift-space": "editor::SelectAll",
+ "ctrl-shift-m": "editor::SelectLargerSyntaxNode",
+ "cmd-shift-a": "editor::SelectLargerSyntaxNode",
+ "shift-f12": "editor::FindAllReferences",
+ "alt-cmd-down": "editor::GoToDefinition",
+ "alt-shift-cmd-down": "editor::FindAllReferences",
+ "ctrl-.": "editor::GoToHunk",
+ "ctrl-,": "editor::GoToPrevHunk",
+ "ctrl-backspace": "editor::DeleteToPreviousWordStart",
+ "ctrl-delete": "editor::DeleteToNextWordEnd"
+ }
+ },
+ {
+ "context": "Editor && mode == full",
+ "bindings": {
+ "cmd-r": "outline::Toggle"
+ }
+ },
+ {
+ "context": "Pane",
+ "bindings": {
+ "f4": "search::SelectNextMatch",
+ "shift-f4": "search::SelectPrevMatch"
+ }
+ },
+ {
+ "context": "Workspace",
+ "bindings": {
+ "ctrl-`": "dock::FocusDock",
+ "cmd-k cmd-b": "workspace::ToggleLeftSidebar",
+ "cmd-t": "file_finder::Toggle",
+ "shift-cmd-r": "project_symbols::Toggle",
+ // Currently busted: https://github.com/zed-industries/feedback/issues/898
+ "ctrl-0": "project_panel::ToggleFocus"
+ }
+ },
+ {
+ "context": "Dock",
+ "bindings": {
+ "ctrl-`": "dock::HideDock"
+ }
+ }
+]
\ No newline at end of file
diff --git a/assets/settings/default.json b/assets/settings/default.json
index 2a5e05b40138053862461f721231e042b2a1cb38..90c47478f3f7c3a97b50cea061907c122bf513c9 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -50,7 +50,7 @@
// "default_dock_anchor": "right"
// 3. Position the dock full screen over the entire workspace"
// "default_dock_anchor": "expanded"
- "default_dock_anchor": "right",
+ "default_dock_anchor": "bottom",
// Whether or not to remove any trailing whitespace from lines of a buffer
// before saving it.
"remove_trailing_whitespace_on_save": true,
diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs
index 06680c1dcb86335c2e120df3043fd413d3f26af1..901ae08fc048cf43c98131566855373d38686225 100644
--- a/crates/call/src/room.rs
+++ b/crates/call/src/room.rs
@@ -20,7 +20,7 @@ use project::Project;
use std::{mem, sync::Arc, time::Duration};
use util::{post_inc, ResultExt, TryFutureExt};
-pub const RECONNECT_TIMEOUT: Duration = client::RECEIVE_TIMEOUT;
+pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs
index 748eb48f7e27368588534266fe34e9183cb01b78..9d486619d255dd7ab214a145f7f31162dd4a9c36 100644
--- a/crates/client/src/telemetry.rs
+++ b/crates/client/src/telemetry.rs
@@ -224,7 +224,7 @@ impl Telemetry {
.header("Content-Type", "application/json")
.body(json_bytes.into())?;
this.http_client.send(request).await?;
- Ok(())
+ anyhow::Ok(())
}
.log_err(),
)
@@ -320,7 +320,7 @@ impl Telemetry {
.header("Content-Type", "application/json")
.body(json_bytes.into())?;
this.http_client.send(request).await?;
- Ok(())
+ anyhow::Ok(())
}
.log_err(),
)
diff --git a/crates/collab/.env.toml b/crates/collab/.env.toml
index b4a6694e5e6578e771406d6947c2c4ad8d2efb0a..01866012ea8a14c94835d0662318d3f8c4df67f5 100644
--- a/crates/collab/.env.toml
+++ b/crates/collab/.env.toml
@@ -1,4 +1,5 @@
DATABASE_URL = "postgres://postgres@localhost/zed"
+DATABASE_MAX_CONNECTIONS = 5
HTTP_PORT = 8080
API_TOKEN = "secret"
INVITE_LINK_PREFIX = "http://localhost:3000/invites/"
diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml
index 86fe9174bf0bfec0aeb5b90daab8d7e09c898bd7..0f06a14b68c1b0e1998554d249fa03c509f2ef2f 100644
--- a/crates/collab/Cargo.toml
+++ b/crates/collab/Cargo.toml
@@ -3,7 +3,7 @@ authors = ["Nathan Sobo "]
default-run = "collab"
edition = "2021"
name = "collab"
-version = "0.6.1"
+version = "0.7.1"
publish = false
[[bin]]
diff --git a/crates/collab/k8s/environments/preview.sh b/crates/collab/k8s/environments/preview.sh
index 4d9dd849e96f9bb5fc672beb0b7547c6246358de..132a1ef53c2b84bb97659b15800888b195b9de65 100644
--- a/crates/collab/k8s/environments/preview.sh
+++ b/crates/collab/k8s/environments/preview.sh
@@ -1,3 +1,4 @@
ZED_ENVIRONMENT=preview
RUST_LOG=info
INVITE_LINK_PREFIX=https://zed.dev/invites/
+DATABASE_MAX_CONNECTIONS=10
diff --git a/crates/collab/k8s/environments/production.sh b/crates/collab/k8s/environments/production.sh
index 83af6630c2e0a52b915c2a1ddb07778e60e16a4e..cb1d4b4de712864003c711dd8b46123b31fa5d5f 100644
--- a/crates/collab/k8s/environments/production.sh
+++ b/crates/collab/k8s/environments/production.sh
@@ -1,3 +1,4 @@
ZED_ENVIRONMENT=production
RUST_LOG=info
INVITE_LINK_PREFIX=https://zed.dev/invites/
+DATABASE_MAX_CONNECTIONS=85
diff --git a/crates/collab/k8s/environments/staging.sh b/crates/collab/k8s/environments/staging.sh
index 82d799e2bc200c44a27a5ce7712fc1106b15970f..b9689ccb19390b412f4a7df9e8fee2e744b0479c 100644
--- a/crates/collab/k8s/environments/staging.sh
+++ b/crates/collab/k8s/environments/staging.sh
@@ -1,3 +1,4 @@
ZED_ENVIRONMENT=staging
RUST_LOG=info
INVITE_LINK_PREFIX=https://staging.zed.dev/invites/
+DATABASE_MAX_CONNECTIONS=5
diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml
index 339d02892ef2cf24f40815c3fd32dc0fb72c317a..0662a287d4b4541f4285e9b4ba7c3c0a2bdafcbf 100644
--- a/crates/collab/k8s/manifest.template.yml
+++ b/crates/collab/k8s/manifest.template.yml
@@ -59,6 +59,13 @@ spec:
ports:
- containerPort: 8080
protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8080
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ timeoutSeconds: 5
readinessProbe:
httpGet:
path: /
@@ -73,6 +80,8 @@ spec:
secretKeyRef:
name: database
key: url
+ - name: DATABASE_MAX_CONNECTIONS
+ value: "${DATABASE_MAX_CONNECTIONS}"
- name: API_TOKEN
valueFrom:
secretKeyRef:
diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs
index c4ff2e39188a1133cc086b00ab5194d242ef0cc8..1d0fc377ab67e96b43265a21f7886ac1d7b6f4b7 100644
--- a/crates/collab/src/db.rs
+++ b/crates/collab/src/db.rs
@@ -1757,17 +1757,14 @@ impl Database {
.add(follower::Column::ProjectId.eq(project_id))
.add(
follower::Column::LeaderConnectionServerId
- .eq(leader_connection.owner_id)
- .and(follower::Column::LeaderConnectionId.eq(leader_connection.id)),
+ .eq(leader_connection.owner_id),
)
+ .add(follower::Column::LeaderConnectionId.eq(leader_connection.id))
.add(
follower::Column::FollowerConnectionServerId
- .eq(follower_connection.owner_id)
- .and(
- follower::Column::FollowerConnectionId
- .eq(follower_connection.id),
- ),
- ),
+ .eq(follower_connection.owner_id),
+ )
+ .add(follower::Column::FollowerConnectionId.eq(follower_connection.id)),
)
.exec(&*tx)
.await?;
@@ -2560,7 +2557,7 @@ impl Database {
&self,
project_id: ProjectId,
connection: ConnectionId,
- ) -> Result> {
+ ) -> Result> {
let room_id = self.room_id_for_project(project_id).await?;
self.room_transaction(room_id, |tx| async move {
let result = project_collaborator::Entity::delete_many()
@@ -2592,13 +2589,39 @@ impl Database {
.map(|collaborator| collaborator.connection())
.collect();
+ follower::Entity::delete_many()
+ .filter(
+ Condition::any()
+ .add(
+ Condition::all()
+ .add(follower::Column::ProjectId.eq(project_id))
+ .add(
+ follower::Column::LeaderConnectionServerId
+ .eq(connection.owner_id),
+ )
+ .add(follower::Column::LeaderConnectionId.eq(connection.id)),
+ )
+ .add(
+ Condition::all()
+ .add(follower::Column::ProjectId.eq(project_id))
+ .add(
+ follower::Column::FollowerConnectionServerId
+ .eq(connection.owner_id),
+ )
+ .add(follower::Column::FollowerConnectionId.eq(connection.id)),
+ ),
+ )
+ .exec(&*tx)
+ .await?;
+
+ let room = self.get_room(project.room_id, &tx).await?;
let left_project = LeftProject {
id: project_id,
host_user_id: project.host_user_id,
host_connection_id: project.host_connection()?,
connection_ids,
};
- Ok(left_project)
+ Ok((room, left_project))
})
.await
}
diff --git a/crates/collab/src/lib.rs b/crates/collab/src/lib.rs
index 1a83193bdfc3455fe23791b19e1b18b3e5673aaa..8c99a5ea0f5623ce5e86938587427de0d616f77c 100644
--- a/crates/collab/src/lib.rs
+++ b/crates/collab/src/lib.rs
@@ -91,6 +91,7 @@ impl std::error::Error for Error {}
pub struct Config {
pub http_port: u16,
pub database_url: String,
+ pub database_max_connections: u32,
pub api_token: String,
pub invite_link_prefix: String,
pub live_kit_server: Option,
@@ -116,7 +117,7 @@ pub struct AppState {
impl AppState {
pub async fn new(config: Config) -> Result> {
let mut db_options = db::ConnectOptions::new(config.database_url.clone());
- db_options.max_connections(5);
+ db_options.max_connections(config.database_max_connections);
let db = Database::new(db_options).await?;
let live_kit_client = if let Some(((server, key), secret)) = config
.live_kit_server
diff --git a/crates/collab/src/main.rs b/crates/collab/src/main.rs
index 0f783c13e58d3b64c0e034c18ea6173d63be4036..30ed35bc354ec74ea4e323f6ad85cc8d353c922d 100644
--- a/crates/collab/src/main.rs
+++ b/crates/collab/src/main.rs
@@ -1,11 +1,12 @@
use anyhow::anyhow;
-use axum::{routing::get, Router};
+use axum::{routing::get, Extension, Router};
use collab::{db, env, executor::Executor, AppState, Config, MigrateConfig, Result};
use db::Database;
use std::{
env::args,
net::{SocketAddr, TcpListener},
path::Path,
+ sync::Arc,
};
use tokio::signal::unix::SignalKind;
use tracing_log::LogTracer;
@@ -66,7 +67,12 @@ async fn main() -> Result<()> {
let app = collab::api::routes(rpc_server.clone(), state.clone())
.merge(collab::rpc::routes(rpc_server.clone()))
- .merge(Router::new().route("/", get(handle_root)));
+ .merge(
+ Router::new()
+ .route("/", get(handle_root))
+ .route("/healthz", get(handle_liveness_probe))
+ .layer(Extension(state.clone())),
+ );
axum::Server::from_tcp(listener)?
.serve(app.into_make_service_with_connect_info::())
@@ -95,6 +101,11 @@ async fn handle_root() -> String {
format!("collab v{VERSION}")
}
+async fn handle_liveness_probe(Extension(state): Extension>) -> Result {
+ state.db.get_all_users(0, 1).await?;
+ Ok("ok".to_string())
+}
+
pub fn init_tracing(config: &Config) -> Option<()> {
use std::str::FromStr;
use tracing_subscriber::layer::SubscriberExt;
diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs
index 01d24024eb7516e87e4c0477d069cae8feb42da6..1deaefec1a446857748e311a34ffdd741385c944 100644
--- a/crates/collab/src/rpc.rs
+++ b/crates/collab/src/rpc.rs
@@ -53,11 +53,11 @@ use std::{
},
time::Duration,
};
-use tokio::sync::watch;
+use tokio::sync::{watch, Semaphore};
use tower::ServiceBuilder;
use tracing::{info_span, instrument, Instrument};
-pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(5);
+pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
pub const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10);
lazy_static! {
@@ -542,8 +542,13 @@ impl Server {
// This arrangement ensures we will attempt to process earlier messages first, but fall
// back to processing messages arrived later in the spirit of making progress.
let mut foreground_message_handlers = FuturesUnordered::new();
+ let concurrent_handlers = Arc::new(Semaphore::new(256));
loop {
- let next_message = incoming_rx.next().fuse();
+ let next_message = async {
+ let permit = concurrent_handlers.clone().acquire_owned().await.unwrap();
+ let message = incoming_rx.next().await;
+ (permit, message)
+ }.fuse();
futures::pin_mut!(next_message);
futures::select_biased! {
_ = teardown.changed().fuse() => return Ok(()),
@@ -554,7 +559,8 @@ impl Server {
break;
}
_ = foreground_message_handlers.next() => {}
- message = next_message => {
+ next_message = next_message => {
+ let (permit, message) = next_message;
if let Some(message) = message {
let type_name = message.payload_type_name();
let span = tracing::info_span!("receive message", %user_id, %login, %connection_id, %address, type_name);
@@ -564,7 +570,10 @@ impl Server {
let handle_message = (handler)(message, session.clone());
drop(span_enter);
- let handle_message = handle_message.instrument(span);
+ let handle_message = async move {
+ handle_message.await;
+ drop(permit);
+ }.instrument(span);
if is_background {
executor.spawn_detached(handle_message);
} else {
@@ -1408,7 +1417,7 @@ 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 project = session
+ let (room, project) = &*session
.db()
.await
.leave_project(project_id, sender_id)
@@ -1419,7 +1428,9 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result
host_connection_id = %project.host_connection_id,
"leave project"
);
+
project_left(&project, &session);
+ room_updated(&room, &session.peer);
Ok(())
}
diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs
index e9ffecf246d295934e15a2a3375af60f78a18c3e..80df0ed6dfb59136ad6183b0a0f42e34a5b63264 100644
--- a/crates/collab/src/tests.rs
+++ b/crates/collab/src/tests.rs
@@ -197,7 +197,8 @@ impl TestServer {
fs: fs.clone(),
build_window_options: |_, _, _| Default::default(),
initialize_workspace: |_, _, _| unimplemented!(),
- dock_default_item_factory: |_, _| unimplemented!(),
+ dock_default_item_factory: |_, _| None,
+ background_actions: || &[],
});
Project::init(&client);
@@ -434,15 +435,7 @@ impl TestClient {
cx: &mut TestAppContext,
) -> ViewHandle {
let (_, root_view) = cx.add_window(|_| EmptyView);
- cx.add_view(&root_view, |cx| {
- Workspace::new(
- Default::default(),
- 0,
- project.clone(),
- |_, _| unimplemented!(),
- cx,
- )
- })
+ cx.add_view(&root_view, |cx| Workspace::test_new(project.clone(), cx))
}
fn create_new_root_dir(&mut self) -> PathBuf {
diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs
index 1ab78ac310b0d23b10f08af62fbe8490f626161b..44a2839f27c750ac795ca6a0aaa936aedbe9ebcf 100644
--- a/crates/collab/src/tests/integration_tests.rs
+++ b/crates/collab/src/tests/integration_tests.rs
@@ -829,7 +829,7 @@ async fn test_server_restarts(
// Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room.
client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
- deterministic.advance_clock(RECEIVE_TIMEOUT);
+ deterministic.advance_clock(RECONNECT_TIMEOUT);
assert_eq!(
room_participants(&room_a, cx_a),
RoomParticipants {
@@ -1001,7 +1001,7 @@ async fn test_server_restarts(
client_a.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
client_b.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending()));
- deterministic.advance_clock(RECEIVE_TIMEOUT);
+ deterministic.advance_clock(RECONNECT_TIMEOUT);
assert_eq!(
room_participants(&room_a, cx_a),
RoomParticipants {
@@ -1449,15 +1449,7 @@ async fn test_host_disconnect(
deterministic.run_until_parked();
assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
- let (_, workspace_b) = cx_b.add_window(|cx| {
- Workspace::new(
- Default::default(),
- 0,
- project_b.clone(),
- |_, _| unimplemented!(),
- cx,
- )
- });
+ let (_, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "b.txt"), None, true, cx)
@@ -4706,15 +4698,7 @@ async fn test_collaborating_with_code_actions(
// Join the project as client B.
let project_b = client_b.build_remote_project(project_id, cx_b).await;
- let (_window_b, workspace_b) = cx_b.add_window(|cx| {
- Workspace::new(
- Default::default(),
- 0,
- project_b.clone(),
- |_, _| unimplemented!(),
- cx,
- )
- });
+ let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "main.rs"), None, true, cx)
@@ -4937,15 +4921,7 @@ async fn test_collaborating_with_renames(
.unwrap();
let project_b = client_b.build_remote_project(project_id, cx_b).await;
- let (_window_b, workspace_b) = cx_b.add_window(|cx| {
- Workspace::new(
- Default::default(),
- 0,
- project_b.clone(),
- |_, _| unimplemented!(),
- cx,
- )
- });
+ let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "one.rs"), None, true, cx)
@@ -5792,11 +5768,12 @@ async fn test_contact_requests(
}
#[gpui::test(iterations = 10)]
-async fn test_following(
+async fn test_basic_following(
deterministic: Arc,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
cx_c: &mut TestAppContext,
+ cx_d: &mut TestAppContext,
) {
deterministic.forbid_parking();
cx_a.update(editor::init);
@@ -5806,11 +5783,14 @@ async fn test_following(
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
let client_c = server.create_client(cx_c, "user_c").await;
+ let client_d = server.create_client(cx_d, "user_d").await;
server
- .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
- .await;
- server
- .make_contacts(&mut [(&client_a, cx_a), (&client_c, cx_c)])
+ .create_room(&mut [
+ (&client_a, cx_a),
+ (&client_b, cx_b),
+ (&client_c, cx_c),
+ (&client_d, cx_d),
+ ])
.await;
let active_call_a = cx_a.read(ActiveCall::global);
let active_call_b = cx_b.read(ActiveCall::global);
@@ -5877,6 +5857,7 @@ async fn test_following(
let peer_id_a = client_a.peer_id().unwrap();
let peer_id_b = client_b.peer_id().unwrap();
let peer_id_c = client_c.peer_id().unwrap();
+ let peer_id_d = client_d.peer_id().unwrap();
// Client A updates their selections in those editors
editor_a1.update(cx_a, |editor, cx| {
@@ -5896,25 +5877,15 @@ async fn test_following(
.await
.unwrap();
- // Client A invites client C to the call.
- active_call_a
- .update(cx_a, |call, cx| {
- call.invite(client_c.current_user_id(cx_c).to_proto(), None, cx)
- })
- .await
- .unwrap();
cx_c.foreground().run_until_parked();
let active_call_c = cx_c.read(ActiveCall::global);
- active_call_c
- .update(cx_c, |call, cx| call.accept_incoming(cx))
- .await
- .unwrap();
let project_c = client_c.build_remote_project(project_id, cx_c).await;
let workspace_c = client_c.build_workspace(&project_c, cx_c);
active_call_c
.update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
.await
.unwrap();
+ drop(project_c);
// Client C also follows client A.
workspace_c
@@ -5926,12 +5897,23 @@ async fn test_following(
.await
.unwrap();
+ cx_d.foreground().run_until_parked();
+ let active_call_d = cx_d.read(ActiveCall::global);
+ let project_d = client_d.build_remote_project(project_id, cx_d).await;
+ let workspace_d = client_d.build_workspace(&project_d, cx_d);
+ active_call_d
+ .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
+ .await
+ .unwrap();
+ drop(project_d);
+
// All clients see that clients B and C are following client A.
cx_c.foreground().run_until_parked();
for (name, active_call, cx) in [
("A", &active_call_a, &cx_a),
("B", &active_call_b, &cx_b),
("C", &active_call_c, &cx_c),
+ ("D", &active_call_d, &cx_d),
] {
active_call.read_with(*cx, |call, cx| {
let room = call.room().unwrap().read(cx);
@@ -5954,6 +5936,7 @@ async fn test_following(
("A", &active_call_a, &cx_a),
("B", &active_call_b, &cx_b),
("C", &active_call_c, &cx_c),
+ ("D", &active_call_d, &cx_d),
] {
active_call.read_with(*cx, |call, cx| {
let room = call.room().unwrap().read(cx);
@@ -5965,6 +5948,90 @@ async fn test_following(
});
}
+ // Client C re-follows client A.
+ workspace_c.update(cx_c, |workspace, cx| {
+ workspace.toggle_follow(&ToggleFollow(peer_id_a), cx);
+ });
+
+ // All clients see that clients B and C are following client A.
+ cx_c.foreground().run_until_parked();
+ for (name, active_call, cx) in [
+ ("A", &active_call_a, &cx_a),
+ ("B", &active_call_b, &cx_b),
+ ("C", &active_call_c, &cx_c),
+ ("D", &active_call_d, &cx_d),
+ ] {
+ active_call.read_with(*cx, |call, cx| {
+ let room = call.room().unwrap().read(cx);
+ assert_eq!(
+ room.followers_for(peer_id_a, project_id),
+ &[peer_id_b, peer_id_c],
+ "checking followers for A as {name}"
+ );
+ });
+ }
+
+ // Client D follows client C.
+ workspace_d
+ .update(cx_d, |workspace, cx| {
+ workspace
+ .toggle_follow(&ToggleFollow(peer_id_c), cx)
+ .unwrap()
+ })
+ .await
+ .unwrap();
+
+ // All clients see that D is following C
+ cx_d.foreground().run_until_parked();
+ for (name, active_call, cx) in [
+ ("A", &active_call_a, &cx_a),
+ ("B", &active_call_b, &cx_b),
+ ("C", &active_call_c, &cx_c),
+ ("D", &active_call_d, &cx_d),
+ ] {
+ active_call.read_with(*cx, |call, cx| {
+ let room = call.room().unwrap().read(cx);
+ assert_eq!(
+ room.followers_for(peer_id_c, project_id),
+ &[peer_id_d],
+ "checking followers for C as {name}"
+ );
+ });
+ }
+
+ // Client C closes the project.
+ cx_c.drop_last(workspace_c);
+
+ // Clients A and B see that client B is following A, and client C is not present in the followers.
+ cx_c.foreground().run_until_parked();
+ for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] {
+ active_call.read_with(*cx, |call, cx| {
+ let room = call.room().unwrap().read(cx);
+ assert_eq!(
+ room.followers_for(peer_id_a, project_id),
+ &[peer_id_b],
+ "checking followers for A as {name}"
+ );
+ });
+ }
+
+ // All clients see that no-one is following C
+ for (name, active_call, cx) in [
+ ("A", &active_call_a, &cx_a),
+ ("B", &active_call_b, &cx_b),
+ ("C", &active_call_c, &cx_c),
+ ("D", &active_call_d, &cx_d),
+ ] {
+ active_call.read_with(*cx, |call, cx| {
+ let room = call.room().unwrap().read(cx);
+ assert_eq!(
+ room.followers_for(peer_id_c, project_id),
+ &[],
+ "checking followers for C as {name}"
+ );
+ });
+ }
+
let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
workspace
.active_item(cx)
diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml
index 899f8cc8b4204dc0c2b6d0c060aafb1a42e7e907..2afeb8ad8ae755b1f8b554d8e4a9bc15732767f3 100644
--- a/crates/collab_ui/Cargo.toml
+++ b/crates/collab_ui/Cargo.toml
@@ -29,6 +29,7 @@ clock = { path = "../clock" }
collections = { path = "../collections" }
context_menu = { path = "../context_menu" }
editor = { path = "../editor" }
+feedback = { path = "../feedback" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
menu = { path = "../menu" }
diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs
index f9f5738ad26762dd9e4fc5c08c6cf48439b90e67..28db4f6aa3d058766c7544b34dc5c23f347b1948 100644
--- a/crates/collab_ui/src/collab_titlebar_item.rs
+++ b/crates/collab_ui/src/collab_titlebar_item.rs
@@ -304,12 +304,22 @@ impl CollabTitlebarItem {
label: "Sign out".into(),
action: Box::new(SignOut),
},
+ ContextMenuItem::Item {
+ label: "Give Feedback".into(),
+ action: Box::new(feedback::feedback_editor::GiveFeedback),
+ },
]
} else {
- vec![ContextMenuItem::Item {
- label: "Sign in".into(),
- action: Box::new(Authenticate),
- }]
+ vec![
+ ContextMenuItem::Item {
+ label: "Sign in".into(),
+ action: Box::new(Authenticate),
+ },
+ ContextMenuItem::Item {
+ label: "Give Feedback".into(),
+ action: Box::new(feedback::feedback_editor::GiveFeedback),
+ },
+ ]
};
user_menu.show(
@@ -572,15 +582,13 @@ impl CollabTitlebarItem {
room: &ModelHandle,
cx: &mut RenderContext,
) -> Vec {
- let project = workspace.read(cx).project().read(cx);
-
let mut participants = room
.read(cx)
.remote_participants()
.values()
.cloned()
.collect::>();
- participants.sort_by_key(|p| Some(project.collaborators().get(&p.peer_id)?.replica_id));
+ participants.sort_by_cached_key(|p| p.user.github_login.clone());
participants
.into_iter()
@@ -823,7 +831,7 @@ impl CollabTitlebarItem {
avatar_style: AvatarStyle,
background_color: Color,
) -> ElementBox {
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(avatar_style.image)
.aligned()
.contained()
diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs
index 6abfec21f74f0918e6338bc77a2e8aa8acfe783b..2dd2b0e6b47e9bbea6e2c916d201b70282517841 100644
--- a/crates/collab_ui/src/collab_ui.rs
+++ b/crates/collab_ui/src/collab_ui.rs
@@ -86,6 +86,7 @@ fn join_project(action: &JoinProject, app_state: Arc, cx: &mut Mutable
0,
project,
app_state.dock_default_item_factory,
+ app_state.background_actions,
cx,
);
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs
index 98f70e83f0ecf1eb3c312429a8461a35b028199e..83f094ebadf0765b03ade8d7d7e18e651e0b85d6 100644
--- a/crates/collab_ui/src/contact_finder.rs
+++ b/crates/collab_ui/src/contact_finder.rs
@@ -1,7 +1,7 @@
use client::{ContactRequestStatus, User, UserStore};
use gpui::{
- elements::*, AnyViewHandle, Entity, ModelHandle, MouseState, MutableAppContext, RenderContext,
- Task, View, ViewContext, ViewHandle,
+ elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, MutableAppContext,
+ RenderContext, Task, View, ViewContext, ViewHandle,
};
use picker::{Picker, PickerDelegate};
use settings::Settings;
@@ -68,7 +68,7 @@ impl PickerDelegate for ContactFinder {
this.potential_contacts = potential_contacts.into();
cx.notify();
});
- Ok(())
+ anyhow::Ok(())
}
.log_err()
.await;
@@ -128,7 +128,7 @@ impl PickerDelegate for ContactFinder {
.style_for(mouse_state, selected);
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(theme.contact_finder.contact_avatar)
.aligned()
.left()
@@ -178,4 +178,14 @@ impl ContactFinder {
selected_index: 0,
}
}
+
+ pub fn editor_text(&self, cx: &AppContext) -> String {
+ self.picker.read(cx).query(cx)
+ }
+
+ pub fn with_editor_text(self, editor_text: String, cx: &mut ViewContext) -> Self {
+ self.picker
+ .update(cx, |picker, cx| picker.set_query(editor_text, cx));
+ self
+ }
}
diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs
index 3bb036d3366a4de8f4bb8e8253ae4470ce86edbc..38444f6f12b52249f9f87705a671a828e7458c99 100644
--- a/crates/collab_ui/src/contact_list.rs
+++ b/crates/collab_ui/src/contact_list.rs
@@ -294,6 +294,16 @@ impl ContactList {
this
}
+ pub fn editor_text(&self, cx: &AppContext) -> String {
+ self.filter_editor.read(cx).text(cx)
+ }
+
+ pub fn with_editor_text(self, editor_text: String, cx: &mut ViewContext) -> Self {
+ self.filter_editor
+ .update(cx, |picker, cx| picker.set_text(editor_text, cx));
+ self
+ }
+
fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext) {
let user_id = request.0;
let user_store = self.user_store.clone();
@@ -726,7 +736,7 @@ impl ContactList {
) -> ElementBox {
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
@@ -1080,7 +1090,7 @@ impl ContactList {
};
Stack::new()
.with_child(
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
@@ -1173,7 +1183,7 @@ impl ContactList {
let mut row = Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs
index 0c67ef4c7c094a17758284bc5b2e8aeac8d30586..017950e6cc777543d7df8fdd98d40f0da8ed1e59 100644
--- a/crates/collab_ui/src/contacts_popover.rs
+++ b/crates/collab_ui/src/contacts_popover.rs
@@ -43,19 +43,23 @@ impl ContactsPopover {
user_store,
_subscription: None,
};
- this.show_contact_list(cx);
+ this.show_contact_list(String::new(), cx);
this
}
fn toggle_contact_finder(&mut self, _: &ToggleContactFinder, cx: &mut ViewContext) {
match &self.child {
- Child::ContactList(_) => self.show_contact_finder(cx),
- Child::ContactFinder(_) => self.show_contact_list(cx),
+ Child::ContactList(list) => self.show_contact_finder(list.read(cx).editor_text(cx), cx),
+ Child::ContactFinder(finder) => {
+ self.show_contact_list(finder.read(cx).editor_text(cx), cx)
+ }
}
}
- fn show_contact_finder(&mut self, cx: &mut ViewContext) {
- let child = cx.add_view(|cx| ContactFinder::new(self.user_store.clone(), cx));
+ fn show_contact_finder(&mut self, editor_text: String, cx: &mut ViewContext) {
+ let child = cx.add_view(|cx| {
+ ContactFinder::new(self.user_store.clone(), cx).with_editor_text(editor_text, cx)
+ });
cx.focus(&child);
self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event {
crate::contact_finder::Event::Dismissed => cx.emit(Event::Dismissed),
@@ -64,9 +68,11 @@ impl ContactsPopover {
cx.notify();
}
- fn show_contact_list(&mut self, cx: &mut ViewContext) {
- let child =
- cx.add_view(|cx| ContactList::new(self.project.clone(), self.user_store.clone(), cx));
+ fn show_contact_list(&mut self, editor_text: String, cx: &mut ViewContext) {
+ let child = cx.add_view(|cx| {
+ ContactList::new(self.project.clone(), self.user_store.clone(), cx)
+ .with_editor_text(editor_text, cx)
+ });
cx.focus(&child);
self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event {
crate::contact_list::Event::Dismissed => cx.emit(Event::Dismissed),
diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs
index a0f54abe386ca56ef416f051dc2474e1736cc332..6fb0278218987d0e50ea7acc8792a24258426702 100644
--- a/crates/collab_ui/src/incoming_call_notification.rs
+++ b/crates/collab_ui/src/incoming_call_notification.rs
@@ -108,7 +108,7 @@ impl IncomingCallNotification {
.unwrap_or(&default_project);
Flex::row()
.with_children(self.call.calling_user.avatar.clone().map(|avatar| {
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(theme.caller_avatar)
.aligned()
.boxed()
diff --git a/crates/collab_ui/src/notifications.rs b/crates/collab_ui/src/notifications.rs
index 06b6cf2a90984fe57b932892f94fda83bb06b369..21c2d2c2185ed69d6c19ad85be51cff1231d5bcb 100644
--- a/crates/collab_ui/src/notifications.rs
+++ b/crates/collab_ui/src/notifications.rs
@@ -24,7 +24,7 @@ pub fn render_user_notification(
.with_child(
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(theme.header_avatar)
.aligned()
.constrained()
diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs
index edf0354eec0d53be306e613697962a72cc34a683..b24f3492da933f4a868be65cfb2f3344679fc6c6 100644
--- a/crates/collab_ui/src/project_shared_notification.rs
+++ b/crates/collab_ui/src/project_shared_notification.rs
@@ -108,7 +108,7 @@ impl ProjectSharedNotification {
let theme = &cx.global::().theme.project_shared_notification;
Flex::row()
.with_children(self.owner.avatar.clone().map(|avatar| {
- Image::new(avatar)
+ Image::from_data(avatar)
.with_style(theme.owner_avatar)
.aligned()
.boxed()
diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs
index 55522966fa48f1f25f402d999917144c0d884be1..52a0e1cdc0b15f62ef2a66da56f39ac0e2169a03 100644
--- a/crates/command_palette/src/command_palette.rs
+++ b/crates/command_palette/src/command_palette.rs
@@ -352,9 +352,7 @@ mod tests {
});
let project = Project::test(app_state.fs.clone(), [], cx).await;
- let (_, workspace) = cx.add_window(|cx| {
- Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx)
- });
+ let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let editor = cx.add_view(&workspace, |cx| {
let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx);
diff --git a/crates/db/src/db.rs b/crates/db/src/db.rs
index 20f2300d89b591230dcc41df4008e8c1fc65ec7a..ae9325ea2ea381d89b7e0cfddb48bc0cb22a0e16 100644
--- a/crates/db/src/db.rs
+++ b/crates/db/src/db.rs
@@ -4,6 +4,7 @@ pub mod query;
// Re-export
pub use anyhow;
use anyhow::Context;
+use gpui::MutableAppContext;
pub use indoc::indoc;
pub use lazy_static;
use parking_lot::{Mutex, RwLock};
@@ -17,6 +18,7 @@ use sqlez::domain::Migrator;
use sqlez::thread_safe_connection::ThreadSafeConnection;
use sqlez_macros::sql;
use std::fs::create_dir_all;
+use std::future::Future;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
@@ -39,6 +41,7 @@ const FALLBACK_DB_NAME: &'static str = "FALLBACK_MEMORY_DB";
const DB_FILE_NAME: &'static str = "db.sqlite";
lazy_static::lazy_static! {
+ // !!!!!!! CHANGE BACK TO DEFAULT FALSE BEFORE SHIPPING
static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty());
static ref DB_FILE_OPERATIONS: Mutex<()> = Mutex::new(());
pub static ref BACKUP_DB_PATH: RwLock