Merge branch 'main' into import-theme

Nate Butler created

Change summary

Cargo.lock                                          |   41 
Cargo.toml                                          |    2 
crates/ai2/Cargo.toml                               |    6 
crates/ai2/src/auth.rs                              |    2 
crates/ai2/src/embedding.rs                         |    2 
crates/ai2/src/prompts/base.rs                      |    2 
crates/ai2/src/prompts/file_context.rs              |    4 
crates/ai2/src/prompts/repository_context.rs        |    4 
crates/ai2/src/providers/open_ai/completion.rs      |    2 
crates/ai2/src/providers/open_ai/embedding.rs       |    4 
crates/ai2/src/test.rs                              |    2 
crates/audio2/Cargo.toml                            |    2 
crates/audio2/src/assets.rs                         |    2 
crates/audio2/src/audio2.rs                         |    2 
crates/call2/Cargo.toml                             |   36 
crates/call2/src/call2.rs                           |   12 
crates/call2/src/call_settings.rs                   |    4 
crates/call2/src/participant.rs                     |   16 
crates/call2/src/room.rs                            |   32 
crates/client2/Cargo.toml                           |   18 
crates/client2/src/client2.rs                       |   28 
crates/client2/src/telemetry.rs                     |    4 
crates/client2/src/test.rs                          |    4 
crates/client2/src/user.rs                          |    6 
crates/copilot2/Cargo.toml                          |   24 
crates/copilot2/src/copilot2.rs                     |   70 
crates/copilot2/src/request.rs                      |   30 
crates/db2/Cargo.toml                               |    4 
crates/db2/src/db2.rs                               |   12 
crates/db2/src/kvp.rs                               |    2 
crates/feature_flags2/Cargo.toml                    |    2 
crates/feature_flags2/src/feature_flags2.rs         |    2 
crates/fs2/Cargo.toml                               |    6 
crates/fs2/src/fs2.rs                               |    8 
crates/fuzzy2/Cargo.toml                            |    2 
crates/fuzzy2/src/paths.rs                          |    2 
crates/fuzzy2/src/strings.rs                        |    2 
crates/gpui2/src/app.rs                             |   66 
crates/gpui2/src/app/async_context.rs               |   10 
crates/gpui2/src/app/test_context.rs                |    6 
crates/gpui2/src/elements/img.rs                    |    4 
crates/gpui2/src/platform.rs                        |    2 
crates/gpui2/src/platform/mac/display_linker.rs     |    4 
crates/gpui2/src/platform/mac/platform.rs           |    2 
crates/gpui2/src/platform/mac/shaders.metal         |    6 
crates/gpui2/src/platform/mac/window.rs             |   33 
crates/gpui2/src/platform/test/platform.rs          |    2 
crates/gpui2/src/window.rs                          |   93 
crates/gpui2_macros/src/test.rs                     |   14 
crates/install_cli2/Cargo.toml                      |    2 
crates/install_cli2/src/install_cli2.rs             |    2 
crates/journal2/Cargo.toml                          |    2 
crates/journal2/src/journal2.rs                     |    2 
crates/language2/Cargo.toml                         |   26 
crates/language2/src/buffer.rs                      |   20 
crates/language2/src/buffer_tests.rs                |   94 
crates/language2/src/diagnostic_set.rs              |    8 
crates/language2/src/highlight_map.rs               |    6 
crates/language2/src/language2.rs                   |   70 
crates/language2/src/language_settings.rs           |    8 
crates/language2/src/outline.rs                     |    6 
crates/language2/src/proto.rs                       |    4 
crates/language2/src/syntax_map/syntax_map_tests.rs |   42 
crates/live_kit_client2/Cargo.toml                  |    4 
crates/lsp2/Cargo.toml                              |    4 
crates/lsp2/src/lsp2.rs                             |   10 
crates/menu2/Cargo.toml                             |    2 
crates/multi_buffer2/Cargo.toml                     |   30 
crates/multi_buffer2/src/anchor.rs                  |    2 
crates/multi_buffer2/src/multi_buffer2.rs           |   84 
crates/prettier2/Cargo.toml                         |   16 
crates/prettier2/src/prettier2.rs                   |   14 
crates/project2/Cargo.toml                          |   54 
crates/project2/src/lsp_command.rs                  |  356 ++--
crates/project2/src/project2.rs                     |  302 +-
crates/project2/src/project_settings.rs             |    4 
crates/project2/src/project_tests.rs                |  441 ++--
crates/project2/src/search.rs                       |    4 
crates/project2/src/terminals.rs                    |   10 
crates/project2/src/worktree.rs                     |   32 
crates/rpc2/Cargo.toml                              |    6 
crates/rpc2/src/conn.rs                             |    4 
crates/rpc2/src/peer.rs                             |   14 
crates/rpc2/src/proto.rs                            |    4 
crates/settings2/Cargo.toml                         |   12 
crates/settings2/src/keymap_file.rs                 |    4 
crates/settings2/src/settings_file.rs               |    4 
crates/settings2/src/settings_store.rs              |    8 
crates/storybook2/src/storybook2.rs                 |    4 
crates/terminal2/Cargo.toml                         |    8 
crates/terminal2/src/mappings/colors.rs             |    2 
crates/terminal2/src/mappings/keys.rs               |    4 
crates/terminal2/src/mappings/mouse.rs              |   24 
crates/terminal2/src/terminal2.rs                   |    6 
crates/terminal2/src/terminal_settings.rs           |    4 
crates/text2/Cargo.toml                             |    2 
crates/text2/src/locator.rs                         |    2 
crates/text2/src/patch.rs                           |   14 
crates/text2/src/tests.rs                           |    4 
crates/theme2/Cargo.toml                            |   12 
crates/theme2/src/colors.rs                         |    8 
crates/theme2/src/default_colors.rs                 |    2 
crates/theme2/src/registry.rs                       |    2 
crates/theme2/src/scale.rs                          |    2 
crates/theme2/src/settings.rs                       |    6 
crates/theme2/src/syntax.rs                         |    2 
crates/theme2/src/theme2.rs                         |    4 
crates/ui2/src/elements/avatar.rs                   |   23 
crates/ui2/src/elements/player.rs                   |    4 
crates/workspace2/Cargo.toml                        |   66 
crates/workspace2/src/dock.rs                       |  750 ++++++++
crates/workspace2/src/item.rs                       |  774 ++++----
crates/workspace2/src/notifications.rs              |  404 ++++
crates/workspace2/src/pane.rs                       | 1303 +++++++-------
crates/workspace2/src/pane/dragged_item_receiver.rs |  239 ++
crates/workspace2/src/pane_group.rs                 |  171 +
crates/workspace2/src/persistence.rs                |  973 +++++++++++
crates/workspace2/src/persistence/model.rs          |   45 
crates/workspace2/src/searchable.rs                 |  285 +++
crates/workspace2/src/shared_screen.rs              |  151 +
crates/workspace2/src/status_bar.rs                 |  301 +++
crates/workspace2/src/toolbar.rs                    |  298 +++
crates/workspace2/src/workspace2.rs                 | 1253 +++++++-------
crates/workspace2/src/workspace_settings.rs         |   56 
crates/zed2/Cargo.toml                              |   44 
crates/zed2/src/assets.rs                           |    3 
crates/zed2/src/languages.rs                        |    6 
crates/zed2/src/languages/c.rs                      |   52 
crates/zed2/src/languages/css.rs                    |    4 
crates/zed2/src/languages/elixir.rs                 |   18 
crates/zed2/src/languages/go.rs                     |   57 
crates/zed2/src/languages/html.rs                   |    4 
crates/zed2/src/languages/json.rs                   |   10 
crates/zed2/src/languages/lua.rs                    |    4 
crates/zed2/src/languages/php.rs                    |   10 
crates/zed2/src/languages/python.rs                 |   46 
crates/zed2/src/languages/ruby.rs                   |   40 
crates/zed2/src/languages/rust.rs                   |   92 
crates/zed2/src/languages/svelte.rs                 |    4 
crates/zed2/src/languages/tailwind.rs               |    6 
crates/zed2/src/languages/typescript.rs             |   28 
crates/zed2/src/languages/vue.rs                    |   14 
crates/zed2/src/languages/yaml.rs                   |    6 
crates/zed2/src/main.rs                             |  134 
crates/zed2/src/zed2.rs                             |  511 ++++-
145 files changed, 7,250 insertions(+), 3,413 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1541,7 +1541,6 @@ dependencies = [
  "schemars",
  "serde",
  "serde_derive",
- "settings",
  "settings2",
  "smol",
  "sum_tree",
@@ -7796,7 +7795,6 @@ dependencies = [
  "anyhow",
  "collections",
  "feature_flags2",
- "fs",
  "fs2",
  "futures 0.3.28",
  "gpui2",
@@ -10813,6 +10811,44 @@ dependencies = [
  "uuid 1.4.1",
 ]
 
+[[package]]
+name = "workspace2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-recursion 1.0.5",
+ "bincode",
+ "call2",
+ "client2",
+ "collections",
+ "db2",
+ "env_logger 0.9.3",
+ "fs2",
+ "futures 0.3.28",
+ "gpui2",
+ "indoc",
+ "install_cli2",
+ "itertools 0.10.5",
+ "language2",
+ "lazy_static",
+ "log",
+ "node_runtime",
+ "parking_lot 0.11.2",
+ "postage",
+ "project2",
+ "schemars",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "settings2",
+ "smallvec",
+ "terminal2",
+ "theme2",
+ "ui2",
+ "util",
+ "uuid 1.4.1",
+]
+
 [[package]]
 name = "ws2_32-sys"
 version = "0.2.1"
@@ -11128,6 +11164,7 @@ dependencies = [
  "urlencoding",
  "util",
  "uuid 1.4.1",
+ "workspace2",
 ]
 
 [[package]]

Cargo.toml 🔗

@@ -100,7 +100,7 @@ members = [
     "crates/semantic_index",
     "crates/vim",
     "crates/vcs_menu",
-    "crates/workspace",
+    "crates/workspace2",
     "crates/welcome",
     "crates/xtask",
     "crates/zed",

crates/ai2/Cargo.toml 🔗

@@ -12,9 +12,9 @@ doctest = false
 test-support = []
 
 [dependencies]
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 util = { path = "../util" }
-language2 = { path = "../language2" }
+language = { package = "language2", path = "../language2" }
 async-trait.workspace = true
 anyhow.workspace = true
 futures.workspace = true
@@ -35,4 +35,4 @@ rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
 bincode = "1.3.3"
 
 [dev-dependencies]
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }

crates/ai2/src/auth.rs 🔗

@@ -1,4 +1,4 @@
-use gpui2::AppContext;
+use gpui::AppContext;
 
 #[derive(Clone, Debug)]
 pub enum ProviderCredential {

crates/ai2/src/embedding.rs 🔗

@@ -81,7 +81,7 @@ mod tests {
     use super::*;
     use rand::prelude::*;
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_similarity(mut rng: StdRng) {
         assert_eq!(
             Embedding::from(vec![1., 0., 0., 0., 0.])

crates/ai2/src/prompts/base.rs 🔗

@@ -2,7 +2,7 @@ use std::cmp::Reverse;
 use std::ops::Range;
 use std::sync::Arc;
 
-use language2::BufferSnapshot;
+use language::BufferSnapshot;
 use util::ResultExt;
 
 use crate::models::LanguageModel;

crates/ai2/src/prompts/file_context.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::anyhow;
-use language2::BufferSnapshot;
-use language2::ToOffset;
+use language::BufferSnapshot;
+use language::ToOffset;
 
 use crate::models::LanguageModel;
 use crate::models::TruncationDirection;

crates/ai2/src/prompts/repository_context.rs 🔗

@@ -2,8 +2,8 @@ use crate::prompts::base::{PromptArguments, PromptTemplate};
 use std::fmt::Write;
 use std::{ops::Range, path::PathBuf};
 
-use gpui2::{AsyncAppContext, Model};
-use language2::{Anchor, Buffer};
+use gpui::{AsyncAppContext, Model};
+use language::{Anchor, Buffer};
 
 #[derive(Clone)]
 pub struct PromptCodeSnippet {

crates/ai2/src/providers/open_ai/completion.rs 🔗

@@ -3,7 +3,7 @@ use futures::{
     future::BoxFuture, io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, FutureExt,
     Stream, StreamExt,
 };
-use gpui2::{AppContext, BackgroundExecutor};
+use gpui::{AppContext, BackgroundExecutor};
 use isahc::{http::StatusCode, Request, RequestExt};
 use parking_lot::RwLock;
 use serde::{Deserialize, Serialize};

crates/ai2/src/providers/open_ai/embedding.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::AsyncReadExt;
-use gpui2::BackgroundExecutor;
-use gpui2::{serde_json, AppContext};
+use gpui::BackgroundExecutor;
+use gpui::{serde_json, AppContext};
 use isahc::http::StatusCode;
 use isahc::prelude::Configurable;
 use isahc::{AsyncBody, Response};

crates/ai2/src/test.rs 🔗

@@ -5,7 +5,7 @@ use std::{
 
 use async_trait::async_trait;
 use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
-use gpui2::AppContext;
+use gpui::AppContext;
 use parking_lot::Mutex;
 
 use crate::{

crates/audio2/Cargo.toml 🔗

@@ -9,7 +9,7 @@ path = "src/audio2.rs"
 doctest = false
 
 [dependencies]
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 collections = { path = "../collections" }
 util = { path = "../util" }
 

crates/audio2/src/assets.rs 🔗

@@ -2,7 +2,7 @@ use std::{io::Cursor, sync::Arc};
 
 use anyhow::Result;
 use collections::HashMap;
-use gpui2::{AppContext, AssetSource};
+use gpui::{AppContext, AssetSource};
 use rodio::{
     source::{Buffered, SamplesConverter},
     Decoder, Source,

crates/audio2/src/audio2.rs 🔗

@@ -1,5 +1,5 @@
 use assets::SoundRegistry;
-use gpui2::{AppContext, AssetSource};
+use gpui::{AppContext, AssetSource};
 use rodio::{OutputStream, OutputStreamHandle};
 use util::ResultExt;
 

crates/call2/Cargo.toml 🔗

@@ -10,26 +10,26 @@ doctest = false
 
 [features]
 test-support = [
-    "client2/test-support",
+    "client/test-support",
     "collections/test-support",
-    "gpui2/test-support",
-    "live_kit_client2/test-support",
-    "project2/test-support",
+    "gpui/test-support",
+    "live_kit_client/test-support",
+    "project/test-support",
     "util/test-support"
 ]
 
 [dependencies]
-audio2 = { path = "../audio2" }
-client2 = { path = "../client2" }
+audio = { package = "audio2", path = "../audio2" }
+client = { package = "client2", path = "../client2" }
 collections = { path = "../collections" }
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 log.workspace = true
-live_kit_client2 = { path = "../live_kit_client2" }
-fs2 = { path = "../fs2" }
-language2 = { path = "../language2" }
+live_kit_client = { package = "live_kit_client2", path = "../live_kit_client2" }
+fs = { package = "fs2", path = "../fs2" }
+language = { package = "language2", path = "../language2" }
 media = { path = "../media" }
-project2 = { path = "../project2" }
-settings2 = { path = "../settings2" }
+project = { package = "project2", path = "../project2" }
+settings = { package = "settings2", path = "../settings2" }
 util = { path = "../util" }
 
 anyhow.workspace = true
@@ -42,11 +42,11 @@ serde_json.workspace = true
 serde_derive.workspace = true
 
 [dev-dependencies]
-client2 = { path = "../client2", features = ["test-support"] }
-fs2 = { path = "../fs2", features = ["test-support"] }
-language2 = { path = "../language2", features = ["test-support"] }
+client = { package = "client2", path = "../client2", features = ["test-support"] }
+fs = { package = "fs2", path = "../fs2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-live_kit_client2 = { path = "../live_kit_client2", features = ["test-support"] }
-project2 = { path = "../project2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+live_kit_client = { package = "live_kit_client2", path = "../live_kit_client2", features = ["test-support"] }
+project = { package = "project2", path = "../project2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/call2/src/call2.rs 🔗

@@ -3,21 +3,21 @@ pub mod participant;
 pub mod room;
 
 use anyhow::{anyhow, Result};
-use audio2::Audio;
+use audio::Audio;
 use call_settings::CallSettings;
-use client2::{
+use client::{
     proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore,
     ZED_ALWAYS_ACTIVE,
 };
 use collections::HashSet;
 use futures::{future::Shared, FutureExt};
-use gpui2::{
+use gpui::{
     AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task,
     WeakModel,
 };
 use postage::watch;
-use project2::Project;
-use settings2::Settings;
+use project::Project;
+use settings::Settings;
 use std::sync::Arc;
 
 pub use participant::ParticipantLocation;
@@ -50,7 +50,7 @@ pub struct ActiveCall {
     ),
     client: Arc<Client>,
     user_store: Model<UserStore>,
-    _subscriptions: Vec<client2::Subscription>,
+    _subscriptions: Vec<client::Subscription>,
 }
 
 impl EventEmitter for ActiveCall {

crates/call2/src/call_settings.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::Result;
-use gpui2::AppContext;
+use gpui::AppContext;
 use schemars::JsonSchema;
 use serde_derive::{Deserialize, Serialize};
-use settings2::Settings;
+use settings::Settings;
 
 #[derive(Deserialize, Debug)]
 pub struct CallSettings {

crates/call2/src/participant.rs 🔗

@@ -1,11 +1,11 @@
 use anyhow::{anyhow, Result};
-use client2::ParticipantIndex;
-use client2::{proto, User};
+use client::ParticipantIndex;
+use client::{proto, User};
 use collections::HashMap;
-use gpui2::WeakModel;
-pub use live_kit_client2::Frame;
-use live_kit_client2::{RemoteAudioTrack, RemoteVideoTrack};
-use project2::Project;
+use gpui::WeakModel;
+pub use live_kit_client::Frame;
+use live_kit_client::{RemoteAudioTrack, RemoteVideoTrack};
+use project::Project;
 use std::sync::Arc;
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -47,6 +47,6 @@ pub struct RemoteParticipant {
     pub participant_index: ParticipantIndex,
     pub muted: bool,
     pub speaking: bool,
-    pub video_tracks: HashMap<live_kit_client2::Sid, Arc<RemoteVideoTrack>>,
-    pub audio_tracks: HashMap<live_kit_client2::Sid, Arc<RemoteAudioTrack>>,
+    pub video_tracks: HashMap<live_kit_client::Sid, Arc<RemoteVideoTrack>>,
+    pub audio_tracks: HashMap<live_kit_client::Sid, Arc<RemoteAudioTrack>>,
 }

crates/call2/src/room.rs 🔗

@@ -4,25 +4,25 @@ use crate::{
     IncomingCall,
 };
 use anyhow::{anyhow, Result};
-use audio2::{Audio, Sound};
-use client2::{
+use audio::{Audio, Sound};
+use client::{
     proto::{self, PeerId},
     Client, ParticipantIndex, TypedEnvelope, User, UserStore,
 };
 use collections::{BTreeMap, HashMap, HashSet};
-use fs2::Fs;
+use fs::Fs;
 use futures::{FutureExt, StreamExt};
-use gpui2::{
+use gpui::{
     AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
 };
-use language2::LanguageRegistry;
-use live_kit_client2::{
+use language::LanguageRegistry;
+use live_kit_client::{
     LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RemoteAudioTrackUpdate,
     RemoteVideoTrackUpdate,
 };
 use postage::{sink::Sink, stream::Stream, watch};
-use project2::Project;
-use settings2::Settings;
+use project::Project;
+use settings::Settings;
 use std::{future::Future, mem, sync::Arc, time::Duration};
 use util::{post_inc, ResultExt, TryFutureExt};
 
@@ -72,8 +72,8 @@ pub struct Room {
     client: Arc<Client>,
     user_store: Model<UserStore>,
     follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec<PeerId>>,
-    client_subscriptions: Vec<client2::Subscription>,
-    _subscriptions: Vec<gpui2::Subscription>,
+    client_subscriptions: Vec<client::Subscription>,
+    _subscriptions: Vec<gpui::Subscription>,
     room_update_completed_tx: watch::Sender<Option<()>>,
     room_update_completed_rx: watch::Receiver<Option<()>>,
     pending_room_update: Option<Task<()>>,
@@ -98,7 +98,7 @@ impl Room {
         if let Some(live_kit) = self.live_kit.as_ref() {
             matches!(
                 *live_kit.room.status().borrow(),
-                live_kit_client2::ConnectionState::Connected { .. }
+                live_kit_client::ConnectionState::Connected { .. }
             )
         } else {
             false
@@ -114,7 +114,7 @@ impl Room {
         cx: &mut ModelContext<Self>,
     ) -> Self {
         let live_kit_room = if let Some(connection_info) = live_kit_connection_info {
-            let room = live_kit_client2::Room::new();
+            let room = live_kit_client::Room::new();
             let mut status = room.status();
             // Consume the initial status of the room.
             let _ = status.try_recv();
@@ -126,7 +126,7 @@ impl Room {
                         break;
                     };
 
-                    if status == live_kit_client2::ConnectionState::Disconnected {
+                    if status == live_kit_client::ConnectionState::Disconnected {
                         this.update(&mut cx, |this, cx| this.leave(cx).log_err())
                             .ok();
                         break;
@@ -341,7 +341,7 @@ impl Room {
     }
 
     pub fn mute_on_join(cx: &AppContext) -> bool {
-        CallSettings::get_global(cx).mute_on_join || client2::IMPERSONATE_LOGIN.is_some()
+        CallSettings::get_global(cx).mute_on_join || client::IMPERSONATE_LOGIN.is_some()
     }
 
     fn from_join_response(
@@ -1504,7 +1504,7 @@ impl Room {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn set_display_sources(&self, sources: Vec<live_kit_client2::MacOSDisplay>) {
+    pub fn set_display_sources(&self, sources: Vec<live_kit_client::MacOSDisplay>) {
         self.live_kit
             .as_ref()
             .unwrap()
@@ -1514,7 +1514,7 @@ impl Room {
 }
 
 struct LiveKitRoom {
-    room: Arc<live_kit_client2::Room>,
+    room: Arc<live_kit_client::Room>,
     screen_track: LocalTrack,
     microphone_track: LocalTrack,
     /// Tracks whether we're currently in a muted state due to auto-mute from deafening or manual mute performed by user.

crates/client2/Cargo.toml 🔗

@@ -9,17 +9,17 @@ path = "src/client2.rs"
 doctest = false
 
 [features]
-test-support = ["collections/test-support", "gpui2/test-support", "rpc2/test-support"]
+test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"]
 
 [dependencies]
 collections = { path = "../collections" }
-db2 = { path = "../db2" }
-gpui2 = { path = "../gpui2" }
+db = { package = "db2", path = "../db2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 util = { path = "../util" }
-rpc2 = { path = "../rpc2" }
+rpc = { package = "rpc2", path = "../rpc2" }
 text = { path = "../text" }
-settings2 = { path = "../settings2" }
-feature_flags2 = { path = "../feature_flags2" }
+settings = { package = "settings2", path = "../settings2" }
+feature_flags = { package = "feature_flags2", path = "../feature_flags2" }
 sum_tree = { path = "../sum_tree" }
 
 anyhow.workspace = true
@@ -46,7 +46,7 @@ url = "2.2"
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-rpc2 = { path = "../rpc2", features = ["test-support"] }
-settings = { path = "../settings", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+rpc = { package = "rpc2", path = "../rpc2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/client2/src/client2.rs 🔗

@@ -14,7 +14,7 @@ use futures::{
     future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _,
     TryStreamExt,
 };
-use gpui2::{
+use gpui::{
     serde_json, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model, SemanticVersion, Task,
     WeakModel,
 };
@@ -22,10 +22,10 @@ use lazy_static::lazy_static;
 use parking_lot::RwLock;
 use postage::watch;
 use rand::prelude::*;
-use rpc2::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
+use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use settings2::Settings;
+use settings::Settings;
 use std::{
     any::TypeId,
     collections::HashMap,
@@ -44,7 +44,7 @@ use util::channel::ReleaseChannel;
 use util::http::HttpClient;
 use util::{ResultExt, TryFutureExt};
 
-pub use rpc2::*;
+pub use rpc::*;
 pub use telemetry::ClickhouseEvent;
 pub use user::*;
 
@@ -367,7 +367,7 @@ pub struct TelemetrySettingsContent {
     pub metrics: Option<bool>,
 }
 
-impl settings2::Settings for TelemetrySettings {
+impl settings::Settings for TelemetrySettings {
     const KEY: Option<&'static str> = Some("telemetry");
 
     type FileContent = TelemetrySettingsContent;
@@ -979,7 +979,7 @@ impl Client {
                 "Authorization",
                 format!("{} {}", credentials.user_id, credentials.access_token),
             )
-            .header("x-zed-protocol-version", rpc2::PROTOCOL_VERSION);
+            .header("x-zed-protocol-version", rpc::PROTOCOL_VERSION);
 
         let http = self.http.clone();
         cx.background_executor().spawn(async move {
@@ -1029,7 +1029,7 @@ impl Client {
             // zed server to encrypt the user's access token, so that it can'be intercepted by
             // any other app running on the user's device.
             let (public_key, private_key) =
-                rpc2::auth::keypair().expect("failed to generate keypair for auth");
+                rpc::auth::keypair().expect("failed to generate keypair for auth");
             let public_key_string =
                 String::try_from(public_key).expect("failed to serialize public key for auth");
 
@@ -1383,12 +1383,12 @@ mod tests {
     use super::*;
     use crate::test::FakeServer;
 
-    use gpui2::{BackgroundExecutor, Context, TestAppContext};
+    use gpui::{BackgroundExecutor, Context, TestAppContext};
     use parking_lot::Mutex;
     use std::future;
     use util::http::FakeHttpClient;
 
-    #[gpui2::test(iterations = 10)]
+    #[gpui::test(iterations = 10)]
     async fn test_reconnection(cx: &mut TestAppContext) {
         let user_id = 5;
         let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
@@ -1422,7 +1422,7 @@ mod tests {
         assert_eq!(server.auth_count(), 2); // Client re-authenticated due to an invalid token
     }
 
-    #[gpui2::test(iterations = 10)]
+    #[gpui::test(iterations = 10)]
     async fn test_connection_timeout(executor: BackgroundExecutor, cx: &mut TestAppContext) {
         let user_id = 5;
         let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
@@ -1490,7 +1490,7 @@ mod tests {
         ));
     }
 
-    #[gpui2::test(iterations = 10)]
+    #[gpui::test(iterations = 10)]
     async fn test_authenticating_more_than_once(
         cx: &mut TestAppContext,
         executor: BackgroundExecutor,
@@ -1541,7 +1541,7 @@ mod tests {
         assert_eq!(decode_worktree_url("not://the-right-format"), None);
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_subscribing_to_entity(cx: &mut TestAppContext) {
         let user_id = 5;
         let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
@@ -1594,7 +1594,7 @@ mod tests {
         done_rx2.next().await.unwrap();
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_subscribing_after_dropping_subscription(cx: &mut TestAppContext) {
         let user_id = 5;
         let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
@@ -1622,7 +1622,7 @@ mod tests {
         done_rx2.next().await.unwrap();
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_dropping_subscription_in_handler(cx: &mut TestAppContext) {
         let user_id = 5;
         let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx));

crates/client2/src/telemetry.rs 🔗

@@ -1,9 +1,9 @@
 use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
-use gpui2::{serde_json, AppContext, AppMetadata, BackgroundExecutor, Task};
+use gpui::{serde_json, AppContext, AppMetadata, BackgroundExecutor, Task};
 use lazy_static::lazy_static;
 use parking_lot::Mutex;
 use serde::Serialize;
-use settings2::Settings;
+use settings::Settings;
 use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration};
 use sysinfo::{
     CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt,

crates/client2/src/test.rs 🔗

@@ -1,9 +1,9 @@
 use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore};
 use anyhow::{anyhow, Result};
 use futures::{stream::BoxStream, StreamExt};
-use gpui2::{BackgroundExecutor, Context, Model, TestAppContext};
+use gpui::{BackgroundExecutor, Context, Model, TestAppContext};
 use parking_lot::Mutex;
-use rpc2::{
+use rpc::{
     proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse},
     ConnectionId, Peer, Receipt, TypedEnvelope,
 };

crates/client2/src/user.rs 🔗

@@ -1,11 +1,11 @@
 use super::{proto, Client, Status, TypedEnvelope};
 use anyhow::{anyhow, Context, Result};
 use collections::{hash_map::Entry, HashMap, HashSet};
-use feature_flags2::FeatureFlagAppExt;
+use feature_flags::FeatureFlagAppExt;
 use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
-use gpui2::{AsyncAppContext, EventEmitter, ImageData, Model, ModelContext, Task};
+use gpui::{AsyncAppContext, EventEmitter, ImageData, Model, ModelContext, Task};
 use postage::{sink::Sink, watch};
-use rpc2::proto::{RequestMessage, UsersResponse};
+use rpc::proto::{RequestMessage, UsersResponse};
 use std::sync::{Arc, Weak};
 use text::ReplicaId;
 use util::http::HttpClient;

crates/copilot2/Cargo.toml 🔗

@@ -11,21 +11,21 @@ doctest = false
 [features]
 test-support = [
     "collections/test-support",
-    "gpui2/test-support",
-    "language2/test-support",
-    "lsp2/test-support",
-    "settings2/test-support",
+    "gpui/test-support",
+    "language/test-support",
+    "lsp/test-support",
+    "settings/test-support",
     "util/test-support",
 ]
 
 [dependencies]
 collections = { path = "../collections" }
 context_menu = { path = "../context_menu" }
-gpui2 = { path = "../gpui2" }
-language2 = { path = "../language2" }
-settings2 = { path = "../settings2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+settings = { package = "settings2", path = "../settings2" }
 theme = { path = "../theme" }
-lsp2 = { path = "../lsp2" }
+lsp = { package = "lsp2", path = "../lsp2" }
 node_runtime = { path = "../node_runtime"}
 util = { path = "../util" }
 async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] }
@@ -42,9 +42,9 @@ parking_lot.workspace = true
 clock = { path = "../clock" }
 collections = { path = "../collections", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-language2 = { path = "../language2", features = ["test-support"] }
-lsp2 = { path = "../lsp2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }
-settings2 = { path = "../settings2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/copilot2/src/copilot2.rs 🔗

@@ -6,20 +6,20 @@ use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
 use collections::{HashMap, HashSet};
 use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
-use gpui2::{
+use gpui::{
     AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model, ModelContext,
     Task, WeakModel,
 };
-use language2::{
+use language::{
     language_settings::{all_language_settings, language_settings},
     point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language,
     LanguageServerName, PointUtf16, ToPointUtf16,
 };
-use lsp2::{LanguageServer, LanguageServerBinary, LanguageServerId};
+use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId};
 use node_runtime::NodeRuntime;
 use parking_lot::Mutex;
 use request::StatusNotification;
-use settings2::SettingsStore;
+use settings::SettingsStore;
 use smol::{fs, io::BufReader, stream::StreamExt};
 use std::{
     ffi::OsString,
@@ -172,11 +172,11 @@ impl Status {
 }
 
 struct RegisteredBuffer {
-    uri: lsp2::Url,
+    uri: lsp::Url,
     language_id: String,
     snapshot: BufferSnapshot,
     snapshot_version: i32,
-    _subscriptions: [gpui2::Subscription; 2],
+    _subscriptions: [gpui::Subscription; 2],
     pending_buffer_change: Task<Option<()>>,
 }
 
@@ -220,8 +220,8 @@ impl RegisteredBuffer {
                                     let new_text = new_snapshot
                                         .text_for_range(edit.new.start.1..edit.new.end.1)
                                         .collect();
-                                    lsp2::TextDocumentContentChangeEvent {
-                                        range: Some(lsp2::Range::new(
+                                    lsp::TextDocumentContentChangeEvent {
+                                        range: Some(lsp::Range::new(
                                             point_to_lsp(edit_start),
                                             point_to_lsp(edit_end),
                                         )),
@@ -243,9 +243,9 @@ impl RegisteredBuffer {
                             buffer.snapshot = new_snapshot;
                             server
                                 .lsp
-                                .notify::<lsp2::notification::DidChangeTextDocument>(
-                                    lsp2::DidChangeTextDocumentParams {
-                                        text_document: lsp2::VersionedTextDocumentIdentifier::new(
+                                .notify::<lsp::notification::DidChangeTextDocument>(
+                                    lsp::DidChangeTextDocumentParams {
+                                        text_document: lsp::VersionedTextDocumentIdentifier::new(
                                             buffer.uri.clone(),
                                             buffer.snapshot_version,
                                         ),
@@ -280,7 +280,7 @@ pub struct Copilot {
     server: CopilotServer,
     buffers: HashSet<WeakModel<Buffer>>,
     server_id: LanguageServerId,
-    _subscription: gpui2::Subscription,
+    _subscription: gpui::Subscription,
 }
 
 pub enum Event {
@@ -608,13 +608,13 @@ impl Copilot {
             registered_buffers
                 .entry(buffer.entity_id())
                 .or_insert_with(|| {
-                    let uri: lsp2::Url = uri_for_buffer(buffer, cx);
+                    let uri: lsp::Url = uri_for_buffer(buffer, cx);
                     let language_id = id_for_language(buffer.read(cx).language());
                     let snapshot = buffer.read(cx).snapshot();
                     server
-                        .notify::<lsp2::notification::DidOpenTextDocument>(
-                            lsp2::DidOpenTextDocumentParams {
-                                text_document: lsp2::TextDocumentItem {
+                        .notify::<lsp::notification::DidOpenTextDocument>(
+                            lsp::DidOpenTextDocumentParams {
+                                text_document: lsp::TextDocumentItem {
                                     uri: uri.clone(),
                                     language_id: language_id.clone(),
                                     version: 0,
@@ -647,29 +647,29 @@ impl Copilot {
     fn handle_buffer_event(
         &mut self,
         buffer: Model<Buffer>,
-        event: &language2::Event,
+        event: &language::Event,
         cx: &mut ModelContext<Self>,
     ) -> Result<()> {
         if let Ok(server) = self.server.as_running() {
             if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.entity_id())
             {
                 match event {
-                    language2::Event::Edited => {
+                    language::Event::Edited => {
                         let _ = registered_buffer.report_changes(&buffer, cx);
                     }
-                    language2::Event::Saved => {
+                    language::Event::Saved => {
                         server
                             .lsp
-                            .notify::<lsp2::notification::DidSaveTextDocument>(
-                                lsp2::DidSaveTextDocumentParams {
-                                    text_document: lsp2::TextDocumentIdentifier::new(
+                            .notify::<lsp::notification::DidSaveTextDocument>(
+                                lsp::DidSaveTextDocumentParams {
+                                    text_document: lsp::TextDocumentIdentifier::new(
                                         registered_buffer.uri.clone(),
                                     ),
                                     text: None,
                                 },
                             )?;
                     }
-                    language2::Event::FileHandleChanged | language2::Event::LanguageChanged => {
+                    language::Event::FileHandleChanged | language::Event::LanguageChanged => {
                         let new_language_id = id_for_language(buffer.read(cx).language());
                         let new_uri = uri_for_buffer(&buffer, cx);
                         if new_uri != registered_buffer.uri
@@ -679,16 +679,16 @@ impl Copilot {
                             registered_buffer.language_id = new_language_id;
                             server
                                 .lsp
-                                .notify::<lsp2::notification::DidCloseTextDocument>(
-                                    lsp2::DidCloseTextDocumentParams {
-                                        text_document: lsp2::TextDocumentIdentifier::new(old_uri),
+                                .notify::<lsp::notification::DidCloseTextDocument>(
+                                    lsp::DidCloseTextDocumentParams {
+                                        text_document: lsp::TextDocumentIdentifier::new(old_uri),
                                     },
                                 )?;
                             server
                                 .lsp
-                                .notify::<lsp2::notification::DidOpenTextDocument>(
-                                    lsp2::DidOpenTextDocumentParams {
-                                        text_document: lsp2::TextDocumentItem::new(
+                                .notify::<lsp::notification::DidOpenTextDocument>(
+                                    lsp::DidOpenTextDocumentParams {
+                                        text_document: lsp::TextDocumentItem::new(
                                             registered_buffer.uri.clone(),
                                             registered_buffer.language_id.clone(),
                                             registered_buffer.snapshot_version,
@@ -711,9 +711,9 @@ impl Copilot {
             if let Some(buffer) = server.registered_buffers.remove(&buffer.entity_id()) {
                 server
                     .lsp
-                    .notify::<lsp2::notification::DidCloseTextDocument>(
-                        lsp2::DidCloseTextDocumentParams {
-                            text_document: lsp2::TextDocumentIdentifier::new(buffer.uri),
+                    .notify::<lsp::notification::DidCloseTextDocument>(
+                        lsp::DidCloseTextDocumentParams {
+                            text_document: lsp::TextDocumentIdentifier::new(buffer.uri),
                         },
                     )
                     .log_err();
@@ -798,7 +798,7 @@ impl Copilot {
     ) -> Task<Result<Vec<Completion>>>
     where
         R: 'static
-            + lsp2::request::Request<
+            + lsp::request::Request<
                 Params = request::GetCompletionsParams,
                 Result = request::GetCompletionsResult,
             >,
@@ -926,9 +926,9 @@ fn id_for_language(language: Option<&Arc<Language>>) -> String {
     }
 }
 
-fn uri_for_buffer(buffer: &Model<Buffer>, cx: &AppContext) -> lsp2::Url {
+fn uri_for_buffer(buffer: &Model<Buffer>, cx: &AppContext) -> lsp::Url {
     if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
-        lsp2::Url::from_file_path(file.abs_path(cx)).unwrap()
+        lsp::Url::from_file_path(file.abs_path(cx)).unwrap()
     } else {
         format!("buffer://{}", buffer.entity_id()).parse().unwrap()
     }

crates/copilot2/src/request.rs 🔗

@@ -8,7 +8,7 @@ pub struct CheckStatusParams {
     pub local_checks_only: bool,
 }
 
-impl lsp2::request::Request for CheckStatus {
+impl lsp::request::Request for CheckStatus {
     type Params = CheckStatusParams;
     type Result = SignInStatus;
     const METHOD: &'static str = "checkStatus";
@@ -33,7 +33,7 @@ pub struct PromptUserDeviceFlow {
     pub verification_uri: String,
 }
 
-impl lsp2::request::Request for SignInInitiate {
+impl lsp::request::Request for SignInInitiate {
     type Params = SignInInitiateParams;
     type Result = SignInInitiateResult;
     const METHOD: &'static str = "signInInitiate";
@@ -66,7 +66,7 @@ pub enum SignInStatus {
     NotSignedIn,
 }
 
-impl lsp2::request::Request for SignInConfirm {
+impl lsp::request::Request for SignInConfirm {
     type Params = SignInConfirmParams;
     type Result = SignInStatus;
     const METHOD: &'static str = "signInConfirm";
@@ -82,7 +82,7 @@ pub struct SignOutParams {}
 #[serde(rename_all = "camelCase")]
 pub struct SignOutResult {}
 
-impl lsp2::request::Request for SignOut {
+impl lsp::request::Request for SignOut {
     type Params = SignOutParams;
     type Result = SignOutResult;
     const METHOD: &'static str = "signOut";
@@ -102,9 +102,9 @@ pub struct GetCompletionsDocument {
     pub tab_size: u32,
     pub indent_size: u32,
     pub insert_spaces: bool,
-    pub uri: lsp2::Url,
+    pub uri: lsp::Url,
     pub relative_path: String,
-    pub position: lsp2::Position,
+    pub position: lsp::Position,
     pub version: usize,
 }
 
@@ -118,13 +118,13 @@ pub struct GetCompletionsResult {
 #[serde(rename_all = "camelCase")]
 pub struct Completion {
     pub text: String,
-    pub position: lsp2::Position,
+    pub position: lsp::Position,
     pub uuid: String,
-    pub range: lsp2::Range,
+    pub range: lsp::Range,
     pub display_text: String,
 }
 
-impl lsp2::request::Request for GetCompletions {
+impl lsp::request::Request for GetCompletions {
     type Params = GetCompletionsParams;
     type Result = GetCompletionsResult;
     const METHOD: &'static str = "getCompletions";
@@ -132,7 +132,7 @@ impl lsp2::request::Request for GetCompletions {
 
 pub enum GetCompletionsCycling {}
 
-impl lsp2::request::Request for GetCompletionsCycling {
+impl lsp::request::Request for GetCompletionsCycling {
     type Params = GetCompletionsParams;
     type Result = GetCompletionsResult;
     const METHOD: &'static str = "getCompletionsCycling";
@@ -149,7 +149,7 @@ pub struct LogMessageParams {
     pub extra: Vec<String>,
 }
 
-impl lsp2::notification::Notification for LogMessage {
+impl lsp::notification::Notification for LogMessage {
     type Params = LogMessageParams;
     const METHOD: &'static str = "LogMessage";
 }
@@ -162,7 +162,7 @@ pub struct StatusNotificationParams {
     pub status: String, // One of Normal/InProgress
 }
 
-impl lsp2::notification::Notification for StatusNotification {
+impl lsp::notification::Notification for StatusNotification {
     type Params = StatusNotificationParams;
     const METHOD: &'static str = "statusNotification";
 }
@@ -176,7 +176,7 @@ pub struct SetEditorInfoParams {
     pub editor_plugin_info: EditorPluginInfo,
 }
 
-impl lsp2::request::Request for SetEditorInfo {
+impl lsp::request::Request for SetEditorInfo {
     type Params = SetEditorInfoParams;
     type Result = String;
     const METHOD: &'static str = "setEditorInfo";
@@ -204,7 +204,7 @@ pub struct NotifyAcceptedParams {
     pub uuid: String,
 }
 
-impl lsp2::request::Request for NotifyAccepted {
+impl lsp::request::Request for NotifyAccepted {
     type Params = NotifyAcceptedParams;
     type Result = String;
     const METHOD: &'static str = "notifyAccepted";
@@ -218,7 +218,7 @@ pub struct NotifyRejectedParams {
     pub uuids: Vec<String>,
 }
 
-impl lsp2::request::Request for NotifyRejected {
+impl lsp::request::Request for NotifyRejected {
     type Params = NotifyRejectedParams;
     type Result = String;
     const METHOD: &'static str = "notifyRejected";

crates/db2/Cargo.toml 🔗

@@ -13,7 +13,7 @@ test-support = []
 
 [dependencies]
 collections = { path = "../collections" }
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 sqlez = { path = "../sqlez" }
 sqlez_macros = { path = "../sqlez_macros" }
 util = { path = "../util" }
@@ -28,6 +28,6 @@ serde_derive.workspace = true
 smol.workspace = true
 
 [dev-dependencies]
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 env_logger.workspace = true
 tempdir.workspace = true

crates/db2/src/db2.rs 🔗

@@ -4,7 +4,7 @@ pub mod query;
 // Re-export
 pub use anyhow;
 use anyhow::Context;
-use gpui2::AppContext;
+use gpui::AppContext;
 pub use indoc::indoc;
 pub use lazy_static;
 pub use smol;
@@ -201,7 +201,7 @@ mod tests {
     use crate::open_db;
 
     // Test bad migration panics
-    #[gpui2::test]
+    #[gpui::test]
     #[should_panic]
     async fn test_bad_migration_panics() {
         enum BadDB {}
@@ -225,8 +225,8 @@ mod tests {
     }
 
     /// Test that DB exists but corrupted (causing recreate)
-    #[gpui2::test]
-    async fn test_db_corruption(cx: &mut gpui2::TestAppContext) {
+    #[gpui::test]
+    async fn test_db_corruption(cx: &mut gpui::TestAppContext) {
         cx.executor().allow_parking();
 
         enum CorruptedDB {}
@@ -269,8 +269,8 @@ mod tests {
     }
 
     /// Test that DB exists but corrupted (causing recreate)
-    #[gpui2::test(iterations = 30)]
-    async fn test_simultaneous_db_corruption(cx: &mut gpui2::TestAppContext) {
+    #[gpui::test(iterations = 30)]
+    async fn test_simultaneous_db_corruption(cx: &mut gpui::TestAppContext) {
         cx.executor().allow_parking();
 
         enum CorruptedDB {}

crates/db2/src/kvp.rs 🔗

@@ -35,7 +35,7 @@ impl KeyValueStore {
 mod tests {
     use crate::kvp::KeyValueStore;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_kvp() {
         let db = KeyValueStore(crate::open_test_db("test_kvp").await);
 

crates/feature_flags2/Cargo.toml 🔗

@@ -8,5 +8,5 @@ publish = false
 path = "src/feature_flags2.rs"
 
 [dependencies]
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 anyhow.workspace = true

crates/fs2/Cargo.toml 🔗

@@ -31,10 +31,10 @@ log.workspace = true
 libc = "0.2"
 time.workspace = true
 
-gpui2 = { path = "../gpui2", optional = true}
+gpui = { package = "gpui2", path = "../gpui2", optional = true}
 
 [dev-dependencies]
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 
 [features]
-test-support = ["gpui2/test-support"]
+test-support = ["gpui/test-support"]

crates/fs2/src/fs2.rs 🔗

@@ -288,7 +288,7 @@ impl Fs for RealFs {
 pub struct FakeFs {
     // Use an unfair lock to ensure tests are deterministic.
     state: Mutex<FakeFsState>,
-    executor: gpui2::BackgroundExecutor,
+    executor: gpui::BackgroundExecutor,
 }
 
 #[cfg(any(test, feature = "test-support"))]
@@ -434,7 +434,7 @@ lazy_static::lazy_static! {
 
 #[cfg(any(test, feature = "test-support"))]
 impl FakeFs {
-    pub fn new(executor: gpui2::BackgroundExecutor) -> Arc<Self> {
+    pub fn new(executor: gpui::BackgroundExecutor) -> Arc<Self> {
         Arc::new(Self {
             executor,
             state: Mutex::new(FakeFsState {
@@ -1222,10 +1222,10 @@ pub fn copy_recursive<'a>(
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui2::BackgroundExecutor;
+    use gpui::BackgroundExecutor;
     use serde_json::json;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_fake_fs(executor: BackgroundExecutor) {
         let fs = FakeFs::new(executor.clone());
         fs.insert_tree(

crates/fuzzy2/Cargo.toml 🔗

@@ -9,5 +9,5 @@ path = "src/fuzzy2.rs"
 doctest = false
 
 [dependencies]
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 util = { path = "../util" }

crates/fuzzy2/src/paths.rs 🔗

@@ -1,4 +1,4 @@
-use gpui2::BackgroundExecutor;
+use gpui::BackgroundExecutor;
 use std::{
     borrow::Cow,
     cmp::{self, Ordering},

crates/fuzzy2/src/strings.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     matcher::{Match, MatchCandidate, Matcher},
     CharBag,
 };
-use gpui2::BackgroundExecutor;
+use gpui::BackgroundExecutor;
 use std::{
     borrow::Cow,
     cmp::{self, Ordering},

crates/gpui2/src/app.rs 🔗

@@ -5,6 +5,7 @@ mod model_context;
 mod test_context;
 
 pub use async_context::*;
+use derive_more::{Deref, DerefMut};
 pub use entity_map::*;
 pub use model_context::*;
 use refineable::Refineable;
@@ -27,7 +28,7 @@ use parking_lot::Mutex;
 use slotmap::SlotMap;
 use std::{
     any::{type_name, Any, TypeId},
-    cell::RefCell,
+    cell::{Ref, RefCell, RefMut},
     marker::PhantomData,
     mem,
     ops::{Deref, DerefMut},
@@ -38,7 +39,31 @@ use std::{
 };
 use util::http::{self, HttpClient};
 
-pub struct App(Rc<RefCell<AppContext>>);
+/// Temporary(?) wrapper around RefCell<AppContext> to help us debug any double borrows.
+/// Strongly consider removing after stabilization.
+pub struct AppCell {
+    app: RefCell<AppContext>,
+}
+
+impl AppCell {
+    pub fn borrow(&self) -> AppRef {
+        AppRef(self.app.borrow())
+    }
+
+    pub fn borrow_mut(&self) -> AppRefMut {
+        // let thread_id = std::thread::current().id();
+        // dbg!("borrowed {thread_id:?}");
+        AppRefMut(self.app.borrow_mut())
+    }
+}
+
+#[derive(Deref, DerefMut)]
+pub struct AppRef<'a>(Ref<'a, AppContext>);
+
+#[derive(Deref, DerefMut)]
+pub struct AppRefMut<'a>(RefMut<'a, AppContext>);
+
+pub struct App(Rc<AppCell>);
 
 /// Represents an application before it is fully launched. Once your app is
 /// configured, you'll start the app with `App::run`.
@@ -112,14 +137,20 @@ impl App {
 }
 
 type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
-type FrameCallback = Box<dyn FnOnce(&mut WindowContext)>;
+pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
 type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
 type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
 type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
 type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
 
+// struct FrameConsumer {
+//     next_frame_callbacks: Vec<FrameCallback>,
+//     task: Task<()>,
+//     display_linker
+// }
+
 pub struct AppContext {
-    this: Weak<RefCell<AppContext>>,
+    this: Weak<AppCell>,
     pub(crate) platform: Rc<dyn Platform>,
     app_metadata: AppMetadata,
     text_system: Arc<TextSystem>,
@@ -127,6 +158,7 @@ pub struct AppContext {
     pending_updates: usize,
     pub(crate) active_drag: Option<AnyDrag>,
     pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
+    pub(crate) frame_consumers: HashMap<DisplayId, Task<()>>,
     pub(crate) background_executor: BackgroundExecutor,
     pub(crate) foreground_executor: ForegroundExecutor,
     pub(crate) svg_renderer: SvgRenderer,
@@ -157,7 +189,7 @@ impl AppContext {
         platform: Rc<dyn Platform>,
         asset_source: Arc<dyn AssetSource>,
         http_client: Arc<dyn HttpClient>,
-    ) -> Rc<RefCell<Self>> {
+    ) -> Rc<AppCell> {
         let executor = platform.background_executor();
         let foreground_executor = platform.foreground_executor();
         assert!(
@@ -174,15 +206,17 @@ impl AppContext {
             app_version: platform.app_version().ok(),
         };
 
-        Rc::new_cyclic(|this| {
-            RefCell::new(AppContext {
+        Rc::new_cyclic(|this| AppCell {
+            app: RefCell::new(AppContext {
                 this: this.clone(),
-                text_system,
                 platform,
                 app_metadata,
+                text_system,
                 flushing_effects: false,
                 pending_updates: 0,
-                next_frame_callbacks: Default::default(),
+                active_drag: None,
+                next_frame_callbacks: HashMap::default(),
+                frame_consumers: HashMap::default(),
                 background_executor: executor,
                 foreground_executor,
                 svg_renderer: SvgRenderer::new(asset_source.clone()),
@@ -205,8 +239,7 @@ impl AppContext {
                 quit_observers: SubscriberSet::new(),
                 layout_id_buffer: Default::default(),
                 propagate_event: true,
-                active_drag: None,
-            })
+            }),
         })
     }
 
@@ -789,10 +822,13 @@ impl Context for AppContext {
 
             let root_view = window.root_view.clone().unwrap();
             let result = update(root_view, &mut WindowContext::new(cx, &mut window));
-            cx.windows
-                .get_mut(handle.id)
-                .ok_or_else(|| anyhow!("window not found"))?
-                .replace(window);
+
+            if !window.removed {
+                cx.windows
+                    .get_mut(handle.id)
+                    .ok_or_else(|| anyhow!("window not found"))?
+                    .replace(window);
+            }
 
             Ok(result)
         })

crates/gpui2/src/app/async_context.rs 🔗

@@ -1,15 +1,15 @@
 use crate::{
-    AnyView, AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model,
-    ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext,
+    AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, ForegroundExecutor,
+    Model, ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext,
     WindowHandle,
 };
 use anyhow::{anyhow, Context as _};
 use derive_more::{Deref, DerefMut};
-use std::{cell::RefCell, future::Future, rc::Weak};
+use std::{future::Future, rc::Weak};
 
 #[derive(Clone)]
 pub struct AsyncAppContext {
-    pub(crate) app: Weak<RefCell<AppContext>>,
+    pub(crate) app: Weak<AppCell>,
     pub(crate) background_executor: BackgroundExecutor,
     pub(crate) foreground_executor: ForegroundExecutor,
 }
@@ -121,7 +121,7 @@ impl AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let app = app.borrow_mut(); // Need this to compile
+        let app = app.borrow_mut();
         Ok(read(app.global(), &app))
     }
 

crates/gpui2/src/app/test_context.rs 🔗

@@ -1,15 +1,15 @@
 use crate::{
-    AnyView, AnyWindowHandle, AppContext, AsyncAppContext, BackgroundExecutor, Context,
+    AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, BackgroundExecutor, Context,
     EventEmitter, ForegroundExecutor, Model, ModelContext, Result, Task, TestDispatcher,
     TestPlatform, WindowContext,
 };
 use anyhow::{anyhow, bail};
 use futures::{Stream, StreamExt};
-use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc, time::Duration};
+use std::{future::Future, rc::Rc, sync::Arc, time::Duration};
 
 #[derive(Clone)]
 pub struct TestAppContext {
-    pub app: Rc<RefCell<AppContext>>,
+    pub app: Rc<AppCell>,
     pub background_executor: BackgroundExecutor,
     pub foreground_executor: ForegroundExecutor,
 }

crates/gpui2/src/elements/img.rs 🔗

@@ -109,7 +109,9 @@ where
         let corner_radii = style.corner_radii;
 
         if let Some(uri) = self.uri.clone() {
-            let image_future = cx.image_cache.get(uri);
+            // eprintln!(">>> image_cache.get({uri}");
+            let image_future = cx.image_cache.get(uri.clone());
+            // eprintln!("<<< image_cache.get({uri}");
             if let Some(data) = image_future
                 .clone()
                 .now_or_never()

crates/gpui2/src/platform.rs 🔗

@@ -69,7 +69,7 @@ pub(crate) trait Platform: 'static {
     fn set_display_link_output_callback(
         &self,
         display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>,
+        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
     );
     fn start_display_link(&self, display_id: DisplayId);
     fn stop_display_link(&self, display_id: DisplayId);

crates/gpui2/src/platform/mac/display_linker.rs 🔗

@@ -26,13 +26,13 @@ impl MacDisplayLinker {
     }
 }
 
-type OutputCallback = Mutex<Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>>;
+type OutputCallback = Mutex<Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>>;
 
 impl MacDisplayLinker {
     pub fn set_output_callback(
         &mut self,
         display_id: DisplayId,
-        output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>,
+        output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
     ) {
         if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } {
             let callback = Arc::new(Mutex::new(output_callback));

crates/gpui2/src/platform/mac/platform.rs 🔗

@@ -494,7 +494,7 @@ impl Platform for MacPlatform {
     fn set_display_link_output_callback(
         &self,
         display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>,
+        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
     ) {
         self.0
             .lock()

crates/gpui2/src/platform/mac/shaders.metal 🔗

@@ -98,10 +98,10 @@ fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
     input.border_color.a *= 1. - saturate(0.5 - inset_distance);
 
     // Alpha-blend the border and the background.
-    float output_alpha =
-        quad.border_color.a + quad.background.a * (1. - quad.border_color.a);
+    float output_alpha = input.border_color.a +
+                         input.background_color.a * (1. - input.border_color.a);
     float3 premultiplied_border_rgb =
-        input.border_color.rgb * quad.border_color.a;
+        input.border_color.rgb * input.border_color.a;
     float3 premultiplied_background_rgb =
         input.background_color.rgb * input.background_color.a;
     float3 premultiplied_output_rgb =

crates/gpui2/src/platform/mac/window.rs 🔗

@@ -678,10 +678,15 @@ impl MacWindow {
 
 impl Drop for MacWindow {
     fn drop(&mut self) {
-        let native_window = self.0.lock().native_window;
-        unsafe {
-            native_window.close();
-        }
+        let this = self.0.lock();
+        let window = this.native_window;
+        this.executor
+            .spawn(async move {
+                unsafe {
+                    window.close();
+                }
+            })
+            .detach();
     }
 }
 
@@ -868,17 +873,25 @@ impl PlatformWindow for MacWindow {
     fn zoom(&self) {
         let this = self.0.lock();
         let window = this.native_window;
-        unsafe {
-            window.zoom_(nil);
-        }
+        this.executor
+            .spawn(async move {
+                unsafe {
+                    window.zoom_(nil);
+                }
+            })
+            .detach();
     }
 
     fn toggle_full_screen(&self) {
         let this = self.0.lock();
         let window = this.native_window;
-        unsafe {
-            window.toggleFullScreen_(nil);
-        }
+        this.executor
+            .spawn(async move {
+                unsafe {
+                    window.toggleFullScreen_(nil);
+                }
+            })
+            .detach();
     }
 
     fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {

crates/gpui2/src/platform/test/platform.rs 🔗

@@ -81,7 +81,7 @@ impl Platform for TestPlatform {
     fn set_display_link_output_callback(
         &self,
         _display_id: DisplayId,
-        _callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp)>,
+        _callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
     ) {
         unimplemented!()
     }

crates/gpui2/src/window.rs 🔗

@@ -4,16 +4,19 @@ use crate::{
     Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId,
     Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
     Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent,
-    MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite,
-    PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
-    SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription,
-    TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
-    WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
+    MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformWindow, Point,
+    PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
+    RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet,
+    Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
+    WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::{anyhow, Result};
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
-use futures::channel::oneshot;
+use futures::{
+    channel::{mpsc, oneshot},
+    StreamExt,
+};
 use parking_lot::RwLock;
 use slotmap::SlotMap;
 use smallvec::SmallVec;
@@ -25,6 +28,7 @@ use std::{
     hash::{Hash, Hasher},
     marker::PhantomData,
     mem,
+    rc::Rc,
     sync::{
         atomic::{AtomicUsize, Ordering::SeqCst},
         Arc,
@@ -161,6 +165,7 @@ impl Drop for FocusHandle {
 // Holds the state for a specific window.
 pub struct Window {
     pub(crate) handle: AnyWindowHandle,
+    pub(crate) removed: bool,
     platform_window: Box<dyn PlatformWindow>,
     display_id: DisplayId,
     sprite_atlas: Arc<dyn PlatformAtlas>,
@@ -253,6 +258,7 @@ impl Window {
 
         Window {
             handle,
+            removed: false,
             platform_window,
             display_id,
             sprite_atlas,
@@ -348,6 +354,11 @@ impl<'a> WindowContext<'a> {
         self.window.dirty = true;
     }
 
+    /// Close this window.
+    pub fn remove_window(&mut self) {
+        self.window.removed = true;
+    }
+
     /// Obtain a new `FocusHandle`, which allows you to track and manipulate the keyboard focus
     /// for elements rendered within this window.
     pub fn focus_handle(&mut self) -> FocusHandle {
@@ -435,42 +446,55 @@ impl<'a> WindowContext<'a> {
     }
 
     /// Schedule the given closure to be run directly after the current frame is rendered.
-    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
-        let f = Box::new(f);
+    pub fn on_next_frame(&mut self, callback: impl FnOnce(&mut WindowContext) + 'static) {
+        let handle = self.window.handle;
         let display_id = self.window.display_id;
 
-        if let Some(callbacks) = self.next_frame_callbacks.get_mut(&display_id) {
-            callbacks.push(f);
-            // If there was already a callback, it means that we already scheduled a frame.
-            if callbacks.len() > 1 {
-                return;
-            }
-        } else {
-            let mut async_cx = self.to_async();
-            self.next_frame_callbacks.insert(display_id, vec![f]);
+        if !self.frame_consumers.contains_key(&display_id) {
+            let (tx, mut rx) = mpsc::unbounded::<()>();
             self.platform.set_display_link_output_callback(
                 display_id,
-                Box::new(move |_current_time, _output_time| {
-                    let _ = async_cx.update(|_, cx| {
-                        let callbacks = cx
+                Box::new(move |_current_time, _output_time| _ = tx.unbounded_send(())),
+            );
+
+            let consumer_task = self.app.spawn(|cx| async move {
+                while rx.next().await.is_some() {
+                    cx.update(|cx| {
+                        for callback in cx
                             .next_frame_callbacks
                             .get_mut(&display_id)
                             .unwrap()
                             .drain(..)
-                            .collect::<Vec<_>>();
-                        for callback in callbacks {
+                            .collect::<SmallVec<[_; 32]>>()
+                        {
                             callback(cx);
                         }
+                    })
+                    .ok();
+
+                    // Flush effects, then stop the display link if no new next_frame_callbacks have been added.
 
-                        if cx.next_frame_callbacks.get(&display_id).unwrap().is_empty() {
+                    cx.update(|cx| {
+                        if cx.next_frame_callbacks.is_empty() {
                             cx.platform.stop_display_link(display_id);
                         }
-                    });
-                }),
-            );
+                    })
+                    .ok();
+                }
+            });
+            self.frame_consumers.insert(display_id, consumer_task);
         }
 
-        self.platform.start_display_link(display_id);
+        if self.next_frame_callbacks.is_empty() {
+            self.platform.start_display_link(display_id);
+        }
+
+        self.next_frame_callbacks
+            .entry(display_id)
+            .or_default()
+            .push(Box::new(move |cx: &mut AppContext| {
+                cx.update_window(handle, |_root_view, cx| callback(cx)).ok();
+            }));
     }
 
     /// Spawn the future returned by the given closure on the application thread pool.
@@ -563,6 +587,21 @@ impl<'a> WindowContext<'a> {
         self.window.bounds
     }
 
+    pub fn is_window_active(&self) -> bool {
+        self.window.active
+    }
+
+    pub fn zoom_window(&self) {
+        self.window.platform_window.zoom();
+    }
+
+    pub fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
+        self.platform
+            .displays()
+            .into_iter()
+            .find(|display| display.id() == self.window.display_id)
+    }
+
     /// The scale factor of the display associated with the window. For example, it could
     /// return 2.0 for a "retina" display, indicating that each logical pixel should actually
     /// be rendered as two pixels on screen.

crates/gpui2_macros/src/test.rs 🔗

@@ -90,7 +90,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                             continue;
                         }
                         Some("BackgroundExecutor") => {
-                            inner_fn_args.extend(quote!(gpui2::BackgroundExecutor::new(
+                            inner_fn_args.extend(quote!(gpui::BackgroundExecutor::new(
                                 std::sync::Arc::new(dispatcher.clone()),
                             ),));
                             continue;
@@ -105,7 +105,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                         {
                             let cx_varname = format_ident!("cx_{}", ix);
                             cx_vars.extend(quote!(
-                                let mut #cx_varname = gpui2::TestAppContext::new(
+                                let mut #cx_varname = gpui::TestAppContext::new(
                                     dispatcher.clone()
                                 );
                             ));
@@ -130,11 +130,11 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
             fn #outer_fn_name() {
                 #inner_fn
 
-                gpui2::run_test(
+                gpui::run_test(
                     #num_iterations as u64,
                     #max_retries,
                     &mut |dispatcher, _seed| {
-                        let executor = gpui2::BackgroundExecutor::new(std::sync::Arc::new(dispatcher.clone()));
+                        let executor = gpui::BackgroundExecutor::new(std::sync::Arc::new(dispatcher.clone()));
                         #cx_vars
                         executor.block_test(#inner_fn_name(#inner_fn_args));
                         #cx_teardowns
@@ -167,7 +167,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                                 let cx_varname = format_ident!("cx_{}", ix);
                                 let cx_varname_lock = format_ident!("cx_{}_lock", ix);
                                 cx_vars.extend(quote!(
-                                    let mut #cx_varname = gpui2::TestAppContext::new(
+                                    let mut #cx_varname = gpui::TestAppContext::new(
                                        dispatcher.clone()
                                     );
                                     let mut #cx_varname_lock = #cx_varname.app.borrow_mut();
@@ -182,7 +182,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                             Some("TestAppContext") => {
                                 let cx_varname = format_ident!("cx_{}", ix);
                                 cx_vars.extend(quote!(
-                                    let mut #cx_varname = gpui2::TestAppContext::new(
+                                    let mut #cx_varname = gpui::TestAppContext::new(
                                         dispatcher.clone()
                                     );
                                 ));
@@ -209,7 +209,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
             fn #outer_fn_name() {
                 #inner_fn
 
-                gpui2::run_test(
+                gpui::run_test(
                     #num_iterations as u64,
                     #max_retries,
                     &mut |dispatcher, _seed| {

crates/install_cli2/Cargo.toml 🔗

@@ -14,5 +14,5 @@ test-support = []
 smol.workspace = true
 anyhow.workspace = true
 log.workspace = true
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 util = { path = "../util" }

crates/journal2/Cargo.toml 🔗

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 editor = { path = "../editor" }
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 util = { path = "../util" }
 workspace = { path = "../workspace" }
 settings2 = { path = "../settings2" }

crates/journal2/src/journal2.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::Result;
 use chrono::{Datelike, Local, NaiveTime, Timelike};
-use gpui2::AppContext;
+use gpui::AppContext;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings2::Settings;

crates/language2/Cargo.toml 🔗

@@ -11,28 +11,28 @@ doctest = false
 [features]
 test-support = [
     "rand",
-    "client2/test-support",
+    "client/test-support",
     "collections/test-support",
-    "lsp2/test-support",
+    "lsp/test-support",
     "text/test-support",
     "tree-sitter-rust",
     "tree-sitter-typescript",
-    "settings2/test-support",
+    "settings/test-support",
     "util/test-support",
 ]
 
 [dependencies]
 clock = { path = "../clock" }
 collections = { path = "../collections" }
-fuzzy2 = { path = "../fuzzy2" }
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 git = { path = "../git" }
-gpui2 = { path = "../gpui2" }
-lsp2 = { path = "../lsp2" }
-rpc2 = { path = "../rpc2" }
-settings2 = { path = "../settings2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+lsp = { package = "lsp2", path = "../lsp2" }
+rpc = { package = "rpc2", path = "../rpc2" }
+settings = { package = "settings2", path = "../settings2" }
 sum_tree = { path = "../sum_tree" }
 text = { path = "../text" }
-theme2 = { path = "../theme2" }
+theme = { package = "theme2", path = "../theme2" }
 util = { path = "../util" }
 
 anyhow.workspace = true
@@ -60,12 +60,12 @@ tree-sitter-rust = { workspace = true, optional = true }
 tree-sitter-typescript = { workspace = true, optional = true }
 
 [dev-dependencies]
-client2 = { path = "../client2", features = ["test-support"] }
+client = { package = "client2", path = "../client2", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-lsp2 = { path = "../lsp2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
 text = { path = "../text", features = ["test-support"] }
-settings2 = { path = "../settings2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 ctor.workspace = true
 env_logger.workspace = true

crates/language2/src/buffer.rs 🔗

@@ -16,8 +16,8 @@ use crate::{
 use anyhow::{anyhow, Result};
 pub use clock::ReplicaId;
 use futures::FutureExt as _;
-use gpui2::{AppContext, EventEmitter, HighlightStyle, ModelContext, Task};
-use lsp2::LanguageServerId;
+use gpui::{AppContext, EventEmitter, HighlightStyle, ModelContext, Task};
+use lsp::LanguageServerId;
 use parking_lot::Mutex;
 use similar::{ChangeTag, TextDiff};
 use smallvec::SmallVec;
@@ -40,7 +40,7 @@ use std::{
 use sum_tree::TreeMap;
 use text::operation_queue::OperationQueue;
 pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, *};
-use theme2::SyntaxTheme;
+use theme::SyntaxTheme;
 #[cfg(any(test, feature = "test-support"))]
 use util::RandomCharIter;
 use util::{RangeExt, TryFutureExt as _};
@@ -48,7 +48,7 @@ use util::{RangeExt, TryFutureExt as _};
 #[cfg(any(test, feature = "test-support"))]
 pub use {tree_sitter_rust, tree_sitter_typescript};
 
-pub use lsp2::DiagnosticSeverity;
+pub use lsp::DiagnosticSeverity;
 
 pub struct Buffer {
     text: TextBuffer,
@@ -149,14 +149,14 @@ pub struct Completion {
     pub new_text: String,
     pub label: CodeLabel,
     pub server_id: LanguageServerId,
-    pub lsp_completion: lsp2::CompletionItem,
+    pub lsp_completion: lsp::CompletionItem,
 }
 
 #[derive(Clone, Debug)]
 pub struct CodeAction {
     pub server_id: LanguageServerId,
     pub range: Range<Anchor>,
-    pub lsp_action: lsp2::CodeAction,
+    pub lsp_action: lsp::CodeAction,
 }
 
 #[derive(Clone, Debug, PartialEq)]
@@ -226,7 +226,7 @@ pub trait File: Send + Sync {
 
     fn as_any(&self) -> &dyn Any;
 
-    fn to_proto(&self) -> rpc2::proto::File;
+    fn to_proto(&self) -> rpc::proto::File;
 }
 
 pub trait LocalFile: File {
@@ -375,7 +375,7 @@ impl Buffer {
             file,
         );
         this.text.set_line_ending(proto::deserialize_line_ending(
-            rpc2::proto::LineEnding::from_i32(message.line_ending)
+            rpc::proto::LineEnding::from_i32(message.line_ending)
                 .ok_or_else(|| anyhow!("missing line_ending"))?,
         ));
         this.saved_version = proto::deserialize_version(&message.saved_version);
@@ -3003,14 +3003,14 @@ impl IndentSize {
 impl Completion {
     pub fn sort_key(&self) -> (usize, &str) {
         let kind_key = match self.lsp_completion.kind {
-            Some(lsp2::CompletionItemKind::VARIABLE) => 0,
+            Some(lsp::CompletionItemKind::VARIABLE) => 0,
             _ => 1,
         };
         (kind_key, &self.label.text[self.label.filter_range.clone()])
     }
 
     pub fn is_snippet(&self) -> bool {
-        self.lsp_completion.insert_text_format == Some(lsp2::InsertTextFormat::SNIPPET)
+        self.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
     }
 }
 

crates/language2/src/buffer_tests.rs 🔗

@@ -5,13 +5,13 @@ use crate::language_settings::{
 use crate::Buffer;
 use clock::ReplicaId;
 use collections::BTreeMap;
-use gpui2::{AppContext, Model};
-use gpui2::{Context, TestAppContext};
+use gpui::{AppContext, Model};
+use gpui::{Context, TestAppContext};
 use indoc::indoc;
 use proto::deserialize_operation;
 use rand::prelude::*;
 use regex::RegexBuilder;
-use settings2::SettingsStore;
+use settings::SettingsStore;
 use std::{
     env,
     ops::Range,
@@ -38,8 +38,8 @@ fn init_logger() {
     }
 }
 
-#[gpui2::test]
-fn test_line_endings(cx: &mut gpui2::AppContext) {
+#[gpui::test]
+fn test_line_endings(cx: &mut gpui::AppContext) {
     init_settings(cx, |_| {});
 
     cx.build_model(|cx| {
@@ -63,7 +63,7 @@ fn test_line_endings(cx: &mut gpui2::AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_select_language() {
     let registry = Arc::new(LanguageRegistry::test());
     registry.add(Arc::new(Language::new(
@@ -132,8 +132,8 @@ fn test_select_language() {
     );
 }
 
-#[gpui2::test]
-fn test_edit_events(cx: &mut gpui2::AppContext) {
+#[gpui::test]
+fn test_edit_events(cx: &mut gpui::AppContext) {
     let mut now = Instant::now();
     let buffer_1_events = Arc::new(Mutex::new(Vec::new()));
     let buffer_2_events = Arc::new(Mutex::new(Vec::new()));
@@ -215,7 +215,7 @@ fn test_edit_events(cx: &mut gpui2::AppContext) {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 async fn test_apply_diff(cx: &mut TestAppContext) {
     let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
     let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
@@ -238,8 +238,8 @@ async fn test_apply_diff(cx: &mut TestAppContext) {
     });
 }
 
-#[gpui2::test(iterations = 10)]
-async fn test_normalize_whitespace(cx: &mut gpui2::TestAppContext) {
+#[gpui::test(iterations = 10)]
+async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
     let text = [
         "zero",     //
         "one  ",    // 2 trailing spaces
@@ -311,8 +311,8 @@ async fn test_normalize_whitespace(cx: &mut gpui2::TestAppContext) {
     });
 }
 
-#[gpui2::test]
-async fn test_reparse(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_reparse(cx: &mut gpui::TestAppContext) {
     let text = "fn a() {}";
     let buffer = cx.build_model(|cx| {
         Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx)
@@ -440,8 +440,8 @@ async fn test_reparse(cx: &mut gpui2::TestAppContext) {
     );
 }
 
-#[gpui2::test]
-async fn test_resetting_language(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
     let buffer = cx.build_model(|cx| {
         let mut buffer =
             Buffer::new(0, cx.entity_id().as_u64(), "{}").with_language(Arc::new(rust_lang()), cx);
@@ -463,8 +463,8 @@ async fn test_resetting_language(cx: &mut gpui2::TestAppContext) {
     assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
 }
 
-#[gpui2::test]
-async fn test_outline(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_outline(cx: &mut gpui::TestAppContext) {
     let text = r#"
         struct Person {
             name: String,
@@ -556,7 +556,7 @@ async fn test_outline(cx: &mut gpui2::TestAppContext) {
     async fn search<'a>(
         outline: &'a Outline<Anchor>,
         query: &'a str,
-        cx: &'a gpui2::TestAppContext,
+        cx: &'a gpui::TestAppContext,
     ) -> Vec<(&'a str, Vec<usize>)> {
         let matches = cx
             .update(|cx| outline.search(query, cx.background_executor().clone()))
@@ -568,8 +568,8 @@ async fn test_outline(cx: &mut gpui2::TestAppContext) {
     }
 }
 
-#[gpui2::test]
-async fn test_outline_nodes_with_newlines(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
     let text = r#"
         impl A for B<
             C
@@ -595,8 +595,8 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui2::TestAppContext) {
     );
 }
 
-#[gpui2::test]
-async fn test_outline_with_extra_context(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
     let language = javascript_lang()
         .with_outline_query(
             r#"
@@ -643,8 +643,8 @@ async fn test_outline_with_extra_context(cx: &mut gpui2::TestAppContext) {
     );
 }
 
-#[gpui2::test]
-async fn test_symbols_containing(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
     let text = r#"
         impl Person {
             fn one() {
@@ -731,7 +731,7 @@ async fn test_symbols_containing(cx: &mut gpui2::TestAppContext) {
     }
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
     let mut assert = |selection_text, range_markers| {
         assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
@@ -847,7 +847,7 @@ fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
     let mut assert = |selection_text, bracket_pair_texts| {
         assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
@@ -879,7 +879,7 @@ fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
     cx.build_model(|cx| {
         let text = "fn a() { b(|c| {}) }";
@@ -918,7 +918,7 @@ fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
     }
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -959,7 +959,7 @@ fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
     init_settings(cx, |settings| {
         settings.defaults.hard_tabs = Some(true);
@@ -1002,7 +1002,7 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1143,7 +1143,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC
     eprintln!("DONE");
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1205,7 +1205,7 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1262,7 +1262,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1280,7 +1280,7 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1322,7 +1322,7 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_block_mode(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1406,7 +1406,7 @@ fn test_autoindent_block_mode(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1486,7 +1486,7 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1530,7 +1530,7 @@ fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
     init_settings(cx, |settings| {
         settings.languages.extend([
@@ -1604,7 +1604,7 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
     init_settings(cx, |settings| {
         settings.defaults.tab_size = Some(2.try_into().unwrap());
@@ -1649,7 +1649,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1738,7 +1738,7 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_language_scope_at_with_rust(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1806,7 +1806,7 @@ fn test_language_scope_at_with_rust(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
     init_settings(cx, |_| {});
 
@@ -1854,8 +1854,8 @@ fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
     });
 }
 
-#[gpui2::test]
-fn test_serialization(cx: &mut gpui2::AppContext) {
+#[gpui::test]
+fn test_serialization(cx: &mut gpui::AppContext) {
     let mut now = Instant::now();
 
     let buffer1 = cx.build_model(|cx| {
@@ -1895,7 +1895,7 @@ fn test_serialization(cx: &mut gpui2::AppContext) {
     assert_eq!(buffer2.read(cx).text(), "abcDF");
 }
 
-#[gpui2::test(iterations = 100)]
+#[gpui::test(iterations = 100)]
 fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
     let min_peers = env::var("MIN_PEERS")
         .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
@@ -2199,7 +2199,7 @@ fn test_contiguous_ranges() {
     );
 }
 
-#[gpui2::test(iterations = 500)]
+#[gpui::test(iterations = 500)]
 fn test_trailing_whitespace_ranges(mut rng: StdRng) {
     // Generate a random multi-line string containing
     // some lines with trailing whitespace.
@@ -2400,7 +2400,7 @@ fn javascript_lang() -> Language {
     .unwrap()
 }
 
-fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui2::TestAppContext) -> String {
+fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui::TestAppContext) -> String {
     buffer.update(cx, |buffer, _| {
         let snapshot = buffer.snapshot();
         let layers = snapshot.syntax.layers(buffer.as_text_snapshot());

crates/language2/src/diagnostic_set.rs 🔗

@@ -1,6 +1,6 @@
 use crate::Diagnostic;
 use collections::HashMap;
-use lsp2::LanguageServerId;
+use lsp::LanguageServerId;
 use std::{
     cmp::{Ordering, Reverse},
     iter,
@@ -37,14 +37,14 @@ pub struct Summary {
 
 impl<T> DiagnosticEntry<T> {
     // Used to provide diagnostic context to lsp codeAction request
-    pub fn to_lsp_diagnostic_stub(&self) -> lsp2::Diagnostic {
+    pub fn to_lsp_diagnostic_stub(&self) -> lsp::Diagnostic {
         let code = self
             .diagnostic
             .code
             .clone()
-            .map(lsp2::NumberOrString::String);
+            .map(lsp::NumberOrString::String);
 
-        lsp2::Diagnostic {
+        lsp::Diagnostic {
             code,
             severity: Some(self.diagnostic.severity),
             ..Default::default()

crates/language2/src/highlight_map.rs 🔗

@@ -1,6 +1,6 @@
-use gpui2::HighlightStyle;
+use gpui::HighlightStyle;
 use std::sync::Arc;
-use theme2::SyntaxTheme;
+use theme::SyntaxTheme;
 
 #[derive(Clone, Debug)]
 pub struct HighlightMap(Arc<[HighlightId]>);
@@ -79,7 +79,7 @@ impl Default for HighlightId {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui2::rgba;
+    use gpui::rgba;
 
     #[test]
     fn test_highlight_map() {

crates/language2/src/language2.rs 🔗

@@ -17,10 +17,10 @@ use futures::{
     future::{BoxFuture, Shared},
     FutureExt, TryFutureExt as _,
 };
-use gpui2::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
+use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
 pub use highlight_map::HighlightMap;
 use lazy_static::lazy_static;
-use lsp2::{CodeActionKind, LanguageServerBinary};
+use lsp::{CodeActionKind, LanguageServerBinary};
 use parking_lot::{Mutex, RwLock};
 use postage::watch;
 use regex::Regex;
@@ -42,7 +42,7 @@ use std::{
     },
 };
 use syntax_map::SyntaxSnapshot;
-use theme2::{SyntaxTheme, ThemeVariant};
+use theme::{SyntaxTheme, ThemeVariant};
 use tree_sitter::{self, Query};
 use unicase::UniCase;
 use util::{http::HttpClient, paths::PathExt};
@@ -51,7 +51,7 @@ use util::{post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
 pub use buffer::Operation;
 pub use buffer::*;
 pub use diagnostic_set::DiagnosticEntry;
-pub use lsp2::LanguageServerId;
+pub use lsp::LanguageServerId;
 pub use outline::{Outline, OutlineItem};
 pub use syntax_map::{OwnedSyntaxLayerInfo, SyntaxLayerInfo};
 pub use text::LineEnding;
@@ -98,7 +98,7 @@ lazy_static! {
 }
 
 pub trait ToLspPosition {
-    fn to_lsp_position(self) -> lsp2::Position;
+    fn to_lsp_position(self) -> lsp::Position;
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -203,17 +203,17 @@ impl CachedLspAdapter {
         self.adapter.workspace_configuration(cx)
     }
 
-    pub fn process_diagnostics(&self, params: &mut lsp2::PublishDiagnosticsParams) {
+    pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
         self.adapter.process_diagnostics(params)
     }
 
-    pub async fn process_completion(&self, completion_item: &mut lsp2::CompletionItem) {
+    pub async fn process_completion(&self, completion_item: &mut lsp::CompletionItem) {
         self.adapter.process_completion(completion_item).await
     }
 
     pub async fn label_for_completion(
         &self,
-        completion_item: &lsp2::CompletionItem,
+        completion_item: &lsp::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         self.adapter
@@ -224,7 +224,7 @@ impl CachedLspAdapter {
     pub async fn label_for_symbol(
         &self,
         name: &str,
-        kind: lsp2::SymbolKind,
+        kind: lsp::SymbolKind,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         self.adapter.label_for_symbol(name, kind, language).await
@@ -289,13 +289,13 @@ pub trait LspAdapter: 'static + Send + Sync {
         container_dir: PathBuf,
     ) -> Option<LanguageServerBinary>;
 
-    fn process_diagnostics(&self, _: &mut lsp2::PublishDiagnosticsParams) {}
+    fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
 
-    async fn process_completion(&self, _: &mut lsp2::CompletionItem) {}
+    async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
 
     async fn label_for_completion(
         &self,
-        _: &lsp2::CompletionItem,
+        _: &lsp::CompletionItem,
         _: &Arc<Language>,
     ) -> Option<CodeLabel> {
         None
@@ -304,7 +304,7 @@ pub trait LspAdapter: 'static + Send + Sync {
     async fn label_for_symbol(
         &self,
         _: &str,
-        _: lsp2::SymbolKind,
+        _: lsp::SymbolKind,
         _: &Arc<Language>,
     ) -> Option<CodeLabel> {
         None
@@ -476,8 +476,8 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D
 pub struct FakeLspAdapter {
     pub name: &'static str,
     pub initialization_options: Option<Value>,
-    pub capabilities: lsp2::ServerCapabilities,
-    pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp2::FakeLanguageServer)>>,
+    pub capabilities: lsp::ServerCapabilities,
+    pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
     pub disk_based_diagnostics_progress_token: Option<String>,
     pub disk_based_diagnostics_sources: Vec<String>,
     pub prettier_plugins: Vec<&'static str>,
@@ -532,7 +532,7 @@ pub struct Language {
 
     #[cfg(any(test, feature = "test-support"))]
     fake_adapter: Option<(
-        mpsc::UnboundedSender<lsp2::FakeLanguageServer>,
+        mpsc::UnboundedSender<lsp::FakeLanguageServer>,
         Arc<FakeLspAdapter>,
     )>,
 }
@@ -649,7 +649,7 @@ struct LanguageRegistryState {
 
 pub struct PendingLanguageServer {
     pub server_id: LanguageServerId,
-    pub task: Task<Result<lsp2::LanguageServer>>,
+    pub task: Task<Result<lsp::LanguageServer>>,
     pub container_dir: Option<Arc<Path>>,
 }
 
@@ -905,7 +905,7 @@ impl LanguageRegistry {
         if language.fake_adapter.is_some() {
             let task = cx.spawn(|cx| async move {
                 let (servers_tx, fake_adapter) = language.fake_adapter.as_ref().unwrap();
-                let (server, mut fake_server) = lsp2::LanguageServer::fake(
+                let (server, mut fake_server) = lsp::LanguageServer::fake(
                     fake_adapter.name.to_string(),
                     fake_adapter.capabilities.clone(),
                     cx.clone(),
@@ -919,7 +919,7 @@ impl LanguageRegistry {
                 cx.background_executor()
                     .spawn(async move {
                         if fake_server
-                            .try_receive_notification::<lsp2::notification::Initialized>()
+                            .try_receive_notification::<lsp::notification::Initialized>()
                             .await
                             .is_some()
                         {
@@ -988,7 +988,7 @@ impl LanguageRegistry {
                     task.await?;
                 }
 
-                lsp2::LanguageServer::new(
+                lsp::LanguageServer::new(
                     stderr_capture,
                     server_id,
                     binary,
@@ -1471,7 +1471,7 @@ impl Language {
     pub async fn set_fake_lsp_adapter(
         &mut self,
         fake_lsp_adapter: Arc<FakeLspAdapter>,
-    ) -> mpsc::UnboundedReceiver<lsp2::FakeLanguageServer> {
+    ) -> mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
         let (servers_tx, servers_rx) = mpsc::unbounded();
         self.fake_adapter = Some((servers_tx, fake_lsp_adapter.clone()));
         let adapter = CachedLspAdapter::new(Arc::new(fake_lsp_adapter)).await;
@@ -1501,7 +1501,7 @@ impl Language {
         None
     }
 
-    pub async fn process_completion(self: &Arc<Self>, completion: &mut lsp2::CompletionItem) {
+    pub async fn process_completion(self: &Arc<Self>, completion: &mut lsp::CompletionItem) {
         for adapter in &self.adapters {
             adapter.process_completion(completion).await;
         }
@@ -1509,7 +1509,7 @@ impl Language {
 
     pub async fn label_for_completion(
         self: &Arc<Self>,
-        completion: &lsp2::CompletionItem,
+        completion: &lsp::CompletionItem,
     ) -> Option<CodeLabel> {
         self.adapters
             .first()
@@ -1521,7 +1521,7 @@ impl Language {
     pub async fn label_for_symbol(
         self: &Arc<Self>,
         name: &str,
-        kind: lsp2::SymbolKind,
+        kind: lsp::SymbolKind,
     ) -> Option<CodeLabel> {
         self.adapters
             .first()
@@ -1745,7 +1745,7 @@ impl Default for FakeLspAdapter {
     fn default() -> Self {
         Self {
             name: "the-fake-language-server",
-            capabilities: lsp2::LanguageServer::full_capabilities(),
+            capabilities: lsp::LanguageServer::full_capabilities(),
             initializer: None,
             disk_based_diagnostics_progress_token: None,
             initialization_options: None,
@@ -1794,7 +1794,7 @@ impl LspAdapter for Arc<FakeLspAdapter> {
         unreachable!();
     }
 
-    fn process_diagnostics(&self, _: &mut lsp2::PublishDiagnosticsParams) {}
+    fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
 
     async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
         self.disk_based_diagnostics_sources.clone()
@@ -1824,22 +1824,22 @@ fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option<u32>)])
     }
 }
 
-pub fn point_to_lsp(point: PointUtf16) -> lsp2::Position {
-    lsp2::Position::new(point.row, point.column)
+pub fn point_to_lsp(point: PointUtf16) -> lsp::Position {
+    lsp::Position::new(point.row, point.column)
 }
 
-pub fn point_from_lsp(point: lsp2::Position) -> Unclipped<PointUtf16> {
+pub fn point_from_lsp(point: lsp::Position) -> Unclipped<PointUtf16> {
     Unclipped(PointUtf16::new(point.line, point.character))
 }
 
-pub fn range_to_lsp(range: Range<PointUtf16>) -> lsp2::Range {
-    lsp2::Range {
+pub fn range_to_lsp(range: Range<PointUtf16>) -> lsp::Range {
+    lsp::Range {
         start: point_to_lsp(range.start),
         end: point_to_lsp(range.end),
     }
 }
 
-pub fn range_from_lsp(range: lsp2::Range) -> Range<Unclipped<PointUtf16>> {
+pub fn range_from_lsp(range: lsp::Range) -> Range<Unclipped<PointUtf16>> {
     let mut start = point_from_lsp(range.start);
     let mut end = point_from_lsp(range.end);
     if start > end {
@@ -1851,9 +1851,9 @@ pub fn range_from_lsp(range: lsp2::Range) -> Range<Unclipped<PointUtf16>> {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui2::TestAppContext;
+    use gpui::TestAppContext;
 
-    #[gpui2::test(iterations = 10)]
+    #[gpui::test(iterations = 10)]
     async fn test_first_line_pattern(cx: &mut TestAppContext) {
         let mut languages = LanguageRegistry::test();
 
@@ -1891,7 +1891,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test(iterations = 10)]
+    #[gpui::test(iterations = 10)]
     async fn test_language_loading(cx: &mut TestAppContext) {
         let mut languages = LanguageRegistry::test();
         languages.set_executor(cx.executor().clone());

crates/language2/src/language_settings.rs 🔗

@@ -2,13 +2,13 @@ use crate::{File, Language};
 use anyhow::Result;
 use collections::{HashMap, HashSet};
 use globset::GlobMatcher;
-use gpui2::AppContext;
+use gpui::AppContext;
 use schemars::{
     schema::{InstanceType, ObjectValidation, Schema, SchemaObject},
     JsonSchema,
 };
 use serde::{Deserialize, Serialize};
-use settings2::Settings;
+use settings::Settings;
 use std::{num::NonZeroU32, path::Path, sync::Arc};
 
 pub fn init(cx: &mut AppContext) {
@@ -255,7 +255,7 @@ impl InlayHintKind {
     }
 }
 
-impl settings2::Settings for AllLanguageSettings {
+impl settings::Settings for AllLanguageSettings {
     const KEY: Option<&'static str> = None;
 
     type FileContent = AllLanguageSettingsContent;
@@ -332,7 +332,7 @@ impl settings2::Settings for AllLanguageSettings {
 
     fn json_schema(
         generator: &mut schemars::gen::SchemaGenerator,
-        params: &settings2::SettingsJsonSchemaParams,
+        params: &settings::SettingsJsonSchemaParams,
         _: &AppContext,
     ) -> schemars::schema::RootSchema {
         let mut root_schema = generator.root_schema_for::<Self::FileContent>();

crates/language2/src/outline.rs 🔗

@@ -1,5 +1,5 @@
-use fuzzy2::{StringMatch, StringMatchCandidate};
-use gpui2::{BackgroundExecutor, HighlightStyle};
+use fuzzy::{StringMatch, StringMatchCandidate};
+use gpui::{BackgroundExecutor, HighlightStyle};
 use std::ops::Range;
 
 #[derive(Debug)]
@@ -61,7 +61,7 @@ impl<T> Outline<T> {
         let query = query.trim_start();
         let is_path_query = query.contains(' ');
         let smart_case = query.chars().any(|c| c.is_uppercase());
-        let mut matches = fuzzy2::match_strings(
+        let mut matches = fuzzy::match_strings(
             if is_path_query {
                 &self.path_candidates
             } else {

crates/language2/src/proto.rs 🔗

@@ -4,8 +4,8 @@ use crate::{
 };
 use anyhow::{anyhow, Result};
 use clock::ReplicaId;
-use lsp2::{DiagnosticSeverity, LanguageServerId};
-use rpc2::proto;
+use lsp::{DiagnosticSeverity, LanguageServerId};
+use rpc::proto;
 use std::{ops::Range, sync::Arc};
 use text::*;
 

crates/language2/src/syntax_map/syntax_map_tests.rs 🔗

@@ -78,7 +78,7 @@ fn test_splice_included_ranges() {
     }
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_syntax_map_layers_for_range() {
     let registry = Arc::new(LanguageRegistry::test());
     let language = Arc::new(rust_lang());
@@ -175,7 +175,7 @@ fn test_syntax_map_layers_for_range() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_dynamic_language_injection() {
     let registry = Arc::new(LanguageRegistry::test());
     let markdown = Arc::new(markdown_lang());
@@ -253,7 +253,7 @@ fn test_dynamic_language_injection() {
     assert!(!syntax_map.contains_unknown_injections());
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_typing_multiple_new_injections() {
     let (buffer, syntax_map) = test_edit_sequence(
         "Rust",
@@ -282,7 +282,7 @@ fn test_typing_multiple_new_injections() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_pasting_new_injection_line_between_others() {
     let (buffer, syntax_map) = test_edit_sequence(
         "Rust",
@@ -329,7 +329,7 @@ fn test_pasting_new_injection_line_between_others() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_joining_injections_with_child_injections() {
     let (buffer, syntax_map) = test_edit_sequence(
         "Rust",
@@ -373,7 +373,7 @@ fn test_joining_injections_with_child_injections() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_editing_edges_of_injection() {
     test_edit_sequence(
         "Rust",
@@ -402,7 +402,7 @@ fn test_editing_edges_of_injection() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_edits_preceding_and_intersecting_injection() {
     test_edit_sequence(
         "Rust",
@@ -414,7 +414,7 @@ fn test_edits_preceding_and_intersecting_injection() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_non_local_changes_create_injections() {
     test_edit_sequence(
         "Rust",
@@ -433,7 +433,7 @@ fn test_non_local_changes_create_injections() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_creating_many_injections_in_one_edit() {
     test_edit_sequence(
         "Rust",
@@ -463,7 +463,7 @@ fn test_creating_many_injections_in_one_edit() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_editing_across_injection_boundary() {
     test_edit_sequence(
         "Rust",
@@ -491,7 +491,7 @@ fn test_editing_across_injection_boundary() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_removing_injection_by_replacing_across_boundary() {
     test_edit_sequence(
         "Rust",
@@ -517,7 +517,7 @@ fn test_removing_injection_by_replacing_across_boundary() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_combined_injections_simple() {
     let (buffer, syntax_map) = test_edit_sequence(
         "ERB",
@@ -564,7 +564,7 @@ fn test_combined_injections_simple() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_combined_injections_empty_ranges() {
     test_edit_sequence(
         "ERB",
@@ -582,7 +582,7 @@ fn test_combined_injections_empty_ranges() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_combined_injections_edit_edges_of_ranges() {
     let (buffer, syntax_map) = test_edit_sequence(
         "ERB",
@@ -613,7 +613,7 @@ fn test_combined_injections_edit_edges_of_ranges() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_combined_injections_splitting_some_injections() {
     let (_buffer, _syntax_map) = test_edit_sequence(
         "ERB",
@@ -638,7 +638,7 @@ fn test_combined_injections_splitting_some_injections() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_combined_injections_editing_after_last_injection() {
     test_edit_sequence(
         "ERB",
@@ -658,7 +658,7 @@ fn test_combined_injections_editing_after_last_injection() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_combined_injections_inside_injections() {
     let (buffer, syntax_map) = test_edit_sequence(
         "Markdown",
@@ -734,7 +734,7 @@ fn test_combined_injections_inside_injections() {
     );
 }
 
-#[gpui2::test]
+#[gpui::test]
 fn test_empty_combined_injections_inside_injections() {
     let (buffer, syntax_map) = test_edit_sequence(
         "Markdown",
@@ -762,7 +762,7 @@ fn test_empty_combined_injections_inside_injections() {
     );
 }
 
-#[gpui2::test(iterations = 50)]
+#[gpui::test(iterations = 50)]
 fn test_random_syntax_map_edits_rust_macros(rng: StdRng) {
     let text = r#"
         fn test_something() {
@@ -788,7 +788,7 @@ fn test_random_syntax_map_edits_rust_macros(rng: StdRng) {
     test_random_edits(text, registry, language, rng);
 }
 
-#[gpui2::test(iterations = 50)]
+#[gpui::test(iterations = 50)]
 fn test_random_syntax_map_edits_with_erb(rng: StdRng) {
     let text = r#"
         <div id="main">
@@ -817,7 +817,7 @@ fn test_random_syntax_map_edits_with_erb(rng: StdRng) {
     test_random_edits(text, registry, language, rng);
 }
 
-#[gpui2::test(iterations = 50)]
+#[gpui::test(iterations = 50)]
 fn test_random_syntax_map_edits_with_heex(rng: StdRng) {
     let text = r#"
         defmodule TheModule do

crates/live_kit_client2/Cargo.toml 🔗

@@ -23,7 +23,7 @@ test-support = [
 
 [dependencies]
 collections = { path = "../collections", optional = true }
-gpui2 = { path = "../gpui2", optional = true }
+gpui2 = { package = "gpui2", path = "../gpui2", optional = true }
 live_kit_server = { path = "../live_kit_server", optional = true }
 media = { path = "../media" }
 
@@ -41,7 +41,7 @@ nanoid = { version ="0.4", optional = true}
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui2 = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 live_kit_server = { path = "../live_kit_server" }
 media = { path = "../media" }
 nanoid = "0.4"

crates/lsp2/Cargo.toml 🔗

@@ -13,7 +13,7 @@ test-support = ["async-pipe"]
 
 [dependencies]
 collections = { path = "../collections" }
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 util = { path = "../util" }
 
 anyhow.workspace = true
@@ -29,7 +29,7 @@ serde_json.workspace = true
 smol.workspace = true
 
 [dev-dependencies]
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 
 async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }

crates/lsp2/src/lsp2.rs 🔗

@@ -5,7 +5,7 @@ pub use lsp_types::*;
 use anyhow::{anyhow, Context, Result};
 use collections::HashMap;
 use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite, FutureExt};
-use gpui2::{AsyncAppContext, BackgroundExecutor, Task};
+use gpui::{AsyncAppContext, BackgroundExecutor, Task};
 use parking_lot::Mutex;
 use postage::{barrier, prelude::Stream};
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
@@ -1038,7 +1038,7 @@ impl FakeLanguageServer {
     where
         T: 'static + request::Request,
         T::Params: 'static + Send,
-        F: 'static + Send + FnMut(T::Params, gpui2::AsyncAppContext) -> Fut,
+        F: 'static + Send + FnMut(T::Params, gpui::AsyncAppContext) -> Fut,
         Fut: 'static + Send + Future<Output = Result<T::Result>>,
     {
         let (responded_tx, responded_rx) = futures::channel::mpsc::unbounded();
@@ -1066,7 +1066,7 @@ impl FakeLanguageServer {
     where
         T: 'static + notification::Notification,
         T::Params: 'static + Send,
-        F: 'static + Send + FnMut(T::Params, gpui2::AsyncAppContext),
+        F: 'static + Send + FnMut(T::Params, gpui::AsyncAppContext),
     {
         let (handled_tx, handled_rx) = futures::channel::mpsc::unbounded();
         self.server.remove_notification_handler::<T>();
@@ -1110,7 +1110,7 @@ impl FakeLanguageServer {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui2::TestAppContext;
+    use gpui::TestAppContext;
 
     #[ctor::ctor]
     fn init_logger() {
@@ -1119,7 +1119,7 @@ mod tests {
         }
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_fake(cx: &mut TestAppContext) {
         let (server, mut fake) =
             LanguageServer::fake("the-lsp".to_string(), Default::default(), cx.to_async());

crates/menu2/Cargo.toml 🔗

@@ -9,4 +9,4 @@ path = "src/menu2.rs"
 doctest = false
 
 [dependencies]
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }

crates/multi_buffer2/Cargo.toml 🔗

@@ -10,29 +10,29 @@ doctest = false
 
 [features]
 test-support = [
-    "copilot2/test-support",
+    "copilot/test-support",
     "text/test-support",
-    "language2/test-support",
-    "gpui2/test-support",
+    "language/test-support",
+    "gpui/test-support",
     "util/test-support",
     "tree-sitter-rust",
     "tree-sitter-typescript"
 ]
 
 [dependencies]
-client2 = { path = "../client2" }
+client = { package = "client2", path = "../client2" }
 clock = { path = "../clock" }
 collections = { path = "../collections" }
 git = { path = "../git" }
-gpui2 = { path = "../gpui2" }
-language2 = { path = "../language2" }
-lsp2 = { path = "../lsp2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+lsp = { package = "lsp2", path = "../lsp2" }
 rich_text = { path = "../rich_text" }
-settings2 = { path = "../settings2" }
+settings = { package = "settings2", path = "../settings2" }
 snippet = { path = "../snippet" }
 sum_tree = { path = "../sum_tree" }
 text = { path = "../text" }
-theme2 = { path = "../theme2" }
+theme = { package = "theme2", path = "../theme2" }
 util = { path = "../util" }
 
 aho-corasick = "1.1"
@@ -59,14 +59,14 @@ tree-sitter-html = { workspace = true, optional = true }
 tree-sitter-typescript = { workspace = true, optional = true }
 
 [dev-dependencies]
-copilot2 = { path = "../copilot2", features = ["test-support"] }
+copilot = { package = "copilot2", path = "../copilot2", features = ["test-support"] }
 text = { path = "../text", features = ["test-support"] }
-language2 = { path = "../language2", features = ["test-support"] }
-lsp2 = { path = "../lsp2", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
-project2 = { path = "../project2", features = ["test-support"] }
-settings2 = { path = "../settings2", features = ["test-support"] }
+project = { package = "project2", path = "../project2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 
 ctor.workspace = true
 env_logger.workspace = true

crates/multi_buffer2/src/anchor.rs 🔗

@@ -1,5 +1,5 @@
 use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint};
-use language2::{OffsetUtf16, Point, TextDimension};
+use language::{OffsetUtf16, Point, TextDimension};
 use std::{
     cmp::Ordering,
     ops::{Range, Sub},

crates/multi_buffer2/src/multi_buffer2.rs 🔗

@@ -6,9 +6,9 @@ use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet};
 use futures::{channel::mpsc, SinkExt};
 use git::diff::DiffHunk;
-use gpui2::{AppContext, EventEmitter, Model, ModelContext};
-pub use language2::Completion;
-use language2::{
+use gpui::{AppContext, EventEmitter, Model, ModelContext};
+pub use language::Completion;
+use language::{
     char_kind,
     language_settings::{language_settings, LanguageSettings},
     AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
@@ -35,11 +35,11 @@ use text::{
     subscription::{Subscription, Topic},
     Edit, TextSummary,
 };
-use theme2::SyntaxTheme;
+use theme::SyntaxTheme;
 use util::post_inc;
 
 #[cfg(any(test, feature = "test-support"))]
-use gpui2::Context;
+use gpui::Context;
 
 const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
 
@@ -62,7 +62,7 @@ pub enum Event {
     ExcerptsAdded {
         buffer: Model<Buffer>,
         predecessor: ExcerptId,
-        excerpts: Vec<(ExcerptId, ExcerptRange<language2::Anchor>)>,
+        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
     },
     ExcerptsRemoved {
         ids: Vec<ExcerptId>,
@@ -130,7 +130,7 @@ struct BufferState {
     last_file_update_count: usize,
     last_git_diff_update_count: usize,
     excerpts: Vec<Locator>,
-    _subscriptions: [gpui2::Subscription; 2],
+    _subscriptions: [gpui::Subscription; 2],
 }
 
 #[derive(Clone, Default)]
@@ -684,7 +684,7 @@ impl MultiBuffer {
 
     pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &mut ModelContext<Self>)
     where
-        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language2::Transaction)>,
+        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
     {
         self.history
             .push_transaction(buffer_transactions, Instant::now(), cx);
@@ -1383,7 +1383,7 @@ impl MultiBuffer {
         &self,
         position: T,
         cx: &AppContext,
-    ) -> Option<(Model<Buffer>, language2::Anchor)> {
+    ) -> Option<(Model<Buffer>, language::Anchor)> {
         let snapshot = self.read(cx);
         let anchor = snapshot.anchor_before(position);
         let buffer = self
@@ -1398,25 +1398,25 @@ impl MultiBuffer {
     fn on_buffer_event(
         &mut self,
         _: Model<Buffer>,
-        event: &language2::Event,
+        event: &language::Event,
         cx: &mut ModelContext<Self>,
     ) {
         cx.emit(match event {
-            language2::Event::Edited => Event::Edited {
+            language::Event::Edited => Event::Edited {
                 sigleton_buffer_edited: true,
             },
-            language2::Event::DirtyChanged => Event::DirtyChanged,
-            language2::Event::Saved => Event::Saved,
-            language2::Event::FileHandleChanged => Event::FileHandleChanged,
-            language2::Event::Reloaded => Event::Reloaded,
-            language2::Event::DiffBaseChanged => Event::DiffBaseChanged,
-            language2::Event::LanguageChanged => Event::LanguageChanged,
-            language2::Event::Reparsed => Event::Reparsed,
-            language2::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated,
-            language2::Event::Closed => Event::Closed,
+            language::Event::DirtyChanged => Event::DirtyChanged,
+            language::Event::Saved => Event::Saved,
+            language::Event::FileHandleChanged => Event::FileHandleChanged,
+            language::Event::Reloaded => Event::Reloaded,
+            language::Event::DiffBaseChanged => Event::DiffBaseChanged,
+            language::Event::LanguageChanged => Event::LanguageChanged,
+            language::Event::Reparsed => Event::Reparsed,
+            language::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated,
+            language::Event::Closed => Event::Closed,
 
             //
-            language2::Event::Operation(_) => return,
+            language::Event::Operation(_) => return,
         });
     }
 
@@ -1648,14 +1648,14 @@ impl MultiBuffer {
 
 #[cfg(any(test, feature = "test-support"))]
 impl MultiBuffer {
-    pub fn build_simple(text: &str, cx: &mut gpui2::AppContext) -> Model<Self> {
+    pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model<Self> {
         let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
         cx.build_model(|cx| Self::singleton(buffer, cx))
     }
 
     pub fn build_multi<const COUNT: usize>(
         excerpts: [(&str, Vec<Range<Point>>); COUNT],
-        cx: &mut gpui2::AppContext,
+        cx: &mut gpui::AppContext,
     ) -> Model<Self> {
         let multi = cx.build_model(|_| Self::new(0));
         for (text, ranges) in excerpts {
@@ -1672,11 +1672,11 @@ impl MultiBuffer {
         multi
     }
 
-    pub fn build_from_buffer(buffer: Model<Buffer>, cx: &mut gpui2::AppContext) -> Model<Self> {
+    pub fn build_from_buffer(buffer: Model<Buffer>, cx: &mut gpui::AppContext) -> Model<Self> {
         cx.build_model(|cx| Self::singleton(buffer, cx))
     }
 
-    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui2::AppContext) -> Model<Self> {
+    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model<Self> {
         cx.build_model(|cx| {
             let mut multibuffer = MultiBuffer::new(0);
             let mutation_count = rng.gen_range(1..=5);
@@ -3409,7 +3409,7 @@ impl History {
         now: Instant,
         cx: &mut ModelContext<MultiBuffer>,
     ) where
-        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language2::Transaction)>,
+        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
     {
         assert_eq!(self.transaction_depth, 0);
         let transaction = Transaction {
@@ -4135,15 +4135,15 @@ where
 mod tests {
     use super::*;
     use futures::StreamExt;
-    use gpui2::{AppContext, Context, TestAppContext};
-    use language2::{Buffer, Rope};
+    use gpui::{AppContext, Context, TestAppContext};
+    use language::{Buffer, Rope};
     use parking_lot::RwLock;
     use rand::prelude::*;
-    use settings2::SettingsStore;
+    use settings::SettingsStore;
     use std::env;
     use util::test::sample_text;
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_singleton(cx: &mut AppContext) {
         let buffer =
             cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
@@ -4171,7 +4171,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_remote(cx: &mut AppContext) {
         let host_buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a"));
         let guest_buffer = cx.build_model(|cx| {
@@ -4183,7 +4183,7 @@ mod tests {
             buffer
                 .apply_ops(
                     ops.into_iter()
-                        .map(|op| language2::proto::deserialize_operation(op).unwrap()),
+                        .map(|op| language::proto::deserialize_operation(op).unwrap()),
                     cx,
                 )
                 .unwrap();
@@ -4202,7 +4202,7 @@ mod tests {
         assert_eq!(snapshot.text(), "abc");
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
         let buffer_1 =
             cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
@@ -4438,7 +4438,7 @@ mod tests {
         }
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_excerpt_events(cx: &mut AppContext) {
         let buffer_1 =
             cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'a')));
@@ -4546,7 +4546,7 @@ mod tests {
         assert_eq!(*follower_edit_event_count.read(), 4);
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
         let buffer =
             cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
@@ -4583,7 +4583,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
         let buffer =
             cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
@@ -4620,7 +4620,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_empty_multibuffer(cx: &mut AppContext) {
         let multibuffer = cx.build_model(|_| MultiBuffer::new(0));
 
@@ -4630,7 +4630,7 @@ mod tests {
         assert_eq!(snapshot.buffer_rows(1).collect::<Vec<_>>(), &[]);
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
         let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
         let multibuffer = cx.build_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
@@ -4650,7 +4650,7 @@ mod tests {
         assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6);
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_multibuffer_anchors(cx: &mut AppContext) {
         let buffer_1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
         let buffer_2 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi"));
@@ -4708,7 +4708,7 @@ mod tests {
         assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14);
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) {
         let buffer_1 = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
         let buffer_2 =
@@ -4840,7 +4840,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test(iterations = 100)]
+    #[gpui::test(iterations = 100)]
     fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
         let operations = env::var("OPERATIONS")
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
@@ -5262,7 +5262,7 @@ mod tests {
         }
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_history(cx: &mut AppContext) {
         let test_settings = SettingsStore::test(cx);
         cx.set_global(test_settings);

crates/prettier2/Cargo.toml 🔗

@@ -12,12 +12,12 @@ doctest = false
 test-support = []
 
 [dependencies]
-client2 = { path = "../client2" }
+client = { package = "client2", path = "../client2" }
 collections = { path = "../collections"}
-language2 = { path = "../language2" }
-gpui2 = { path = "../gpui2" }
-fs2 = { path = "../fs2" }
-lsp2 = { path = "../lsp2" }
+language = { package = "language2", path = "../language2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+fs = { package = "fs2", path = "../fs2" }
+lsp = { package = "lsp2", path = "../lsp2" }
 node_runtime = { path = "../node_runtime"}
 util = { path = "../util" }
 
@@ -30,6 +30,6 @@ futures.workspace = true
 parking_lot.workspace = true
 
 [dev-dependencies]
-language2 = { path = "../language2", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-fs2 = { path = "../fs2",  features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+fs = { package = "fs2", path = "../fs2",  features = ["test-support"] }

crates/prettier2/src/prettier2.rs 🔗

@@ -1,9 +1,9 @@
 use anyhow::Context;
 use collections::HashMap;
-use fs2::Fs;
-use gpui2::{AsyncAppContext, Model};
-use language2::{language_settings::language_settings, Buffer, Diff};
-use lsp2::{LanguageServer, LanguageServerId};
+use fs::Fs;
+use gpui::{AsyncAppContext, Model};
+use language::{language_settings::language_settings, Buffer, Diff};
+use lsp::{LanguageServer, LanguageServerId};
 use node_runtime::NodeRuntime;
 use serde::{Deserialize, Serialize};
 use std::{
@@ -141,7 +141,7 @@ impl Prettier {
         node: Arc<dyn NodeRuntime>,
         cx: AsyncAppContext,
     ) -> anyhow::Result<Self> {
-        use lsp2::LanguageServerBinary;
+        use lsp::LanguageServerBinary;
 
         let executor = cx.background_executor().clone();
         anyhow::ensure!(
@@ -453,7 +453,7 @@ struct FormatResult {
     text: String,
 }
 
-impl lsp2::request::Request for Format {
+impl lsp::request::Request for Format {
     type Params = FormatParams;
     type Result = FormatResult;
     const METHOD: &'static str = "prettier/format";
@@ -461,7 +461,7 @@ impl lsp2::request::Request for Format {
 
 enum ClearCache {}
 
-impl lsp2::request::Request for ClearCache {
+impl lsp::request::Request for ClearCache {
     type Params = ();
     type Result = ();
     const METHOD: &'static str = "prettier/clear_cache";

crates/project2/Cargo.toml 🔗

@@ -10,35 +10,35 @@ doctest = false
 
 [features]
 test-support = [
-    "client2/test-support",
-    "db2/test-support",
-    "language2/test-support",
-    "settings2/test-support",
+    "client/test-support",
+    "db/test-support",
+    "language/test-support",
+    "settings/test-support",
     "text/test-support",
-    "prettier2/test-support",
-    "gpui2/test-support",
+    "prettier/test-support",
+    "gpui/test-support",
 ]
 
 [dependencies]
 text = { path = "../text" }
-copilot2 = { path = "../copilot2" }
-client2 = { path = "../client2" }
+copilot = { package = "copilot2", path = "../copilot2" }
+client = { package = "client2", path = "../client2" }
 clock = { path = "../clock" }
 collections = { path = "../collections" }
-db2 = { path = "../db2" }
-fs2 = { path = "../fs2" }
+db = { package = "db2", path = "../db2" }
+fs = { package = "fs2", path = "../fs2" }
 fsevent = { path = "../fsevent" }
-fuzzy2 = { path = "../fuzzy2" }
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
 git = { path = "../git" }
-gpui2 = { path = "../gpui2" }
-language2 = { path = "../language2" }
-lsp2 = { path = "../lsp2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+lsp = { package = "lsp2", path = "../lsp2" }
 node_runtime = { path = "../node_runtime" }
-prettier2 = { path = "../prettier2" }
-rpc2 = { path = "../rpc2" }
-settings2 = { path = "../settings2" }
+prettier = { package = "prettier2", path = "../prettier2" }
+rpc = { package = "rpc2", path = "../rpc2" }
+settings = { package = "settings2", path = "../settings2" }
 sum_tree = { path = "../sum_tree" }
-terminal2 = { path = "../terminal2" }
+terminal = { package = "terminal2", path = "../terminal2" }
 util = { path = "../util" }
 
 aho-corasick = "1.1"
@@ -69,17 +69,17 @@ itertools = "0.10"
 ctor.workspace = true
 env_logger.workspace = true
 pretty_assertions.workspace = true
-client2 = { path = "../client2", features = ["test-support"] }
+client = { package = "client2", path = "../client2", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-db2 = { path = "../db2", features = ["test-support"] }
-fs2 = { path = "../fs2",  features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-language2 = { path = "../language2", features = ["test-support"] }
-lsp2 = { path = "../lsp2", features = ["test-support"] }
-settings2 = { path = "../settings2", features = ["test-support"] }
-prettier2 = { path = "../prettier2", features = ["test-support"] }
+db = { package = "db2", path = "../db2", features = ["test-support"] }
+fs = { package = "fs2", path = "../fs2",  features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
+prettier = { package = "prettier2", path = "../prettier2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
-rpc2 = { path = "../rpc2", features = ["test-support"] }
+rpc = { package = "rpc2", path = "../rpc2", features = ["test-support"] }
 git2.workspace = true
 tempdir.workspace = true
 unindent.workspace = true

crates/project2/src/lsp_command.rs 🔗

@@ -5,10 +5,10 @@ use crate::{
 };
 use anyhow::{anyhow, Context, Result};
 use async_trait::async_trait;
-use client2::proto::{self, PeerId};
+use client::proto::{self, PeerId};
 use futures::future;
-use gpui2::{AppContext, AsyncAppContext, Model};
-use language2::{
+use gpui::{AppContext, AsyncAppContext, Model};
+use language::{
     language_settings::{language_settings, InlayHintKind},
     point_from_lsp, point_to_lsp,
     proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
@@ -16,29 +16,29 @@ use language2::{
     CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction,
     Unclipped,
 };
-use lsp2::{
+use lsp::{
     CompletionListItemDefaultsEditRange, DocumentHighlightKind, LanguageServer, LanguageServerId,
     OneOf, ServerCapabilities,
 };
 use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
 use text::LineEnding;
 
-pub fn lsp_formatting_options(tab_size: u32) -> lsp2::FormattingOptions {
-    lsp2::FormattingOptions {
+pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
+    lsp::FormattingOptions {
         tab_size,
         insert_spaces: true,
         insert_final_newline: Some(true),
-        ..lsp2::FormattingOptions::default()
+        ..lsp::FormattingOptions::default()
     }
 }
 
 #[async_trait(?Send)]
 pub(crate) trait LspCommand: 'static + Sized + Send {
     type Response: 'static + Default + Send;
-    type LspRequest: 'static + Send + lsp2::request::Request;
+    type LspRequest: 'static + Send + lsp::request::Request;
     type ProtoRequest: 'static + Send + proto::RequestMessage;
 
-    fn check_capabilities(&self, _: &lsp2::ServerCapabilities) -> bool {
+    fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
         true
     }
 
@@ -48,11 +48,11 @@ pub(crate) trait LspCommand: 'static + Sized + Send {
         buffer: &Buffer,
         language_server: &Arc<LanguageServer>,
         cx: &AppContext,
-    ) -> <Self::LspRequest as lsp2::request::Request>::Params;
+    ) -> <Self::LspRequest as lsp::request::Request>::Params;
 
     async fn response_from_lsp(
         self,
-        message: <Self::LspRequest as lsp2::request::Request>::Result,
+        message: <Self::LspRequest as lsp::request::Request>::Result,
         project: Model<Project>,
         buffer: Model<Buffer>,
         server_id: LanguageServerId,
@@ -140,8 +140,8 @@ pub(crate) struct FormattingOptions {
     tab_size: u32,
 }
 
-impl From<lsp2::FormattingOptions> for FormattingOptions {
-    fn from(value: lsp2::FormattingOptions) -> Self {
+impl From<lsp::FormattingOptions> for FormattingOptions {
+    fn from(value: lsp::FormattingOptions) -> Self {
         Self {
             tab_size: value.tab_size,
         }
@@ -151,11 +151,11 @@ impl From<lsp2::FormattingOptions> for FormattingOptions {
 #[async_trait(?Send)]
 impl LspCommand for PrepareRename {
     type Response = Option<Range<Anchor>>;
-    type LspRequest = lsp2::request::PrepareRenameRequest;
+    type LspRequest = lsp::request::PrepareRenameRequest;
     type ProtoRequest = proto::PrepareRename;
 
     fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
-        if let Some(lsp2::OneOf::Right(rename)) = &capabilities.rename_provider {
+        if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
             rename.prepare_provider == Some(true)
         } else {
             false
@@ -168,10 +168,10 @@ impl LspCommand for PrepareRename {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::TextDocumentPositionParams {
-        lsp2::TextDocumentPositionParams {
-            text_document: lsp2::TextDocumentIdentifier {
-                uri: lsp2::Url::from_file_path(path).unwrap(),
+    ) -> lsp::TextDocumentPositionParams {
+        lsp::TextDocumentPositionParams {
+            text_document: lsp::TextDocumentIdentifier {
+                uri: lsp::Url::from_file_path(path).unwrap(),
             },
             position: point_to_lsp(self.position),
         }
@@ -179,7 +179,7 @@ impl LspCommand for PrepareRename {
 
     async fn response_from_lsp(
         self,
-        message: Option<lsp2::PrepareRenameResponse>,
+        message: Option<lsp::PrepareRenameResponse>,
         _: Model<Project>,
         buffer: Model<Buffer>,
         _: LanguageServerId,
@@ -187,8 +187,8 @@ impl LspCommand for PrepareRename {
     ) -> Result<Option<Range<Anchor>>> {
         buffer.update(&mut cx, |buffer, _| {
             if let Some(
-                lsp2::PrepareRenameResponse::Range(range)
-                | lsp2::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
+                lsp::PrepareRenameResponse::Range(range)
+                | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
             ) = message
             {
                 let Range { start, end } = range_from_lsp(range);
@@ -206,7 +206,7 @@ impl LspCommand for PrepareRename {
         proto::PrepareRename {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             version: serialize_version(&buffer.version()),
@@ -245,10 +245,10 @@ impl LspCommand for PrepareRename {
             can_rename: range.is_some(),
             start: range
                 .as_ref()
-                .map(|range| language2::proto::serialize_anchor(&range.start)),
+                .map(|range| language::proto::serialize_anchor(&range.start)),
             end: range
                 .as_ref()
-                .map(|range| language2::proto::serialize_anchor(&range.end)),
+                .map(|range| language::proto::serialize_anchor(&range.end)),
             version: serialize_version(buffer_version),
         }
     }
@@ -282,7 +282,7 @@ impl LspCommand for PrepareRename {
 #[async_trait(?Send)]
 impl LspCommand for PerformRename {
     type Response = ProjectTransaction;
-    type LspRequest = lsp2::request::Rename;
+    type LspRequest = lsp::request::Rename;
     type ProtoRequest = proto::PerformRename;
 
     fn to_lsp(
@@ -291,11 +291,11 @@ impl LspCommand for PerformRename {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::RenameParams {
-        lsp2::RenameParams {
-            text_document_position: lsp2::TextDocumentPositionParams {
-                text_document: lsp2::TextDocumentIdentifier {
-                    uri: lsp2::Url::from_file_path(path).unwrap(),
+    ) -> lsp::RenameParams {
+        lsp::RenameParams {
+            text_document_position: lsp::TextDocumentPositionParams {
+                text_document: lsp::TextDocumentIdentifier {
+                    uri: lsp::Url::from_file_path(path).unwrap(),
                 },
                 position: point_to_lsp(self.position),
             },
@@ -306,7 +306,7 @@ impl LspCommand for PerformRename {
 
     async fn response_from_lsp(
         self,
-        message: Option<lsp2::WorkspaceEdit>,
+        message: Option<lsp::WorkspaceEdit>,
         project: Model<Project>,
         buffer: Model<Buffer>,
         server_id: LanguageServerId,
@@ -333,7 +333,7 @@ impl LspCommand for PerformRename {
         proto::PerformRename {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             new_name: self.new_name.clone(),
@@ -401,7 +401,7 @@ impl LspCommand for PerformRename {
 #[async_trait(?Send)]
 impl LspCommand for GetDefinition {
     type Response = Vec<LocationLink>;
-    type LspRequest = lsp2::request::GotoDefinition;
+    type LspRequest = lsp::request::GotoDefinition;
     type ProtoRequest = proto::GetDefinition;
 
     fn to_lsp(
@@ -410,11 +410,11 @@ impl LspCommand for GetDefinition {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::GotoDefinitionParams {
-        lsp2::GotoDefinitionParams {
-            text_document_position_params: lsp2::TextDocumentPositionParams {
-                text_document: lsp2::TextDocumentIdentifier {
-                    uri: lsp2::Url::from_file_path(path).unwrap(),
+    ) -> lsp::GotoDefinitionParams {
+        lsp::GotoDefinitionParams {
+            text_document_position_params: lsp::TextDocumentPositionParams {
+                text_document: lsp::TextDocumentIdentifier {
+                    uri: lsp::Url::from_file_path(path).unwrap(),
                 },
                 position: point_to_lsp(self.position),
             },
@@ -425,7 +425,7 @@ impl LspCommand for GetDefinition {
 
     async fn response_from_lsp(
         self,
-        message: Option<lsp2::GotoDefinitionResponse>,
+        message: Option<lsp::GotoDefinitionResponse>,
         project: Model<Project>,
         buffer: Model<Buffer>,
         server_id: LanguageServerId,
@@ -438,7 +438,7 @@ impl LspCommand for GetDefinition {
         proto::GetDefinition {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             version: serialize_version(&buffer.version()),
@@ -494,13 +494,13 @@ impl LspCommand for GetDefinition {
 #[async_trait(?Send)]
 impl LspCommand for GetTypeDefinition {
     type Response = Vec<LocationLink>;
-    type LspRequest = lsp2::request::GotoTypeDefinition;
+    type LspRequest = lsp::request::GotoTypeDefinition;
     type ProtoRequest = proto::GetTypeDefinition;
 
     fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
         match &capabilities.type_definition_provider {
             None => false,
-            Some(lsp2::TypeDefinitionProviderCapability::Simple(false)) => false,
+            Some(lsp::TypeDefinitionProviderCapability::Simple(false)) => false,
             _ => true,
         }
     }
@@ -511,11 +511,11 @@ impl LspCommand for GetTypeDefinition {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::GotoTypeDefinitionParams {
-        lsp2::GotoTypeDefinitionParams {
-            text_document_position_params: lsp2::TextDocumentPositionParams {
-                text_document: lsp2::TextDocumentIdentifier {
-                    uri: lsp2::Url::from_file_path(path).unwrap(),
+    ) -> lsp::GotoTypeDefinitionParams {
+        lsp::GotoTypeDefinitionParams {
+            text_document_position_params: lsp::TextDocumentPositionParams {
+                text_document: lsp::TextDocumentIdentifier {
+                    uri: lsp::Url::from_file_path(path).unwrap(),
                 },
                 position: point_to_lsp(self.position),
             },
@@ -526,7 +526,7 @@ impl LspCommand for GetTypeDefinition {
 
     async fn response_from_lsp(
         self,
-        message: Option<lsp2::GotoTypeDefinitionResponse>,
+        message: Option<lsp::GotoTypeDefinitionResponse>,
         project: Model<Project>,
         buffer: Model<Buffer>,
         server_id: LanguageServerId,
@@ -539,7 +539,7 @@ impl LspCommand for GetTypeDefinition {
         proto::GetTypeDefinition {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             version: serialize_version(&buffer.version()),
@@ -670,7 +670,7 @@ async fn location_links_from_proto(
 }
 
 async fn location_links_from_lsp(
-    message: Option<lsp2::GotoDefinitionResponse>,
+    message: Option<lsp::GotoDefinitionResponse>,
     project: Model<Project>,
     buffer: Model<Buffer>,
     server_id: LanguageServerId,
@@ -683,15 +683,15 @@ async fn location_links_from_lsp(
 
     let mut unresolved_links = Vec::new();
     match message {
-        lsp2::GotoDefinitionResponse::Scalar(loc) => {
+        lsp::GotoDefinitionResponse::Scalar(loc) => {
             unresolved_links.push((None, loc.uri, loc.range));
         }
 
-        lsp2::GotoDefinitionResponse::Array(locs) => {
+        lsp::GotoDefinitionResponse::Array(locs) => {
             unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
         }
 
-        lsp2::GotoDefinitionResponse::Link(links) => {
+        lsp::GotoDefinitionResponse::Link(links) => {
             unresolved_links.extend(links.into_iter().map(|l| {
                 (
                     l.origin_selection_range,
@@ -786,7 +786,7 @@ fn location_links_to_proto(
 #[async_trait(?Send)]
 impl LspCommand for GetReferences {
     type Response = Vec<Location>;
-    type LspRequest = lsp2::request::References;
+    type LspRequest = lsp::request::References;
     type ProtoRequest = proto::GetReferences;
 
     fn to_lsp(
@@ -795,17 +795,17 @@ impl LspCommand for GetReferences {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::ReferenceParams {
-        lsp2::ReferenceParams {
-            text_document_position: lsp2::TextDocumentPositionParams {
-                text_document: lsp2::TextDocumentIdentifier {
-                    uri: lsp2::Url::from_file_path(path).unwrap(),
+    ) -> lsp::ReferenceParams {
+        lsp::ReferenceParams {
+            text_document_position: lsp::TextDocumentPositionParams {
+                text_document: lsp::TextDocumentIdentifier {
+                    uri: lsp::Url::from_file_path(path).unwrap(),
                 },
                 position: point_to_lsp(self.position),
             },
             work_done_progress_params: Default::default(),
             partial_result_params: Default::default(),
-            context: lsp2::ReferenceContext {
+            context: lsp::ReferenceContext {
                 include_declaration: true,
             },
         }
@@ -813,7 +813,7 @@ impl LspCommand for GetReferences {
 
     async fn response_from_lsp(
         self,
-        locations: Option<Vec<lsp2::Location>>,
+        locations: Option<Vec<lsp::Location>>,
         project: Model<Project>,
         buffer: Model<Buffer>,
         server_id: LanguageServerId,
@@ -859,7 +859,7 @@ impl LspCommand for GetReferences {
         proto::GetReferences {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             version: serialize_version(&buffer.version()),
@@ -948,7 +948,7 @@ impl LspCommand for GetReferences {
 #[async_trait(?Send)]
 impl LspCommand for GetDocumentHighlights {
     type Response = Vec<DocumentHighlight>;
-    type LspRequest = lsp2::request::DocumentHighlightRequest;
+    type LspRequest = lsp::request::DocumentHighlightRequest;
     type ProtoRequest = proto::GetDocumentHighlights;
 
     fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
@@ -961,11 +961,11 @@ impl LspCommand for GetDocumentHighlights {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::DocumentHighlightParams {
-        lsp2::DocumentHighlightParams {
-            text_document_position_params: lsp2::TextDocumentPositionParams {
-                text_document: lsp2::TextDocumentIdentifier {
-                    uri: lsp2::Url::from_file_path(path).unwrap(),
+    ) -> lsp::DocumentHighlightParams {
+        lsp::DocumentHighlightParams {
+            text_document_position_params: lsp::TextDocumentPositionParams {
+                text_document: lsp::TextDocumentIdentifier {
+                    uri: lsp::Url::from_file_path(path).unwrap(),
                 },
                 position: point_to_lsp(self.position),
             },
@@ -976,7 +976,7 @@ impl LspCommand for GetDocumentHighlights {
 
     async fn response_from_lsp(
         self,
-        lsp_highlights: Option<Vec<lsp2::DocumentHighlight>>,
+        lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
         _: Model<Project>,
         buffer: Model<Buffer>,
         _: LanguageServerId,
@@ -996,7 +996,7 @@ impl LspCommand for GetDocumentHighlights {
                         range: buffer.anchor_after(start)..buffer.anchor_before(end),
                         kind: lsp_highlight
                             .kind
-                            .unwrap_or(lsp2::DocumentHighlightKind::READ),
+                            .unwrap_or(lsp::DocumentHighlightKind::READ),
                     }
                 })
                 .collect()
@@ -1007,7 +1007,7 @@ impl LspCommand for GetDocumentHighlights {
         proto::GetDocumentHighlights {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             version: serialize_version(&buffer.version()),
@@ -1099,7 +1099,7 @@ impl LspCommand for GetDocumentHighlights {
 #[async_trait(?Send)]
 impl LspCommand for GetHover {
     type Response = Option<Hover>;
-    type LspRequest = lsp2::request::HoverRequest;
+    type LspRequest = lsp::request::HoverRequest;
     type ProtoRequest = proto::GetHover;
 
     fn to_lsp(
@@ -1108,11 +1108,11 @@ impl LspCommand for GetHover {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::HoverParams {
-        lsp2::HoverParams {
-            text_document_position_params: lsp2::TextDocumentPositionParams {
-                text_document: lsp2::TextDocumentIdentifier {
-                    uri: lsp2::Url::from_file_path(path).unwrap(),
+    ) -> lsp::HoverParams {
+        lsp::HoverParams {
+            text_document_position_params: lsp::TextDocumentPositionParams {
+                text_document: lsp::TextDocumentIdentifier {
+                    uri: lsp::Url::from_file_path(path).unwrap(),
                 },
                 position: point_to_lsp(self.position),
             },
@@ -1122,7 +1122,7 @@ impl LspCommand for GetHover {
 
     async fn response_from_lsp(
         self,
-        message: Option<lsp2::Hover>,
+        message: Option<lsp::Hover>,
         _: Model<Project>,
         buffer: Model<Buffer>,
         _: LanguageServerId,
@@ -1144,15 +1144,13 @@ impl LspCommand for GetHover {
             )
         })?;
 
-        fn hover_blocks_from_marked_string(
-            marked_string: lsp2::MarkedString,
-        ) -> Option<HoverBlock> {
+        fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option<HoverBlock> {
             let block = match marked_string {
-                lsp2::MarkedString::String(content) => HoverBlock {
+                lsp::MarkedString::String(content) => HoverBlock {
                     text: content,
                     kind: HoverBlockKind::Markdown,
                 },
-                lsp2::MarkedString::LanguageString(lsp2::LanguageString { language, value }) => {
+                lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
                     HoverBlock {
                         text: value,
                         kind: HoverBlockKind::Code { language },
@@ -1167,18 +1165,18 @@ impl LspCommand for GetHover {
         }
 
         let contents = match hover.contents {
-            lsp2::HoverContents::Scalar(marked_string) => {
+            lsp::HoverContents::Scalar(marked_string) => {
                 hover_blocks_from_marked_string(marked_string)
                     .into_iter()
                     .collect()
             }
-            lsp2::HoverContents::Array(marked_strings) => marked_strings
+            lsp::HoverContents::Array(marked_strings) => marked_strings
                 .into_iter()
                 .filter_map(hover_blocks_from_marked_string)
                 .collect(),
-            lsp2::HoverContents::Markup(markup_content) => vec![HoverBlock {
+            lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
                 text: markup_content.value,
-                kind: if markup_content.kind == lsp2::MarkupKind::Markdown {
+                kind: if markup_content.kind == lsp::MarkupKind::Markdown {
                     HoverBlockKind::Markdown
                 } else {
                     HoverBlockKind::PlainText
@@ -1197,7 +1195,7 @@ impl LspCommand for GetHover {
         proto::GetHover {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             version: serialize_version(&buffer.version),
@@ -1234,8 +1232,8 @@ impl LspCommand for GetHover {
         if let Some(response) = response {
             let (start, end) = if let Some(range) = response.range {
                 (
-                    Some(language2::proto::serialize_anchor(&range.start)),
-                    Some(language2::proto::serialize_anchor(&range.end)),
+                    Some(language::proto::serialize_anchor(&range.start)),
+                    Some(language::proto::serialize_anchor(&range.end)),
                 )
             } else {
                 (None, None)
@@ -1296,8 +1294,8 @@ impl LspCommand for GetHover {
 
         let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
         let range = if let (Some(start), Some(end)) = (message.start, message.end) {
-            language2::proto::deserialize_anchor(start)
-                .and_then(|start| language2::proto::deserialize_anchor(end).map(|end| start..end))
+            language::proto::deserialize_anchor(start)
+                .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
         } else {
             None
         };
@@ -1317,7 +1315,7 @@ impl LspCommand for GetHover {
 #[async_trait(?Send)]
 impl LspCommand for GetCompletions {
     type Response = Vec<Completion>;
-    type LspRequest = lsp2::request::Completion;
+    type LspRequest = lsp::request::Completion;
     type ProtoRequest = proto::GetCompletions;
 
     fn to_lsp(
@@ -1326,10 +1324,10 @@ impl LspCommand for GetCompletions {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::CompletionParams {
-        lsp2::CompletionParams {
-            text_document_position: lsp2::TextDocumentPositionParams::new(
-                lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
+    ) -> lsp::CompletionParams {
+        lsp::CompletionParams {
+            text_document_position: lsp::TextDocumentPositionParams::new(
+                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
                 point_to_lsp(self.position),
             ),
             context: Default::default(),
@@ -1340,7 +1338,7 @@ impl LspCommand for GetCompletions {
 
     async fn response_from_lsp(
         self,
-        completions: Option<lsp2::CompletionResponse>,
+        completions: Option<lsp::CompletionResponse>,
         _: Model<Project>,
         buffer: Model<Buffer>,
         server_id: LanguageServerId,
@@ -1349,9 +1347,9 @@ impl LspCommand for GetCompletions {
         let mut response_list = None;
         let completions = if let Some(completions) = completions {
             match completions {
-                lsp2::CompletionResponse::Array(completions) => completions,
+                lsp::CompletionResponse::Array(completions) => completions,
 
-                lsp2::CompletionResponse::List(mut list) => {
+                lsp::CompletionResponse::List(mut list) => {
                     let items = std::mem::take(&mut list.items);
                     response_list = Some(list);
                     items
@@ -1373,7 +1371,7 @@ impl LspCommand for GetCompletions {
                     let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
                         // If the language server provides a range to overwrite, then
                         // check that the range is valid.
-                        Some(lsp2::CompletionTextEdit::Edit(edit)) => {
+                        Some(lsp::CompletionTextEdit::Edit(edit)) => {
                             let range = range_from_lsp(edit.range);
                             let start = snapshot.clip_point_utf16(range.start, Bias::Left);
                             let end = snapshot.clip_point_utf16(range.end, Bias::Left);
@@ -1439,7 +1437,7 @@ impl LspCommand for GetCompletions {
                             (range, text)
                         }
 
-                        Some(lsp2::CompletionTextEdit::InsertAndReplace(_)) => {
+                        Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
                             log::info!("unsupported insert/replace completion");
                             return None;
                         }
@@ -1457,7 +1455,7 @@ impl LspCommand for GetCompletions {
                             old_range,
                             new_text,
                             label: label.unwrap_or_else(|| {
-                                language2::CodeLabel::plain(
+                                language::CodeLabel::plain(
                                     lsp_completion.label.clone(),
                                     lsp_completion.filter_text.as_deref(),
                                 )
@@ -1477,7 +1475,7 @@ impl LspCommand for GetCompletions {
         proto::GetCompletions {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(&anchor)),
+            position: Some(language::proto::serialize_anchor(&anchor)),
             version: serialize_version(&buffer.version()),
         }
     }
@@ -1494,7 +1492,7 @@ impl LspCommand for GetCompletions {
             .await?;
         let position = message
             .position
-            .and_then(language2::proto::deserialize_anchor)
+            .and_then(language::proto::deserialize_anchor)
             .map(|p| {
                 buffer.update(&mut cx, |buffer, _| {
                     buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
@@ -1514,7 +1512,7 @@ impl LspCommand for GetCompletions {
         proto::GetCompletionsResponse {
             completions: completions
                 .iter()
-                .map(language2::proto::serialize_completion)
+                .map(language::proto::serialize_completion)
                 .collect(),
             version: serialize_version(&buffer_version),
         }
@@ -1535,7 +1533,7 @@ impl LspCommand for GetCompletions {
 
         let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
         let completions = message.completions.into_iter().map(|completion| {
-            language2::proto::deserialize_completion(completion, language.clone())
+            language::proto::deserialize_completion(completion, language.clone())
         });
         future::try_join_all(completions).await
     }
@@ -1548,13 +1546,13 @@ impl LspCommand for GetCompletions {
 #[async_trait(?Send)]
 impl LspCommand for GetCodeActions {
     type Response = Vec<CodeAction>;
-    type LspRequest = lsp2::request::CodeActionRequest;
+    type LspRequest = lsp::request::CodeActionRequest;
     type ProtoRequest = proto::GetCodeActions;
 
     fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
         match &capabilities.code_action_provider {
             None => false,
-            Some(lsp2::CodeActionProviderCapability::Simple(false)) => false,
+            Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
             _ => true,
         }
     }
@@ -1565,30 +1563,30 @@ impl LspCommand for GetCodeActions {
         buffer: &Buffer,
         language_server: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::CodeActionParams {
+    ) -> lsp::CodeActionParams {
         let relevant_diagnostics = buffer
             .snapshot()
             .diagnostics_in_range::<_, usize>(self.range.clone(), false)
             .map(|entry| entry.to_lsp_diagnostic_stub())
             .collect();
-        lsp2::CodeActionParams {
-            text_document: lsp2::TextDocumentIdentifier::new(
-                lsp2::Url::from_file_path(path).unwrap(),
+        lsp::CodeActionParams {
+            text_document: lsp::TextDocumentIdentifier::new(
+                lsp::Url::from_file_path(path).unwrap(),
             ),
             range: range_to_lsp(self.range.to_point_utf16(buffer)),
             work_done_progress_params: Default::default(),
             partial_result_params: Default::default(),
-            context: lsp2::CodeActionContext {
+            context: lsp::CodeActionContext {
                 diagnostics: relevant_diagnostics,
                 only: language_server.code_action_kinds(),
-                ..lsp2::CodeActionContext::default()
+                ..lsp::CodeActionContext::default()
             },
         }
     }
 
     async fn response_from_lsp(
         self,
-        actions: Option<lsp2::CodeActionResponse>,
+        actions: Option<lsp::CodeActionResponse>,
         _: Model<Project>,
         _: Model<Buffer>,
         server_id: LanguageServerId,
@@ -1598,7 +1596,7 @@ impl LspCommand for GetCodeActions {
             .unwrap_or_default()
             .into_iter()
             .filter_map(|entry| {
-                if let lsp2::CodeActionOrCommand::CodeAction(lsp_action) = entry {
+                if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
                     Some(CodeAction {
                         server_id,
                         range: self.range.clone(),
@@ -1615,8 +1613,8 @@ impl LspCommand for GetCodeActions {
         proto::GetCodeActions {
             project_id,
             buffer_id: buffer.remote_id(),
-            start: Some(language2::proto::serialize_anchor(&self.range.start)),
-            end: Some(language2::proto::serialize_anchor(&self.range.end)),
+            start: Some(language::proto::serialize_anchor(&self.range.start)),
+            end: Some(language::proto::serialize_anchor(&self.range.end)),
             version: serialize_version(&buffer.version()),
         }
     }
@@ -1629,11 +1627,11 @@ impl LspCommand for GetCodeActions {
     ) -> Result<Self> {
         let start = message
             .start
-            .and_then(language2::proto::deserialize_anchor)
+            .and_then(language::proto::deserialize_anchor)
             .ok_or_else(|| anyhow!("invalid start"))?;
         let end = message
             .end
-            .and_then(language2::proto::deserialize_anchor)
+            .and_then(language::proto::deserialize_anchor)
             .ok_or_else(|| anyhow!("invalid end"))?;
         buffer
             .update(&mut cx, |buffer, _| {
@@ -1654,7 +1652,7 @@ impl LspCommand for GetCodeActions {
         proto::GetCodeActionsResponse {
             actions: code_actions
                 .iter()
-                .map(language2::proto::serialize_code_action)
+                .map(language::proto::serialize_code_action)
                 .collect(),
             version: serialize_version(&buffer_version),
         }
@@ -1675,7 +1673,7 @@ impl LspCommand for GetCodeActions {
         message
             .actions
             .into_iter()
-            .map(language2::proto::deserialize_code_action)
+            .map(language::proto::deserialize_code_action)
             .collect()
     }
 
@@ -1687,10 +1685,10 @@ impl LspCommand for GetCodeActions {
 #[async_trait(?Send)]
 impl LspCommand for OnTypeFormatting {
     type Response = Option<Transaction>;
-    type LspRequest = lsp2::request::OnTypeFormatting;
+    type LspRequest = lsp::request::OnTypeFormatting;
     type ProtoRequest = proto::OnTypeFormatting;
 
-    fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
+    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
         let Some(on_type_formatting_options) =
             &server_capabilities.document_on_type_formatting_provider
         else {
@@ -1712,10 +1710,10 @@ impl LspCommand for OnTypeFormatting {
         _: &Buffer,
         _: &Arc<LanguageServer>,
         _: &AppContext,
-    ) -> lsp2::DocumentOnTypeFormattingParams {
-        lsp2::DocumentOnTypeFormattingParams {
-            text_document_position: lsp2::TextDocumentPositionParams::new(
-                lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path(path).unwrap()),
+    ) -> lsp::DocumentOnTypeFormattingParams {
+        lsp::DocumentOnTypeFormattingParams {
+            text_document_position: lsp::TextDocumentPositionParams::new(
+                lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
                 point_to_lsp(self.position),
             ),
             ch: self.trigger.clone(),
@@ -1725,7 +1723,7 @@ impl LspCommand for OnTypeFormatting {
 
     async fn response_from_lsp(
         self,
-        message: Option<Vec<lsp2::TextEdit>>,
+        message: Option<Vec<lsp::TextEdit>>,
         project: Model<Project>,
         buffer: Model<Buffer>,
         server_id: LanguageServerId,
@@ -1753,7 +1751,7 @@ impl LspCommand for OnTypeFormatting {
         proto::OnTypeFormatting {
             project_id,
             buffer_id: buffer.remote_id(),
-            position: Some(language2::proto::serialize_anchor(
+            position: Some(language::proto::serialize_anchor(
                 &buffer.anchor_before(self.position),
             )),
             trigger: self.trigger.clone(),
@@ -1798,7 +1796,7 @@ impl LspCommand for OnTypeFormatting {
     ) -> proto::OnTypeFormattingResponse {
         proto::OnTypeFormattingResponse {
             transaction: response
-                .map(|transaction| language2::proto::serialize_transaction(&transaction)),
+                .map(|transaction| language::proto::serialize_transaction(&transaction)),
         }
     }
 
@@ -1812,9 +1810,7 @@ impl LspCommand for OnTypeFormatting {
         let Some(transaction) = message.transaction else {
             return Ok(None);
         };
-        Ok(Some(language2::proto::deserialize_transaction(
-            transaction,
-        )?))
+        Ok(Some(language::proto::deserialize_transaction(transaction)?))
     }
 
     fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
@@ -1824,7 +1820,7 @@ impl LspCommand for OnTypeFormatting {
 
 impl InlayHints {
     pub async fn lsp_to_project_hint(
-        lsp_hint: lsp2::InlayHint,
+        lsp_hint: lsp::InlayHint,
         buffer_handle: &Model<Buffer>,
         server_id: LanguageServerId,
         resolve_state: ResolveState,
@@ -1832,8 +1828,8 @@ impl InlayHints {
         cx: &mut AsyncAppContext,
     ) -> anyhow::Result<InlayHint> {
         let kind = lsp_hint.kind.and_then(|kind| match kind {
-            lsp2::InlayHintKind::TYPE => Some(InlayHintKind::Type),
-            lsp2::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
+            lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
+            lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
             _ => None,
         });
 
@@ -1861,12 +1857,12 @@ impl InlayHints {
             label,
             kind,
             tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
-                lsp2::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
-                lsp2::InlayHintTooltip::MarkupContent(markup_content) => {
+                lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
+                lsp::InlayHintTooltip::MarkupContent(markup_content) => {
                     InlayHintTooltip::MarkupContent(MarkupContent {
                         kind: match markup_content.kind {
-                            lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
-                            lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
+                            lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
+                            lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
                         },
                         value: markup_content.value,
                     })
@@ -1877,25 +1873,25 @@ impl InlayHints {
     }
 
     async fn lsp_inlay_label_to_project(
-        lsp_label: lsp2::InlayHintLabel,
+        lsp_label: lsp::InlayHintLabel,
         server_id: LanguageServerId,
     ) -> anyhow::Result<InlayHintLabel> {
         let label = match lsp_label {
-            lsp2::InlayHintLabel::String(s) => InlayHintLabel::String(s),
-            lsp2::InlayHintLabel::LabelParts(lsp_parts) => {
+            lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
+            lsp::InlayHintLabel::LabelParts(lsp_parts) => {
                 let mut parts = Vec::with_capacity(lsp_parts.len());
                 for lsp_part in lsp_parts {
                     parts.push(InlayHintLabelPart {
                         value: lsp_part.value,
                         tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
-                            lsp2::InlayHintLabelPartTooltip::String(s) => {
+                            lsp::InlayHintLabelPartTooltip::String(s) => {
                                 InlayHintLabelPartTooltip::String(s)
                             }
-                            lsp2::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
+                            lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
                                 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
                                     kind: match markup_content.kind {
-                                        lsp2::MarkupKind::PlainText => HoverBlockKind::PlainText,
-                                        lsp2::MarkupKind::Markdown => HoverBlockKind::Markdown,
+                                        lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
+                                        lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
                                     },
                                     value: markup_content.value,
                                 })
@@ -1933,7 +1929,7 @@ impl InlayHints {
             lsp_resolve_state,
         });
         proto::InlayHint {
-            position: Some(language2::proto::serialize_anchor(&response_hint.position)),
+            position: Some(language::proto::serialize_anchor(&response_hint.position)),
             padding_left: response_hint.padding_left,
             padding_right: response_hint.padding_right,
             label: Some(proto::InlayHintLabel {
@@ -1992,7 +1988,7 @@ impl InlayHints {
         let resolve_state_data = resolve_state
             .lsp_resolve_state.as_ref()
             .map(|lsp_resolve_state| {
-                serde_json::from_str::<Option<lsp2::LSPAny>>(&lsp_resolve_state.value)
+                serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
                     .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
                     .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
             })
@@ -2015,7 +2011,7 @@ impl InlayHints {
         Ok(InlayHint {
             position: message_hint
                 .position
-                .and_then(language2::proto::deserialize_anchor)
+                .and_then(language::proto::deserialize_anchor)
                 .context("invalid position")?,
             label: match message_hint
                 .label
@@ -2058,10 +2054,10 @@ impl InlayHints {
                                 {
                                     Some(((uri, range), server_id)) => Some((
                                         LanguageServerId(server_id as usize),
-                                        lsp2::Location {
-                                            uri: lsp2::Url::parse(&uri)
+                                        lsp::Location {
+                                            uri: lsp::Url::parse(&uri)
                                                 .context("invalid uri in hint part {part:?}")?,
-                                            range: lsp2::Range::new(
+                                            range: lsp::Range::new(
                                                 point_to_lsp(PointUtf16::new(
                                                     range.start.row,
                                                     range.start.column,
@@ -2107,22 +2103,22 @@ impl InlayHints {
         })
     }
 
-    pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp2::InlayHint {
-        lsp2::InlayHint {
+    pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
+        lsp::InlayHint {
             position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
             kind: hint.kind.map(|kind| match kind {
-                InlayHintKind::Type => lsp2::InlayHintKind::TYPE,
-                InlayHintKind::Parameter => lsp2::InlayHintKind::PARAMETER,
+                InlayHintKind::Type => lsp::InlayHintKind::TYPE,
+                InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
             }),
             text_edits: None,
             tooltip: hint.tooltip.and_then(|tooltip| {
                 Some(match tooltip {
-                    InlayHintTooltip::String(s) => lsp2::InlayHintTooltip::String(s),
+                    InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
                     InlayHintTooltip::MarkupContent(markup_content) => {
-                        lsp2::InlayHintTooltip::MarkupContent(lsp2::MarkupContent {
+                        lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
                             kind: match markup_content.kind {
-                                HoverBlockKind::PlainText => lsp2::MarkupKind::PlainText,
-                                HoverBlockKind::Markdown => lsp2::MarkupKind::Markdown,
+                                HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
+                                HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
                                 HoverBlockKind::Code { .. } => return None,
                             },
                             value: markup_content.value,
@@ -2131,26 +2127,26 @@ impl InlayHints {
                 })
             }),
             label: match hint.label {
-                InlayHintLabel::String(s) => lsp2::InlayHintLabel::String(s),
-                InlayHintLabel::LabelParts(label_parts) => lsp2::InlayHintLabel::LabelParts(
+                InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
+                InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
                     label_parts
                         .into_iter()
-                        .map(|part| lsp2::InlayHintLabelPart {
+                        .map(|part| lsp::InlayHintLabelPart {
                             value: part.value,
                             tooltip: part.tooltip.and_then(|tooltip| {
                                 Some(match tooltip {
                                     InlayHintLabelPartTooltip::String(s) => {
-                                        lsp2::InlayHintLabelPartTooltip::String(s)
+                                        lsp::InlayHintLabelPartTooltip::String(s)
                                     }
                                     InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
-                                        lsp2::InlayHintLabelPartTooltip::MarkupContent(
-                                            lsp2::MarkupContent {
+                                        lsp::InlayHintLabelPartTooltip::MarkupContent(
+                                            lsp::MarkupContent {
                                                 kind: match markup_content.kind {
                                                     HoverBlockKind::PlainText => {
-                                                        lsp2::MarkupKind::PlainText
+                                                        lsp::MarkupKind::PlainText
                                                     }
                                                     HoverBlockKind::Markdown => {
-                                                        lsp2::MarkupKind::Markdown
+                                                        lsp::MarkupKind::Markdown
                                                     }
                                                     HoverBlockKind::Code { .. } => return None,
                                                 },
@@ -2182,8 +2178,8 @@ impl InlayHints {
             .and_then(|options| match options {
                 OneOf::Left(_is_supported) => None,
                 OneOf::Right(capabilities) => match capabilities {
-                    lsp2::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
-                    lsp2::InlayHintServerCapabilities::RegistrationOptions(o) => {
+                    lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
+                    lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
                         o.inlay_hint_options.resolve_provider
                     }
                 },
@@ -2195,18 +2191,18 @@ impl InlayHints {
 #[async_trait(?Send)]
 impl LspCommand for InlayHints {
     type Response = Vec<InlayHint>;
-    type LspRequest = lsp2::InlayHintRequest;
+    type LspRequest = lsp::InlayHintRequest;
     type ProtoRequest = proto::InlayHints;
 
-    fn check_capabilities(&self, server_capabilities: &lsp2::ServerCapabilities) -> bool {
+    fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
         let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
             return false;
         };
         match inlay_hint_provider {
-            lsp2::OneOf::Left(enabled) => *enabled,
-            lsp2::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
-                lsp2::InlayHintServerCapabilities::Options(_) => true,
-                lsp2::InlayHintServerCapabilities::RegistrationOptions(_) => false,
+            lsp::OneOf::Left(enabled) => *enabled,
+            lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
+                lsp::InlayHintServerCapabilities::Options(_) => true,
+                lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
             },
         }
     }

crates/project2/src/project2.rs 🔗

@@ -11,10 +11,10 @@ mod project_tests;
 mod worktree_tests;
 
 use anyhow::{anyhow, Context as _, Result};
-use client2::{proto, Client, Collaborator, TypedEnvelope, UserStore};
+use client::{proto, Client, Collaborator, TypedEnvelope, UserStore};
 use clock::ReplicaId;
 use collections::{hash_map, BTreeMap, HashMap, HashSet};
-use copilot2::Copilot;
+use copilot::Copilot;
 use futures::{
     channel::{
         mpsc::{self, UnboundedReceiver},
@@ -25,12 +25,12 @@ use futures::{
     AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt,
 };
 use globset::{Glob, GlobSet, GlobSetBuilder};
-use gpui2::{
+use gpui::{
     AnyModel, AppContext, AsyncAppContext, BackgroundExecutor, Context, Entity, EventEmitter,
     Model, ModelContext, Task, WeakModel,
 };
 use itertools::Itertools;
-use language2::{
+use language::{
     language_settings::{
         language_settings, FormatOnSave, Formatter, InlayHintKind, LanguageSettings,
     },
@@ -46,7 +46,7 @@ use language2::{
     ToOffset, ToPointUtf16, Transaction, Unclipped,
 };
 use log::error;
-use lsp2::{
+use lsp::{
     DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
     DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId, OneOf,
 };
@@ -54,12 +54,12 @@ use lsp_command::*;
 use node_runtime::NodeRuntime;
 use parking_lot::Mutex;
 use postage::watch;
-use prettier2::{LocateStart, Prettier};
+use prettier::{LocateStart, Prettier};
 use project_settings::{LspSettings, ProjectSettings};
 use rand::prelude::*;
 use search::SearchQuery;
 use serde::Serialize;
-use settings2::{Settings, SettingsStore};
+use settings::{Settings, SettingsStore};
 use sha2::{Digest, Sha256};
 use similar::{ChangeTag, TextDiff};
 use smol::channel::{Receiver, Sender};
@@ -86,9 +86,9 @@ use util::{
     paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _,
 };
 
-pub use fs2::*;
+pub use fs::*;
 #[cfg(any(test, feature = "test-support"))]
-pub use prettier2::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
+pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
 pub use worktree::*;
 
 const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
@@ -123,7 +123,7 @@ pub struct Project {
     language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
     language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
     last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
-    client: Arc<client2::Client>,
+    client: Arc<client::Client>,
     next_entry_id: Arc<AtomicUsize>,
     join_project_response_message_id: u32,
     next_diagnostic_group_id: usize,
@@ -131,8 +131,8 @@ pub struct Project {
     fs: Arc<dyn Fs>,
     client_state: Option<ProjectClientState>,
     collaborators: HashMap<proto::PeerId, Collaborator>,
-    client_subscriptions: Vec<client2::Subscription>,
-    _subscriptions: Vec<gpui2::Subscription>,
+    client_subscriptions: Vec<client::Subscription>,
+    _subscriptions: Vec<gpui::Subscription>,
     next_buffer_id: u64,
     opened_buffer: (watch::Sender<()>, watch::Receiver<()>),
     shared_buffers: HashMap<proto::PeerId, HashSet<u64>>,
@@ -158,8 +158,8 @@ pub struct Project {
     _maintain_buffer_languages: Task<()>,
     _maintain_workspace_config: Task<Result<()>>,
     terminals: Terminals,
-    copilot_lsp_subscription: Option<gpui2::Subscription>,
-    copilot_log_subscription: Option<lsp2::Subscription>,
+    copilot_lsp_subscription: Option<gpui::Subscription>,
+    copilot_log_subscription: Option<lsp::Subscription>,
     current_lsp_settings: HashMap<Arc<str>, LspSettings>,
     node: Option<Arc<dyn NodeRuntime>>,
     #[cfg(not(any(test, feature = "test-support")))]
@@ -352,12 +352,12 @@ pub struct DiagnosticSummary {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Location {
     pub buffer: Model<Buffer>,
-    pub range: Range<language2::Anchor>,
+    pub range: Range<language::Anchor>,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct InlayHint {
-    pub position: language2::Anchor,
+    pub position: language::Anchor,
     pub label: InlayHintLabel,
     pub kind: Option<InlayHintKind>,
     pub padding_left: bool,
@@ -369,7 +369,7 @@ pub struct InlayHint {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ResolveState {
     Resolved,
-    CanResolve(LanguageServerId, Option<lsp2::LSPAny>),
+    CanResolve(LanguageServerId, Option<lsp::LSPAny>),
     Resolving,
 }
 
@@ -392,7 +392,7 @@ pub enum InlayHintLabel {
 pub struct InlayHintLabelPart {
     pub value: String,
     pub tooltip: Option<InlayHintLabelPartTooltip>,
-    pub location: Option<(LanguageServerId, lsp2::Location)>,
+    pub location: Option<(LanguageServerId, lsp::Location)>,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -421,7 +421,7 @@ pub struct LocationLink {
 
 #[derive(Debug)]
 pub struct DocumentHighlight {
-    pub range: Range<language2::Anchor>,
+    pub range: Range<language::Anchor>,
     pub kind: DocumentHighlightKind,
 }
 
@@ -432,7 +432,7 @@ pub struct Symbol {
     pub path: ProjectPath,
     pub label: CodeLabel,
     pub name: String,
-    pub kind: lsp2::SymbolKind,
+    pub kind: lsp::SymbolKind,
     pub range: Range<Unclipped<PointUtf16>>,
     pub signature: [u8; 32],
 }
@@ -453,7 +453,7 @@ pub enum HoverBlockKind {
 #[derive(Debug)]
 pub struct Hover {
     pub contents: Vec<HoverBlock>,
-    pub range: Option<Range<language2::Anchor>>,
+    pub range: Option<Range<language::Anchor>>,
     pub language: Option<Arc<Language>>,
 }
 
@@ -464,7 +464,7 @@ impl Hover {
 }
 
 #[derive(Default)]
-pub struct ProjectTransaction(pub HashMap<Model<Buffer>, language2::Transaction>);
+pub struct ProjectTransaction(pub HashMap<Model<Buffer>, language::Transaction>);
 
 impl DiagnosticSummary {
     fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
@@ -859,12 +859,12 @@ impl Project {
     pub async fn test(
         fs: Arc<dyn Fs>,
         root_paths: impl IntoIterator<Item = &Path>,
-        cx: &mut gpui2::TestAppContext,
+        cx: &mut gpui::TestAppContext,
     ) -> Model<Project> {
         let mut languages = LanguageRegistry::test();
         languages.set_executor(cx.executor().clone());
         let http_client = util::http::FakeHttpClient::with_404_response();
-        let client = cx.update(|cx| client2::Client::new(http_client.clone(), cx));
+        let client = cx.update(|cx| client::Client::new(http_client.clone(), cx));
         let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http_client, cx));
         let project = cx.update(|cx| {
             Project::local(
@@ -1669,10 +1669,8 @@ impl Project {
         }
         let id = post_inc(&mut self.next_buffer_id);
         let buffer = cx.build_model(|cx| {
-            Buffer::new(self.replica_id(), id, text).with_language(
-                language.unwrap_or_else(|| language2::PLAIN_TEXT.clone()),
-                cx,
-            )
+            Buffer::new(self.replica_id(), id, text)
+                .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx)
         });
         self.register_buffer(&buffer, cx)?;
         Ok(buffer)
@@ -1812,7 +1810,7 @@ impl Project {
     /// LanguageServerName is owned, because it is inserted into a map
     pub fn open_local_buffer_via_lsp(
         &mut self,
-        abs_path: lsp2::Url,
+        abs_path: lsp::Url,
         language_server_id: LanguageServerId,
         language_server_name: LanguageServerName,
         cx: &mut ModelContext<Self>,
@@ -2019,13 +2017,13 @@ impl Project {
         cx.observe_release(buffer, |this, buffer, cx| {
             if let Some(file) = File::from_dyn(buffer.file()) {
                 if file.is_local() {
-                    let uri = lsp2::Url::from_file_path(file.abs_path(cx)).unwrap();
+                    let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
                     for server in this.language_servers_for_buffer(buffer, cx) {
                         server
                             .1
-                            .notify::<lsp2::notification::DidCloseTextDocument>(
-                                lsp2::DidCloseTextDocumentParams {
-                                    text_document: lsp2::TextDocumentIdentifier::new(uri.clone()),
+                            .notify::<lsp::notification::DidCloseTextDocument>(
+                                lsp::DidCloseTextDocumentParams {
+                                    text_document: lsp::TextDocumentIdentifier::new(uri.clone()),
                                 },
                             )
                             .log_err();
@@ -2053,7 +2051,7 @@ impl Project {
             }
 
             let abs_path = file.abs_path(cx);
-            let uri = lsp2::Url::from_file_path(&abs_path)
+            let uri = lsp::Url::from_file_path(&abs_path)
                 .unwrap_or_else(|()| panic!("Failed to register file {abs_path:?}"));
             let initial_snapshot = buffer.text_snapshot();
             let language = buffer.language().cloned();
@@ -2086,9 +2084,9 @@ impl Project {
                     };
 
                     server
-                        .notify::<lsp2::notification::DidOpenTextDocument>(
-                            lsp2::DidOpenTextDocumentParams {
-                                text_document: lsp2::TextDocumentItem::new(
+                        .notify::<lsp::notification::DidOpenTextDocument>(
+                            lsp::DidOpenTextDocumentParams {
+                                text_document: lsp::TextDocumentItem::new(
                                     uri.clone(),
                                     language_id.unwrap_or_default(),
                                     0,
@@ -2145,12 +2143,12 @@ impl Project {
             }
 
             self.buffer_snapshots.remove(&buffer.remote_id());
-            let file_url = lsp2::Url::from_file_path(old_path).unwrap();
+            let file_url = lsp::Url::from_file_path(old_path).unwrap();
             for (_, language_server) in self.language_servers_for_buffer(buffer, cx) {
                 language_server
-                    .notify::<lsp2::notification::DidCloseTextDocument>(
-                        lsp2::DidCloseTextDocumentParams {
-                            text_document: lsp2::TextDocumentIdentifier::new(file_url.clone()),
+                    .notify::<lsp::notification::DidCloseTextDocument>(
+                        lsp::DidCloseTextDocumentParams {
+                            text_document: lsp::TextDocumentIdentifier::new(file_url.clone()),
                         },
                     )
                     .log_err();
@@ -2294,7 +2292,7 @@ impl Project {
                 self.buffer_ordered_messages_tx
                     .unbounded_send(BufferOrderedMessage::Operation {
                         buffer_id: buffer.read(cx).remote_id(),
-                        operation: language2::proto::serialize_operation(operation),
+                        operation: language::proto::serialize_operation(operation),
                     })
                     .ok();
             }
@@ -2303,7 +2301,7 @@ impl Project {
                 let buffer = buffer.read(cx);
                 let file = File::from_dyn(buffer.file())?;
                 let abs_path = file.as_local()?.abs_path(cx);
-                let uri = lsp2::Url::from_file_path(abs_path).unwrap();
+                let uri = lsp::Url::from_file_path(abs_path).unwrap();
                 let next_snapshot = buffer.text_snapshot();
 
                 let language_servers: Vec<_> = self
@@ -2331,8 +2329,8 @@ impl Project {
                                 let new_text = next_snapshot
                                     .text_for_range(edit.new.start.1..edit.new.end.1)
                                     .collect();
-                                lsp2::TextDocumentContentChangeEvent {
-                                    range: Some(lsp2::Range::new(
+                                lsp::TextDocumentContentChangeEvent {
+                                    range: Some(lsp::Range::new(
                                         point_to_lsp(edit_start),
                                         point_to_lsp(edit_end),
                                     )),
@@ -2348,19 +2346,19 @@ impl Project {
                         .text_document_sync
                         .as_ref()
                         .and_then(|sync| match sync {
-                            lsp2::TextDocumentSyncCapability::Kind(kind) => Some(*kind),
-                            lsp2::TextDocumentSyncCapability::Options(options) => options.change,
+                            lsp::TextDocumentSyncCapability::Kind(kind) => Some(*kind),
+                            lsp::TextDocumentSyncCapability::Options(options) => options.change,
                         });
 
                     let content_changes: Vec<_> = match document_sync_kind {
-                        Some(lsp2::TextDocumentSyncKind::FULL) => {
-                            vec![lsp2::TextDocumentContentChangeEvent {
+                        Some(lsp::TextDocumentSyncKind::FULL) => {
+                            vec![lsp::TextDocumentContentChangeEvent {
                                 range: None,
                                 range_length: None,
                                 text: next_snapshot.text(),
                             }]
                         }
-                        Some(lsp2::TextDocumentSyncKind::INCREMENTAL) => build_incremental_change(),
+                        Some(lsp::TextDocumentSyncKind::INCREMENTAL) => build_incremental_change(),
                         _ => {
                             #[cfg(any(test, feature = "test-support"))]
                             {
@@ -2382,9 +2380,9 @@ impl Project {
                     });
 
                     language_server
-                        .notify::<lsp2::notification::DidChangeTextDocument>(
-                            lsp2::DidChangeTextDocumentParams {
-                                text_document: lsp2::VersionedTextDocumentIdentifier::new(
+                        .notify::<lsp::notification::DidChangeTextDocument>(
+                            lsp::DidChangeTextDocumentParams {
+                                text_document: lsp::VersionedTextDocumentIdentifier::new(
                                     uri.clone(),
                                     next_version,
                                 ),
@@ -2399,16 +2397,16 @@ impl Project {
                 let file = File::from_dyn(buffer.read(cx).file())?;
                 let worktree_id = file.worktree_id(cx);
                 let abs_path = file.as_local()?.abs_path(cx);
-                let text_document = lsp2::TextDocumentIdentifier {
-                    uri: lsp2::Url::from_file_path(abs_path).unwrap(),
+                let text_document = lsp::TextDocumentIdentifier {
+                    uri: lsp::Url::from_file_path(abs_path).unwrap(),
                 };
 
                 for (_, _, server) in self.language_servers_for_worktree(worktree_id) {
                     let text = include_text(server.as_ref()).then(|| buffer.read(cx).text());
 
                     server
-                        .notify::<lsp2::notification::DidSaveTextDocument>(
-                            lsp2::DidSaveTextDocumentParams {
+                        .notify::<lsp::notification::DidSaveTextDocument>(
+                            lsp::DidSaveTextDocumentParams {
                                 text_document: text_document.clone(),
                                 text,
                             },
@@ -2621,7 +2619,7 @@ impl Project {
                                 if let Some(handle) = buffer.upgrade() {
                                     let buffer = &handle.read(cx);
                                     if buffer.language().is_none()
-                                        || buffer.language() == Some(&*language2::PLAIN_TEXT)
+                                        || buffer.language() == Some(&*language::PLAIN_TEXT)
                                     {
                                         plain_text_buffers.push(handle);
                                     } else if buffer.contains_unknown_injections() {
@@ -2671,8 +2669,8 @@ impl Project {
                     let workspace_config =
                         cx.update(|cx| adapter.workspace_configuration(cx))?.await;
                     server
-                        .notify::<lsp2::notification::DidChangeConfiguration>(
-                            lsp2::DidChangeConfigurationParams {
+                        .notify::<lsp::notification::DidChangeConfiguration>(
+                            lsp::DidChangeConfigurationParams {
                                 settings: workspace_config.clone(),
                             },
                         )
@@ -2992,7 +2990,7 @@ impl Project {
         let language_server = pending_server.task.await?;
 
         language_server
-            .on_notification::<lsp2::notification::PublishDiagnostics, _>({
+            .on_notification::<lsp::notification::PublishDiagnostics, _>({
                 let adapter = adapter.clone();
                 let this = this.clone();
                 move |mut params, mut cx| {
@@ -3015,7 +3013,7 @@ impl Project {
             .detach();
 
         language_server
-            .on_request::<lsp2::request::WorkspaceConfiguration, _, _>({
+            .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
                 let adapter = adapter.clone();
                 move |params, cx| {
                     let adapter = adapter.clone();
@@ -3045,7 +3043,7 @@ impl Project {
         // avoid stalling any language server like `gopls` which waits for a response
         // to these requests when initializing.
         language_server
-            .on_request::<lsp2::request::WorkDoneProgressCreate, _, _>({
+            .on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
                 let this = this.clone();
                 move |params, mut cx| {
                     let this = this.clone();
@@ -3053,7 +3051,7 @@ impl Project {
                         this.update(&mut cx, |this, _| {
                             if let Some(status) = this.language_server_statuses.get_mut(&server_id)
                             {
-                                if let lsp2::NumberOrString::String(token) = params.token {
+                                if let lsp::NumberOrString::String(token) = params.token {
                                     status.progress_tokens.insert(token);
                                 }
                             }
@@ -3066,7 +3064,7 @@ impl Project {
             .detach();
 
         language_server
-            .on_request::<lsp2::request::RegisterCapability, _, _>({
+            .on_request::<lsp::request::RegisterCapability, _, _>({
                 let this = this.clone();
                 move |params, mut cx| {
                     let this = this.clone();
@@ -3090,7 +3088,7 @@ impl Project {
             .detach();
 
         language_server
-            .on_request::<lsp2::request::ApplyWorkspaceEdit, _, _>({
+            .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
                 let adapter = adapter.clone();
                 let this = this.clone();
                 move |params, cx| {
@@ -3106,7 +3104,7 @@ impl Project {
             .detach();
 
         language_server
-            .on_request::<lsp2::request::InlayHintRefreshRequest, _, _>({
+            .on_request::<lsp::request::InlayHintRefreshRequest, _, _>({
                 let this = this.clone();
                 move |(), mut cx| {
                     let this = this.clone();
@@ -3128,7 +3126,7 @@ impl Project {
             adapter.disk_based_diagnostics_progress_token.clone();
 
         language_server
-            .on_notification::<lsp2::notification::Progress, _>(move |params, mut cx| {
+            .on_notification::<lsp::notification::Progress, _>(move |params, mut cx| {
                 if let Some(this) = this.upgrade() {
                     this.update(&mut cx, |this, cx| {
                         this.on_lsp_progress(
@@ -3146,8 +3144,8 @@ impl Project {
         let language_server = language_server.initialize(initialization_options).await?;
 
         language_server
-            .notify::<lsp2::notification::DidChangeConfiguration>(
-                lsp2::DidChangeConfigurationParams {
+            .notify::<lsp::notification::DidChangeConfiguration>(
+                lsp::DidChangeConfigurationParams {
                     settings: workspace_config,
                 },
             )
@@ -3250,10 +3248,10 @@ impl Project {
                 let snapshot = versions.last().unwrap();
                 let version = snapshot.version;
                 let initial_snapshot = &snapshot.snapshot;
-                let uri = lsp2::Url::from_file_path(file.abs_path(cx)).unwrap();
-                language_server.notify::<lsp2::notification::DidOpenTextDocument>(
-                    lsp2::DidOpenTextDocumentParams {
-                        text_document: lsp2::TextDocumentItem::new(
+                let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
+                language_server.notify::<lsp::notification::DidOpenTextDocument>(
+                    lsp::DidOpenTextDocumentParams {
+                        text_document: lsp::TextDocumentItem::new(
                             uri,
                             adapter
                                 .language_ids
@@ -3516,19 +3514,19 @@ impl Project {
 
     fn on_lsp_progress(
         &mut self,
-        progress: lsp2::ProgressParams,
+        progress: lsp::ProgressParams,
         language_server_id: LanguageServerId,
         disk_based_diagnostics_progress_token: Option<String>,
         cx: &mut ModelContext<Self>,
     ) {
         let token = match progress.token {
-            lsp2::NumberOrString::String(token) => token,
-            lsp2::NumberOrString::Number(token) => {
+            lsp::NumberOrString::String(token) => token,
+            lsp::NumberOrString::Number(token) => {
                 log::info!("skipping numeric progress token {}", token);
                 return;
             }
         };
-        let lsp2::ProgressParamsValue::WorkDone(progress) = progress.value;
+        let lsp::ProgressParamsValue::WorkDone(progress) = progress.value;
         let language_server_status =
             if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
                 status
@@ -3547,7 +3545,7 @@ impl Project {
             });
 
         match progress {
-            lsp2::WorkDoneProgress::Begin(report) => {
+            lsp::WorkDoneProgress::Begin(report) => {
                 if is_disk_based_diagnostics_progress {
                     language_server_status.has_pending_diagnostic_updates = true;
                     self.disk_based_diagnostics_started(language_server_id, cx);
@@ -3582,7 +3580,7 @@ impl Project {
                         .ok();
                 }
             }
-            lsp2::WorkDoneProgress::Report(report) => {
+            lsp::WorkDoneProgress::Report(report) => {
                 if !is_disk_based_diagnostics_progress {
                     self.on_lsp_work_progress(
                         language_server_id,
@@ -3608,7 +3606,7 @@ impl Project {
                         .ok();
                 }
             }
-            lsp2::WorkDoneProgress::End(_) => {
+            lsp::WorkDoneProgress::End(_) => {
                 language_server_status.progress_tokens.remove(&token);
 
                 if is_disk_based_diagnostics_progress {
@@ -3707,15 +3705,15 @@ impl Project {
                         let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
                             if let Some(abs_path) = tree.abs_path().to_str() {
                                 let relative_glob_pattern = match &watcher.glob_pattern {
-                                    lsp2::GlobPattern::String(s) => s
+                                    lsp::GlobPattern::String(s) => s
                                         .strip_prefix(abs_path)
                                         .and_then(|s| s.strip_prefix(std::path::MAIN_SEPARATOR)),
-                                    lsp2::GlobPattern::Relative(rp) => {
+                                    lsp::GlobPattern::Relative(rp) => {
                                         let base_uri = match &rp.base_uri {
-                                            lsp2::OneOf::Left(workspace_folder) => {
+                                            lsp::OneOf::Left(workspace_folder) => {
                                                 &workspace_folder.uri
                                             }
-                                            lsp2::OneOf::Right(base_uri) => base_uri,
+                                            lsp::OneOf::Right(base_uri) => base_uri,
                                         };
                                         base_uri.to_file_path().ok().and_then(|file_path| {
                                             (file_path.to_str() == Some(abs_path))
@@ -3760,11 +3758,11 @@ impl Project {
 
     async fn on_lsp_workspace_edit(
         this: WeakModel<Self>,
-        params: lsp2::ApplyWorkspaceEditParams,
+        params: lsp::ApplyWorkspaceEditParams,
         server_id: LanguageServerId,
         adapter: Arc<CachedLspAdapter>,
         mut cx: AsyncAppContext,
-    ) -> Result<lsp2::ApplyWorkspaceEditResponse> {
+    ) -> Result<lsp::ApplyWorkspaceEditResponse> {
         let this = this
             .upgrade()
             .ok_or_else(|| anyhow!("project project closed"))?;
@@ -3787,7 +3785,7 @@ impl Project {
                     .insert(server_id, transaction);
             }
         })?;
-        Ok(lsp2::ApplyWorkspaceEditResponse {
+        Ok(lsp::ApplyWorkspaceEditResponse {
             applied: true,
             failed_change: None,
             failure_reason: None,
@@ -3803,7 +3801,7 @@ impl Project {
     pub fn update_diagnostics(
         &mut self,
         language_server_id: LanguageServerId,
-        mut params: lsp2::PublishDiagnosticsParams,
+        mut params: lsp::PublishDiagnosticsParams,
         disk_based_sources: &[String],
         cx: &mut ModelContext<Self>,
     ) -> Result<()> {
@@ -3822,8 +3820,8 @@ impl Project {
         for diagnostic in &params.diagnostics {
             let source = diagnostic.source.as_ref();
             let code = diagnostic.code.as_ref().map(|code| match code {
-                lsp2::NumberOrString::Number(code) => code.to_string(),
-                lsp2::NumberOrString::String(code) => code.clone(),
+                lsp::NumberOrString::Number(code) => code.to_string(),
+                lsp::NumberOrString::String(code) => code.clone(),
             });
             let range = range_from_lsp(diagnostic.range);
             let is_supporting = diagnostic
@@ -4378,9 +4376,9 @@ impl Project {
         tab_size: NonZeroU32,
         cx: &mut AsyncAppContext,
     ) -> Result<Vec<(Range<Anchor>, String)>> {
-        let uri = lsp2::Url::from_file_path(abs_path)
+        let uri = lsp::Url::from_file_path(abs_path)
             .map_err(|_| anyhow!("failed to convert abs path to uri"))?;
-        let text_document = lsp2::TextDocumentIdentifier::new(uri);
+        let text_document = lsp::TextDocumentIdentifier::new(uri);
         let capabilities = &language_server.capabilities();
 
         let formatting_provider = capabilities.document_formatting_provider.as_ref();
@@ -4388,20 +4386,20 @@ impl Project {
 
         let lsp_edits = if matches!(formatting_provider, Some(p) if *p != OneOf::Left(false)) {
             language_server
-                .request::<lsp2::request::Formatting>(lsp2::DocumentFormattingParams {
+                .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
                     text_document,
                     options: lsp_command::lsp_formatting_options(tab_size.get()),
                     work_done_progress_params: Default::default(),
                 })
                 .await?
         } else if matches!(range_formatting_provider, Some(p) if *p != OneOf::Left(false)) {
-            let buffer_start = lsp2::Position::new(0, 0);
+            let buffer_start = lsp::Position::new(0, 0);
             let buffer_end = buffer.update(cx, |b, _| point_to_lsp(b.max_point_utf16()))?;
 
             language_server
-                .request::<lsp2::request::RangeFormatting>(lsp2::DocumentRangeFormattingParams {
+                .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
                     text_document,
-                    range: lsp2::Range::new(buffer_start, buffer_end),
+                    range: lsp::Range::new(buffer_start, buffer_end),
                     options: lsp_command::lsp_formatting_options(tab_size.get()),
                     work_done_progress_params: Default::default(),
                 })
@@ -4564,8 +4562,8 @@ impl Project {
 
                 requests.push(
                     server
-                        .request::<lsp2::request::WorkspaceSymbolRequest>(
-                            lsp2::WorkspaceSymbolParams {
+                        .request::<lsp::request::WorkspaceSymbolRequest>(
+                            lsp::WorkspaceSymbolParams {
                                 query: query.to_string(),
                                 ..Default::default()
                             },
@@ -4573,12 +4571,12 @@ impl Project {
                         .log_err()
                         .map(move |response| {
                             let lsp_symbols = response.flatten().map(|symbol_response| match symbol_response {
-                                lsp2::WorkspaceSymbolResponse::Flat(flat_responses) => {
+                                lsp::WorkspaceSymbolResponse::Flat(flat_responses) => {
                                     flat_responses.into_iter().map(|lsp_symbol| {
                                         (lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location)
                                     }).collect::<Vec<_>>()
                                 }
-                                lsp2::WorkspaceSymbolResponse::Nested(nested_responses) => {
+                                lsp::WorkspaceSymbolResponse::Nested(nested_responses) => {
                                     nested_responses.into_iter().filter_map(|lsp_symbol| {
                                         let location = match lsp_symbol.location {
                                             OneOf::Left(location) => location,
@@ -4728,7 +4726,7 @@ impl Project {
                 return Task::ready(Err(anyhow!("worktree not found for symbol")));
             };
             let symbol_abs_path = worktree_abs_path.join(&symbol.path.path);
-            let symbol_uri = if let Ok(uri) = lsp2::Url::from_file_path(symbol_abs_path) {
+            let symbol_uri = if let Ok(uri) = lsp::Url::from_file_path(symbol_abs_path) {
                 uri
             } else {
                 return Task::ready(Err(anyhow!("invalid symbol path")));
@@ -4852,7 +4850,7 @@ impl Project {
                     .unwrap_or(false);
                 let additional_text_edits = if can_resolve {
                     lang_server
-                        .request::<lsp2::request::ResolveCompletionItem>(completion.lsp_completion)
+                        .request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion)
                         .await?
                         .additional_text_edits
                 } else {
@@ -4911,12 +4909,12 @@ impl Project {
                     .request(proto::ApplyCompletionAdditionalEdits {
                         project_id,
                         buffer_id,
-                        completion: Some(language2::proto::serialize_completion(&completion)),
+                        completion: Some(language::proto::serialize_completion(&completion)),
                     })
                     .await?;
 
                 if let Some(transaction) = response.transaction {
-                    let transaction = language2::proto::deserialize_transaction(transaction)?;
+                    let transaction = language::proto::deserialize_transaction(transaction)?;
                     buffer_handle
                         .update(&mut cx, |buffer, _| {
                             buffer.wait_for_edits(transaction.edit_ids.iter().copied())
@@ -4981,7 +4979,7 @@ impl Project {
                 {
                     *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap();
                     action.lsp_action = lang_server
-                        .request::<lsp2::request::CodeActionResolveRequest>(action.lsp_action)
+                        .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
                         .await?;
                 } else {
                     let actions = this
@@ -5017,7 +5015,7 @@ impl Project {
                     })?;
 
                     let result = lang_server
-                        .request::<lsp2::request::ExecuteCommand>(lsp2::ExecuteCommandParams {
+                        .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
                             command: command.command,
                             arguments: command.arguments.unwrap_or_default(),
                             ..Default::default()
@@ -5043,7 +5041,7 @@ impl Project {
             let request = proto::ApplyCodeAction {
                 project_id,
                 buffer_id: buffer_handle.read(cx).remote_id(),
-                action: Some(language2::proto::serialize_code_action(&action)),
+                action: Some(language::proto::serialize_code_action(&action)),
             };
             cx.spawn(move |this, mut cx| async move {
                 let response = client
@@ -5115,7 +5113,7 @@ impl Project {
                     .request(request)
                     .await?
                     .transaction
-                    .map(language2::proto::deserialize_transaction)
+                    .map(language::proto::deserialize_transaction)
                     .transpose()
             })
         } else {
@@ -5126,7 +5124,7 @@ impl Project {
     async fn deserialize_edits(
         this: Model<Self>,
         buffer_to_edit: Model<Buffer>,
-        edits: Vec<lsp2::TextEdit>,
+        edits: Vec<lsp::TextEdit>,
         push_to_history: bool,
         _: Arc<CachedLspAdapter>,
         language_server: Arc<LanguageServer>,
@@ -5167,7 +5165,7 @@ impl Project {
 
     async fn deserialize_workspace_edit(
         this: Model<Self>,
-        edit: lsp2::WorkspaceEdit,
+        edit: lsp::WorkspaceEdit,
         push_to_history: bool,
         lsp_adapter: Arc<CachedLspAdapter>,
         language_server: Arc<LanguageServer>,
@@ -5177,15 +5175,15 @@ impl Project {
         let mut operations = Vec::new();
         if let Some(document_changes) = edit.document_changes {
             match document_changes {
-                lsp2::DocumentChanges::Edits(edits) => {
-                    operations.extend(edits.into_iter().map(lsp2::DocumentChangeOperation::Edit))
+                lsp::DocumentChanges::Edits(edits) => {
+                    operations.extend(edits.into_iter().map(lsp::DocumentChangeOperation::Edit))
                 }
-                lsp2::DocumentChanges::Operations(ops) => operations = ops,
+                lsp::DocumentChanges::Operations(ops) => operations = ops,
             }
         } else if let Some(changes) = edit.changes {
             operations.extend(changes.into_iter().map(|(uri, edits)| {
-                lsp2::DocumentChangeOperation::Edit(lsp2::TextDocumentEdit {
-                    text_document: lsp2::OptionalVersionedTextDocumentIdentifier {
+                lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
+                    text_document: lsp::OptionalVersionedTextDocumentIdentifier {
                         uri,
                         version: None,
                     },
@@ -5197,7 +5195,7 @@ impl Project {
         let mut project_transaction = ProjectTransaction::default();
         for operation in operations {
             match operation {
-                lsp2::DocumentChangeOperation::Op(lsp2::ResourceOp::Create(op)) => {
+                lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(op)) => {
                     let abs_path = op
                         .uri
                         .to_file_path()
@@ -5212,7 +5210,7 @@ impl Project {
                         fs.create_file(
                             &abs_path,
                             op.options
-                                .map(|options| fs2::CreateOptions {
+                                .map(|options| fs::CreateOptions {
                                     overwrite: options.overwrite.unwrap_or(false),
                                     ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
                                 })
@@ -5222,7 +5220,7 @@ impl Project {
                     }
                 }
 
-                lsp2::DocumentChangeOperation::Op(lsp2::ResourceOp::Rename(op)) => {
+                lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Rename(op)) => {
                     let source_abs_path = op
                         .old_uri
                         .to_file_path()
@@ -5235,7 +5233,7 @@ impl Project {
                         &source_abs_path,
                         &target_abs_path,
                         op.options
-                            .map(|options| fs2::RenameOptions {
+                            .map(|options| fs::RenameOptions {
                                 overwrite: options.overwrite.unwrap_or(false),
                                 ignore_if_exists: options.ignore_if_exists.unwrap_or(false),
                             })
@@ -5244,14 +5242,14 @@ impl Project {
                     .await?;
                 }
 
-                lsp2::DocumentChangeOperation::Op(lsp2::ResourceOp::Delete(op)) => {
+                lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Delete(op)) => {
                     let abs_path = op
                         .uri
                         .to_file_path()
                         .map_err(|_| anyhow!("can't convert URI to path"))?;
                     let options = op
                         .options
-                        .map(|options| fs2::RemoveOptions {
+                        .map(|options| fs::RemoveOptions {
                             recursive: options.recursive.unwrap_or(false),
                             ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false),
                         })
@@ -5263,7 +5261,7 @@ impl Project {
                     }
                 }
 
-                lsp2::DocumentChangeOperation::Edit(op) => {
+                lsp::DocumentChangeOperation::Edit(op) => {
                     let buffer_to_edit = this
                         .update(cx, |this, cx| {
                             this.open_local_buffer_via_lsp(
@@ -5466,7 +5464,7 @@ impl Project {
 
             let buffer_snapshot = buffer.snapshot();
             cx.spawn(move |_, mut cx| async move {
-                let resolve_task = lang_server.request::<lsp2::request::InlayHintResolveRequest>(
+                let resolve_task = lang_server.request::<lsp::request::InlayHintResolveRequest>(
                     InlayHints::project_to_lsp_hint(hint, &buffer_snapshot),
                 );
                 let resolved_hint = resolve_task
@@ -5846,8 +5844,8 @@ impl Project {
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<R::Response>>
     where
-        <R::LspRequest as lsp2::request::Request>::Result: Send,
-        <R::LspRequest as lsp2::request::Request>::Params: Send,
+        <R::LspRequest as lsp::request::Request>::Result: Send,
+        <R::LspRequest as lsp::request::Request>::Params: Send,
     {
         let buffer = buffer_handle.read(cx);
         if self.is_local() {
@@ -6281,7 +6279,7 @@ impl Project {
             }) = self.language_servers.get(server_id)
             {
                 if let Some(watched_paths) = watched_paths.get(&worktree_id) {
-                    let params = lsp2::DidChangeWatchedFilesParams {
+                    let params = lsp::DidChangeWatchedFilesParams {
                         changes: changes
                             .iter()
                             .filter_map(|(path, _, change)| {
@@ -6290,13 +6288,13 @@ impl Project {
                                 }
                                 let typ = match change {
                                     PathChange::Loaded => return None,
-                                    PathChange::Added => lsp2::FileChangeType::CREATED,
-                                    PathChange::Removed => lsp2::FileChangeType::DELETED,
-                                    PathChange::Updated => lsp2::FileChangeType::CHANGED,
-                                    PathChange::AddedOrUpdated => lsp2::FileChangeType::CHANGED,
+                                    PathChange::Added => lsp::FileChangeType::CREATED,
+                                    PathChange::Removed => lsp::FileChangeType::DELETED,
+                                    PathChange::Updated => lsp::FileChangeType::CHANGED,
+                                    PathChange::AddedOrUpdated => lsp::FileChangeType::CHANGED,
                                 };
-                                Some(lsp2::FileEvent {
-                                    uri: lsp2::Url::from_file_path(abs_path.join(path)).unwrap(),
+                                Some(lsp::FileEvent {
+                                    uri: lsp::Url::from_file_path(abs_path.join(path)).unwrap(),
                                     typ,
                                 })
                             })
@@ -6305,7 +6303,7 @@ impl Project {
 
                     if !params.changes.is_empty() {
                         server
-                            .notify::<lsp2::notification::DidChangeWatchedFiles>(params)
+                            .notify::<lsp::notification::DidChangeWatchedFiles>(params)
                             .log_err();
                     }
                 }
@@ -7080,7 +7078,7 @@ impl Project {
             let ops = payload
                 .operations
                 .into_iter()
-                .map(language2::proto::deserialize_operation)
+                .map(language::proto::deserialize_operation)
                 .collect::<Result<Vec<_>, _>>()?;
             let is_remote = this.is_remote();
             match this.opened_buffers.entry(buffer_id) {
@@ -7124,7 +7122,7 @@ impl Project {
                             anyhow!("no worktree found for id {}", file.worktree_id)
                         })?;
                         buffer_file = Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
-                            as Arc<dyn language2::File>);
+                            as Arc<dyn language::File>);
                     }
 
                     let buffer_id = state.id;
@@ -7149,7 +7147,7 @@ impl Project {
                     let operations = chunk
                         .operations
                         .into_iter()
-                        .map(language2::proto::deserialize_operation)
+                        .map(language::proto::deserialize_operation)
                         .collect::<Result<Vec<_>>>()?;
                     buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
 
@@ -7255,9 +7253,7 @@ impl Project {
             buffer_id,
             version: serialize_version(buffer.saved_version()),
             mtime: Some(buffer.saved_mtime().into()),
-            fingerprint: language2::proto::serialize_fingerprint(
-                buffer.saved_version_fingerprint(),
-            ),
+            fingerprint: language::proto::serialize_fingerprint(buffer.saved_version_fingerprint()),
         })?)
     }
 
@@ -7310,7 +7306,7 @@ impl Project {
             this.shared_buffers.entry(guest_id).or_default().clear();
             for buffer in envelope.payload.buffers {
                 let buffer_id = buffer.id;
-                let remote_version = language2::proto::deserialize_version(&buffer.version);
+                let remote_version = language::proto::deserialize_version(&buffer.version);
                 if let Some(buffer) = this.buffer_for_id(buffer_id) {
                     this.shared_buffers
                         .entry(guest_id)
@@ -7320,7 +7316,7 @@ impl Project {
                     let buffer = buffer.read(cx);
                     response.buffers.push(proto::BufferVersion {
                         id: buffer_id,
-                        version: language2::proto::serialize_version(&buffer.version),
+                        version: language::proto::serialize_version(&buffer.version),
                     });
 
                     let operations = buffer.serialize_ops(Some(remote_version), cx);
@@ -7347,12 +7343,12 @@ impl Project {
                         .send(proto::BufferReloaded {
                             project_id,
                             buffer_id,
-                            version: language2::proto::serialize_version(buffer.saved_version()),
+                            version: language::proto::serialize_version(buffer.saved_version()),
                             mtime: Some(buffer.saved_mtime().into()),
-                            fingerprint: language2::proto::serialize_fingerprint(
+                            fingerprint: language::proto::serialize_fingerprint(
                                 buffer.saved_version_fingerprint(),
                             ),
-                            line_ending: language2::proto::serialize_line_ending(
+                            line_ending: language::proto::serialize_line_ending(
                                 buffer.line_ending(),
                             ) as i32,
                         })
@@ -7426,7 +7422,7 @@ impl Project {
                 .and_then(|buffer| buffer.upgrade())
                 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
             let language = buffer.read(cx).language();
-            let completion = language2::proto::deserialize_completion(
+            let completion = language::proto::deserialize_completion(
                 envelope
                     .payload
                     .completion
@@ -7446,7 +7442,7 @@ impl Project {
             transaction: apply_additional_edits
                 .await?
                 .as_ref()
-                .map(language2::proto::serialize_transaction),
+                .map(language::proto::serialize_transaction),
         })
     }
 
@@ -7457,7 +7453,7 @@ impl Project {
         mut cx: AsyncAppContext,
     ) -> Result<proto::ApplyCodeActionResponse> {
         let sender_id = envelope.original_sender_id()?;
-        let action = language2::proto::deserialize_code_action(
+        let action = language::proto::deserialize_code_action(
             envelope
                 .payload
                 .action
@@ -7509,7 +7505,7 @@ impl Project {
         let transaction = on_type_formatting
             .await?
             .as_ref()
-            .map(language2::proto::serialize_transaction);
+            .map(language::proto::serialize_transaction);
         Ok(proto::OnTypeFormattingResponse { transaction })
     }
 
@@ -7616,8 +7612,8 @@ impl Project {
         mut cx: AsyncAppContext,
     ) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
     where
-        <T::LspRequest as lsp2::request::Request>::Params: Send,
-        <T::LspRequest as lsp2::request::Request>::Result: Send,
+        <T::LspRequest as lsp::request::Request>::Params: Send,
+        <T::LspRequest as lsp::request::Request>::Result: Send,
     {
         let sender_id = envelope.original_sender_id()?;
         let buffer_id = T::buffer_id_from_proto(&envelope.payload);
@@ -7801,7 +7797,7 @@ impl Project {
                 .push(self.create_buffer_for_peer(&buffer, peer_id, cx));
             serialized_transaction
                 .transactions
-                .push(language2::proto::serialize_transaction(&transaction));
+                .push(language::proto::serialize_transaction(&transaction));
         }
         serialized_transaction
     }
@@ -7821,7 +7817,7 @@ impl Project {
                         this.wait_for_remote_buffer(buffer_id, cx)
                     })?
                     .await?;
-                let transaction = language2::proto::deserialize_transaction(transaction)?;
+                let transaction = language::proto::deserialize_transaction(transaction)?;
                 project_transaction.0.insert(buffer, transaction);
             }
 

crates/project2/src/project_settings.rs 🔗

@@ -1,8 +1,8 @@
 use collections::HashMap;
-use gpui2::AppContext;
+use gpui::AppContext;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use settings2::Settings;
+use settings::Settings;
 use std::sync::Arc;
 
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]

crates/project2/src/project_tests.rs 🔗

@@ -1,13 +1,13 @@
 use crate::{search::PathMatcher, Event, *};
-use fs2::FakeFs;
+use fs::FakeFs;
 use futures::{future, StreamExt};
-use gpui2::AppContext;
-use language2::{
+use gpui::AppContext;
+use language::{
     language_settings::{AllLanguageSettings, LanguageSettingsContent},
     tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig,
     LineEnding, OffsetRangeExt, Point, ToPoint,
 };
-use lsp2::Url;
+use lsp::Url;
 use parking_lot::Mutex;
 use pretty_assertions::assert_eq;
 use serde_json::json;
@@ -15,8 +15,8 @@ use std::{os, task::Poll};
 use unindent::Unindent as _;
 use util::{assert_set_eq, test::temp_tree};
 
-#[gpui2::test]
-async fn test_block_via_channel(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_block_via_channel(cx: &mut gpui::TestAppContext) {
     cx.executor().allow_parking();
 
     let (tx, mut rx) = futures::channel::mpsc::unbounded();
@@ -28,8 +28,8 @@ async fn test_block_via_channel(cx: &mut gpui2::TestAppContext) {
     rx.next().await.unwrap();
 }
 
-#[gpui2::test]
-async fn test_block_via_smol(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_block_via_smol(cx: &mut gpui::TestAppContext) {
     cx.executor().allow_parking();
 
     let io_task = smol::unblock(move || {
@@ -45,8 +45,8 @@ async fn test_block_via_smol(cx: &mut gpui2::TestAppContext) {
     task.await;
 }
 
-#[gpui2::test]
-async fn test_symlinks(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_symlinks(cx: &mut gpui::TestAppContext) {
     init_test(cx);
     cx.executor().allow_parking();
 
@@ -85,8 +85,8 @@ async fn test_symlinks(cx: &mut gpui2::TestAppContext) {
     });
 }
 
-#[gpui2::test]
-async fn test_managing_project_specific_settings(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let fs = FakeFs::new(cx.executor().clone());
@@ -142,8 +142,8 @@ async fn test_managing_project_specific_settings(cx: &mut gpui2::TestAppContext)
     });
 }
 
-#[gpui2::test]
-async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut rust_language = Language::new(
@@ -165,8 +165,8 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     let mut fake_rust_servers = rust_language
         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
             name: "the-rust-language-server",
-            capabilities: lsp2::ServerCapabilities {
-                completion_provider: Some(lsp2::CompletionOptions {
+            capabilities: lsp::ServerCapabilities {
+                completion_provider: Some(lsp::CompletionOptions {
                     trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
                     ..Default::default()
                 }),
@@ -178,8 +178,8 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     let mut fake_json_servers = json_language
         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
             name: "the-json-language-server",
-            capabilities: lsp2::ServerCapabilities {
-                completion_provider: Some(lsp2::CompletionOptions {
+            capabilities: lsp::ServerCapabilities {
+                completion_provider: Some(lsp::CompletionOptions {
                     trigger_characters: Some(vec![":".to_string()]),
                     ..Default::default()
                 }),
@@ -237,11 +237,11 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentItem {
-            uri: lsp2::Url::from_file_path("/the-root/test.rs").unwrap(),
+        lsp::TextDocumentItem {
+            uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
             version: 0,
             text: "const A: i32 = 1;".to_string(),
             language_id: Default::default()
@@ -263,11 +263,11 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     rust_buffer.update(cx, |buffer, cx| buffer.edit([(16..16, "2")], None, cx));
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidChangeTextDocument>()
+            .receive_notification::<lsp::notification::DidChangeTextDocument>()
             .await
             .text_document,
-        lsp2::VersionedTextDocumentIdentifier::new(
-            lsp2::Url::from_file_path("/the-root/test.rs").unwrap(),
+        lsp::VersionedTextDocumentIdentifier::new(
+            lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
             1
         )
     );
@@ -284,11 +284,11 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     let mut fake_json_server = fake_json_servers.next().await.unwrap();
     assert_eq!(
         fake_json_server
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentItem {
-            uri: lsp2::Url::from_file_path("/the-root/package.json").unwrap(),
+        lsp::TextDocumentItem {
+            uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(),
             version: 0,
             text: "{\"a\": 1}".to_string(),
             language_id: Default::default()
@@ -323,11 +323,11 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     });
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidChangeTextDocument>()
+            .receive_notification::<lsp::notification::DidChangeTextDocument>()
             .await
             .text_document,
-        lsp2::VersionedTextDocumentIdentifier::new(
-            lsp2::Url::from_file_path("/the-root/test2.rs").unwrap(),
+        lsp::VersionedTextDocumentIdentifier::new(
+            lsp::Url::from_file_path("/the-root/test2.rs").unwrap(),
             1
         )
     );
@@ -339,21 +339,17 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
         .unwrap();
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidSaveTextDocument>()
+            .receive_notification::<lsp::notification::DidSaveTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentIdentifier::new(
-            lsp2::Url::from_file_path("/the-root/Cargo.toml").unwrap()
-        )
+        lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap())
     );
     assert_eq!(
         fake_json_server
-            .receive_notification::<lsp2::notification::DidSaveTextDocument>()
+            .receive_notification::<lsp::notification::DidSaveTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentIdentifier::new(
-            lsp2::Url::from_file_path("/the-root/Cargo.toml").unwrap()
-        )
+        lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap())
     );
 
     // Renames are reported only to servers matching the buffer's language.
@@ -366,18 +362,18 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     .unwrap();
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidCloseTextDocument>()
+            .receive_notification::<lsp::notification::DidCloseTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path("/the-root/test2.rs").unwrap()),
+        lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/test2.rs").unwrap()),
     );
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentItem {
-            uri: lsp2::Url::from_file_path("/the-root/test3.rs").unwrap(),
+        lsp::TextDocumentItem {
+            uri: lsp::Url::from_file_path("/the-root/test3.rs").unwrap(),
             version: 0,
             text: rust_buffer2.update(cx, |buffer, _| buffer.text()),
             language_id: Default::default()
@@ -416,18 +412,18 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     .unwrap();
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidCloseTextDocument>()
+            .receive_notification::<lsp::notification::DidCloseTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path("/the-root/test3.rs").unwrap(),),
+        lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/test3.rs").unwrap(),),
     );
     assert_eq!(
         fake_json_server
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentItem {
-            uri: lsp2::Url::from_file_path("/the-root/test3.json").unwrap(),
+        lsp::TextDocumentItem {
+            uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
             version: 0,
             text: rust_buffer2.update(cx, |buffer, _| buffer.text()),
             language_id: Default::default()
@@ -449,11 +445,11 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     rust_buffer2.update(cx, |buffer, cx| buffer.edit([(0..0, "// ")], None, cx));
     assert_eq!(
         fake_json_server
-            .receive_notification::<lsp2::notification::DidChangeTextDocument>()
+            .receive_notification::<lsp::notification::DidChangeTextDocument>()
             .await
             .text_document,
-        lsp2::VersionedTextDocumentIdentifier::new(
-            lsp2::Url::from_file_path("/the-root/test3.json").unwrap(),
+        lsp::VersionedTextDocumentIdentifier::new(
+            lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
             1
         )
     );
@@ -467,9 +463,9 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     });
 
     let mut rust_shutdown_requests = fake_rust_server
-        .handle_request::<lsp2::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
+        .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
     let mut json_shutdown_requests = fake_json_server
-        .handle_request::<lsp2::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
+        .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
     futures::join!(rust_shutdown_requests.next(), json_shutdown_requests.next());
 
     let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
@@ -478,11 +474,11 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     // Ensure rust document is reopened in new rust language server
     assert_eq!(
         fake_rust_server
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document,
-        lsp2::TextDocumentItem {
-            uri: lsp2::Url::from_file_path("/the-root/test.rs").unwrap(),
+        lsp::TextDocumentItem {
+            uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(),
             version: 0,
             text: rust_buffer.update(cx, |buffer, _| buffer.text()),
             language_id: Default::default()
@@ -493,23 +489,23 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
     assert_set_eq!(
         [
             fake_json_server
-                .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+                .receive_notification::<lsp::notification::DidOpenTextDocument>()
                 .await
                 .text_document,
             fake_json_server
-                .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+                .receive_notification::<lsp::notification::DidOpenTextDocument>()
                 .await
                 .text_document,
         ],
         [
-            lsp2::TextDocumentItem {
-                uri: lsp2::Url::from_file_path("/the-root/package.json").unwrap(),
+            lsp::TextDocumentItem {
+                uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(),
                 version: 0,
                 text: json_buffer.update(cx, |buffer, _| buffer.text()),
                 language_id: Default::default()
             },
-            lsp2::TextDocumentItem {
-                uri: lsp2::Url::from_file_path("/the-root/test3.json").unwrap(),
+            lsp::TextDocumentItem {
+                uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(),
                 version: 0,
                 text: rust_buffer2.update(cx, |buffer, _| buffer.text()),
                 language_id: Default::default()
@@ -519,21 +515,21 @@ async fn test_managing_language_servers(cx: &mut gpui2::TestAppContext) {
 
     // Close notifications are reported only to servers matching the buffer's language.
     cx.update(|_| drop(json_buffer));
-    let close_message = lsp2::DidCloseTextDocumentParams {
-        text_document: lsp2::TextDocumentIdentifier::new(
-            lsp2::Url::from_file_path("/the-root/package.json").unwrap(),
+    let close_message = lsp::DidCloseTextDocumentParams {
+        text_document: lsp::TextDocumentIdentifier::new(
+            lsp::Url::from_file_path("/the-root/package.json").unwrap(),
         ),
     };
     assert_eq!(
         fake_json_server
-            .receive_notification::<lsp2::notification::DidCloseTextDocument>()
+            .receive_notification::<lsp::notification::DidCloseTextDocument>()
             .await,
         close_message,
     );
 }
 
-#[gpui2::test]
-async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut language = Language::new(
@@ -622,27 +618,27 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui2::TestAppCo
     let fake_server = fake_servers.next().await.unwrap();
     let file_changes = Arc::new(Mutex::new(Vec::new()));
     fake_server
-        .request::<lsp2::request::RegisterCapability>(lsp2::RegistrationParams {
-            registrations: vec![lsp2::Registration {
+        .request::<lsp::request::RegisterCapability>(lsp::RegistrationParams {
+            registrations: vec![lsp::Registration {
                 id: Default::default(),
                 method: "workspace/didChangeWatchedFiles".to_string(),
                 register_options: serde_json::to_value(
-                    lsp2::DidChangeWatchedFilesRegistrationOptions {
+                    lsp::DidChangeWatchedFilesRegistrationOptions {
                         watchers: vec![
-                            lsp2::FileSystemWatcher {
-                                glob_pattern: lsp2::GlobPattern::String(
+                            lsp::FileSystemWatcher {
+                                glob_pattern: lsp::GlobPattern::String(
                                     "/the-root/Cargo.toml".to_string(),
                                 ),
                                 kind: None,
                             },
-                            lsp2::FileSystemWatcher {
-                                glob_pattern: lsp2::GlobPattern::String(
+                            lsp::FileSystemWatcher {
+                                glob_pattern: lsp::GlobPattern::String(
                                     "/the-root/src/*.{rs,c}".to_string(),
                                 ),
                                 kind: None,
                             },
-                            lsp2::FileSystemWatcher {
-                                glob_pattern: lsp2::GlobPattern::String(
+                            lsp::FileSystemWatcher {
+                                glob_pattern: lsp::GlobPattern::String(
                                     "/the-root/target/y/**/*.rs".to_string(),
                                 ),
                                 kind: None,
@@ -655,7 +651,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui2::TestAppCo
         })
         .await
         .unwrap();
-    fake_server.handle_notification::<lsp2::notification::DidChangeWatchedFiles, _>({
+    fake_server.handle_notification::<lsp::notification::DidChangeWatchedFiles, _>({
         let file_changes = file_changes.clone();
         move |params, _| {
             let mut file_changes = file_changes.lock();
@@ -718,24 +714,24 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui2::TestAppCo
     assert_eq!(
         &*file_changes.lock(),
         &[
-            lsp2::FileEvent {
-                uri: lsp2::Url::from_file_path("/the-root/src/b.rs").unwrap(),
-                typ: lsp2::FileChangeType::DELETED,
+            lsp::FileEvent {
+                uri: lsp::Url::from_file_path("/the-root/src/b.rs").unwrap(),
+                typ: lsp::FileChangeType::DELETED,
             },
-            lsp2::FileEvent {
-                uri: lsp2::Url::from_file_path("/the-root/src/c.rs").unwrap(),
-                typ: lsp2::FileChangeType::CREATED,
+            lsp::FileEvent {
+                uri: lsp::Url::from_file_path("/the-root/src/c.rs").unwrap(),
+                typ: lsp::FileChangeType::CREATED,
             },
-            lsp2::FileEvent {
-                uri: lsp2::Url::from_file_path("/the-root/target/y/out/y2.rs").unwrap(),
-                typ: lsp2::FileChangeType::CREATED,
+            lsp::FileEvent {
+                uri: lsp::Url::from_file_path("/the-root/target/y/out/y2.rs").unwrap(),
+                typ: lsp::FileChangeType::CREATED,
             },
         ]
     );
 }
 
-#[gpui2::test]
-async fn test_single_file_worktrees_diagnostics(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let fs = FakeFs::new(cx.executor().clone());
@@ -763,15 +759,12 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui2::TestAppContext)
         project
             .update_diagnostics(
                 LanguageServerId(0),
-                lsp2::PublishDiagnosticsParams {
+                lsp::PublishDiagnosticsParams {
                     uri: Url::from_file_path("/dir/a.rs").unwrap(),
                     version: None,
-                    diagnostics: vec![lsp2::Diagnostic {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 4),
-                            lsp2::Position::new(0, 5),
-                        ),
-                        severity: Some(lsp2::DiagnosticSeverity::ERROR),
+                    diagnostics: vec![lsp::Diagnostic {
+                        range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 5)),
+                        severity: Some(lsp::DiagnosticSeverity::ERROR),
                         message: "error 1".to_string(),
                         ..Default::default()
                     }],
@@ -783,15 +776,12 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui2::TestAppContext)
         project
             .update_diagnostics(
                 LanguageServerId(0),
-                lsp2::PublishDiagnosticsParams {
+                lsp::PublishDiagnosticsParams {
                     uri: Url::from_file_path("/dir/b.rs").unwrap(),
                     version: None,
-                    diagnostics: vec![lsp2::Diagnostic {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 4),
-                            lsp2::Position::new(0, 5),
-                        ),
-                        severity: Some(lsp2::DiagnosticSeverity::WARNING),
+                    diagnostics: vec![lsp::Diagnostic {
+                        range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 5)),
+                        severity: Some(lsp::DiagnosticSeverity::WARNING),
                         message: "error 2".to_string(),
                         ..Default::default()
                     }],
@@ -832,8 +822,8 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui2::TestAppContext)
     });
 }
 
-#[gpui2::test]
-async fn test_hidden_worktrees_diagnostics(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let fs = FakeFs::new(cx.executor().clone());
@@ -862,15 +852,12 @@ async fn test_hidden_worktrees_diagnostics(cx: &mut gpui2::TestAppContext) {
         project
             .update_diagnostics(
                 LanguageServerId(0),
-                lsp2::PublishDiagnosticsParams {
+                lsp::PublishDiagnosticsParams {
                     uri: Url::from_file_path("/root/other.rs").unwrap(),
                     version: None,
-                    diagnostics: vec![lsp2::Diagnostic {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 8),
-                            lsp2::Position::new(0, 9),
-                        ),
-                        severity: Some(lsp2::DiagnosticSeverity::ERROR),
+                    diagnostics: vec![lsp::Diagnostic {
+                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 9)),
+                        severity: Some(lsp::DiagnosticSeverity::ERROR),
                         message: "unknown variable 'c'".to_string(),
                         ..Default::default()
                     }],
@@ -906,8 +893,8 @@ async fn test_hidden_worktrees_diagnostics(cx: &mut gpui2::TestAppContext) {
     });
 }
 
-#[gpui2::test]
-async fn test_disk_based_diagnostics_progress(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let progress_token = "the-progress-token";
@@ -965,12 +952,12 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui2::TestAppContext) {
         }
     );
 
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
         uri: Url::from_file_path("/dir/a.rs").unwrap(),
         version: None,
-        diagnostics: vec![lsp2::Diagnostic {
-            range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
-            severity: Some(lsp2::DiagnosticSeverity::ERROR),
+        diagnostics: vec![lsp::Diagnostic {
+            range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
+            severity: Some(lsp::DiagnosticSeverity::ERROR),
             message: "undefined variable 'A'".to_string(),
             ..Default::default()
         }],
@@ -1006,7 +993,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui2::TestAppContext) {
             &[DiagnosticEntry {
                 range: Point::new(0, 9)..Point::new(0, 10),
                 diagnostic: Diagnostic {
-                    severity: lsp2::DiagnosticSeverity::ERROR,
+                    severity: lsp::DiagnosticSeverity::ERROR,
                     message: "undefined variable 'A'".to_string(),
                     group_id: 0,
                     is_primary: true,
@@ -1017,7 +1004,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui2::TestAppContext) {
     });
 
     // Ensure publishing empty diagnostics twice only results in one update event.
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
         uri: Url::from_file_path("/dir/a.rs").unwrap(),
         version: None,
         diagnostics: Default::default(),
@@ -1030,7 +1017,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui2::TestAppContext) {
         }
     );
 
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
         uri: Url::from_file_path("/dir/a.rs").unwrap(),
         version: None,
         diagnostics: Default::default(),
@@ -1039,8 +1026,8 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui2::TestAppContext) {
     assert_eq!(futures::poll!(events.next()), Poll::Pending);
 }
 
-#[gpui2::test]
-async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let progress_token = "the-progress-token";
@@ -1121,8 +1108,8 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui2::TestApp
     });
 }
 
-#[gpui2::test]
-async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut language = Language::new(
@@ -1151,12 +1138,12 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui2::TestA
 
     // Publish diagnostics
     let fake_server = fake_servers.next().await.unwrap();
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
         uri: Url::from_file_path("/dir/a.rs").unwrap(),
         version: None,
-        diagnostics: vec![lsp2::Diagnostic {
-            range: lsp2::Range::new(lsp2::Position::new(0, 0), lsp2::Position::new(0, 0)),
-            severity: Some(lsp2::DiagnosticSeverity::ERROR),
+        diagnostics: vec![lsp::Diagnostic {
+            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
+            severity: Some(lsp::DiagnosticSeverity::ERROR),
             message: "the message".to_string(),
             ..Default::default()
         }],
@@ -1210,8 +1197,8 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui2::TestA
     });
 }
 
-#[gpui2::test]
-async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut language = Language::new(
@@ -1241,8 +1228,8 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui2::
 
     // Before restarting the server, report diagnostics with an unknown buffer version.
     let fake_server = fake_servers.next().await.unwrap();
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
-        uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
+        uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
         version: Some(10000),
         diagnostics: Vec::new(),
     });
@@ -1253,14 +1240,14 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui2::
     });
     let mut fake_server = fake_servers.next().await.unwrap();
     let notification = fake_server
-        .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+        .receive_notification::<lsp::notification::DidOpenTextDocument>()
         .await
         .text_document;
     assert_eq!(notification.version, 0);
 }
 
-#[gpui2::test]
-async fn test_toggling_enable_language_server(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut rust = Language::new(
@@ -1314,7 +1301,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui2::TestAppContext) {
     let mut fake_rust_server_1 = fake_rust_servers.next().await.unwrap();
     assert_eq!(
         fake_rust_server_1
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document
             .uri
@@ -1325,7 +1312,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui2::TestAppContext) {
     let mut fake_js_server = fake_js_servers.next().await.unwrap();
     assert_eq!(
         fake_js_server
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document
             .uri
@@ -1348,7 +1335,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui2::TestAppContext) {
         })
     });
     fake_rust_server_1
-        .receive_notification::<lsp2::notification::Exit>()
+        .receive_notification::<lsp::notification::Exit>()
         .await;
 
     // Enable Rust and disable JavaScript language servers, ensuring that the
@@ -1376,7 +1363,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui2::TestAppContext) {
     let mut fake_rust_server_2 = fake_rust_servers.next().await.unwrap();
     assert_eq!(
         fake_rust_server_2
-            .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+            .receive_notification::<lsp::notification::DidOpenTextDocument>()
             .await
             .text_document
             .uri
@@ -1384,12 +1371,12 @@ async fn test_toggling_enable_language_server(cx: &mut gpui2::TestAppContext) {
         "file:///dir/a.rs"
     );
     fake_js_server
-        .receive_notification::<lsp2::notification::Exit>()
+        .receive_notification::<lsp::notification::Exit>()
         .await;
 }
 
-#[gpui2::test(iterations = 3)]
-async fn test_transforming_diagnostics(cx: &mut gpui2::TestAppContext) {
+#[gpui::test(iterations = 3)]
+async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut language = Language::new(
@@ -1427,37 +1414,37 @@ async fn test_transforming_diagnostics(cx: &mut gpui2::TestAppContext) {
 
     let mut fake_server = fake_servers.next().await.unwrap();
     let open_notification = fake_server
-        .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+        .receive_notification::<lsp::notification::DidOpenTextDocument>()
         .await;
 
     // Edit the buffer, moving the content down
     buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "\n\n")], None, cx));
     let change_notification_1 = fake_server
-        .receive_notification::<lsp2::notification::DidChangeTextDocument>()
+        .receive_notification::<lsp::notification::DidChangeTextDocument>()
         .await;
     assert!(change_notification_1.text_document.version > open_notification.text_document.version);
 
     // Report some diagnostics for the initial version of the buffer
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
-        uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
+        uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
         version: Some(open_notification.text_document.version),
         diagnostics: vec![
-            lsp2::Diagnostic {
-                range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
+            lsp::Diagnostic {
+                range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
                 severity: Some(DiagnosticSeverity::ERROR),
                 message: "undefined variable 'A'".to_string(),
                 source: Some("disk".to_string()),
                 ..Default::default()
             },
-            lsp2::Diagnostic {
-                range: lsp2::Range::new(lsp2::Position::new(1, 9), lsp2::Position::new(1, 11)),
+            lsp::Diagnostic {
+                range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
                 severity: Some(DiagnosticSeverity::ERROR),
                 message: "undefined variable 'BB'".to_string(),
                 source: Some("disk".to_string()),
                 ..Default::default()
             },
-            lsp2::Diagnostic {
-                range: lsp2::Range::new(lsp2::Position::new(2, 9), lsp2::Position::new(2, 12)),
+            lsp::Diagnostic {
+                range: lsp::Range::new(lsp::Position::new(2, 9), lsp::Position::new(2, 12)),
                 severity: Some(DiagnosticSeverity::ERROR),
                 source: Some("disk".to_string()),
                 message: "undefined variable 'CCC'".to_string(),
@@ -1524,19 +1511,19 @@ async fn test_transforming_diagnostics(cx: &mut gpui2::TestAppContext) {
     });
 
     // Ensure overlapping diagnostics are highlighted correctly.
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
-        uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
+        uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
         version: Some(open_notification.text_document.version),
         diagnostics: vec![
-            lsp2::Diagnostic {
-                range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
+            lsp::Diagnostic {
+                range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
                 severity: Some(DiagnosticSeverity::ERROR),
                 message: "undefined variable 'A'".to_string(),
                 source: Some("disk".to_string()),
                 ..Default::default()
             },
-            lsp2::Diagnostic {
-                range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 12)),
+            lsp::Diagnostic {
+                range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 12)),
                 severity: Some(DiagnosticSeverity::WARNING),
                 message: "unreachable statement".to_string(),
                 source: Some("disk".to_string()),
@@ -1609,26 +1596,26 @@ async fn test_transforming_diagnostics(cx: &mut gpui2::TestAppContext) {
         buffer.edit([(Point::new(3, 10)..Point::new(3, 10), "xxx")], None, cx);
     });
     let change_notification_2 = fake_server
-        .receive_notification::<lsp2::notification::DidChangeTextDocument>()
+        .receive_notification::<lsp::notification::DidChangeTextDocument>()
         .await;
     assert!(
         change_notification_2.text_document.version > change_notification_1.text_document.version
     );
 
     // Handle out-of-order diagnostics
-    fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
-        uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
+    fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
+        uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(),
         version: Some(change_notification_2.text_document.version),
         diagnostics: vec![
-            lsp2::Diagnostic {
-                range: lsp2::Range::new(lsp2::Position::new(1, 9), lsp2::Position::new(1, 11)),
+            lsp::Diagnostic {
+                range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
                 severity: Some(DiagnosticSeverity::ERROR),
                 message: "undefined variable 'BB'".to_string(),
                 source: Some("disk".to_string()),
                 ..Default::default()
             },
-            lsp2::Diagnostic {
-                range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
+            lsp::Diagnostic {
+                range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
                 severity: Some(DiagnosticSeverity::WARNING),
                 message: "undefined variable 'A'".to_string(),
                 source: Some("disk".to_string()),
@@ -1674,8 +1661,8 @@ async fn test_transforming_diagnostics(cx: &mut gpui2::TestAppContext) {
     });
 }
 
-#[gpui2::test]
-async fn test_empty_diagnostic_ranges(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let text = concat!(
@@ -1743,8 +1730,8 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui2::TestAppContext) {
     });
 }
 
-#[gpui2::test]
-async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let fs = FakeFs::new(cx.executor().clone());
@@ -1799,8 +1786,8 @@ async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui2::TestApp
     });
 }
 
-#[gpui2::test]
-async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut language = Language::new(
@@ -1844,7 +1831,7 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui2::TestAppContext)
 
     let mut fake_server = fake_servers.next().await.unwrap();
     let lsp_document_version = fake_server
-        .receive_notification::<lsp2::notification::DidOpenTextDocument>()
+        .receive_notification::<lsp::notification::DidOpenTextDocument>()
         .await
         .text_document
         .version;
@@ -1901,11 +1888,8 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui2::TestAppContext)
                 &buffer,
                 vec![
                     // replace body of first function
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 0),
-                            lsp2::Position::new(3, 0),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(3, 0)),
                         new_text: "
                             fn a() {
                                 f10();
@@ -1914,26 +1898,17 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui2::TestAppContext)
                         .unindent(),
                     },
                     // edit inside second function
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(4, 6),
-                            lsp2::Position::new(4, 6),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(4, 6), lsp::Position::new(4, 6)),
                         new_text: "00".into(),
                     },
                     // edit inside third function via two distinct edits
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(7, 5),
-                            lsp2::Position::new(7, 5),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(7, 5), lsp::Position::new(7, 5)),
                         new_text: "4000".into(),
                     },
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(7, 5),
-                            lsp2::Position::new(7, 6),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(7, 5), lsp::Position::new(7, 6)),
                         new_text: "".into(),
                     },
                 ],
@@ -1969,8 +1944,8 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui2::TestAppContext)
     });
 }
 
-#[gpui2::test]
-async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let text = "
@@ -2007,27 +1982,18 @@ async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui2::TestA
                 &buffer,
                 [
                     // Replace the first use statement without editing the semicolon.
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 4),
-                            lsp2::Position::new(0, 8),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 8)),
                         new_text: "a::{b, c}".into(),
                     },
                     // Reinsert the remainder of the file between the semicolon and the final
                     // newline of the file.
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 9),
-                            lsp2::Position::new(0, 9),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 9)),
                         new_text: "\n\n".into(),
                     },
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 9),
-                            lsp2::Position::new(0, 9),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 9)),
                         new_text: "
                             fn f() {
                                 b();
@@ -2036,11 +2002,8 @@ async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui2::TestA
                         .unindent(),
                     },
                     // Delete everything after the first newline of the file.
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(1, 0),
-                            lsp2::Position::new(7, 0),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(7, 0)),
                         new_text: "".into(),
                     },
                 ],
@@ -2089,8 +2052,8 @@ async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui2::TestA
     });
 }
 
-#[gpui2::test]
-async fn test_invalid_edits_from_lsp2(cx: &mut gpui2::TestAppContext) {
+#[gpui::test]
+async fn test_invalid_edits_from_lsp2(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let text = "
@@ -2126,32 +2089,20 @@ async fn test_invalid_edits_from_lsp2(cx: &mut gpui2::TestAppContext) {
             project.edits_from_lsp(
                 &buffer,
                 [
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 9),
-                            lsp2::Position::new(0, 9),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 9)),
                         new_text: "\n\n".into(),
                     },
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 8),
-                            lsp2::Position::new(0, 4),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 4)),
                         new_text: "a::{b, c}".into(),
                     },
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(1, 0),
-                            lsp2::Position::new(99, 0),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(99, 0)),
                         new_text: "".into(),
                     },
-                    lsp2::TextEdit {
-                        range: lsp2::Range::new(
-                            lsp2::Position::new(0, 9),
-                            lsp2::Position::new(0, 9),
-                        ),
+                    lsp::TextEdit {
+                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 9)),
                         new_text: "
                             fn f() {
                                 b();
@@ -2222,8 +2173,8 @@ fn chunks_with_diagnostics<T: ToOffset + ToPoint>(
     chunks
 }
 
-#[gpui2::test(iterations = 10)]
-async fn test_definition(cx: &mut gpui2::TestAppContext) {
+#[gpui::test(iterations = 10)]
+async fn test_definition(cx: &mut gpui::TestAppContext) {
     init_test(cx);
 
     let mut language = Language::new(
@@ -2255,18 +2206,18 @@ async fn test_definition(cx: &mut gpui2::TestAppContext) {
         .unwrap();
 
     let fake_server = fake_servers.next().await.unwrap();
-    fake_server.handle_request::<lsp2::request::GotoDefinition, _, _>(|params, _| async move {
+    fake_server.handle_request::<lsp::request::GotoDefinition, _, _>(|params, _| async move {
         let params = params.text_document_position_params;
         assert_eq!(
             params.text_document.uri.to_file_path().unwrap(),
             Path::new("/dir/b.rs"),
         );
-        assert_eq!(params.position, lsp2::Position::new(0, 22));
+        assert_eq!(params.position, lsp::Position::new(0, 22));
 
-        Ok(Some(lsp2::GotoDefinitionResponse::Scalar(
-            lsp2::Location::new(
-                lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
-                lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
+        Ok(Some(lsp::GotoDefinitionResponse::Scalar(
+            lsp::Location::new(
+                lsp::Url::from_file_path("/dir/a.rs").unwrap(),
+                lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
             ),
         )))
     });

crates/project2/src/search.rs 🔗

@@ -1,9 +1,9 @@
 use aho_corasick::{AhoCorasick, AhoCorasickBuilder};
 use anyhow::{Context, Result};
-use client2::proto;
+use client::proto;
 use globset::{Glob, GlobMatcher};
 use itertools::Itertools;
-use language2::{char_kind, BufferSnapshot};
+use language::{char_kind, BufferSnapshot};
 use regex::{Regex, RegexBuilder};
 use smol::future::yield_now;
 use std::{

crates/project2/src/terminals.rs 🔗

@@ -1,8 +1,8 @@
 use crate::Project;
-use gpui2::{AnyWindowHandle, Context, Entity, Model, ModelContext, WeakModel};
-use settings2::Settings;
+use gpui::{AnyWindowHandle, Context, Entity, Model, ModelContext, WeakModel};
+use settings::Settings;
 use std::path::{Path, PathBuf};
-use terminal2::{
+use terminal::{
     terminal_settings::{self, TerminalSettings, VenvSettingsContent},
     Terminal, TerminalBuilder,
 };
@@ -11,7 +11,7 @@ use terminal2::{
 use std::os::unix::ffi::OsStrExt;
 
 pub struct Terminals {
-    pub(crate) local_handles: Vec<WeakModel<terminal2::Terminal>>,
+    pub(crate) local_handles: Vec<WeakModel<terminal::Terminal>>,
 }
 
 impl Project {
@@ -121,7 +121,7 @@ impl Project {
         }
     }
 
-    pub fn local_terminal_handles(&self) -> &Vec<WeakModel<terminal2::Terminal>> {
+    pub fn local_terminal_handles(&self) -> &Vec<WeakModel<terminal::Terminal>> {
         &self.terminals.local_handles
     }
 }

crates/project2/src/worktree.rs 🔗

@@ -3,10 +3,10 @@ use crate::{
 };
 use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
 use anyhow::{anyhow, Context as _, Result};
-use client2::{proto, Client};
+use client::{proto, Client};
 use clock::ReplicaId;
 use collections::{HashMap, HashSet, VecDeque};
-use fs2::{
+use fs::{
     repository::{GitFileStatus, GitRepository, RepoPath},
     Fs,
 };
@@ -19,20 +19,20 @@ use futures::{
     task::Poll,
     FutureExt as _, Stream, StreamExt,
 };
-use fuzzy2::CharBag;
+use fuzzy::CharBag;
 use git::{DOT_GIT, GITIGNORE};
-use gpui2::{
+use gpui::{
     AppContext, AsyncAppContext, BackgroundExecutor, Context, EventEmitter, Model, ModelContext,
     Task,
 };
-use language2::{
+use language::{
     proto::{
         deserialize_fingerprint, deserialize_version, serialize_fingerprint, serialize_line_ending,
         serialize_version,
     },
     Buffer, DiagnosticEntry, File as _, LineEnding, PointUtf16, Rope, RopeFingerprint, Unclipped,
 };
-use lsp2::LanguageServerId;
+use lsp::LanguageServerId;
 use parking_lot::Mutex;
 use postage::{
     barrier,
@@ -2587,8 +2587,8 @@ pub struct File {
     pub(crate) is_deleted: bool,
 }
 
-impl language2::File for File {
-    fn as_local(&self) -> Option<&dyn language2::LocalFile> {
+impl language::File for File {
+    fn as_local(&self) -> Option<&dyn language::LocalFile> {
         if self.is_local {
             Some(self)
         } else {
@@ -2648,8 +2648,8 @@ impl language2::File for File {
         self
     }
 
-    fn to_proto(&self) -> rpc2::proto::File {
-        rpc2::proto::File {
+    fn to_proto(&self) -> rpc::proto::File {
+        rpc::proto::File {
             worktree_id: self.worktree.entity_id().as_u64(),
             entry_id: self.entry_id.to_proto(),
             path: self.path.to_string_lossy().into(),
@@ -2659,7 +2659,7 @@ impl language2::File for File {
     }
 }
 
-impl language2::LocalFile for File {
+impl language::LocalFile for File {
     fn abs_path(&self, cx: &AppContext) -> PathBuf {
         let worktree_path = &self.worktree.read(cx).as_local().unwrap().abs_path;
         if self.path.as_ref() == Path::new("") {
@@ -2716,7 +2716,7 @@ impl File {
     }
 
     pub fn from_proto(
-        proto: rpc2::proto::File,
+        proto: rpc::proto::File,
         worktree: Model<Worktree>,
         cx: &AppContext,
     ) -> Result<Self> {
@@ -2740,7 +2740,7 @@ impl File {
         })
     }
 
-    pub fn from_dyn(file: Option<&Arc<dyn language2::File>>) -> Option<&Self> {
+    pub fn from_dyn(file: Option<&Arc<dyn language::File>>) -> Option<&Self> {
         file.and_then(|f| f.as_any().downcast_ref())
     }
 
@@ -2818,7 +2818,7 @@ pub type UpdatedGitRepositoriesSet = Arc<[(Arc<Path>, GitRepositoryChange)]>;
 impl Entry {
     fn new(
         path: Arc<Path>,
-        metadata: &fs2::Metadata,
+        metadata: &fs::Metadata,
         next_entry_id: &AtomicUsize,
         root_char_bag: CharBag,
     ) -> Self {
@@ -4037,7 +4037,7 @@ pub trait WorktreeModelHandle {
     #[cfg(any(test, feature = "test-support"))]
     fn flush_fs_events<'a>(
         &self,
-        cx: &'a mut gpui2::TestAppContext,
+        cx: &'a mut gpui::TestAppContext,
     ) -> futures::future::LocalBoxFuture<'a, ()>;
 }
 
@@ -4051,7 +4051,7 @@ impl WorktreeModelHandle for Model<Worktree> {
     #[cfg(any(test, feature = "test-support"))]
     fn flush_fs_events<'a>(
         &self,
-        cx: &'a mut gpui2::TestAppContext,
+        cx: &'a mut gpui::TestAppContext,
     ) -> futures::future::LocalBoxFuture<'a, ()> {
         let file_name = "fs-event-sentinel";
 

crates/rpc2/Cargo.toml 🔗

@@ -10,12 +10,12 @@ path = "src/rpc.rs"
 doctest = false
 
 [features]
-test-support = ["collections/test-support", "gpui2/test-support"]
+test-support = ["collections/test-support", "gpui/test-support"]
 
 [dependencies]
 clock = { path = "../clock" }
 collections = { path = "../collections" }
-gpui2 = { path = "../gpui2", optional = true }
+gpui = { package = "gpui2", path = "../gpui2", optional = true }
 util = { path = "../util" }
 anyhow.workspace = true
 async-lock = "2.4"
@@ -37,7 +37,7 @@ prost-build = "0.9"
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 smol.workspace = true
 tempdir.workspace = true
 ctor.workspace = true

crates/rpc2/src/conn.rs 🔗

@@ -34,7 +34,7 @@ impl Connection {
 
     #[cfg(any(test, feature = "test-support"))]
     pub fn in_memory(
-        executor: gpui2::BackgroundExecutor,
+        executor: gpui::BackgroundExecutor,
     ) -> (Self, Self, std::sync::Arc<std::sync::atomic::AtomicBool>) {
         use std::sync::{
             atomic::{AtomicBool, Ordering::SeqCst},
@@ -53,7 +53,7 @@ impl Connection {
         #[allow(clippy::type_complexity)]
         fn channel(
             killed: Arc<AtomicBool>,
-            executor: gpui2::BackgroundExecutor,
+            executor: gpui::BackgroundExecutor,
         ) -> (
             Box<dyn Send + Unpin + futures::Sink<WebSocketMessage, Error = anyhow::Error>>,
             Box<dyn Send + Unpin + futures::Stream<Item = Result<WebSocketMessage, anyhow::Error>>>,

crates/rpc2/src/peer.rs 🔗

@@ -342,7 +342,7 @@ impl Peer {
     pub fn add_test_connection(
         self: &Arc<Self>,
         connection: Connection,
-        executor: gpui2::BackgroundExecutor,
+        executor: gpui::BackgroundExecutor,
     ) -> (
         ConnectionId,
         impl Future<Output = anyhow::Result<()>> + Send,
@@ -557,7 +557,7 @@ mod tests {
     use super::*;
     use crate::TypedEnvelope;
     use async_tungstenite::tungstenite::Message as WebSocketMessage;
-    use gpui2::TestAppContext;
+    use gpui::TestAppContext;
 
     fn init_logger() {
         if std::env::var("RUST_LOG").is_ok() {
@@ -565,7 +565,7 @@ mod tests {
         }
     }
 
-    #[gpui2::test(iterations = 50)]
+    #[gpui::test(iterations = 50)]
     async fn test_request_response(cx: &mut TestAppContext) {
         init_logger();
 
@@ -663,7 +663,7 @@ mod tests {
         }
     }
 
-    #[gpui2::test(iterations = 50)]
+    #[gpui::test(iterations = 50)]
     async fn test_order_of_response_and_incoming(cx: &mut TestAppContext) {
         let executor = cx.executor();
         let server = Peer::new(0);
@@ -761,7 +761,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test(iterations = 50)]
+    #[gpui::test(iterations = 50)]
     async fn test_dropping_request_before_completion(cx: &mut TestAppContext) {
         let executor = cx.executor().clone();
         let server = Peer::new(0);
@@ -873,7 +873,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test(iterations = 50)]
+    #[gpui::test(iterations = 50)]
     async fn test_disconnect(cx: &mut TestAppContext) {
         let executor = cx.executor();
 
@@ -909,7 +909,7 @@ mod tests {
             .is_err());
     }
 
-    #[gpui2::test(iterations = 50)]
+    #[gpui::test(iterations = 50)]
     async fn test_io_error(cx: &mut TestAppContext) {
         let executor = cx.executor();
         let (client_conn, mut server_conn, _kill) = Connection::in_memory(executor.clone());

crates/rpc2/src/proto.rs 🔗

@@ -616,7 +616,7 @@ pub fn split_worktree_update(
 mod tests {
     use super::*;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_buffer_size() {
         let (tx, rx) = futures::channel::mpsc::unbounded();
         let mut sink = MessageStream::new(tx.sink_map_err(|_| anyhow!("")));
@@ -648,7 +648,7 @@ mod tests {
         assert!(stream.encoding_buffer.capacity() <= MAX_BUFFER_LEN);
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_converting_peer_id_from_and_to_u64() {
         let peer_id = PeerId {
             owner_id: 10,

crates/settings2/Cargo.toml 🔗

@@ -9,14 +9,14 @@ path = "src/settings2.rs"
 doctest = false
 
 [features]
-test-support = ["gpui2/test-support", "fs/test-support"]
+test-support = ["gpui/test-support", "fs/test-support"]
 
 [dependencies]
 collections = { path = "../collections" }
-gpui2 = { path = "../gpui2" }
+gpui = {package = "gpui2",  path = "../gpui2" }
 sqlez = { path = "../sqlez" }
-fs2 = { path = "../fs2" }
-feature_flags2 = { path = "../feature_flags2" }
+fs = {package = "fs2",  path = "../fs2" }
+feature_flags = {package = "feature_flags2",  path = "../feature_flags2" }
 util = { path = "../util" }
 
 anyhow.workspace = true
@@ -35,8 +35,8 @@ tree-sitter.workspace = true
 tree-sitter-json = "*"
 
 [dev-dependencies]
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-fs = { path = "../fs", features = ["test-support"] }
+gpui = {package = "gpui2",  path = "../gpui2", features = ["test-support"] }
+fs = { package = "fs2", path = "../fs2", features = ["test-support"] }
 indoc.workspace = true
 pretty_assertions.workspace = true
 unindent.workspace = true

crates/settings2/src/keymap_file.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{settings_store::parse_json_with_comments, SettingsAssets};
 use anyhow::{anyhow, Context, Result};
 use collections::BTreeMap;
-use gpui2::{AppContext, KeyBinding, SharedString};
+use gpui::{AppContext, KeyBinding, SharedString};
 use schemars::{
     gen::{SchemaGenerator, SchemaSettings},
     schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
@@ -137,7 +137,7 @@ impl KeymapFile {
     }
 }
 
-fn no_action() -> Box<dyn gpui2::Action> {
+fn no_action() -> Box<dyn gpui::Action> {
     todo!()
 }
 

crates/settings2/src/settings_file.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{settings_store::SettingsStore, Settings};
 use anyhow::Result;
-use fs2::Fs;
+use fs::Fs;
 use futures::{channel::mpsc, StreamExt};
-use gpui2::{AppContext, BackgroundExecutor};
+use gpui::{AppContext, BackgroundExecutor};
 use std::{io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration};
 use util::{paths, ResultExt};
 

crates/settings2/src/settings_store.rs 🔗

@@ -1,6 +1,6 @@
 use anyhow::{anyhow, Context, Result};
 use collections::{btree_map, hash_map, BTreeMap, HashMap};
-use gpui2::AppContext;
+use gpui::AppContext;
 use lazy_static::lazy_static;
 use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
 use serde::{de::DeserializeOwned, Deserialize as _, Serialize};
@@ -877,7 +877,7 @@ mod tests {
     use serde_derive::Deserialize;
     use unindent::Unindent;
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_settings_store_basic(cx: &mut AppContext) {
         let mut store = SettingsStore::default();
         store.register_setting::<UserSettings>(cx);
@@ -994,7 +994,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_setting_store_assign_json_before_register(cx: &mut AppContext) {
         let mut store = SettingsStore::default();
         store
@@ -1037,7 +1037,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_setting_store_update(cx: &mut AppContext) {
         let mut store = SettingsStore::default();
         store.register_setting::<MultiKeySettings>(cx);

crates/storybook2/src/storybook2.rs 🔗

@@ -82,8 +82,8 @@ fn main() {
                 ..Default::default()
             },
             move |cx| {
-                let theme_settings = ThemeSettings::get_global(cx);
-                cx.set_rem_size(theme_settings.ui_font_size);
+                let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
+                cx.set_rem_size(ui_font_size);
 
                 cx.build_view(|cx| StoryWrapper::new(selector.story(cx)))
             },

crates/terminal2/Cargo.toml 🔗

@@ -10,10 +10,10 @@ doctest = false
 
 
 [dependencies]
-gpui2 = { path = "../gpui2" }
-settings2 = { path = "../settings2" }
-db2 = { path = "../db2" }
-theme2 = { path = "../theme2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+settings = { package = "settings2", path = "../settings2" }
+db = { package = "db2", path = "../db2" }
+theme = { package = "theme2", path = "../theme2" }
 util = { path = "../util" }
 
 alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "33306142195b354ef3485ca2b1d8a85dfc6605ca" }

crates/terminal2/src/mappings/colors.rs 🔗

@@ -113,7 +113,7 @@ use alacritty_terminal::term::color::Rgb as AlacRgb;
 //     let b = (i % 36) % 6;
 //     (r, g, b)
 // }
-use gpui2::Rgba;
+use gpui::Rgba;
 
 //Convenience method to convert from a GPUI color to an alacritty Rgb
 pub fn to_alac_rgb(color: impl Into<Rgba>) -> AlacRgb {

crates/terminal2/src/mappings/keys.rs 🔗

@@ -1,6 +1,6 @@
 /// The mappings defined in this file where created from reading the alacritty source
 use alacritty_terminal::term::TermMode;
-use gpui2::Keystroke;
+use gpui::Keystroke;
 
 #[derive(Debug, PartialEq, Eq)]
 enum AlacModifiers {
@@ -278,7 +278,7 @@ fn modifier_code(keystroke: &Keystroke) -> u32 {
 
 #[cfg(test)]
 mod test {
-    use gpui2::Modifiers;
+    use gpui::Modifiers;
 
     use super::*;
 

crates/terminal2/src/mappings/mouse.rs 🔗

@@ -6,7 +6,7 @@ use alacritty_terminal::grid::Dimensions;
 /// with modifications for our circumstances
 use alacritty_terminal::index::{Column as GridCol, Line as GridLine, Point as AlacPoint, Side};
 use alacritty_terminal::term::TermMode;
-use gpui2::{px, Modifiers, MouseButton, MouseMoveEvent, Pixels, Point, ScrollWheelEvent};
+use gpui::{px, Modifiers, MouseButton, MouseMoveEvent, Pixels, Point, ScrollWheelEvent};
 
 use crate::TerminalSize;
 
@@ -45,10 +45,10 @@ impl AlacMouseButton {
     fn from_move(e: &MouseMoveEvent) -> Self {
         match e.pressed_button {
             Some(b) => match b {
-                gpui2::MouseButton::Left => AlacMouseButton::LeftMove,
-                gpui2::MouseButton::Middle => AlacMouseButton::MiddleMove,
-                gpui2::MouseButton::Right => AlacMouseButton::RightMove,
-                gpui2::MouseButton::Navigate(_) => AlacMouseButton::Other,
+                gpui::MouseButton::Left => AlacMouseButton::LeftMove,
+                gpui::MouseButton::Middle => AlacMouseButton::MiddleMove,
+                gpui::MouseButton::Right => AlacMouseButton::RightMove,
+                gpui::MouseButton::Navigate(_) => AlacMouseButton::Other,
             },
             None => AlacMouseButton::NoneMove,
         }
@@ -56,17 +56,17 @@ impl AlacMouseButton {
 
     fn from_button(e: MouseButton) -> Self {
         match e {
-            gpui2::MouseButton::Left => AlacMouseButton::LeftButton,
-            gpui2::MouseButton::Right => AlacMouseButton::MiddleButton,
-            gpui2::MouseButton::Middle => AlacMouseButton::RightButton,
-            gpui2::MouseButton::Navigate(_) => AlacMouseButton::Other,
+            gpui::MouseButton::Left => AlacMouseButton::LeftButton,
+            gpui::MouseButton::Right => AlacMouseButton::MiddleButton,
+            gpui::MouseButton::Middle => AlacMouseButton::RightButton,
+            gpui::MouseButton::Navigate(_) => AlacMouseButton::Other,
         }
     }
 
     fn from_scroll(e: &ScrollWheelEvent) -> Self {
         let is_positive = match e.delta {
-            gpui2::ScrollDelta::Pixels(pixels) => pixels.y > px(0.),
-            gpui2::ScrollDelta::Lines(lines) => lines.y > 0.,
+            gpui::ScrollDelta::Pixels(pixels) => pixels.y > px(0.),
+            gpui::ScrollDelta::Lines(lines) => lines.y > 0.,
         };
 
         if is_positive {
@@ -118,7 +118,7 @@ pub fn alt_scroll(scroll_lines: i32) -> Vec<u8> {
 
 pub fn mouse_button_report(
     point: AlacPoint,
-    button: gpui2::MouseButton,
+    button: gpui::MouseButton,
     modifiers: Modifiers,
     pressed: bool,
     mode: TermMode,

crates/terminal2/src/terminal2.rs 🔗

@@ -33,7 +33,7 @@ use mappings::mouse::{
 
 use procinfo::LocalProcessInfo;
 use serde::{Deserialize, Serialize};
-use settings2::Settings;
+use settings::Settings;
 use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings};
 use util::truncate_and_trailoff;
 
@@ -49,7 +49,7 @@ use std::{
 };
 use thiserror::Error;
 
-use gpui2::{
+use gpui::{
     px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla, Keystroke,
     ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
     Point, ScrollWheelEvent, Size, Task, TouchPhase,
@@ -1409,7 +1409,7 @@ mod tests {
         index::{Column, Line, Point as AlacPoint},
         term::cell::Cell,
     };
-    use gpui2::{point, size, Pixels};
+    use gpui::{point, size, Pixels};
     use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng, Rng};
 
     use crate::{content_index_for_mouse, IndexedCell, TerminalContent, TerminalSize};

crates/terminal2/src/terminal_settings.rs 🔗

@@ -1,4 +1,4 @@
-use gpui2::{AppContext, FontFeatures};
+use gpui::{AppContext, FontFeatures};
 use schemars::JsonSchema;
 use serde_derive::{Deserialize, Serialize};
 use std::{collections::HashMap, path::PathBuf};
@@ -98,7 +98,7 @@ impl TerminalSettings {
     // }
 }
 
-impl settings2::Settings for TerminalSettings {
+impl settings::Settings for TerminalSettings {
     const KEY: Option<&'static str> = Some("terminal");
 
     type FileContent = TerminalSettingsContent;

crates/text2/Cargo.toml 🔗

@@ -30,7 +30,7 @@ regex.workspace = true
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 ctor.workspace = true
 env_logger.workspace = true

crates/text2/src/locator.rs 🔗

@@ -91,7 +91,7 @@ mod tests {
     use rand::prelude::*;
     use std::mem;
 
-    #[gpui2::test(iterations = 100)]
+    #[gpui::test(iterations = 100)]
     fn test_locators(mut rng: StdRng) {
         let mut lhs = Default::default();
         let mut rhs = Default::default();

crates/text2/src/patch.rs 🔗

@@ -256,7 +256,7 @@ mod tests {
     use rand::prelude::*;
     use std::env;
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_one_disjoint_edit() {
         assert_patch_composition(
             Patch(vec![Edit {
@@ -301,7 +301,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_one_overlapping_edit() {
         assert_patch_composition(
             Patch(vec![Edit {
@@ -319,7 +319,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_two_disjoint_and_overlapping() {
         assert_patch_composition(
             Patch(vec![
@@ -355,7 +355,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_two_new_edits_overlapping_one_old_edit() {
         assert_patch_composition(
             Patch(vec![Edit {
@@ -421,7 +421,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_two_new_edits_touching_one_old_edit() {
         assert_patch_composition(
             Patch(vec![
@@ -457,7 +457,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     fn test_old_to_new() {
         let patch = Patch(vec![
             Edit {
@@ -481,7 +481,7 @@ mod tests {
         assert_eq!(patch.old_to_new(9), 12);
     }
 
-    #[gpui2::test(iterations = 100)]
+    #[gpui::test(iterations = 100)]
     fn test_random_patch_compositions(mut rng: StdRng) {
         let operations = env::var("OPERATIONS")
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))

crates/text2/src/tests.rs 🔗

@@ -32,7 +32,7 @@ fn test_edit() {
     assert_eq!(buffer.text(), "ghiamnoef");
 }
 
-#[gpui2::test(iterations = 100)]
+#[gpui::test(iterations = 100)]
 fn test_random_edits(mut rng: StdRng) {
     let operations = env::var("OPERATIONS")
         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
@@ -687,7 +687,7 @@ fn test_concurrent_edits() {
     assert_eq!(buffer3.text(), "a12c34e56");
 }
 
-#[gpui2::test(iterations = 100)]
+#[gpui::test(iterations = 100)]
 fn test_random_concurrent_edits(mut rng: StdRng) {
     let peers = env::var("PEERS")
         .map(|i| i.parse().expect("invalid `PEERS` variable"))

crates/theme2/Cargo.toml 🔗

@@ -6,9 +6,9 @@ publish = false
 
 [features]
 test-support = [
-    "gpui2/test-support",
+    "gpui/test-support",
     "fs/test-support",
-    "settings2/test-support"
+    "settings/test-support"
 ]
 
 [lib]
@@ -18,7 +18,7 @@ doctest = false
 [dependencies]
 anyhow.workspace = true
 fs = { path = "../fs" }
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 indexmap = "1.6.2"
 parking_lot.workspace = true
 refineable.workspace = true
@@ -26,11 +26,11 @@ schemars.workspace = true
 serde.workspace = true
 serde_derive.workspace = true
 serde_json.workspace = true
-settings2 = { path = "../settings2" }
+settings = { package = "settings2", path = "../settings2" }
 toml.workspace = true
 util = { path = "../util" }
 
 [dev-dependencies]
-gpui2 = { path = "../gpui2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
-settings2 = { path = "../settings2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }

crates/theme2/src/colors.rs 🔗

@@ -1,4 +1,4 @@
-use gpui2::Hsla;
+use gpui::Hsla;
 use refineable::Refineable;
 
 use crate::SyntaxTheme;
@@ -126,7 +126,7 @@ mod tests {
     fn override_a_single_theme_color() {
         let mut colors = ThemeColors::default_light();
 
-        let magenta: Hsla = gpui2::rgb(0xff00ff);
+        let magenta: Hsla = gpui::rgb(0xff00ff);
 
         assert_ne!(colors.text, magenta);
 
@@ -144,8 +144,8 @@ mod tests {
     fn override_multiple_theme_colors() {
         let mut colors = ThemeColors::default_light();
 
-        let magenta: Hsla = gpui2::rgb(0xff00ff);
-        let green: Hsla = gpui2::rgb(0x00ff00);
+        let magenta: Hsla = gpui::rgb(0xff00ff);
+        let green: Hsla = gpui::rgb(0x00ff00);
 
         assert_ne!(colors.text, magenta);
         assert_ne!(colors.background, green);

crates/theme2/src/default_colors.rs 🔗

@@ -1,6 +1,6 @@
 use std::num::ParseIntError;
 
-use gpui2::{hsla, Hsla, Rgba};
+use gpui::{hsla, Hsla, Rgba};
 
 use crate::{
     colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors},

crates/theme2/src/registry.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{zed_pro_family, ThemeFamily, ThemeVariant};
 use anyhow::{anyhow, Result};
-use gpui2::SharedString;
+use gpui::SharedString;
 use std::{collections::HashMap, sync::Arc};
 
 pub struct ThemeRegistry {

crates/theme2/src/scale.rs 🔗

@@ -1,4 +1,4 @@
-use gpui2::{AppContext, Hsla, SharedString};
+use gpui::{AppContext, Hsla, SharedString};
 
 use crate::{ActiveTheme, Appearance};
 

crates/theme2/src/settings.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{ThemeRegistry, ThemeVariant};
 use anyhow::Result;
-use gpui2::{px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels};
+use gpui::{px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels};
 use schemars::{
     gen::SchemaGenerator,
     schema::{InstanceType, Schema, SchemaObject},
@@ -8,7 +8,7 @@ use schemars::{
 };
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
-use settings2::{Settings, SettingsJsonSchemaParams};
+use settings::{Settings, SettingsJsonSchemaParams};
 use std::sync::Arc;
 use util::ResultExt as _;
 
@@ -105,7 +105,7 @@ pub fn reset_font_size(cx: &mut AppContext) {
     }
 }
 
-impl settings2::Settings for ThemeSettings {
+impl settings::Settings for ThemeSettings {
     const KEY: Option<&'static str> = None;
 
     type FileContent = ThemeSettingsContent;

crates/theme2/src/syntax.rs 🔗

@@ -1,4 +1,4 @@
-use gpui2::{HighlightStyle, Hsla};
+use gpui::{HighlightStyle, Hsla};
 
 #[derive(Clone, Default)]
 pub struct SyntaxTheme {

crates/theme2/src/theme2.rs 🔗

@@ -6,6 +6,7 @@ mod scale;
 mod settings;
 mod syntax;
 
+use ::settings::Settings;
 pub use colors::*;
 pub use default_colors::*;
 pub use default_theme::*;
@@ -14,8 +15,7 @@ pub use scale::*;
 pub use settings::*;
 pub use syntax::*;
 
-use gpui2::{AppContext, Hsla, SharedString};
-use settings2::Settings;
+use gpui::{AppContext, Hsla, SharedString};
 
 #[derive(Debug, PartialEq, Clone, Copy)]
 pub enum Appearance {

crates/ui2/src/elements/avatar.rs 🔗

@@ -58,11 +58,26 @@ mod stories {
                 .child(Avatar::new(
                     "https://avatars.githubusercontent.com/u/1714999?v=4",
                 ))
+                .child(Avatar::new(
+                    "https://avatars.githubusercontent.com/u/326587?v=4",
+                ))
+                // .child(Avatar::new(
+                //     "https://avatars.githubusercontent.com/u/326587?v=4",
+                // ))
+                // .child(Avatar::new(
+                //     "https://avatars.githubusercontent.com/u/482957?v=4",
+                // ))
+                // .child(Avatar::new(
+                //     "https://avatars.githubusercontent.com/u/1714999?v=4",
+                // ))
+                // .child(Avatar::new(
+                //     "https://avatars.githubusercontent.com/u/1486634?v=4",
+                // ))
                 .child(Story::label(cx, "Rounded rectangle"))
-                .child(
-                    Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
-                        .shape(Shape::RoundedRectangle),
-                )
+            // .child(
+            //     Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
+            //         .shape(Shape::RoundedRectangle),
+            // )
         }
     }
 }

crates/ui2/src/elements/player.rs 🔗

@@ -139,11 +139,11 @@ impl Player {
     }
 
     pub fn cursor_color<V: 'static>(&self, cx: &mut ViewContext<V>) -> Hsla {
-        cx.theme().styles.player.0[self.index].cursor
+        cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].cursor
     }
 
     pub fn selection_color<V: 'static>(&self, cx: &mut ViewContext<V>) -> Hsla {
-        cx.theme().styles.player.0[self.index].selection
+        cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].selection
     }
 
     pub fn avatar_src(&self) -> &str {

crates/workspace2/Cargo.toml 🔗

@@ -0,0 +1,66 @@
+[package]
+name = "workspace2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/workspace2.rs"
+doctest = false
+
+[features]
+test-support = [
+    "call2/test-support",
+    "client2/test-support",
+    "project2/test-support",
+    "settings2/test-support",
+    "gpui2/test-support",
+    "fs2/test-support"
+]
+
+[dependencies]
+db2 = { path = "../db2" }
+call2 = { path = "../call2" }
+client2 = { path = "../client2" }
+collections = { path = "../collections" }
+# context_menu = { path = "../context_menu" }
+fs2 = { path = "../fs2" }
+gpui2 = { path = "../gpui2" }
+install_cli2 = { path = "../install_cli2" }
+language2 = { path = "../language2" }
+#menu = { path = "../menu" }
+node_runtime = { path = "../node_runtime" }
+project2 = { path = "../project2" }
+settings2 = { path = "../settings2" }
+terminal2 = { path = "../terminal2" }
+theme2 = { path = "../theme2" }
+util = { path = "../util" }
+ui = { package = "ui2", path = "../ui2" }
+
+async-recursion = "1.0.0"
+itertools = "0.10"
+bincode = "1.2.1"
+anyhow.workspace = true
+futures.workspace = true
+lazy_static.workspace = true
+log.workspace = true
+parking_lot.workspace = true
+postage.workspace = true
+schemars.workspace = true
+serde.workspace = true
+serde_derive.workspace = true
+serde_json.workspace = true
+smallvec.workspace = true
+uuid.workspace = true
+
+[dev-dependencies]
+call2 = { path = "../call2", features = ["test-support"] }
+client2 = { path = "../client2", features = ["test-support"] }
+gpui2 = { path = "../gpui2", features = ["test-support"] }
+project2 = { path = "../project2", features = ["test-support"] }
+settings2 = { path = "../settings2", features = ["test-support"] }
+fs2 = { path = "../fs2", features = ["test-support"] }
+db2 = { path = "../db2", features = ["test-support"] }
+
+indoc.workspace = true
+env_logger.workspace = true

crates/workspace2/src/dock.rs 🔗

@@ -0,0 +1,750 @@
+use crate::{status_bar::StatusItemView, Axis, Workspace};
+use gpui2::{
+    div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, ParentElement, Render,
+    Subscription, View, ViewContext, WeakView, WindowContext,
+};
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+use std::sync::Arc;
+
+pub trait Panel: Render + EventEmitter {
+    fn persistent_name(&self) -> &'static str;
+    fn position(&self, cx: &WindowContext) -> DockPosition;
+    fn position_is_valid(&self, position: DockPosition) -> bool;
+    fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>);
+    fn size(&self, cx: &WindowContext) -> f32;
+    fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>);
+    fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
+    fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>);
+    fn icon_label(&self, _: &WindowContext) -> Option<String> {
+        None
+    }
+    fn should_change_position_on_event(_: &Self::Event) -> bool;
+    fn should_zoom_in_on_event(_: &Self::Event) -> bool {
+        false
+    }
+    fn should_zoom_out_on_event(_: &Self::Event) -> bool {
+        false
+    }
+    fn is_zoomed(&self, _cx: &WindowContext) -> bool {
+        false
+    }
+    fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
+    fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
+    fn should_activate_on_event(_: &Self::Event) -> bool {
+        false
+    }
+    fn should_close_on_event(_: &Self::Event) -> bool {
+        false
+    }
+    fn has_focus(&self, cx: &WindowContext) -> bool;
+    fn is_focus_event(_: &Self::Event) -> bool;
+}
+
+pub trait PanelHandle: Send + Sync {
+    fn id(&self) -> EntityId;
+    fn persistent_name(&self, cx: &WindowContext) -> &'static str;
+    fn position(&self, cx: &WindowContext) -> DockPosition;
+    fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool;
+    fn set_position(&self, position: DockPosition, cx: &mut WindowContext);
+    fn is_zoomed(&self, cx: &WindowContext) -> bool;
+    fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext);
+    fn set_active(&self, active: bool, cx: &mut WindowContext);
+    fn size(&self, cx: &WindowContext) -> f32;
+    fn set_size(&self, size: Option<f32>, cx: &mut WindowContext);
+    fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
+    fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>);
+    fn icon_label(&self, cx: &WindowContext) -> Option<String>;
+    fn has_focus(&self, cx: &WindowContext) -> bool;
+    fn to_any(&self) -> AnyView;
+}
+
+impl<T> PanelHandle for View<T>
+where
+    T: Panel,
+{
+    fn id(&self) -> EntityId {
+        self.entity_id()
+    }
+
+    fn persistent_name(&self, cx: &WindowContext) -> &'static str {
+        self.read(cx).persistent_name()
+    }
+
+    fn position(&self, cx: &WindowContext) -> DockPosition {
+        self.read(cx).position(cx)
+    }
+
+    fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool {
+        self.read(cx).position_is_valid(position)
+    }
+
+    fn set_position(&self, position: DockPosition, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.set_position(position, cx))
+    }
+
+    fn is_zoomed(&self, cx: &WindowContext) -> bool {
+        self.read(cx).is_zoomed(cx)
+    }
+
+    fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.set_zoomed(zoomed, cx))
+    }
+
+    fn set_active(&self, active: bool, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.set_active(active, cx))
+    }
+
+    fn size(&self, cx: &WindowContext) -> f32 {
+        self.read(cx).size(cx)
+    }
+
+    fn set_size(&self, size: Option<f32>, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.set_size(size, cx))
+    }
+
+    fn icon_path(&self, cx: &WindowContext) -> Option<&'static str> {
+        self.read(cx).icon_path(cx)
+    }
+
+    fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>) {
+        self.read(cx).icon_tooltip()
+    }
+
+    fn icon_label(&self, cx: &WindowContext) -> Option<String> {
+        self.read(cx).icon_label(cx)
+    }
+
+    fn has_focus(&self, cx: &WindowContext) -> bool {
+        self.read(cx).has_focus(cx)
+    }
+
+    fn to_any(&self) -> AnyView {
+        self.clone().into()
+    }
+}
+
+impl From<&dyn PanelHandle> for AnyView {
+    fn from(val: &dyn PanelHandle) -> Self {
+        val.to_any()
+    }
+}
+
+pub struct Dock {
+    position: DockPosition,
+    panel_entries: Vec<PanelEntry>,
+    is_open: bool,
+    active_panel_index: usize,
+}
+
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
+#[serde(rename_all = "lowercase")]
+pub enum DockPosition {
+    Left,
+    Bottom,
+    Right,
+}
+
+impl DockPosition {
+    fn to_label(&self) -> &'static str {
+        match self {
+            Self::Left => "left",
+            Self::Bottom => "bottom",
+            Self::Right => "right",
+        }
+    }
+
+    // todo!()
+    // fn to_resize_handle_side(self) -> HandleSide {
+    //     match self {
+    //         Self::Left => HandleSide::Right,
+    //         Self::Bottom => HandleSide::Top,
+    //         Self::Right => HandleSide::Left,
+    //     }
+    // }
+
+    pub fn axis(&self) -> Axis {
+        match self {
+            Self::Left | Self::Right => Axis::Horizontal,
+            Self::Bottom => Axis::Vertical,
+        }
+    }
+}
+
+struct PanelEntry {
+    panel: Arc<dyn PanelHandle>,
+    // todo!()
+    // context_menu: View<ContextMenu>,
+    _subscriptions: [Subscription; 2],
+}
+
+pub struct PanelButtons {
+    dock: View<Dock>,
+    workspace: WeakView<Workspace>,
+}
+
+impl Dock {
+    pub fn new(position: DockPosition) -> Self {
+        Self {
+            position,
+            panel_entries: Default::default(),
+            active_panel_index: 0,
+            is_open: false,
+        }
+    }
+
+    pub fn position(&self) -> DockPosition {
+        self.position
+    }
+
+    pub fn is_open(&self) -> bool {
+        self.is_open
+    }
+
+    //     pub fn has_focus(&self, cx: &WindowContext) -> bool {
+    //         self.visible_panel()
+    //             .map_or(false, |panel| panel.has_focus(cx))
+    //     }
+
+    //     pub fn panel<T: Panel>(&self) -> Option<View<T>> {
+    //         self.panel_entries
+    //             .iter()
+    //             .find_map(|entry| entry.panel.as_any().clone().downcast())
+    //     }
+
+    //     pub fn panel_index_for_type<T: Panel>(&self) -> Option<usize> {
+    //         self.panel_entries
+    //             .iter()
+    //             .position(|entry| entry.panel.as_any().is::<T>())
+    //     }
+
+    pub fn panel_index_for_ui_name(&self, _ui_name: &str, _cx: &AppContext) -> Option<usize> {
+        todo!()
+        // self.panel_entries.iter().position(|entry| {
+        //     let panel = entry.panel.as_any();
+        //     cx.view_ui_name(panel.window(), panel.id()) == Some(ui_name)
+        // })
+    }
+
+    //     pub fn active_panel_index(&self) -> usize {
+    //         self.active_panel_index
+    //     }
+
+    pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
+        if open != self.is_open {
+            self.is_open = open;
+            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+                active_panel.panel.set_active(open, cx);
+            }
+
+            cx.notify();
+        }
+    }
+
+    //     pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
+    //         for entry in &mut self.panel_entries {
+    //             if entry.panel.as_any() == panel {
+    //                 if zoomed != entry.panel.is_zoomed(cx) {
+    //                     entry.panel.set_zoomed(zoomed, cx);
+    //                 }
+    //             } else if entry.panel.is_zoomed(cx) {
+    //                 entry.panel.set_zoomed(false, cx);
+    //             }
+    //         }
+
+    //         cx.notify();
+    //     }
+
+    //     pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
+    //         for entry in &mut self.panel_entries {
+    //             if entry.panel.is_zoomed(cx) {
+    //                 entry.panel.set_zoomed(false, cx);
+    //             }
+    //         }
+    //     }
+
+    //     pub(crate) fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
+    //         let subscriptions = [
+    //             cx.observe(&panel, |_, _, cx| cx.notify()),
+    //             cx.subscribe(&panel, |this, panel, event, cx| {
+    //                 if T::should_activate_on_event(event) {
+    //                     if let Some(ix) = this
+    //                         .panel_entries
+    //                         .iter()
+    //                         .position(|entry| entry.panel.id() == panel.id())
+    //                     {
+    //                         this.set_open(true, cx);
+    //                         this.activate_panel(ix, cx);
+    //                         cx.focus(&panel);
+    //                     }
+    //                 } else if T::should_close_on_event(event)
+    //                     && this.visible_panel().map_or(false, |p| p.id() == panel.id())
+    //                 {
+    //                     this.set_open(false, cx);
+    //                 }
+    //             }),
+    //         ];
+
+    //         let dock_view_id = cx.view_id();
+    //         self.panel_entries.push(PanelEntry {
+    //             panel: Arc::new(panel),
+    //             // todo!()
+    //             // context_menu: cx.add_view(|cx| {
+    //             //     let mut menu = ContextMenu::new(dock_view_id, cx);
+    //             //     menu.set_position_mode(OverlayPositionMode::Local);
+    //             //     menu
+    //             // }),
+    //             _subscriptions: subscriptions,
+    //         });
+    //         cx.notify()
+    //     }
+
+    //     pub fn remove_panel<T: Panel>(&mut self, panel: &View<T>, cx: &mut ViewContext<Self>) {
+    //         if let Some(panel_ix) = self
+    //             .panel_entries
+    //             .iter()
+    //             .position(|entry| entry.panel.id() == panel.id())
+    //         {
+    //             if panel_ix == self.active_panel_index {
+    //                 self.active_panel_index = 0;
+    //                 self.set_open(false, cx);
+    //             } else if panel_ix < self.active_panel_index {
+    //                 self.active_panel_index -= 1;
+    //             }
+    //             self.panel_entries.remove(panel_ix);
+    //             cx.notify();
+    //         }
+    //     }
+
+    //     pub fn panels_len(&self) -> usize {
+    //         self.panel_entries.len()
+    //     }
+
+    pub fn activate_panel(&mut self, panel_ix: usize, cx: &mut ViewContext<Self>) {
+        if panel_ix != self.active_panel_index {
+            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+                active_panel.panel.set_active(false, cx);
+            }
+
+            self.active_panel_index = panel_ix;
+            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+                active_panel.panel.set_active(true, cx);
+            }
+
+            cx.notify();
+        }
+    }
+
+    pub fn visible_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
+        let entry = self.visible_entry()?;
+        Some(&entry.panel)
+    }
+
+    pub fn active_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
+        Some(&self.panel_entries.get(self.active_panel_index)?.panel)
+    }
+
+    fn visible_entry(&self) -> Option<&PanelEntry> {
+        if self.is_open {
+            self.panel_entries.get(self.active_panel_index)
+        } else {
+            None
+        }
+    }
+
+    //     pub fn zoomed_panel(&self, cx: &WindowContext) -> Option<Arc<dyn PanelHandle>> {
+    //         let entry = self.visible_entry()?;
+    //         if entry.panel.is_zoomed(cx) {
+    //             Some(entry.panel.clone())
+    //         } else {
+    //             None
+    //         }
+    //     }
+
+    //     pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<f32> {
+    //         self.panel_entries
+    //             .iter()
+    //             .find(|entry| entry.panel.id() == panel.id())
+    //             .map(|entry| entry.panel.size(cx))
+    //     }
+
+    //     pub fn active_panel_size(&self, cx: &WindowContext) -> Option<f32> {
+    //         if self.is_open {
+    //             self.panel_entries
+    //                 .get(self.active_panel_index)
+    //                 .map(|entry| entry.panel.size(cx))
+    //         } else {
+    //             None
+    //         }
+    //     }
+
+    //     pub fn resize_active_panel(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
+    //         if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) {
+    //             entry.panel.set_size(size, cx);
+    //             cx.notify();
+    //         }
+    //     }
+
+    //     pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement<Workspace> {
+    //         todo!()
+    // if let Some(active_entry) = self.visible_entry() {
+    //     Empty::new()
+    //         .into_any()
+    //         .contained()
+    //         .with_style(self.style(cx))
+    //         .resizable::<WorkspaceBounds>(
+    //             self.position.to_resize_handle_side(),
+    //             active_entry.panel.size(cx),
+    //             |_, _, _| {},
+    //         )
+    //         .into_any()
+    // } else {
+    //     Empty::new().into_any()
+    // }
+    //     }
+}
+
+// todo!()
+// impl View for Dock {
+//     fn ui_name() -> &'static str {
+//         "Dock"
+//     }
+
+//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+//         if let Some(active_entry) = self.visible_entry() {
+//             let style = self.style(cx);
+//             ChildView::new(active_entry.panel.as_any(), cx)
+//                 .contained()
+//                 .with_style(style)
+//                 .resizable::<WorkspaceBounds>(
+//                     self.position.to_resize_handle_side(),
+//                     active_entry.panel.size(cx),
+//                     |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx),
+//                 )
+//                 .into_any()
+//         } else {
+//             Empty::new().into_any()
+//         }
+//     }
+
+//     fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+//         if cx.is_self_focused() {
+//             if let Some(active_entry) = self.visible_entry() {
+//                 cx.focus(active_entry.panel.as_any());
+//             } else {
+//                 cx.focus_parent();
+//             }
+//         }
+//     }
+// }
+
+impl PanelButtons {
+    pub fn new(
+        dock: View<Dock>,
+        workspace: WeakView<Workspace>,
+        cx: &mut ViewContext<Self>,
+    ) -> Self {
+        cx.observe(&dock, |_, _, cx| cx.notify()).detach();
+        Self { dock, workspace }
+    }
+}
+
+impl EventEmitter for PanelButtons {
+    type Event = ();
+}
+
+// impl Render for PanelButtons {
+//     type Element = ();
+
+//     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+//         todo!("")
+//     }
+
+//     fn ui_name() -> &'static str {
+//         "PanelButtons"
+//     }
+
+//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+//         let theme = &settings::get::<ThemeSettings>(cx).theme;
+//         let tooltip_style = theme.tooltip.clone();
+//         let theme = &theme.workspace.status_bar.panel_buttons;
+//         let button_style = theme.button.clone();
+//         let dock = self.dock.read(cx);
+//         let active_ix = dock.active_panel_index;
+//         let is_open = dock.is_open;
+//         let dock_position = dock.position;
+//         let group_style = match dock_position {
+//             DockPosition::Left => theme.group_left,
+//             DockPosition::Bottom => theme.group_bottom,
+//             DockPosition::Right => theme.group_right,
+//         };
+//         let menu_corner = match dock_position {
+//             DockPosition::Left => AnchorCorner::BottomLeft,
+//             DockPosition::Bottom | DockPosition::Right => AnchorCorner::BottomRight,
+//         };
+
+//         let panels = dock
+//             .panel_entries
+//             .iter()
+//             .map(|item| (item.panel.clone(), item.context_menu.clone()))
+//             .collect::<Vec<_>>();
+//         Flex::row()
+//             .with_children(panels.into_iter().enumerate().filter_map(
+//                 |(panel_ix, (view, context_menu))| {
+//                     let icon_path = view.icon_path(cx)?;
+//                     let is_active = is_open && panel_ix == active_ix;
+//                     let (tooltip, tooltip_action) = if is_active {
+//                         (
+//                             format!("Close {} dock", dock_position.to_label()),
+//                             Some(match dock_position {
+//                                 DockPosition::Left => crate::ToggleLeftDock.boxed_clone(),
+//                                 DockPosition::Bottom => crate::ToggleBottomDock.boxed_clone(),
+//                                 DockPosition::Right => crate::ToggleRightDock.boxed_clone(),
+//                             }),
+//                         )
+//                     } else {
+//                         view.icon_tooltip(cx)
+//                     };
+//                     Some(
+//                         Stack::new()
+//                             .with_child(
+//                                 MouseEventHandler::new::<Self, _>(panel_ix, cx, |state, cx| {
+//                                     let style = button_style.in_state(is_active);
+
+//                                     let style = style.style_for(state);
+//                                     Flex::row()
+//                                         .with_child(
+//                                             Svg::new(icon_path)
+//                                                 .with_color(style.icon_color)
+//                                                 .constrained()
+//                                                 .with_width(style.icon_size)
+//                                                 .aligned(),
+//                                         )
+//                                         .with_children(if let Some(label) = view.icon_label(cx) {
+//                                             Some(
+//                                                 Label::new(label, style.label.text.clone())
+//                                                     .contained()
+//                                                     .with_style(style.label.container)
+//                                                     .aligned(),
+//                                             )
+//                                         } else {
+//                                             None
+//                                         })
+//                                         .constrained()
+//                                         .with_height(style.icon_size)
+//                                         .contained()
+//                                         .with_style(style.container)
+//                                 })
+//                                 .with_cursor_style(CursorStyle::PointingHand)
+//                                 .on_click(MouseButton::Left, {
+//                                     let tooltip_action =
+//                                         tooltip_action.as_ref().map(|action| action.boxed_clone());
+//                                     move |_, this, cx| {
+//                                         if let Some(tooltip_action) = &tooltip_action {
+//                                             let window = cx.window();
+//                                             let view_id = this.workspace.id();
+//                                             let tooltip_action = tooltip_action.boxed_clone();
+//                                             cx.spawn(|_, mut cx| async move {
+//                                                 window.dispatch_action(
+//                                                     view_id,
+//                                                     &*tooltip_action,
+//                                                     &mut cx,
+//                                                 );
+//                                             })
+//                                             .detach();
+//                                         }
+//                                     }
+//                                 })
+//                                 .on_click(MouseButton::Right, {
+//                                     let view = view.clone();
+//                                     let menu = context_menu.clone();
+//                                     move |_, _, cx| {
+//                                         const POSITIONS: [DockPosition; 3] = [
+//                                             DockPosition::Left,
+//                                             DockPosition::Right,
+//                                             DockPosition::Bottom,
+//                                         ];
+
+//                                         menu.update(cx, |menu, cx| {
+//                                             let items = POSITIONS
+//                                                 .into_iter()
+//                                                 .filter(|position| {
+//                                                     *position != dock_position
+//                                                         && view.position_is_valid(*position, cx)
+//                                                 })
+//                                                 .map(|position| {
+//                                                     let view = view.clone();
+//                                                     ContextMenuItem::handler(
+//                                                         format!("Dock {}", position.to_label()),
+//                                                         move |cx| view.set_position(position, cx),
+//                                                     )
+//                                                 })
+//                                                 .collect();
+//                                             menu.show(Default::default(), menu_corner, items, cx);
+//                                         })
+//                                     }
+//                                 })
+//                                 .with_tooltip::<Self>(
+//                                     panel_ix,
+//                                     tooltip,
+//                                     tooltip_action,
+//                                     tooltip_style.clone(),
+//                                     cx,
+//                                 ),
+//                             )
+//                             .with_child(ChildView::new(&context_menu, cx)),
+//                     )
+//                 },
+//             ))
+//             .contained()
+//             .with_style(group_style)
+//             .into_any()
+//     }
+// }
+
+impl Render for PanelButtons {
+    type Element = Div<Self>;
+
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+        // todo!()
+        let dock = self.dock.read(cx);
+        div().children(
+            dock.panel_entries
+                .iter()
+                .map(|panel| panel.panel.persistent_name(cx)),
+        )
+    }
+}
+
+impl StatusItemView for PanelButtons {
+    fn set_active_pane_item(
+        &mut self,
+        _active_pane_item: Option<&dyn crate::ItemHandle>,
+        _cx: &mut ViewContext<Self>,
+    ) {
+        // todo!(This is empty in the old `workspace::dock`)
+    }
+}
+
+#[cfg(any(test, feature = "test-support"))]
+pub mod test {
+    use super::*;
+    use gpui2::{div, Div, ViewContext, WindowContext};
+
+    #[derive(Debug)]
+    pub enum TestPanelEvent {
+        PositionChanged,
+        Activated,
+        Closed,
+        ZoomIn,
+        ZoomOut,
+        Focus,
+    }
+
+    pub struct TestPanel {
+        pub position: DockPosition,
+        pub zoomed: bool,
+        pub active: bool,
+        pub has_focus: bool,
+        pub size: f32,
+    }
+
+    impl EventEmitter for TestPanel {
+        type Event = TestPanelEvent;
+    }
+
+    impl TestPanel {
+        pub fn new(position: DockPosition) -> Self {
+            Self {
+                position,
+                zoomed: false,
+                active: false,
+                has_focus: false,
+                size: 300.,
+            }
+        }
+    }
+
+    impl Render for TestPanel {
+        type Element = Div<Self>;
+
+        fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+            div()
+        }
+    }
+
+    impl Panel for TestPanel {
+        fn persistent_name(&self) -> &'static str {
+            "TestPanel"
+        }
+
+        fn position(&self, _: &gpui2::WindowContext) -> super::DockPosition {
+            self.position
+        }
+
+        fn position_is_valid(&self, _: super::DockPosition) -> bool {
+            true
+        }
+
+        fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
+            self.position = position;
+            cx.emit(TestPanelEvent::PositionChanged);
+        }
+
+        fn size(&self, _: &WindowContext) -> f32 {
+            self.size
+        }
+
+        fn set_size(&mut self, size: Option<f32>, _: &mut ViewContext<Self>) {
+            self.size = size.unwrap_or(300.);
+        }
+
+        fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
+            Some("icons/test_panel.svg")
+        }
+
+        fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
+            ("Test Panel".into(), None)
+        }
+
+        fn should_change_position_on_event(event: &Self::Event) -> bool {
+            matches!(event, TestPanelEvent::PositionChanged)
+        }
+
+        fn should_zoom_in_on_event(event: &Self::Event) -> bool {
+            matches!(event, TestPanelEvent::ZoomIn)
+        }
+
+        fn should_zoom_out_on_event(event: &Self::Event) -> bool {
+            matches!(event, TestPanelEvent::ZoomOut)
+        }
+
+        fn is_zoomed(&self, _: &WindowContext) -> bool {
+            self.zoomed
+        }
+
+        fn set_zoomed(&mut self, zoomed: bool, _cx: &mut ViewContext<Self>) {
+            self.zoomed = zoomed;
+        }
+
+        fn set_active(&mut self, active: bool, _cx: &mut ViewContext<Self>) {
+            self.active = active;
+        }
+
+        fn should_activate_on_event(event: &Self::Event) -> bool {
+            matches!(event, TestPanelEvent::Activated)
+        }
+
+        fn should_close_on_event(event: &Self::Event) -> bool {
+            matches!(event, TestPanelEvent::Closed)
+        }
+
+        fn has_focus(&self, _cx: &WindowContext) -> bool {
+            self.has_focus
+        }
+
+        fn is_focus_event(event: &Self::Event) -> bool {
+            matches!(event, TestPanelEvent::Focus)
+        }
+    }
+}

crates/workspace2/src/item.rs 🔗

@@ -1,88 +1,80 @@
-// use crate::{
-//     pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders,
-//     ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
-// };
-// use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
+use crate::{
+    pane::{self, Pane},
+    persistence::model::ItemId,
+    searchable::SearchableItemHandle,
+    workspace_settings::{AutosaveSetting, WorkspaceSettings},
+    DelayedDebouncedEditAction, FollowableItemBuilders, ItemNavHistory, ToolbarItemLocation,
+    ViewId, Workspace, WorkspaceId,
+};
 use anyhow::Result;
 use client2::{
-    proto::{self, PeerId, ViewId},
+    proto::{self, PeerId},
     Client,
 };
+use gpui2::{
+    AnyElement, AnyView, AppContext, Entity, EntityId, EventEmitter, HighlightStyle, Model, Pixels,
+    Point, Render, SharedString, Task, View, ViewContext, WeakView, WindowContext,
+};
+use parking_lot::Mutex;
+use project2::{Project, ProjectEntryId, ProjectPath};
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
 use settings2::Settings;
-use theme2::Theme;
-// use client2::{
-//     proto::{self, PeerId},
-//     Client,
-// };
-// use gpui2::geometry::vector::Vector2F;
-// use gpui2::AnyWindowHandle;
-// use gpui2::{
-//     fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, Handle, Task, View,
-//     ViewContext, View, WeakViewHandle, WindowContext,
-// };
-// use project2::{Project, ProjectEntryId, ProjectPath};
-// use schemars::JsonSchema;
-// use serde_derive::{Deserialize, Serialize};
-// use settings2::Setting;
-// use smallvec::SmallVec;
-// use std::{
-//     any::{Any, TypeId},
-//     borrow::Cow,
-//     cell::RefCell,
-//     fmt,
-//     ops::Range,
-//     path::PathBuf,
-//     rc::Rc,
-//     sync::{
-//         atomic::{AtomicBool, Ordering},
-//         Arc,
-//     },
-//     time::Duration,
-// };
-// use theme2::Theme;
-
-// #[derive(Deserialize)]
-// pub struct ItemSettings {
-//     pub git_status: bool,
-//     pub close_position: ClosePosition,
-// }
+use smallvec::SmallVec;
+use std::{
+    any::{Any, TypeId},
+    ops::Range,
+    path::PathBuf,
+    sync::{
+        atomic::{AtomicBool, Ordering},
+        Arc,
+    },
+    time::Duration,
+};
+use theme2::ThemeVariant;
 
-// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
-// #[serde(rename_all = "lowercase")]
-// pub enum ClosePosition {
-//     Left,
-//     #[default]
-//     Right,
-// }
+#[derive(Deserialize)]
+pub struct ItemSettings {
+    pub git_status: bool,
+    pub close_position: ClosePosition,
+}
 
-// impl ClosePosition {
-//     pub fn right(&self) -> bool {
-//         match self {
-//             ClosePosition::Left => false,
-//             ClosePosition::Right => true,
-//         }
-//     }
-// }
+#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "lowercase")]
+pub enum ClosePosition {
+    Left,
+    #[default]
+    Right,
+}
 
-// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
-// pub struct ItemSettingsContent {
-//     git_status: Option<bool>,
-//     close_position: Option<ClosePosition>,
-// }
+impl ClosePosition {
+    pub fn right(&self) -> bool {
+        match self {
+            ClosePosition::Left => false,
+            ClosePosition::Right => true,
+        }
+    }
+}
 
-// impl Setting for ItemSettings {
-//     const KEY: Option<&'static str> = Some("tabs");
+#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
+pub struct ItemSettingsContent {
+    git_status: Option<bool>,
+    close_position: Option<ClosePosition>,
+}
 
-//     type FileContent = ItemSettingsContent;
+impl Settings for ItemSettings {
+    const KEY: Option<&'static str> = Some("tabs");
 
-//     fn load(
-//         default_value: &Self::FileContent,
-//         user_values: &[&Self::FileContent],
-//         _: &gpui2::AppContext,
-//     ) -> anyhow::Result<Self> {
-//         Self::load_via_json_merge(default_value, user_values)
-//     }
-// }
+    type FileContent = ItemSettingsContent;
+
+    fn load(
+        default_value: &Self::FileContent,
+        user_values: &[&Self::FileContent],
+        _: &mut AppContext,
+    ) -> Result<Self> {
+        Self::load_via_json_merge(default_value, user_values)
+    }
+}
 
 #[derive(Eq, PartialEq, Hash, Debug)]
 pub enum ItemEvent {
@@ -98,12 +90,12 @@ pub struct BreadcrumbText {
     pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
 }
 
-pub trait Item: EventEmitter + Sized {
-    //     fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
-    //     fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
-    //     fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
-    //         false
-    //     }
+pub trait Item: Render + EventEmitter + Send {
+    fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
+    fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
+    fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
+        false
+    }
     fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
         None
     }
@@ -117,131 +109,103 @@ pub trait Item: EventEmitter + Sized {
     fn is_singleton(&self, _cx: &AppContext) -> bool {
         false
     }
-    //     fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
-    fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext<Self>) -> Option<Self>
+    fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
+    fn clone_on_split(
+        &self,
+        _workspace_id: WorkspaceId,
+        _: &mut ViewContext<Self>,
+    ) -> Option<View<Self>>
     where
         Self: Sized,
     {
         None
     }
-    //     fn is_dirty(&self, _: &AppContext) -> bool {
-    //         false
-    //     }
-    //     fn has_conflict(&self, _: &AppContext) -> bool {
-    //         false
-    //     }
-    //     fn can_save(&self, _cx: &AppContext) -> bool {
-    //         false
-    //     }
-    //     fn save(
-    //         &mut self,
-    //         _project: Handle<Project>,
-    //         _cx: &mut ViewContext<Self>,
-    //     ) -> Task<Result<()>> {
-    //         unimplemented!("save() must be implemented if can_save() returns true")
-    //     }
-    //     fn save_as(
-    //         &mut self,
-    //         _project: Handle<Project>,
-    //         _abs_path: PathBuf,
-    //         _cx: &mut ViewContext<Self>,
-    //     ) -> Task<Result<()>> {
-    //         unimplemented!("save_as() must be implemented if can_save() returns true")
-    //     }
-    //     fn reload(
-    //         &mut self,
-    //         _project: Handle<Project>,
-    //         _cx: &mut ViewContext<Self>,
-    //     ) -> Task<Result<()>> {
-    //         unimplemented!("reload() must be implemented if can_save() returns true")
-    //     }
+    fn is_dirty(&self, _: &AppContext) -> bool {
+        false
+    }
+    fn has_conflict(&self, _: &AppContext) -> bool {
+        false
+    }
+    fn can_save(&self, _cx: &AppContext) -> bool {
+        false
+    }
+    fn save(&mut self, _project: Model<Project>, _cx: &mut ViewContext<Self>) -> Task<Result<()>> {
+        unimplemented!("save() must be implemented if can_save() returns true")
+    }
+    fn save_as(
+        &mut self,
+        _project: Model<Project>,
+        _abs_path: PathBuf,
+        _cx: &mut ViewContext<Self>,
+    ) -> Task<Result<()>> {
+        unimplemented!("save_as() must be implemented if can_save() returns true")
+    }
+    fn reload(
+        &mut self,
+        _project: Model<Project>,
+        _cx: &mut ViewContext<Self>,
+    ) -> Task<Result<()>> {
+        unimplemented!("reload() must be implemented if can_save() returns true")
+    }
     fn to_item_events(_event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
         SmallVec::new()
     }
-    //     fn should_close_item_on_event(_: &Self::Event) -> bool {
-    //         false
-    //     }
-    //     fn should_update_tab_on_event(_: &Self::Event) -> bool {
-    //         false
-    //     }
-
-    //     fn act_as_type<'a>(
-    //         &'a self,
-    //         type_id: TypeId,
-    //         self_handle: &'a View<Self>,
-    //         _: &'a AppContext,
-    //     ) -> Option<&AnyViewHandle> {
-    //         if TypeId::of::<Self>() == type_id {
-    //             Some(self_handle)
-    //         } else {
-    //             None
-    //         }
-    //     }
-
-    //     fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
-    //         None
-    //     }
-
-    //     fn breadcrumb_location(&self) -> ToolbarItemLocation {
-    //         ToolbarItemLocation::Hidden
-    //     }
-
-    //     fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
-    //         None
-    //     }
-
-    //     fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
-
-    //     fn serialized_item_kind() -> Option<&'static str> {
-    //         None
-    //     }
-
-    //     fn deserialize(
-    //         _project: Handle<Project>,
-    //         _workspace: WeakViewHandle<Workspace>,
-    //         _workspace_id: WorkspaceId,
-    //         _item_id: ItemId,
-    //         _cx: &mut ViewContext<Pane>,
-    //     ) -> Task<Result<View<Self>>> {
-    //         unimplemented!(
-    //             "deserialize() must be implemented if serialized_item_kind() returns Some(_)"
-    //         )
-    //     }
-    //     fn show_toolbar(&self) -> bool {
-    //         true
-    //     }
-    //     fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Vector2F> {
-    //         None
-    //     }
-}
+    fn should_close_item_on_event(_: &Self::Event) -> bool {
+        false
+    }
+    fn should_update_tab_on_event(_: &Self::Event) -> bool {
+        false
+    }
 
-use std::{
-    any::Any,
-    cell::RefCell,
-    ops::Range,
-    path::PathBuf,
-    rc::Rc,
-    sync::{
-        atomic::{AtomicBool, Ordering},
-        Arc,
-    },
-    time::Duration,
-};
+    fn act_as_type<'a>(
+        &'a self,
+        type_id: TypeId,
+        self_handle: &'a View<Self>,
+        _: &'a AppContext,
+    ) -> Option<AnyView> {
+        if TypeId::of::<Self>() == type_id {
+            Some(self_handle.clone().into())
+        } else {
+            None
+        }
+    }
 
-use gpui2::{
-    AnyElement, AnyWindowHandle, AppContext, EventEmitter, Handle, HighlightStyle, Pixels, Point,
-    SharedString, Task, View, ViewContext, VisualContext, WindowContext,
-};
-use project2::{Project, ProjectEntryId, ProjectPath};
-use smallvec::SmallVec;
+    fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+        None
+    }
 
-use crate::{
-    pane::{self, Pane},
-    searchable::SearchableItemHandle,
-    workspace_settings::{AutosaveSetting, WorkspaceSettings},
-    DelayedDebouncedEditAction, FollowableItemBuilders, ToolbarItemLocation, Workspace,
-    WorkspaceId,
-};
+    fn breadcrumb_location(&self) -> ToolbarItemLocation {
+        ToolbarItemLocation::Hidden
+    }
+
+    fn breadcrumbs(&self, _theme: &ThemeVariant, _cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+        None
+    }
+
+    fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
+
+    fn serialized_item_kind() -> Option<&'static str> {
+        None
+    }
+
+    fn deserialize(
+        _project: Model<Project>,
+        _workspace: WeakView<Workspace>,
+        _workspace_id: WorkspaceId,
+        _item_id: ItemId,
+        _cx: &mut ViewContext<Pane>,
+    ) -> Task<Result<View<Self>>> {
+        unimplemented!(
+            "deserialize() must be implemented if serialized_item_kind() returns Some(_)"
+        )
+    }
+    fn show_toolbar(&self) -> bool {
+        true
+    }
+    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Point<Pixels>> {
+        None
+    }
+}
 
 pub trait ItemHandle: 'static + Send {
     fn subscribe_to_item_events(
@@ -273,52 +237,49 @@ pub trait ItemHandle: 'static + Send {
     fn deactivated(&self, cx: &mut WindowContext);
     fn workspace_deactivated(&self, cx: &mut WindowContext);
     fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
-    fn id(&self) -> usize;
-    fn window(&self) -> AnyWindowHandle;
-    // fn as_any(&self) -> &AnyView; todo!()
+    fn id(&self) -> EntityId;
+    fn to_any(&self) -> AnyView;
     fn is_dirty(&self, cx: &AppContext) -> bool;
     fn has_conflict(&self, cx: &AppContext) -> bool;
     fn can_save(&self, cx: &AppContext) -> bool;
-    fn save(&self, project: Handle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
+    fn save(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
     fn save_as(
         &self,
-        project: Handle<Project>,
+        project: Model<Project>,
         abs_path: PathBuf,
         cx: &mut WindowContext,
     ) -> Task<Result<()>>;
-    fn reload(&self, project: Handle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
-    // fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>; todo!()
+    fn reload(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
+    fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyView>;
     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
     fn on_release(
-        &self,
+        &mut self,
         cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext)>,
+        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
     ) -> gpui2::Subscription;
     fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
     fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
-    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>>;
+    fn breadcrumbs(&self, theme: &ThemeVariant, cx: &AppContext) -> Option<Vec<BreadcrumbText>>;
     fn serialized_item_kind(&self) -> Option<&'static str>;
     fn show_toolbar(&self, cx: &AppContext) -> bool;
     fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>>;
 }
 
-pub trait WeakItemHandle {
-    fn id(&self) -> usize;
-    fn window(&self) -> AnyWindowHandle;
-    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
+pub trait WeakItemHandle: Send + Sync {
+    fn id(&self) -> EntityId;
+    fn upgrade(&self) -> Option<Box<dyn ItemHandle>>;
 }
 
-// todo!()
-// impl dyn ItemHandle {
-//     pub fn downcast<T: View>(&self) -> Option<View<T>> {
-//         self.as_any().clone().downcast()
-//     }
+impl dyn ItemHandle {
+    pub fn downcast<V: 'static>(&self) -> Option<View<V>> {
+        self.to_any().downcast().ok()
+    }
 
-//     pub fn act_as<T: View>(&self, cx: &AppContext) -> Option<View<T>> {
-//         self.act_as_type(TypeId::of::<T>(), cx)
-//             .and_then(|t| t.clone().downcast())
-//     }
-// }
+    pub fn act_as<V: 'static>(&self, cx: &AppContext) -> Option<View<V>> {
+        self.act_as_type(TypeId::of::<V>(), cx)
+            .and_then(|t| t.downcast().ok())
+    }
+}
 
 impl<T: Item> ItemHandle for View<T> {
     fn subscribe_to_item_events(
@@ -399,10 +360,8 @@ impl<T: Item> ItemHandle for View<T> {
         workspace_id: WorkspaceId,
         cx: &mut WindowContext,
     ) -> Option<Box<dyn ItemHandle>> {
-        self.update(cx, |item, cx| {
-            cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx))
-        })
-        .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
+        self.update(cx, |item, cx| item.clone_on_split(workspace_id, cx))
+            .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
     }
 
     fn added_to_pane(
@@ -439,109 +398,107 @@ impl<T: Item> ItemHandle for View<T> {
             .is_none()
         {
             let mut pending_autosave = DelayedDebouncedEditAction::new();
-            let pending_update = Rc::new(RefCell::new(None));
-            let pending_update_scheduled = Rc::new(AtomicBool::new(false));
-
-            let mut event_subscription =
-                Some(cx.subscribe(self, move |workspace, item, event, cx| {
-                    let pane = if let Some(pane) = workspace
-                        .panes_by_item
-                        .get(&item.id())
-                        .and_then(|pane| pane.upgrade(cx))
+            let pending_update = Arc::new(Mutex::new(None));
+            let pending_update_scheduled = Arc::new(AtomicBool::new(false));
+
+            let event_subscription = Some(cx.subscribe(self, move |workspace, item, event, cx| {
+                let pane = if let Some(pane) = workspace
+                    .panes_by_item
+                    .get(&item.id())
+                    .and_then(|pane| pane.upgrade())
+                {
+                    pane
+                } else {
+                    log::error!("unexpected item event after pane was dropped");
+                    return;
+                };
+
+                if let Some(item) = item.to_followable_item_handle(cx) {
+                    let _is_project_item = item.is_project_item(cx);
+                    let leader_id = workspace.leader_for_pane(&pane);
+
+                    if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
+                        workspace.unfollow(&pane, cx);
+                    }
+
+                    if item.add_event_to_update_proto(event, &mut *pending_update.lock(), cx)
+                        && !pending_update_scheduled.load(Ordering::SeqCst)
                     {
-                        pane
-                    } else {
-                        log::error!("unexpected item event after pane was dropped");
-                        return;
-                    };
-
-                    if let Some(item) = item.to_followable_item_handle(cx) {
-                        let is_project_item = item.is_project_item(cx);
-                        let leader_id = workspace.leader_for_pane(&pane);
-
-                        if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
-                            workspace.unfollow(&pane, cx);
+                        pending_update_scheduled.store(true, Ordering::SeqCst);
+                        todo!("replace with on_next_frame?");
+                        // cx.after_window_update({
+                        //     let pending_update = pending_update.clone();
+                        //     let pending_update_scheduled = pending_update_scheduled.clone();
+                        //     move |this, cx| {
+                        //         pending_update_scheduled.store(false, Ordering::SeqCst);
+                        //         this.update_followers(
+                        //             is_project_item,
+                        //             proto::update_followers::Variant::UpdateView(
+                        //                 proto::UpdateView {
+                        //                     id: item
+                        //                         .remote_id(&this.app_state.client, cx)
+                        //                         .map(|id| id.to_proto()),
+                        //                     variant: pending_update.borrow_mut().take(),
+                        //                     leader_id,
+                        //                 },
+                        //             ),
+                        //             cx,
+                        //         );
+                        //     }
+                        // });
+                    }
+                }
+
+                for item_event in T::to_item_events(event).into_iter() {
+                    match item_event {
+                        ItemEvent::CloseItem => {
+                            pane.update(cx, |pane, cx| {
+                                pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx)
+                            })
+                            .detach_and_log_err(cx);
+                            return;
                         }
 
-                        if item.add_event_to_update_proto(
-                            event,
-                            &mut *pending_update.borrow_mut(),
-                            cx,
-                        ) && !pending_update_scheduled.load(Ordering::SeqCst)
-                        {
-                            pending_update_scheduled.store(true, Ordering::SeqCst);
-                            cx.after_window_update({
-                                let pending_update = pending_update.clone();
-                                let pending_update_scheduled = pending_update_scheduled.clone();
-                                move |this, cx| {
-                                    pending_update_scheduled.store(false, Ordering::SeqCst);
-                                    this.update_followers(
-                                        is_project_item,
-                                        proto::update_followers::Variant::UpdateView(
-                                            proto::UpdateView {
-                                                id: item
-                                                    .remote_id(&this.app_state.client, cx)
-                                                    .map(|id| id.to_proto()),
-                                                variant: pending_update.borrow_mut().take(),
-                                                leader_id,
-                                            },
-                                        ),
-                                        cx,
-                                    );
-                                }
+                        ItemEvent::UpdateTab => {
+                            pane.update(cx, |_, cx| {
+                                cx.emit(pane::Event::ChangeItemTitle);
+                                cx.notify();
                             });
                         }
-                    }
 
-                    for item_event in T::to_item_events(event).into_iter() {
-                        match item_event {
-                            ItemEvent::CloseItem => {
-                                pane.update(cx, |pane, cx| {
-                                    pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx)
-                                })
-                                .detach_and_log_err(cx);
-                                return;
-                            }
-
-                            ItemEvent::UpdateTab => {
-                                pane.update(cx, |_, cx| {
-                                    cx.emit(pane::Event::ChangeItemTitle);
-                                    cx.notify();
+                        ItemEvent::Edit => {
+                            let autosave = WorkspaceSettings::get_global(cx).autosave;
+                            if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
+                                let delay = Duration::from_millis(milliseconds);
+                                let item = item.clone();
+                                pending_autosave.fire_new(delay, cx, move |workspace, cx| {
+                                    Pane::autosave_item(&item, workspace.project().clone(), cx)
                                 });
                             }
-
-                            ItemEvent::Edit => {
-                                let autosave = WorkspaceSettings::get_global(cx).autosave;
-                                if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
-                                    let delay = Duration::from_millis(milliseconds);
-                                    let item = item.clone();
-                                    pending_autosave.fire_new(delay, cx, move |workspace, cx| {
-                                        Pane::autosave_item(&item, workspace.project().clone(), cx)
-                                    });
-                                }
-                            }
-
-                            _ => {}
                         }
-                    }
-                }));
 
-            cx.observe_focus(self, move |workspace, item, focused, cx| {
-                if !focused
-                    && WorkspaceSettings::get_global(cx).autosave == AutosaveSetting::OnFocusChange
-                {
-                    Pane::autosave_item(&item, workspace.project.clone(), cx)
-                        .detach_and_log_err(cx);
+                        _ => {}
+                    }
                 }
-            })
-            .detach();
-
-            let item_id = self.id();
-            cx.observe_release(self, move |workspace, _, _| {
-                workspace.panes_by_item.remove(&item_id);
-                event_subscription.take();
-            })
-            .detach();
+            }));
+
+            todo!("observe focus");
+            // cx.observe_focus(self, move |workspace, item, focused, cx| {
+            //     if !focused
+            //         && WorkspaceSettings::get_global(cx).autosave == AutosaveSetting::OnFocusChange
+            //     {
+            //         Pane::autosave_item(&item, workspace.project.clone(), cx)
+            //             .detach_and_log_err(cx);
+            //     }
+            // })
+            // .detach();
+
+            // let item_id = self.id();
+            // cx.observe_release(self, move |workspace, _, _| {
+            //     workspace.panes_by_item.remove(&item_id);
+            //     event_subscription.take();
+            // })
+            // .detach();
         }
 
         cx.defer(|workspace, cx| {
@@ -561,20 +518,14 @@ impl<T: Item> ItemHandle for View<T> {
         self.update(cx, |this, cx| this.navigate(data, cx))
     }
 
-    fn id(&self) -> usize {
-        self.id()
+    fn id(&self) -> EntityId {
+        self.entity_id()
     }
 
-    fn window(&self) -> AnyWindowHandle {
-        todo!()
-        // AnyViewHandle::window(self)
+    fn to_any(&self) -> AnyView {
+        self.clone().into()
     }
 
-    // todo!()
-    // fn as_any(&self) -> &AnyViewHandle {
-    //     self
-    // }
-
     fn is_dirty(&self, cx: &AppContext) -> bool {
         self.read(cx).is_dirty(cx)
     }
@@ -587,42 +538,41 @@ impl<T: Item> ItemHandle for View<T> {
         self.read(cx).can_save(cx)
     }
 
-    fn save(&self, project: Handle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
+    fn save(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
         self.update(cx, |item, cx| item.save(project, cx))
     }
 
     fn save_as(
         &self,
-        project: Handle<Project>,
+        project: Model<Project>,
         abs_path: PathBuf,
         cx: &mut WindowContext,
     ) -> Task<anyhow::Result<()>> {
         self.update(cx, |item, cx| item.save_as(project, abs_path, cx))
     }
 
-    fn reload(&self, project: Handle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
+    fn reload(&self, project: Model<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
         self.update(cx, |item, cx| item.reload(project, cx))
     }
 
-    // todo!()
-    // fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle> {
-    //     self.read(cx).act_as_type(type_id, self, cx)
-    // }
+    fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<AnyView> {
+        self.read(cx).act_as_type(type_id, self, cx)
+    }
 
     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
         if cx.has_global::<FollowableItemBuilders>() {
             let builders = cx.global::<FollowableItemBuilders>();
-            let item = self.as_any();
-            Some(builders.get(&item.view_type())?.1(item))
+            let item = self.to_any();
+            Some(builders.get(&item.entity_type())?.1(&item))
         } else {
             None
         }
     }
 
     fn on_release(
-        &self,
+        &mut self,
         cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext)>,
+        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
     ) -> gpui2::Subscription {
         cx.observe_release(self, move |_, cx| callback(cx))
     }
@@ -635,7 +585,7 @@ impl<T: Item> ItemHandle for View<T> {
         self.read(cx).breadcrumb_location()
     }
 
-    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+    fn breadcrumbs(&self, theme: &ThemeVariant, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
         self.read(cx).breadcrumbs(theme, cx)
     }
 
@@ -652,17 +602,17 @@ impl<T: Item> ItemHandle for View<T> {
     }
 }
 
-// impl From<Box<dyn ItemHandle>> for AnyViewHandle {
-//     fn from(val: Box<dyn ItemHandle>) -> Self {
-//         val.as_any().clone()
-//     }
-// }
+impl From<Box<dyn ItemHandle>> for AnyView {
+    fn from(val: Box<dyn ItemHandle>) -> Self {
+        val.to_any()
+    }
+}
 
-// impl From<&Box<dyn ItemHandle>> for AnyViewHandle {
-//     fn from(val: &Box<dyn ItemHandle>) -> Self {
-//         val.as_any().clone()
-//     }
-// }
+impl From<&Box<dyn ItemHandle>> for AnyView {
+    fn from(val: &Box<dyn ItemHandle>) -> Self {
+        val.to_any()
+    }
+}
 
 impl Clone for Box<dyn ItemHandle> {
     fn clone(&self) -> Box<dyn ItemHandle> {
@@ -670,26 +620,22 @@ impl Clone for Box<dyn ItemHandle> {
     }
 }
 
-// impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
-//     fn id(&self) -> usize {
-//         self.id()
-//     }
-
-//     fn window(&self) -> AnyWindowHandle {
-//         self.window()
-//     }
+impl<T: Item> WeakItemHandle for WeakView<T> {
+    fn id(&self) -> EntityId {
+        self.entity_id()
+    }
 
-//     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
-//         self.upgrade(cx).map(|v| Box::new(v) as Box<dyn ItemHandle>)
-//     }
-// }
+    fn upgrade(&self) -> Option<Box<dyn ItemHandle>> {
+        self.upgrade().map(|v| Box::new(v) as Box<dyn ItemHandle>)
+    }
+}
 
 pub trait ProjectItem: Item {
     type Item: project2::Item;
 
     fn for_project_item(
-        project: Handle<Project>,
-        item: Handle<Self::Item>,
+        project: Model<Project>,
+        item: Model<Self::Item>,
         cx: &mut ViewContext<Self>,
     ) -> Self
     where
@@ -714,7 +660,7 @@ pub trait FollowableItem: Item {
     ) -> bool;
     fn apply_update_proto(
         &mut self,
-        project: &Handle<Project>,
+        project: &Model<Project>,
         message: proto::update_view::Variant,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<()>>;
@@ -736,7 +682,7 @@ pub trait FollowableItemHandle: ItemHandle {
     ) -> bool;
     fn apply_update_proto(
         &self,
-        project: &Handle<Project>,
+        project: &Model<Project>,
         message: proto::update_view::Variant,
         cx: &mut WindowContext,
     ) -> Task<Result<()>>;
@@ -744,65 +690,65 @@ pub trait FollowableItemHandle: ItemHandle {
     fn is_project_item(&self, cx: &AppContext) -> bool;
 }
 
-// impl<T: FollowableItem> FollowableItemHandle for View<T> {
-//     fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId> {
-//         self.read(cx).remote_id().or_else(|| {
-//             client.peer_id().map(|creator| ViewId {
-//                 creator,
-//                 id: self.id() as u64,
-//             })
-//         })
-//     }
+impl<T: FollowableItem> FollowableItemHandle for View<T> {
+    fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId> {
+        self.read(cx).remote_id().or_else(|| {
+            client.peer_id().map(|creator| ViewId {
+                creator,
+                id: self.id().as_u64(),
+            })
+        })
+    }
 
-//     fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext) {
-//         self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx))
-//     }
+    fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx))
+    }
 
-//     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
-//         self.read(cx).to_state_proto(cx)
-//     }
+    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
+        self.read(cx).to_state_proto(cx)
+    }
 
-//     fn add_event_to_update_proto(
-//         &self,
-//         event: &dyn Any,
-//         update: &mut Option<proto::update_view::Variant>,
-//         cx: &AppContext,
-//     ) -> bool {
-//         if let Some(event) = event.downcast_ref() {
-//             self.read(cx).add_event_to_update_proto(event, update, cx)
-//         } else {
-//             false
-//         }
-//     }
+    fn add_event_to_update_proto(
+        &self,
+        event: &dyn Any,
+        update: &mut Option<proto::update_view::Variant>,
+        cx: &AppContext,
+    ) -> bool {
+        if let Some(event) = event.downcast_ref() {
+            self.read(cx).add_event_to_update_proto(event, update, cx)
+        } else {
+            false
+        }
+    }
 
-//     fn apply_update_proto(
-//         &self,
-//         project: &Handle<Project>,
-//         message: proto::update_view::Variant,
-//         cx: &mut WindowContext,
-//     ) -> Task<Result<()>> {
-//         self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
-//     }
+    fn apply_update_proto(
+        &self,
+        project: &Model<Project>,
+        message: proto::update_view::Variant,
+        cx: &mut WindowContext,
+    ) -> Task<Result<()>> {
+        self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
+    }
 
-//     fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
-//         if let Some(event) = event.downcast_ref() {
-//             T::should_unfollow_on_event(event, cx)
-//         } else {
-//             false
-//         }
-//     }
+    fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
+        if let Some(event) = event.downcast_ref() {
+            T::should_unfollow_on_event(event, cx)
+        } else {
+            false
+        }
+    }
 
-//     fn is_project_item(&self, cx: &AppContext) -> bool {
-//         self.read(cx).is_project_item(cx)
-//     }
-// }
+    fn is_project_item(&self, cx: &AppContext) -> bool {
+        self.read(cx).is_project_item(cx)
+    }
+}
 
 // #[cfg(any(test, feature = "test-support"))]
 // pub mod test {
 //     use super::{Item, ItemEvent};
 //     use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
 //     use gpui2::{
-//         elements::Empty, AnyElement, AppContext, Element, Entity, Handle, Task, View,
+//         elements::Empty, AnyElement, AppContext, Element, Entity, Model, Task, View,
 //         ViewContext, View, WeakViewHandle,
 //     };
 //     use project2::{Project, ProjectEntryId, ProjectPath, WorktreeId};
@@ -824,7 +770,7 @@ pub trait FollowableItemHandle: ItemHandle {
 //         pub is_dirty: bool,
 //         pub is_singleton: bool,
 //         pub has_conflict: bool,
-//         pub project_items: Vec<Handle<TestProjectItem>>,
+//         pub project_items: Vec<Model<TestProjectItem>>,
 //         pub nav_history: Option<ItemNavHistory>,
 //         pub tab_descriptions: Option<Vec<&'static str>>,
 //         pub tab_detail: Cell<Option<usize>>,
@@ -869,7 +815,7 @@ pub trait FollowableItemHandle: ItemHandle {
 //     }
 
 //     impl TestProjectItem {
-//         pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Handle<Self> {
+//         pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Model<Self> {
 //             let entry_id = Some(ProjectEntryId::from_proto(id));
 //             let project_path = Some(ProjectPath {
 //                 worktree_id: WorktreeId::from_usize(0),
@@ -881,7 +827,7 @@ pub trait FollowableItemHandle: ItemHandle {
 //             })
 //         }
 
-//         pub fn new_untitled(cx: &mut AppContext) -> Handle<Self> {
+//         pub fn new_untitled(cx: &mut AppContext) -> Model<Self> {
 //             cx.add_model(|_| Self {
 //                 project_path: None,
 //                 entry_id: None,
@@ -934,7 +880,7 @@ pub trait FollowableItemHandle: ItemHandle {
 //             self
 //         }
 
-//         pub fn with_project_items(mut self, items: &[Handle<TestProjectItem>]) -> Self {
+//         pub fn with_project_items(mut self, items: &[Model<TestProjectItem>]) -> Self {
 //             self.project_items.clear();
 //             self.project_items.extend(items.iter().cloned());
 //             self
@@ -1045,7 +991,7 @@ pub trait FollowableItemHandle: ItemHandle {
 
 //         fn save(
 //             &mut self,
-//             _: Handle<Project>,
+//             _: Model<Project>,
 //             _: &mut ViewContext<Self>,
 //         ) -> Task<anyhow::Result<()>> {
 //             self.save_count += 1;

crates/workspace2/src/notifications.rs 🔗

@@ -0,0 +1,404 @@
+use crate::{Toast, Workspace};
+use collections::HashMap;
+use gpui2::{AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext};
+use std::{any::TypeId, ops::DerefMut};
+
+pub fn init(cx: &mut AppContext) {
+    cx.set_global(NotificationTracker::new());
+    // todo!()
+    // simple_message_notification::init(cx);
+}
+
+pub trait Notification: EventEmitter + Render {
+    fn should_dismiss_notification_on_event(&self, event: &Self::Event) -> bool;
+}
+
+pub trait NotificationHandle: Send {
+    fn id(&self) -> EntityId;
+    fn to_any(&self) -> AnyView;
+}
+
+impl<T: Notification> NotificationHandle for View<T> {
+    fn id(&self) -> EntityId {
+        self.entity_id()
+    }
+
+    fn to_any(&self) -> AnyView {
+        self.clone().into()
+    }
+}
+
+impl From<&dyn NotificationHandle> for AnyView {
+    fn from(val: &dyn NotificationHandle) -> Self {
+        val.to_any()
+    }
+}
+
+pub(crate) struct NotificationTracker {
+    notifications_sent: HashMap<TypeId, Vec<usize>>,
+}
+
+impl std::ops::Deref for NotificationTracker {
+    type Target = HashMap<TypeId, Vec<usize>>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.notifications_sent
+    }
+}
+
+impl DerefMut for NotificationTracker {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.notifications_sent
+    }
+}
+
+impl NotificationTracker {
+    fn new() -> Self {
+        Self {
+            notifications_sent: Default::default(),
+        }
+    }
+}
+
+impl Workspace {
+    pub fn has_shown_notification_once<V: Notification>(
+        &self,
+        id: usize,
+        cx: &ViewContext<Self>,
+    ) -> bool {
+        cx.global::<NotificationTracker>()
+            .get(&TypeId::of::<V>())
+            .map(|ids| ids.contains(&id))
+            .unwrap_or(false)
+    }
+
+    pub fn show_notification_once<V: Notification>(
+        &mut self,
+        id: usize,
+        cx: &mut ViewContext<Self>,
+        build_notification: impl FnOnce(&mut ViewContext<Self>) -> View<V>,
+    ) {
+        if !self.has_shown_notification_once::<V>(id, cx) {
+            let tracker = cx.global_mut::<NotificationTracker>();
+            let entry = tracker.entry(TypeId::of::<V>()).or_default();
+            entry.push(id);
+            self.show_notification::<V>(id, cx, build_notification)
+        }
+    }
+
+    pub fn show_notification<V: Notification>(
+        &mut self,
+        id: usize,
+        cx: &mut ViewContext<Self>,
+        build_notification: impl FnOnce(&mut ViewContext<Self>) -> View<V>,
+    ) {
+        let type_id = TypeId::of::<V>();
+        if self
+            .notifications
+            .iter()
+            .all(|(existing_type_id, existing_id, _)| {
+                (*existing_type_id, *existing_id) != (type_id, id)
+            })
+        {
+            let notification = build_notification(cx);
+            cx.subscribe(&notification, move |this, handle, event, cx| {
+                if handle.read(cx).should_dismiss_notification_on_event(event) {
+                    this.dismiss_notification_internal(type_id, id, cx);
+                }
+            })
+            .detach();
+            self.notifications
+                .push((type_id, id, Box::new(notification)));
+            cx.notify();
+        }
+    }
+
+    pub fn dismiss_notification<V: Notification>(&mut self, id: usize, cx: &mut ViewContext<Self>) {
+        let type_id = TypeId::of::<V>();
+
+        self.dismiss_notification_internal(type_id, id, cx)
+    }
+
+    pub fn show_toast(&mut self, toast: Toast, cx: &mut ViewContext<Self>) {
+        todo!()
+        // self.dismiss_notification::<simple_message_notification::MessageNotification>(toast.id, cx);
+        // self.show_notification(toast.id, cx, |cx| {
+        //     cx.add_view(|_cx| match toast.on_click.as_ref() {
+        //         Some((click_msg, on_click)) => {
+        //             let on_click = on_click.clone();
+        //             simple_message_notification::MessageNotification::new(toast.msg.clone())
+        //                 .with_click_message(click_msg.clone())
+        //                 .on_click(move |cx| on_click(cx))
+        //         }
+        //         None => simple_message_notification::MessageNotification::new(toast.msg.clone()),
+        //     })
+        // })
+    }
+
+    pub fn dismiss_toast(&mut self, id: usize, cx: &mut ViewContext<Self>) {
+        todo!()
+        // self.dismiss_notification::<simple_message_notification::MessageNotification>(id, cx);
+    }
+
+    fn dismiss_notification_internal(
+        &mut self,
+        type_id: TypeId,
+        id: usize,
+        cx: &mut ViewContext<Self>,
+    ) {
+        self.notifications
+            .retain(|(existing_type_id, existing_id, _)| {
+                if (*existing_type_id, *existing_id) == (type_id, id) {
+                    cx.notify();
+                    false
+                } else {
+                    true
+                }
+            });
+    }
+}
+
+pub mod simple_message_notification {
+    use super::Notification;
+    use gpui2::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext};
+    use serde::Deserialize;
+    use std::{borrow::Cow, sync::Arc};
+
+    // todo!()
+    // actions!(message_notifications, [CancelMessageNotification]);
+
+    #[derive(Clone, Default, Deserialize, PartialEq)]
+    pub struct OsOpen(pub Cow<'static, str>);
+
+    impl OsOpen {
+        pub fn new<I: Into<Cow<'static, str>>>(url: I) -> Self {
+            OsOpen(url.into())
+        }
+    }
+
+    // todo!()
+    //     impl_actions!(message_notifications, [OsOpen]);
+    //
+    // todo!()
+    //     pub fn init(cx: &mut AppContext) {
+    //         cx.add_action(MessageNotification::dismiss);
+    //         cx.add_action(
+    //             |_workspace: &mut Workspace, open_action: &OsOpen, cx: &mut ViewContext<Workspace>| {
+    //                 cx.platform().open_url(open_action.0.as_ref());
+    //             },
+    //         )
+    //     }
+
+    enum NotificationMessage {
+        Text(Cow<'static, str>),
+        Element(fn(TextStyle, &AppContext) -> AnyElement<MessageNotification>),
+    }
+
+    pub struct MessageNotification {
+        message: NotificationMessage,
+        on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>) + Send + Sync>>,
+        click_message: Option<Cow<'static, str>>,
+    }
+
+    pub enum MessageNotificationEvent {
+        Dismiss,
+    }
+
+    impl EventEmitter for MessageNotification {
+        type Event = MessageNotificationEvent;
+    }
+
+    impl MessageNotification {
+        pub fn new<S>(message: S) -> MessageNotification
+        where
+            S: Into<Cow<'static, str>>,
+        {
+            Self {
+                message: NotificationMessage::Text(message.into()),
+                on_click: None,
+                click_message: None,
+            }
+        }
+
+        // todo!()
+        //         pub fn new_element(
+        //             message: fn(TextStyle, &AppContext) -> AnyElement<MessageNotification>,
+        //         ) -> MessageNotification {
+        //             Self {
+        //                 message: NotificationMessage::Element(message),
+        //                 on_click: None,
+        //                 click_message: None,
+        //             }
+        //         }
+
+        //         pub fn with_click_message<S>(mut self, message: S) -> Self
+        //         where
+        //             S: Into<Cow<'static, str>>,
+        //         {
+        //             self.click_message = Some(message.into());
+        //             self
+        //         }
+
+        //         pub fn on_click<F>(mut self, on_click: F) -> Self
+        //         where
+        //             F: 'static + Fn(&mut ViewContext<Self>),
+        //         {
+        //             self.on_click = Some(Arc::new(on_click));
+        //             self
+        //         }
+
+        //         pub fn dismiss(&mut self, _: &CancelMessageNotification, cx: &mut ViewContext<Self>) {
+        //             cx.emit(MessageNotificationEvent::Dismiss);
+        //         }
+    }
+
+    impl Render for MessageNotification {
+        type Element = Div<Self>;
+
+        fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+            todo!()
+        }
+    }
+    // todo!()
+    //     impl View for MessageNotification {
+    //         fn ui_name() -> &'static str {
+    //             "MessageNotification"
+    //         }
+
+    //         fn render(&mut self, cx: &mut gpui2::ViewContext<Self>) -> gpui::AnyElement<Self> {
+    //             let theme = theme2::current(cx).clone();
+    //             let theme = &theme.simple_message_notification;
+
+    //             enum MessageNotificationTag {}
+
+    //             let click_message = self.click_message.clone();
+    //             let message = match &self.message {
+    //                 NotificationMessage::Text(text) => {
+    //                     Text::new(text.to_owned(), theme.message.text.clone()).into_any()
+    //                 }
+    //                 NotificationMessage::Element(e) => e(theme.message.text.clone(), cx),
+    //             };
+    //             let on_click = self.on_click.clone();
+    //             let has_click_action = on_click.is_some();
+
+    //             Flex::column()
+    //                 .with_child(
+    //                     Flex::row()
+    //                         .with_child(
+    //                             message
+    //                                 .contained()
+    //                                 .with_style(theme.message.container)
+    //                                 .aligned()
+    //                                 .top()
+    //                                 .left()
+    //                                 .flex(1., true),
+    //                         )
+    //                         .with_child(
+    //                             MouseEventHandler::new::<Cancel, _>(0, cx, |state, _| {
+    //                                 let style = theme.dismiss_button.style_for(state);
+    //                                 Svg::new("icons/x.svg")
+    //                                     .with_color(style.color)
+    //                                     .constrained()
+    //                                     .with_width(style.icon_width)
+    //                                     .aligned()
+    //                                     .contained()
+    //                                     .with_style(style.container)
+    //                                     .constrained()
+    //                                     .with_width(style.button_width)
+    //                                     .with_height(style.button_width)
+    //                             })
+    //                             .with_padding(Padding::uniform(5.))
+    //                             .on_click(MouseButton::Left, move |_, this, cx| {
+    //                                 this.dismiss(&Default::default(), cx);
+    //                             })
+    //                             .with_cursor_style(CursorStyle::PointingHand)
+    //                             .aligned()
+    //                             .constrained()
+    //                             .with_height(cx.font_cache().line_height(theme.message.text.font_size))
+    //                             .aligned()
+    //                             .top()
+    //                             .flex_float(),
+    //                         ),
+    //                 )
+    //                 .with_children({
+    //                     click_message
+    //                         .map(|click_message| {
+    //                             MouseEventHandler::new::<MessageNotificationTag, _>(
+    //                                 0,
+    //                                 cx,
+    //                                 |state, _| {
+    //                                     let style = theme.action_message.style_for(state);
+
+    //                                     Flex::row()
+    //                                         .with_child(
+    //                                             Text::new(click_message, style.text.clone())
+    //                                                 .contained()
+    //                                                 .with_style(style.container),
+    //                                         )
+    //                                         .contained()
+    //                                 },
+    //                             )
+    //                             .on_click(MouseButton::Left, move |_, this, cx| {
+    //                                 if let Some(on_click) = on_click.as_ref() {
+    //                                     on_click(cx);
+    //                                     this.dismiss(&Default::default(), cx);
+    //                                 }
+    //                             })
+    //                             // Since we're not using a proper overlay, we have to capture these extra events
+    //                             .on_down(MouseButton::Left, |_, _, _| {})
+    //                             .on_up(MouseButton::Left, |_, _, _| {})
+    //                             .with_cursor_style(if has_click_action {
+    //                                 CursorStyle::PointingHand
+    //                             } else {
+    //                                 CursorStyle::Arrow
+    //                             })
+    //                         })
+    //                         .into_iter()
+    //                 })
+    //                 .into_any()
+    //         }
+    //     }
+
+    impl Notification for MessageNotification {
+        fn should_dismiss_notification_on_event(&self, event: &Self::Event) -> bool {
+            match event {
+                MessageNotificationEvent::Dismiss => true,
+            }
+        }
+    }
+}
+
+pub trait NotifyResultExt {
+    type Ok;
+
+    fn notify_err(
+        self,
+        workspace: &mut Workspace,
+        cx: &mut ViewContext<Workspace>,
+    ) -> Option<Self::Ok>;
+}
+
+impl<T, E> NotifyResultExt for Result<T, E>
+where
+    E: std::fmt::Debug,
+{
+    type Ok = T;
+
+    fn notify_err(self, workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Option<T> {
+        match self {
+            Ok(value) => Some(value),
+            Err(err) => {
+                log::error!("TODO {err:?}");
+                // todo!()
+                // workspace.show_notification(0, cx, |cx| {
+                //     cx.add_view(|_cx| {
+                //         simple_message_notification::MessageNotification::new(format!(
+                //             "Error: {err:?}",
+                //         ))
+                //     })
+                // });
+                None
+            }
+        }
+    }
+}

crates/workspace2/src/pane.rs 🔗

@@ -1,48 +1,33 @@
 // mod dragged_item_receiver;
 
-// use super::{ItemHandle, SplitDirection};
-// pub use crate::toolbar::Toolbar;
-// use crate::{
-//     item::{ItemSettings, WeakItemHandle},
-//     notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile, NewSearch, ToggleZoom,
-//     Workspace, WorkspaceSettings,
-// };
-// use anyhow::Result;
-// use collections::{HashMap, HashSet, VecDeque};
-// // use context_menu::{ContextMenu, ContextMenuItem};
-
-// use dragged_item_receiver::dragged_item_receiver;
-// use fs2::repository::GitFileStatus;
-// use futures::StreamExt;
-// use gpui2::{
-//     actions,
-//     elements::*,
-//     geometry::{
-//         rect::RectF,
-//         vector::{vec2f, Vector2F},
-//     },
-//     impl_actions,
-//     keymap_matcher::KeymapContext,
-//     platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
-//     Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
-//     ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
-//     WindowContext,
-// };
-// use project2::{Project, ProjectEntryId, ProjectPath};
+use crate::{
+    item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
+    toolbar::Toolbar,
+    workspace_settings::{AutosaveSetting, WorkspaceSettings},
+    SplitDirection, Workspace,
+};
+use anyhow::Result;
+use collections::{HashMap, HashSet, VecDeque};
+use gpui2::{
+    AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, Model, PromptLevel,
+    Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+};
+use parking_lot::Mutex;
+use project2::{Project, ProjectEntryId, ProjectPath};
 use serde::Deserialize;
-// use std::{
-//     any::Any,
-//     cell::RefCell,
-//     cmp, mem,
-//     path::{Path, PathBuf},
-//     rc::Rc,
-//     sync::{
-//         atomic::{AtomicUsize, Ordering},
-//         Arc,
-//     },
-// };
-// use theme2::{Theme, ThemeSettings};
-// use util::truncate_and_remove_front;
+use settings2::Settings;
+use std::{
+    any::Any,
+    cmp, fmt, mem,
+    path::{Path, PathBuf},
+    sync::{
+        atomic::{AtomicUsize, Ordering},
+        Arc,
+    },
+};
+use ui::v_stack;
+use ui::{prelude::*, Icon, IconButton, IconColor, IconElement};
+use util::truncate_and_remove_front;
 
 #[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
 #[serde(rename_all = "camelCase")]
@@ -69,19 +54,19 @@ pub enum SaveIntent {
 // #[derive(Clone, PartialEq)]
 // pub struct CloseItemById {
 //     pub item_id: usize,
-//     pub pane: WeakViewHandle<Pane>,
+//     pub pane: WeakView<Pane>,
 // }
 
 // #[derive(Clone, PartialEq)]
 // pub struct CloseItemsToTheLeftById {
 //     pub item_id: usize,
-//     pub pane: WeakViewHandle<Pane>,
+//     pub pane: WeakView<Pane>,
 // }
 
 // #[derive(Clone, PartialEq)]
 // pub struct CloseItemsToTheRightById {
 //     pub item_id: usize,
-//     pub pane: WeakViewHandle<Pane>,
+//     pub pane: WeakView<Pane>,
 // }
 
 // #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
@@ -96,6 +81,7 @@ pub enum SaveIntent {
 //     pub save_intent: Option<SaveIntent>,
 // }
 
+// todo!()
 // actions!(
 //     pane,
 //     [
@@ -118,40 +104,40 @@ pub enum SaveIntent {
 
 // impl_actions!(pane, [ActivateItem, CloseActiveItem, CloseAllItems]);
 
-// const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
-
-// pub fn init(cx: &mut AppContext) {
-//     cx.add_action(Pane::toggle_zoom);
-//     cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
-//         pane.activate_item(action.0, true, true, cx);
-//     });
-//     cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| {
-//         pane.activate_item(pane.items.len() - 1, true, true, cx);
-//     });
-//     cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
-//         pane.activate_prev_item(true, cx);
-//     });
-//     cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
-//         pane.activate_next_item(true, cx);
-//     });
-//     cx.add_async_action(Pane::close_active_item);
-//     cx.add_async_action(Pane::close_inactive_items);
-//     cx.add_async_action(Pane::close_clean_items);
-//     cx.add_async_action(Pane::close_items_to_the_left);
-//     cx.add_async_action(Pane::close_items_to_the_right);
-//     cx.add_async_action(Pane::close_all_items);
-//     cx.add_action(|pane: &mut Pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx));
-//     cx.add_action(|pane: &mut Pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx));
-//     cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx));
-//     cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
-// }
+const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
+
+pub fn init(cx: &mut AppContext) {
+    // todo!()
+    //     cx.add_action(Pane::toggle_zoom);
+    //     cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
+    //         pane.activate_item(action.0, true, true, cx);
+    //     });
+    //     cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| {
+    //         pane.activate_item(pane.items.len() - 1, true, true, cx);
+    //     });
+    //     cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
+    //         pane.activate_prev_item(true, cx);
+    //     });
+    //     cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
+    //         pane.activate_next_item(true, cx);
+    //     });
+    //     cx.add_async_action(Pane::close_active_item);
+    //     cx.add_async_action(Pane::close_inactive_items);
+    //     cx.add_async_action(Pane::close_clean_items);
+    //     cx.add_async_action(Pane::close_items_to_the_left);
+    //     cx.add_async_action(Pane::close_items_to_the_right);
+    //     cx.add_async_action(Pane::close_all_items);
+    //     cx.add_action(|pane: &mut Pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx));
+    //     cx.add_action(|pane: &mut Pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx));
+    //     cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx));
+    //     cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
+}
 
-#[derive(Debug)]
 pub enum Event {
     AddItem { item: Box<dyn ItemHandle> },
     ActivateItem { local: bool },
     Remove,
-    RemoveItem { item_id: usize },
+    RemoveItem { item_id: EntityId },
     Split(SplitDirection),
     ChangeItemTitle,
     Focus,
@@ -159,36 +145,45 @@ pub enum Event {
     ZoomOut,
 }
 
-use crate::{
-    item::{ItemHandle, WeakItemHandle},
-    SplitDirection,
-};
-use collections::{HashMap, VecDeque};
-use gpui2::{Handle, ViewContext, WeakView};
-use project2::{Project, ProjectEntryId, ProjectPath};
-use std::{
-    any::Any,
-    cell::RefCell,
-    cmp, mem,
-    path::PathBuf,
-    rc::Rc,
-    sync::{atomic::AtomicUsize, Arc},
-};
+impl fmt::Debug for Event {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Event::AddItem { item } => f.debug_struct("AddItem").field("item", &item.id()).finish(),
+            Event::ActivateItem { local } => f
+                .debug_struct("ActivateItem")
+                .field("local", local)
+                .finish(),
+            Event::Remove => f.write_str("Remove"),
+            Event::RemoveItem { item_id } => f
+                .debug_struct("RemoveItem")
+                .field("item_id", item_id)
+                .finish(),
+            Event::Split(direction) => f
+                .debug_struct("Split")
+                .field("direction", direction)
+                .finish(),
+            Event::ChangeItemTitle => f.write_str("ChangeItemTitle"),
+            Event::Focus => f.write_str("Focus"),
+            Event::ZoomIn => f.write_str("ZoomIn"),
+            Event::ZoomOut => f.write_str("ZoomOut"),
+        }
+    }
+}
 
 pub struct Pane {
     items: Vec<Box<dyn ItemHandle>>,
-    //     activation_history: Vec<usize>,
-    //     zoomed: bool,
-    //     active_item_index: usize,
+    activation_history: Vec<EntityId>,
+    zoomed: bool,
+    active_item_index: usize,
     //     last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>,
-    //     autoscroll: bool,
+    autoscroll: bool,
     nav_history: NavHistory,
-    //     toolbar: ViewHandle<Toolbar>,
+    toolbar: View<Toolbar>,
     //     tab_bar_context_menu: TabBarContextMenu,
     //     tab_context_menu: ViewHandle<ContextMenu>,
-    //     workspace: WeakViewHandle<Workspace>,
-    project: Handle<Project>,
-    //     has_focus: bool,
+    workspace: WeakView<Workspace>,
+    project: Model<Project>,
+    has_focus: bool,
     //     can_drop: Rc<dyn Fn(&DragAndDrop<Workspace>, &WindowContext) -> bool>,
     //     can_split: bool,
     //     render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>,
@@ -196,18 +191,18 @@ pub struct Pane {
 
 pub struct ItemNavHistory {
     history: NavHistory,
-    item: Rc<dyn WeakItemHandle>,
+    item: Arc<dyn WeakItemHandle>,
 }
 
 #[derive(Clone)]
-pub struct NavHistory(Rc<RefCell<NavHistoryState>>);
+pub struct NavHistory(Arc<Mutex<NavHistoryState>>);
 
 struct NavHistoryState {
     mode: NavigationMode,
     backward_stack: VecDeque<NavigationEntry>,
     forward_stack: VecDeque<NavigationEntry>,
     closed_stack: VecDeque<NavigationEntry>,
-    paths_by_item: HashMap<usize, (ProjectPath, Option<PathBuf>)>,
+    paths_by_item: HashMap<EntityId, (ProjectPath, Option<PathBuf>)>,
     pane: WeakView<Pane>,
     next_timestamp: Arc<AtomicUsize>,
 }
@@ -229,14 +224,14 @@ impl Default for NavigationMode {
 }
 
 pub struct NavigationEntry {
-    pub item: Rc<dyn WeakItemHandle>,
-    pub data: Option<Box<dyn Any>>,
+    pub item: Arc<dyn WeakItemHandle>,
+    pub data: Option<Box<dyn Any + Send>>,
     pub timestamp: usize,
 }
 
 // pub struct DraggedItem {
 //     pub handle: Box<dyn ItemHandle>,
-//     pub pane: WeakViewHandle<Pane>,
+//     pub pane: WeakView<Pane>,
 // }
 
 // pub enum ReorderBehavior {
@@ -315,114 +310,119 @@ pub struct NavigationEntry {
 //     .into_any_named("nav button")
 // }
 
-impl Pane {
-    //     pub fn new(
-    //         workspace: WeakViewHandle<Workspace>,
-    //         project: ModelHandle<Project>,
-    //         next_timestamp: Arc<AtomicUsize>,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Self {
-    //         let pane_view_id = cx.view_id();
-    //         let handle = cx.weak_handle();
-    //         let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx));
-    //         context_menu.update(cx, |menu, _| {
-    //             menu.set_position_mode(OverlayPositionMode::Local)
-    //         });
-
-    //         Self {
-    //             items: Vec::new(),
-    //             activation_history: Vec::new(),
-    //             zoomed: false,
-    //             active_item_index: 0,
-    //             last_focused_view_by_item: Default::default(),
-    //             autoscroll: false,
-    //             nav_history: NavHistory(Rc::new(RefCell::new(NavHistoryState {
-    //                 mode: NavigationMode::Normal,
-    //                 backward_stack: Default::default(),
-    //                 forward_stack: Default::default(),
-    //                 closed_stack: Default::default(),
-    //                 paths_by_item: Default::default(),
-    //                 pane: handle.clone(),
-    //                 next_timestamp,
-    //             }))),
-    //             toolbar: cx.add_view(|_| Toolbar::new()),
-    //             tab_bar_context_menu: TabBarContextMenu {
-    //                 kind: TabBarContextMenuKind::New,
-    //                 handle: context_menu,
-    //             },
-    //             tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
-    //             workspace,
-    //             project,
-    //             has_focus: false,
-    //             can_drop: Rc::new(|_, _| true),
-    //             can_split: true,
-    //             render_tab_bar_buttons: Rc::new(move |pane, cx| {
-    //                 Flex::row()
-    //                     // New menu
-    //                     .with_child(Self::render_tab_bar_button(
-    //                         0,
-    //                         "icons/plus.svg",
-    //                         false,
-    //                         Some(("New...".into(), None)),
-    //                         cx,
-    //                         |pane, cx| pane.deploy_new_menu(cx),
-    //                         |pane, cx| {
-    //                             pane.tab_bar_context_menu
-    //                                 .handle
-    //                                 .update(cx, |menu, _| menu.delay_cancel())
-    //                         },
-    //                         pane.tab_bar_context_menu
-    //                             .handle_if_kind(TabBarContextMenuKind::New),
-    //                     ))
-    //                     .with_child(Self::render_tab_bar_button(
-    //                         1,
-    //                         "icons/split.svg",
-    //                         false,
-    //                         Some(("Split Pane".into(), None)),
-    //                         cx,
-    //                         |pane, cx| pane.deploy_split_menu(cx),
-    //                         |pane, cx| {
-    //                             pane.tab_bar_context_menu
-    //                                 .handle
-    //                                 .update(cx, |menu, _| menu.delay_cancel())
-    //                         },
-    //                         pane.tab_bar_context_menu
-    //                             .handle_if_kind(TabBarContextMenuKind::Split),
-    //                     ))
-    //                     .with_child({
-    //                         let icon_path;
-    //                         let tooltip_label;
-    //                         if pane.is_zoomed() {
-    //                             icon_path = "icons/minimize.svg";
-    //                             tooltip_label = "Zoom In";
-    //                         } else {
-    //                             icon_path = "icons/maximize.svg";
-    //                             tooltip_label = "Zoom In";
-    //                         }
+impl EventEmitter for Pane {
+    type Event = Event;
+}
 
-    //                         Pane::render_tab_bar_button(
-    //                             2,
-    //                             icon_path,
-    //                             pane.is_zoomed(),
-    //                             Some((tooltip_label, Some(Box::new(ToggleZoom)))),
-    //                             cx,
-    //                             move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
-    //                             move |_, _| {},
-    //                             None,
-    //                         )
-    //                     })
-    //                     .into_any()
-    //             }),
-    //         }
-    //     }
+impl Pane {
+    pub fn new(
+        workspace: WeakView<Workspace>,
+        project: Model<Project>,
+        next_timestamp: Arc<AtomicUsize>,
+        cx: &mut ViewContext<Self>,
+    ) -> Self {
+        // todo!("context menu")
+        // let pane_view_id = cx.view_id();
+        // let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx));
+        // context_menu.update(cx, |menu, _| {
+        //     menu.set_position_mode(OverlayPositionMode::Local)
+        // });
+
+        let handle = cx.view().downgrade();
+        Self {
+            items: Vec::new(),
+            activation_history: Vec::new(),
+            zoomed: false,
+            active_item_index: 0,
+            // last_focused_view_by_item: Default::default(),
+            autoscroll: false,
+            nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState {
+                mode: NavigationMode::Normal,
+                backward_stack: Default::default(),
+                forward_stack: Default::default(),
+                closed_stack: Default::default(),
+                paths_by_item: Default::default(),
+                pane: handle.clone(),
+                next_timestamp,
+            }))),
+            toolbar: cx.build_view(|_| Toolbar::new()),
+            // tab_bar_context_menu: TabBarContextMenu {
+            //     kind: TabBarContextMenuKind::New,
+            //     handle: context_menu,
+            // },
+            // tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
+            workspace,
+            project,
+            has_focus: false,
+            // can_drop: Rc::new(|_, _| true),
+            // can_split: true,
+            // render_tab_bar_buttons: Rc::new(move |pane, cx| {
+            //     Flex::row()
+            //         // New menu
+            //         .with_child(Self::render_tab_bar_button(
+            //             0,
+            //             "icons/plus.svg",
+            //             false,
+            //             Some(("New...".into(), None)),
+            //             cx,
+            //             |pane, cx| pane.deploy_new_menu(cx),
+            //             |pane, cx| {
+            //                 pane.tab_bar_context_menu
+            //                     .handle
+            //                     .update(cx, |menu, _| menu.delay_cancel())
+            //             },
+            //             pane.tab_bar_context_menu
+            //                 .handle_if_kind(TabBarContextMenuKind::New),
+            //         ))
+            //         .with_child(Self::render_tab_bar_button(
+            //             1,
+            //             "icons/split.svg",
+            //             false,
+            //             Some(("Split Pane".into(), None)),
+            //             cx,
+            //             |pane, cx| pane.deploy_split_menu(cx),
+            //             |pane, cx| {
+            //                 pane.tab_bar_context_menu
+            //                     .handle
+            //                     .update(cx, |menu, _| menu.delay_cancel())
+            //             },
+            //             pane.tab_bar_context_menu
+            //                 .handle_if_kind(TabBarContextMenuKind::Split),
+            //         ))
+            //         .with_child({
+            //             let icon_path;
+            //             let tooltip_label;
+            //             if pane.is_zoomed() {
+            //                 icon_path = "icons/minimize.svg";
+            //                 tooltip_label = "Zoom In";
+            //             } else {
+            //                 icon_path = "icons/maximize.svg";
+            //                 tooltip_label = "Zoom In";
+            //             }
+
+            //             Pane::render_tab_bar_button(
+            //                 2,
+            //                 icon_path,
+            //                 pane.is_zoomed(),
+            //                 Some((tooltip_label, Some(Box::new(ToggleZoom)))),
+            //                 cx,
+            //                 move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
+            //                 move |_, _| {},
+            //                 None,
+            //             )
+            //         })
+            //         .into_any()
+            // }),
+        }
+    }
 
-    //     pub(crate) fn workspace(&self) -> &WeakViewHandle<Workspace> {
+    //     pub(crate) fn workspace(&self) -> &WeakView<Workspace> {
     //         &self.workspace
     //     }
 
-    //     pub fn has_focus(&self) -> bool {
-    //         self.has_focus
-    //     }
+    pub fn has_focus(&self) -> bool {
+        self.has_focus
+    }
 
     //     pub fn active_item_index(&self) -> usize {
     //         self.active_item_index
@@ -455,40 +455,40 @@ impl Pane {
     //         cx.notify();
     //     }
 
-    //     pub fn nav_history_for_item<T: Item>(&self, item: &ViewHandle<T>) -> ItemNavHistory {
-    //         ItemNavHistory {
-    //             history: self.nav_history.clone(),
-    //             item: Rc::new(item.downgrade()),
-    //         }
-    //     }
+    pub fn nav_history_for_item<T: Item>(&self, item: &View<T>) -> ItemNavHistory {
+        ItemNavHistory {
+            history: self.nav_history.clone(),
+            item: Arc::new(item.downgrade()),
+        }
+    }
 
-    //     pub fn nav_history(&self) -> &NavHistory {
-    //         &self.nav_history
-    //     }
+    pub fn nav_history(&self) -> &NavHistory {
+        &self.nav_history
+    }
 
-    //     pub fn nav_history_mut(&mut self) -> &mut NavHistory {
-    //         &mut self.nav_history
-    //     }
+    pub fn nav_history_mut(&mut self) -> &mut NavHistory {
+        &mut self.nav_history
+    }
 
-    //     pub fn disable_history(&mut self) {
-    //         self.nav_history.disable();
-    //     }
+    pub fn disable_history(&mut self) {
+        self.nav_history.disable();
+    }
 
-    //     pub fn enable_history(&mut self) {
-    //         self.nav_history.enable();
-    //     }
+    pub fn enable_history(&mut self) {
+        self.nav_history.enable();
+    }
 
-    //     pub fn can_navigate_backward(&self) -> bool {
-    //         !self.nav_history.0.borrow().backward_stack.is_empty()
-    //     }
+    pub fn can_navigate_backward(&self) -> bool {
+        !self.nav_history.0.lock().backward_stack.is_empty()
+    }
 
-    //     pub fn can_navigate_forward(&self) -> bool {
-    //         !self.nav_history.0.borrow().forward_stack.is_empty()
-    //     }
+    pub fn can_navigate_forward(&self) -> bool {
+        !self.nav_history.0.lock().forward_stack.is_empty()
+    }
 
-    //     fn history_updated(&mut self, cx: &mut ViewContext<Self>) {
-    //         self.toolbar.update(cx, |_, cx| cx.notify());
-    //     }
+    fn history_updated(&mut self, cx: &mut ViewContext<Self>) {
+        self.toolbar.update(cx, |_, cx| cx.notify());
+    }
 
     pub(crate) fn open_item(
         &mut self,
@@ -532,7 +532,7 @@ impl Pane {
                     let abs_path = project.absolute_path(&project_path, cx);
                     self.nav_history
                         .0
-                        .borrow_mut()
+                        .lock()
                         .paths_by_item
                         .insert(item.id(), (project_path, abs_path));
                 }
@@ -615,13 +615,13 @@ impl Pane {
         cx.emit(Event::AddItem { item });
     }
 
-    //     pub fn items_len(&self) -> usize {
-    //         self.items.len()
-    //     }
+    pub fn items_len(&self) -> usize {
+        self.items.len()
+    }
 
-    //     pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> + DoubleEndedIterator {
-    //         self.items.iter()
-    //     }
+    pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> + DoubleEndedIterator {
+        self.items.iter()
+    }
 
     //     pub fn items_of_type<T: View>(&self) -> impl '_ + Iterator<Item = ViewHandle<T>> {
     //         self.items
@@ -629,9 +629,9 @@ impl Pane {
     //             .filter_map(|item| item.as_any().clone().downcast())
     //     }
 
-    //     pub fn active_item(&self) -> Option<Box<dyn ItemHandle>> {
-    //         self.items.get(self.active_item_index).cloned()
-    //     }
+    pub fn active_item(&self) -> Option<Box<dyn ItemHandle>> {
+        self.items.get(self.active_item_index).cloned()
+    }
 
     //     pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
     //         self.items
@@ -653,9 +653,9 @@ impl Pane {
     //         })
     //     }
 
-    //     pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option<usize> {
-    //         self.items.iter().position(|i| i.id() == item.id())
-    //     }
+    pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option<usize> {
+        self.items.iter().position(|i| i.id() == item.id())
+    }
 
     //     pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
     //         // Potentially warn the user of the new keybinding
@@ -751,448 +751,445 @@ impl Pane {
     //         ))
     //     }
 
-    //     pub fn close_item_by_id(
-    //         &mut self,
-    //         item_id_to_close: usize,
-    //         save_intent: SaveIntent,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Task<Result<()>> {
-    //         self.close_items(cx, save_intent, move |view_id| view_id == item_id_to_close)
+    pub fn close_item_by_id(
+        &mut self,
+        item_id_to_close: EntityId,
+        save_intent: SaveIntent,
+        cx: &mut ViewContext<Self>,
+    ) -> Task<Result<()>> {
+        self.close_items(cx, save_intent, move |view_id| view_id == item_id_to_close)
+    }
+
+    // pub fn close_inactive_items(
+    //     &mut self,
+    //     _: &CloseInactiveItems,
+    //     cx: &mut ViewContext<Self>,
+    // ) -> Option<Task<Result<()>>> {
+    //     if self.items.is_empty() {
+    //         return None;
     //     }
 
-    //     pub fn close_inactive_items(
-    //         &mut self,
-    //         _: &CloseInactiveItems,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Option<Task<Result<()>>> {
-    //         if self.items.is_empty() {
-    //             return None;
-    //         }
+    //     let active_item_id = self.items[self.active_item_index].id();
+    //     Some(self.close_items(cx, SaveIntent::Close, move |item_id| {
+    //         item_id != active_item_id
+    //     }))
+    // }
 
-    //         let active_item_id = self.items[self.active_item_index].id();
-    //         Some(self.close_items(cx, SaveIntent::Close, move |item_id| {
-    //             item_id != active_item_id
-    //         }))
-    //     }
+    // pub fn close_clean_items(
+    //     &mut self,
+    //     _: &CloseCleanItems,
+    //     cx: &mut ViewContext<Self>,
+    // ) -> Option<Task<Result<()>>> {
+    //     let item_ids: Vec<_> = self
+    //         .items()
+    //         .filter(|item| !item.is_dirty(cx))
+    //         .map(|item| item.id())
+    //         .collect();
+    //     Some(self.close_items(cx, SaveIntent::Close, move |item_id| {
+    //         item_ids.contains(&item_id)
+    //     }))
+    // }
 
-    //     pub fn close_clean_items(
-    //         &mut self,
-    //         _: &CloseCleanItems,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Option<Task<Result<()>>> {
-    //         let item_ids: Vec<_> = self
-    //             .items()
-    //             .filter(|item| !item.is_dirty(cx))
-    //             .map(|item| item.id())
-    //             .collect();
-    //         Some(self.close_items(cx, SaveIntent::Close, move |item_id| {
-    //             item_ids.contains(&item_id)
-    //         }))
+    // pub fn close_items_to_the_left(
+    //     &mut self,
+    //     _: &CloseItemsToTheLeft,
+    //     cx: &mut ViewContext<Self>,
+    // ) -> Option<Task<Result<()>>> {
+    //     if self.items.is_empty() {
+    //         return None;
     //     }
+    //     let active_item_id = self.items[self.active_item_index].id();
+    //     Some(self.close_items_to_the_left_by_id(active_item_id, cx))
+    // }
 
-    //     pub fn close_items_to_the_left(
-    //         &mut self,
-    //         _: &CloseItemsToTheLeft,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Option<Task<Result<()>>> {
-    //         if self.items.is_empty() {
-    //             return None;
-    //         }
-    //         let active_item_id = self.items[self.active_item_index].id();
-    //         Some(self.close_items_to_the_left_by_id(active_item_id, cx))
-    //     }
+    // pub fn close_items_to_the_left_by_id(
+    //     &mut self,
+    //     item_id: usize,
+    //     cx: &mut ViewContext<Self>,
+    // ) -> Task<Result<()>> {
+    //     let item_ids: Vec<_> = self
+    //         .items()
+    //         .take_while(|item| item.id() != item_id)
+    //         .map(|item| item.id())
+    //         .collect();
+    //     self.close_items(cx, SaveIntent::Close, move |item_id| {
+    //         item_ids.contains(&item_id)
+    //     })
+    // }
 
-    //     pub fn close_items_to_the_left_by_id(
-    //         &mut self,
-    //         item_id: usize,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Task<Result<()>> {
-    //         let item_ids: Vec<_> = self
-    //             .items()
-    //             .take_while(|item| item.id() != item_id)
-    //             .map(|item| item.id())
-    //             .collect();
-    //         self.close_items(cx, SaveIntent::Close, move |item_id| {
-    //             item_ids.contains(&item_id)
-    //         })
+    // pub fn close_items_to_the_right(
+    //     &mut self,
+    //     _: &CloseItemsToTheRight,
+    //     cx: &mut ViewContext<Self>,
+    // ) -> Option<Task<Result<()>>> {
+    //     if self.items.is_empty() {
+    //         return None;
     //     }
+    //     let active_item_id = self.items[self.active_item_index].id();
+    //     Some(self.close_items_to_the_right_by_id(active_item_id, cx))
+    // }
 
-    //     pub fn close_items_to_the_right(
-    //         &mut self,
-    //         _: &CloseItemsToTheRight,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Option<Task<Result<()>>> {
-    //         if self.items.is_empty() {
-    //             return None;
-    //         }
-    //         let active_item_id = self.items[self.active_item_index].id();
-    //         Some(self.close_items_to_the_right_by_id(active_item_id, cx))
-    //     }
+    // pub fn close_items_to_the_right_by_id(
+    //     &mut self,
+    //     item_id: usize,
+    //     cx: &mut ViewContext<Self>,
+    // ) -> Task<Result<()>> {
+    //     let item_ids: Vec<_> = self
+    //         .items()
+    //         .rev()
+    //         .take_while(|item| item.id() != item_id)
+    //         .map(|item| item.id())
+    //         .collect();
+    //     self.close_items(cx, SaveIntent::Close, move |item_id| {
+    //         item_ids.contains(&item_id)
+    //     })
+    // }
 
-    //     pub fn close_items_to_the_right_by_id(
-    //         &mut self,
-    //         item_id: usize,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Task<Result<()>> {
-    //         let item_ids: Vec<_> = self
-    //             .items()
-    //             .rev()
-    //             .take_while(|item| item.id() != item_id)
-    //             .map(|item| item.id())
-    //             .collect();
-    //         self.close_items(cx, SaveIntent::Close, move |item_id| {
-    //             item_ids.contains(&item_id)
-    //         })
+    // pub fn close_all_items(
+    //     &mut self,
+    //     action: &CloseAllItems,
+    //     cx: &mut ViewContext<Self>,
+    // ) -> Option<Task<Result<()>>> {
+    //     if self.items.is_empty() {
+    //         return None;
     //     }
 
-    //     pub fn close_all_items(
-    //         &mut self,
-    //         action: &CloseAllItems,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Option<Task<Result<()>>> {
-    //         if self.items.is_empty() {
-    //             return None;
-    //         }
+    //     Some(
+    //         self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| {
+    //             true
+    //         }),
+    //     )
+    // }
 
-    //         Some(
-    //             self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| {
-    //                 true
-    //             }),
-    //         )
-    //     }
+    pub(super) fn file_names_for_prompt(
+        items: &mut dyn Iterator<Item = &Box<dyn ItemHandle>>,
+        all_dirty_items: usize,
+        cx: &AppContext,
+    ) -> String {
+        /// Quantity of item paths displayed in prompt prior to cutoff..
+        const FILE_NAMES_CUTOFF_POINT: usize = 10;
+        let mut file_names: Vec<_> = items
+            .filter_map(|item| {
+                item.project_path(cx).and_then(|project_path| {
+                    project_path
+                        .path
+                        .file_name()
+                        .and_then(|name| name.to_str().map(ToOwned::to_owned))
+                })
+            })
+            .take(FILE_NAMES_CUTOFF_POINT)
+            .collect();
+        let should_display_followup_text =
+            all_dirty_items > FILE_NAMES_CUTOFF_POINT || file_names.len() != all_dirty_items;
+        if should_display_followup_text {
+            let not_shown_files = all_dirty_items - file_names.len();
+            if not_shown_files == 1 {
+                file_names.push(".. 1 file not shown".into());
+            } else {
+                file_names.push(format!(".. {} files not shown", not_shown_files).into());
+            }
+        }
+        let file_names = file_names.join("\n");
+        format!(
+            "Do you want to save changes to the following {} files?\n{file_names}",
+            all_dirty_items
+        )
+    }
 
-    //     pub(super) fn file_names_for_prompt(
-    //         items: &mut dyn Iterator<Item = &Box<dyn ItemHandle>>,
-    //         all_dirty_items: usize,
-    //         cx: &AppContext,
-    //     ) -> String {
-    //         /// Quantity of item paths displayed in prompt prior to cutoff..
-    //         const FILE_NAMES_CUTOFF_POINT: usize = 10;
-    //         let mut file_names: Vec<_> = items
-    //             .filter_map(|item| {
-    //                 item.project_path(cx).and_then(|project_path| {
-    //                     project_path
-    //                         .path
-    //                         .file_name()
-    //                         .and_then(|name| name.to_str().map(ToOwned::to_owned))
-    //                 })
-    //             })
-    //             .take(FILE_NAMES_CUTOFF_POINT)
-    //             .collect();
-    //         let should_display_followup_text =
-    //             all_dirty_items > FILE_NAMES_CUTOFF_POINT || file_names.len() != all_dirty_items;
-    //         if should_display_followup_text {
-    //             let not_shown_files = all_dirty_items - file_names.len();
-    //             if not_shown_files == 1 {
-    //                 file_names.push(".. 1 file not shown".into());
-    //             } else {
-    //                 file_names.push(format!(".. {} files not shown", not_shown_files).into());
-    //             }
-    //         }
-    //         let file_names = file_names.join("\n");
-    //         format!(
-    //             "Do you want to save changes to the following {} files?\n{file_names}",
-    //             all_dirty_items
-    //         )
-    //     }
+    pub fn close_items(
+        &mut self,
+        cx: &mut ViewContext<Pane>,
+        mut save_intent: SaveIntent,
+        should_close: impl 'static + Fn(EntityId) -> bool,
+    ) -> Task<Result<()>> {
+        // Find the items to close.
+        let mut items_to_close = Vec::new();
+        let mut dirty_items = Vec::new();
+        for item in &self.items {
+            if should_close(item.id()) {
+                items_to_close.push(item.boxed_clone());
+                if item.is_dirty(cx) {
+                    dirty_items.push(item.boxed_clone());
+                }
+            }
+        }
 
-    //     pub fn close_items(
-    //         &mut self,
-    //         cx: &mut ViewContext<Pane>,
-    //         mut save_intent: SaveIntent,
-    //         should_close: impl 'static + Fn(usize) -> bool,
-    //     ) -> Task<Result<()>> {
-    //         // Find the items to close.
-    //         let mut items_to_close = Vec::new();
-    //         let mut dirty_items = Vec::new();
-    //         for item in &self.items {
-    //             if should_close(item.id()) {
-    //                 items_to_close.push(item.boxed_clone());
-    //                 if item.is_dirty(cx) {
-    //                     dirty_items.push(item.boxed_clone());
-    //                 }
-    //             }
-    //         }
+        // If a buffer is open both in a singleton editor and in a multibuffer, make sure
+        // to focus the singleton buffer when prompting to save that buffer, as opposed
+        // to focusing the multibuffer, because this gives the user a more clear idea
+        // of what content they would be saving.
+        items_to_close.sort_by_key(|item| !item.is_singleton(cx));
+
+        let workspace = self.workspace.clone();
+        cx.spawn(|pane, mut cx| async move {
+            if save_intent == SaveIntent::Close && dirty_items.len() > 1 {
+                let answer = pane.update(&mut cx, |_, cx| {
+                    let prompt =
+                        Self::file_names_for_prompt(&mut dirty_items.iter(), dirty_items.len(), cx);
+                    cx.prompt(
+                        PromptLevel::Warning,
+                        &prompt,
+                        &["Save all", "Discard all", "Cancel"],
+                    )
+                })?;
+                match answer.await {
+                    Ok(0) => save_intent = SaveIntent::SaveAll,
+                    Ok(1) => save_intent = SaveIntent::Skip,
+                    _ => {}
+                }
+            }
+            let mut saved_project_items_ids = HashSet::default();
+            for item in items_to_close.clone() {
+                // Find the item's current index and its set of project item models. Avoid
+                // storing these in advance, in case they have changed since this task
+                // was started.
+                let (item_ix, mut project_item_ids) = pane.update(&mut cx, |pane, cx| {
+                    (pane.index_for_item(&*item), item.project_item_model_ids(cx))
+                })?;
+                let item_ix = if let Some(ix) = item_ix {
+                    ix
+                } else {
+                    continue;
+                };
+
+                // Check if this view has any project items that are not open anywhere else
+                // in the workspace, AND that the user has not already been prompted to save.
+                // If there are any such project entries, prompt the user to save this item.
+                let project = workspace.update(&mut cx, |workspace, cx| {
+                    for item in workspace.items(cx) {
+                        if !items_to_close
+                            .iter()
+                            .any(|item_to_close| item_to_close.id() == item.id())
+                        {
+                            let other_project_item_ids = item.project_item_model_ids(cx);
+                            project_item_ids.retain(|id| !other_project_item_ids.contains(id));
+                        }
+                    }
+                    workspace.project().clone()
+                })?;
+                let should_save = project_item_ids
+                    .iter()
+                    .any(|id| saved_project_items_ids.insert(*id));
+
+                if should_save
+                    && !Self::save_item(
+                        project.clone(),
+                        &pane,
+                        item_ix,
+                        &*item,
+                        save_intent,
+                        &mut cx,
+                    )
+                    .await?
+                {
+                    break;
+                }
 
-    //         // If a buffer is open both in a singleton editor and in a multibuffer, make sure
-    //         // to focus the singleton buffer when prompting to save that buffer, as opposed
-    //         // to focusing the multibuffer, because this gives the user a more clear idea
-    //         // of what content they would be saving.
-    //         items_to_close.sort_by_key(|item| !item.is_singleton(cx));
-
-    //         let workspace = self.workspace.clone();
-    //         cx.spawn(|pane, mut cx| async move {
-    //             if save_intent == SaveIntent::Close && dirty_items.len() > 1 {
-    //                 let mut answer = pane.update(&mut cx, |_, cx| {
-    //                     let prompt =
-    //                         Self::file_names_for_prompt(&mut dirty_items.iter(), dirty_items.len(), cx);
-    //                     cx.prompt(
-    //                         PromptLevel::Warning,
-    //                         &prompt,
-    //                         &["Save all", "Discard all", "Cancel"],
-    //                     )
-    //                 })?;
-    //                 match answer.next().await {
-    //                     Some(0) => save_intent = SaveIntent::SaveAll,
-    //                     Some(1) => save_intent = SaveIntent::Skip,
-    //                     _ => {}
-    //                 }
-    //             }
-    //             let mut saved_project_items_ids = HashSet::default();
-    //             for item in items_to_close.clone() {
-    //                 // Find the item's current index and its set of project item models. Avoid
-    //                 // storing these in advance, in case they have changed since this task
-    //                 // was started.
-    //                 let (item_ix, mut project_item_ids) = pane.read_with(&cx, |pane, cx| {
-    //                     (pane.index_for_item(&*item), item.project_item_model_ids(cx))
-    //                 })?;
-    //                 let item_ix = if let Some(ix) = item_ix {
-    //                     ix
-    //                 } else {
-    //                     continue;
-    //                 };
-
-    //                 // Check if this view has any project items that are not open anywhere else
-    //                 // in the workspace, AND that the user has not already been prompted to save.
-    //                 // If there are any such project entries, prompt the user to save this item.
-    //                 let project = workspace.read_with(&cx, |workspace, cx| {
-    //                     for item in workspace.items(cx) {
-    //                         if !items_to_close
-    //                             .iter()
-    //                             .any(|item_to_close| item_to_close.id() == item.id())
-    //                         {
-    //                             let other_project_item_ids = item.project_item_model_ids(cx);
-    //                             project_item_ids.retain(|id| !other_project_item_ids.contains(id));
-    //                         }
-    //                     }
-    //                     workspace.project().clone()
-    //                 })?;
-    //                 let should_save = project_item_ids
-    //                     .iter()
-    //                     .any(|id| saved_project_items_ids.insert(*id));
-
-    //                 if should_save
-    //                     && !Self::save_item(
-    //                         project.clone(),
-    //                         &pane,
-    //                         item_ix,
-    //                         &*item,
-    //                         save_intent,
-    //                         &mut cx,
-    //                     )
-    //                     .await?
-    //                 {
-    //                     break;
-    //                 }
+                // Remove the item from the pane.
+                pane.update(&mut cx, |pane, cx| {
+                    if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) {
+                        pane.remove_item(item_ix, false, cx);
+                    }
+                })?;
+            }
 
-    //                 // Remove the item from the pane.
-    //                 pane.update(&mut cx, |pane, cx| {
-    //                     if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) {
-    //                         pane.remove_item(item_ix, false, cx);
-    //                     }
-    //                 })?;
-    //             }
+            pane.update(&mut cx, |_, cx| cx.notify())?;
+            Ok(())
+        })
+    }
 
-    //             pane.update(&mut cx, |_, cx| cx.notify())?;
-    //             Ok(())
-    //         })
-    //     }
+    pub fn remove_item(
+        &mut self,
+        item_index: usize,
+        activate_pane: bool,
+        cx: &mut ViewContext<Self>,
+    ) {
+        self.activation_history
+            .retain(|&history_entry| history_entry != self.items[item_index].id());
+
+        if item_index == self.active_item_index {
+            let index_to_activate = self
+                .activation_history
+                .pop()
+                .and_then(|last_activated_item| {
+                    self.items.iter().enumerate().find_map(|(index, item)| {
+                        (item.id() == last_activated_item).then_some(index)
+                    })
+                })
+                // We didn't have a valid activation history entry, so fallback
+                // to activating the item to the left
+                .unwrap_or_else(|| item_index.min(self.items.len()).saturating_sub(1));
 
-    //     pub fn remove_item(
-    //         &mut self,
-    //         item_index: usize,
-    //         activate_pane: bool,
-    //         cx: &mut ViewContext<Self>,
-    //     ) {
-    //         self.activation_history
-    //             .retain(|&history_entry| history_entry != self.items[item_index].id());
-
-    //         if item_index == self.active_item_index {
-    //             let index_to_activate = self
-    //                 .activation_history
-    //                 .pop()
-    //                 .and_then(|last_activated_item| {
-    //                     self.items.iter().enumerate().find_map(|(index, item)| {
-    //                         (item.id() == last_activated_item).then_some(index)
-    //                     })
-    //                 })
-    //                 // We didn't have a valid activation history entry, so fallback
-    //                 // to activating the item to the left
-    //                 .unwrap_or_else(|| item_index.min(self.items.len()).saturating_sub(1));
-
-    //             let should_activate = activate_pane || self.has_focus;
-    //             self.activate_item(index_to_activate, should_activate, should_activate, cx);
-    //         }
+            let should_activate = activate_pane || self.has_focus;
+            self.activate_item(index_to_activate, should_activate, should_activate, cx);
+        }
 
-    //         let item = self.items.remove(item_index);
+        let item = self.items.remove(item_index);
 
-    //         cx.emit(Event::RemoveItem { item_id: item.id() });
-    //         if self.items.is_empty() {
-    //             item.deactivated(cx);
-    //             self.update_toolbar(cx);
-    //             cx.emit(Event::Remove);
-    //         }
+        cx.emit(Event::RemoveItem { item_id: item.id() });
+        if self.items.is_empty() {
+            item.deactivated(cx);
+            self.update_toolbar(cx);
+            cx.emit(Event::Remove);
+        }
 
-    //         if item_index < self.active_item_index {
-    //             self.active_item_index -= 1;
-    //         }
+        if item_index < self.active_item_index {
+            self.active_item_index -= 1;
+        }
 
-    //         self.nav_history.set_mode(NavigationMode::ClosingItem);
-    //         item.deactivated(cx);
-    //         self.nav_history.set_mode(NavigationMode::Normal);
-
-    //         if let Some(path) = item.project_path(cx) {
-    //             let abs_path = self
-    //                 .nav_history
-    //                 .0
-    //                 .borrow()
-    //                 .paths_by_item
-    //                 .get(&item.id())
-    //                 .and_then(|(_, abs_path)| abs_path.clone());
-
-    //             self.nav_history
-    //                 .0
-    //                 .borrow_mut()
-    //                 .paths_by_item
-    //                 .insert(item.id(), (path, abs_path));
-    //         } else {
-    //             self.nav_history
-    //                 .0
-    //                 .borrow_mut()
-    //                 .paths_by_item
-    //                 .remove(&item.id());
-    //         }
+        self.nav_history.set_mode(NavigationMode::ClosingItem);
+        item.deactivated(cx);
+        self.nav_history.set_mode(NavigationMode::Normal);
+
+        if let Some(path) = item.project_path(cx) {
+            let abs_path = self
+                .nav_history
+                .0
+                .lock()
+                .paths_by_item
+                .get(&item.id())
+                .and_then(|(_, abs_path)| abs_path.clone());
+
+            self.nav_history
+                .0
+                .lock()
+                .paths_by_item
+                .insert(item.id(), (path, abs_path));
+        } else {
+            self.nav_history.0.lock().paths_by_item.remove(&item.id());
+        }
 
-    //         if self.items.is_empty() && self.zoomed {
-    //             cx.emit(Event::ZoomOut);
-    //         }
+        if self.items.is_empty() && self.zoomed {
+            cx.emit(Event::ZoomOut);
+        }
 
-    //         cx.notify();
-    //     }
+        cx.notify();
+    }
 
-    //     pub async fn save_item(
-    //         project: ModelHandle<Project>,
-    //         pane: &WeakViewHandle<Pane>,
-    //         item_ix: usize,
-    //         item: &dyn ItemHandle,
-    //         save_intent: SaveIntent,
-    //         cx: &mut AsyncAppContext,
-    //     ) -> Result<bool> {
-    //         const CONFLICT_MESSAGE: &str =
-    //             "This file has changed on disk since you started editing it. Do you want to overwrite it?";
-
-    //         if save_intent == SaveIntent::Skip {
-    //             return Ok(true);
-    //         }
+    pub async fn save_item(
+        project: Model<Project>,
+        pane: &WeakView<Pane>,
+        item_ix: usize,
+        item: &dyn ItemHandle,
+        save_intent: SaveIntent,
+        cx: &mut AsyncWindowContext,
+    ) -> Result<bool> {
+        const CONFLICT_MESSAGE: &str =
+                "This file has changed on disk since you started editing it. Do you want to overwrite it?";
+
+        if save_intent == SaveIntent::Skip {
+            return Ok(true);
+        }
 
-    //         let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.read(|cx| {
-    //             (
-    //                 item.has_conflict(cx),
-    //                 item.is_dirty(cx),
-    //                 item.can_save(cx),
-    //                 item.is_singleton(cx),
-    //             )
-    //         });
+        let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.update(|_, cx| {
+            (
+                item.has_conflict(cx),
+                item.is_dirty(cx),
+                item.can_save(cx),
+                item.is_singleton(cx),
+            )
+        })?;
 
-    //         // when saving a single buffer, we ignore whether or not it's dirty.
-    //         if save_intent == SaveIntent::Save {
-    //             is_dirty = true;
-    //         }
+        // when saving a single buffer, we ignore whether or not it's dirty.
+        if save_intent == SaveIntent::Save {
+            is_dirty = true;
+        }
 
-    //         if save_intent == SaveIntent::SaveAs {
-    //             is_dirty = true;
-    //             has_conflict = false;
-    //             can_save = false;
-    //         }
+        if save_intent == SaveIntent::SaveAs {
+            is_dirty = true;
+            has_conflict = false;
+            can_save = false;
+        }
 
-    //         if save_intent == SaveIntent::Overwrite {
-    //             has_conflict = false;
-    //         }
+        if save_intent == SaveIntent::Overwrite {
+            has_conflict = false;
+        }
 
-    //         if has_conflict && can_save {
-    //             let mut answer = pane.update(cx, |pane, cx| {
-    //                 pane.activate_item(item_ix, true, true, cx);
-    //                 cx.prompt(
-    //                     PromptLevel::Warning,
-    //                     CONFLICT_MESSAGE,
-    //                     &["Overwrite", "Discard", "Cancel"],
-    //                 )
-    //             })?;
-    //             match answer.next().await {
-    //                 Some(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?,
-    //                 Some(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?,
-    //                 _ => return Ok(false),
-    //             }
-    //         } else if is_dirty && (can_save || can_save_as) {
-    //             if save_intent == SaveIntent::Close {
-    //                 let will_autosave = cx.read(|cx| {
-    //                     matches!(
-    //                         settings::get::<WorkspaceSettings>(cx).autosave,
-    //                         AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
-    //                     ) && Self::can_autosave_item(&*item, cx)
-    //                 });
-    //                 if !will_autosave {
-    //                     let mut answer = pane.update(cx, |pane, cx| {
-    //                         pane.activate_item(item_ix, true, true, cx);
-    //                         let prompt = dirty_message_for(item.project_path(cx));
-    //                         cx.prompt(
-    //                             PromptLevel::Warning,
-    //                             &prompt,
-    //                             &["Save", "Don't Save", "Cancel"],
-    //                         )
-    //                     })?;
-    //                     match answer.next().await {
-    //                         Some(0) => {}
-    //                         Some(1) => return Ok(true), // Don't save his file
-    //                         _ => return Ok(false),      // Cancel
-    //                     }
-    //                 }
-    //             }
+        if has_conflict && can_save {
+            let answer = pane.update(cx, |pane, cx| {
+                pane.activate_item(item_ix, true, true, cx);
+                cx.prompt(
+                    PromptLevel::Warning,
+                    CONFLICT_MESSAGE,
+                    &["Overwrite", "Discard", "Cancel"],
+                )
+            })?;
+            match answer.await {
+                Ok(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?,
+                Ok(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?,
+                _ => return Ok(false),
+            }
+        } else if is_dirty && (can_save || can_save_as) {
+            if save_intent == SaveIntent::Close {
+                let will_autosave = cx.update(|_, cx| {
+                    matches!(
+                        WorkspaceSettings::get_global(cx).autosave,
+                        AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
+                    ) && Self::can_autosave_item(&*item, cx)
+                })?;
+                if !will_autosave {
+                    let answer = pane.update(cx, |pane, cx| {
+                        pane.activate_item(item_ix, true, true, cx);
+                        let prompt = dirty_message_for(item.project_path(cx));
+                        cx.prompt(
+                            PromptLevel::Warning,
+                            &prompt,
+                            &["Save", "Don't Save", "Cancel"],
+                        )
+                    })?;
+                    match answer.await {
+                        Ok(0) => {}
+                        Ok(1) => return Ok(true), // Don't save this file
+                        _ => return Ok(false),    // Cancel
+                    }
+                }
+            }
 
-    //             if can_save {
-    //                 pane.update(cx, |_, cx| item.save(project, cx))?.await?;
-    //             } else if can_save_as {
-    //                 let start_abs_path = project
-    //                     .read_with(cx, |project, cx| {
-    //                         let worktree = project.visible_worktrees(cx).next()?;
-    //                         Some(worktree.read(cx).as_local()?.abs_path().to_path_buf())
-    //                     })
-    //                     .unwrap_or_else(|| Path::new("").into());
-
-    //                 let mut abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path));
-    //                 if let Some(abs_path) = abs_path.next().await.flatten() {
-    //                     pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
-    //                         .await?;
-    //                 } else {
-    //                     return Ok(false);
-    //                 }
-    //             }
-    //         }
-    //         Ok(true)
-    //     }
+            if can_save {
+                pane.update(cx, |_, cx| item.save(project, cx))?.await?;
+            } else if can_save_as {
+                let start_abs_path = project
+                    .update(cx, |project, cx| {
+                        let worktree = project.visible_worktrees(cx).next()?;
+                        Some(worktree.read(cx).as_local()?.abs_path().to_path_buf())
+                    })?
+                    .unwrap_or_else(|| Path::new("").into());
+
+                let abs_path = cx.update(|_, cx| cx.prompt_for_new_path(&start_abs_path))?;
+                if let Some(abs_path) = abs_path.await.ok().flatten() {
+                    pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
+                        .await?;
+                } else {
+                    return Ok(false);
+                }
+            }
+        }
+        Ok(true)
+    }
 
-    //     fn can_autosave_item(item: &dyn ItemHandle, cx: &AppContext) -> bool {
-    //         let is_deleted = item.project_entry_ids(cx).is_empty();
-    //         item.is_dirty(cx) && !item.has_conflict(cx) && item.can_save(cx) && !is_deleted
-    //     }
+    fn can_autosave_item(item: &dyn ItemHandle, cx: &AppContext) -> bool {
+        let is_deleted = item.project_entry_ids(cx).is_empty();
+        item.is_dirty(cx) && !item.has_conflict(cx) && item.can_save(cx) && !is_deleted
+    }
 
-    //     pub fn autosave_item(
-    //         item: &dyn ItemHandle,
-    //         project: ModelHandle<Project>,
-    //         cx: &mut WindowContext,
-    //     ) -> Task<Result<()>> {
-    //         if Self::can_autosave_item(item, cx) {
-    //             item.save(project, cx)
-    //         } else {
-    //             Task::ready(Ok(()))
-    //         }
-    //     }
+    pub fn autosave_item(
+        item: &dyn ItemHandle,
+        project: Model<Project>,
+        cx: &mut WindowContext,
+    ) -> Task<Result<()>> {
+        if Self::can_autosave_item(item, cx) {
+            item.save(project, cx)
+        } else {
+            Task::ready(Ok(()))
+        }
+    }
 
-    //     pub fn focus_active_item(&mut self, cx: &mut ViewContext<Self>) {
-    //         if let Some(active_item) = self.active_item() {
-    //             cx.focus(active_item.as_any());
-    //         }
-    //     }
+    pub fn focus_active_item(&mut self, cx: &mut ViewContext<Self>) {
+        todo!();
+        // if let Some(active_item) = self.active_item() {
+        //     cx.focus(active_item.as_any());
+        // }
+    }
 
     //     pub fn split(&mut self, direction: SplitDirection, cx: &mut ViewContext<Self>) {
     //         cx.emit(Event::Split(direction));

crates/workspace2/src/pane/dragged_item_receiver.rs 🔗

@@ -0,0 +1,239 @@
+use super::DraggedItem;
+use crate::{Pane, SplitDirection, Workspace};
+use gpui2::{
+    color::Color,
+    elements::{Canvas, MouseEventHandler, ParentElement, Stack},
+    geometry::{rect::RectF, vector::Vector2F},
+    platform::MouseButton,
+    scene::MouseUp,
+    AppContext, Element, EventContext, MouseState, Quad, ViewContext, WeakViewHandle,
+};
+use project2::ProjectEntryId;
+
+pub fn dragged_item_receiver<Tag, D, F>(
+    pane: &Pane,
+    region_id: usize,
+    drop_index: usize,
+    allow_same_pane: bool,
+    split_margin: Option<f32>,
+    cx: &mut ViewContext<Pane>,
+    render_child: F,
+) -> MouseEventHandler<Pane>
+where
+    Tag: 'static,
+    D: Element<Pane>,
+    F: FnOnce(&mut MouseState, &mut ViewContext<Pane>) -> D,
+{
+    let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
+    let drag_position = if (pane.can_drop)(drag_and_drop, cx) {
+        drag_and_drop
+            .currently_dragged::<DraggedItem>(cx.window())
+            .map(|(drag_position, _)| drag_position)
+            .or_else(|| {
+                drag_and_drop
+                    .currently_dragged::<ProjectEntryId>(cx.window())
+                    .map(|(drag_position, _)| drag_position)
+            })
+    } else {
+        None
+    };
+
+    let mut handler = MouseEventHandler::above::<Tag, _>(region_id, cx, |state, cx| {
+        // Observing hovered will cause a render when the mouse enters regardless
+        // of if mouse position was accessed before
+        let drag_position = if state.dragging() {
+            drag_position
+        } else {
+            None
+        };
+        Stack::new()
+            .with_child(render_child(state, cx))
+            .with_children(drag_position.map(|drag_position| {
+                Canvas::new(move |bounds, _, _, cx| {
+                    if bounds.contains_point(drag_position) {
+                        let overlay_region = split_margin
+                            .and_then(|split_margin| {
+                                drop_split_direction(drag_position, bounds, split_margin)
+                                    .map(|dir| (dir, split_margin))
+                            })
+                            .map(|(dir, margin)| dir.along_edge(bounds, margin))
+                            .unwrap_or(bounds);
+
+                        cx.scene().push_stacking_context(None, None);
+                        let background = overlay_color(cx);
+                        cx.scene().push_quad(Quad {
+                            bounds: overlay_region,
+                            background: Some(background),
+                            border: Default::default(),
+                            corner_radii: Default::default(),
+                        });
+                        cx.scene().pop_stacking_context();
+                    }
+                })
+            }))
+    });
+
+    if drag_position.is_some() {
+        handler = handler
+            .on_up(MouseButton::Left, {
+                move |event, pane, cx| {
+                    let workspace = pane.workspace.clone();
+                    let pane = cx.weak_handle();
+                    handle_dropped_item(
+                        event,
+                        workspace,
+                        &pane,
+                        drop_index,
+                        allow_same_pane,
+                        split_margin,
+                        cx,
+                    );
+                    cx.notify();
+                }
+            })
+            .on_move(|_, _, cx| {
+                let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
+
+                if drag_and_drop
+                    .currently_dragged::<DraggedItem>(cx.window())
+                    .is_some()
+                    || drag_and_drop
+                        .currently_dragged::<ProjectEntryId>(cx.window())
+                        .is_some()
+                {
+                    cx.notify();
+                } else {
+                    cx.propagate_event();
+                }
+            })
+    }
+
+    handler
+}
+
+pub fn handle_dropped_item<V: 'static>(
+    event: MouseUp,
+    workspace: WeakViewHandle<Workspace>,
+    pane: &WeakViewHandle<Pane>,
+    index: usize,
+    allow_same_pane: bool,
+    split_margin: Option<f32>,
+    cx: &mut EventContext<V>,
+) {
+    enum Action {
+        Move(WeakViewHandle<Pane>, usize),
+        Open(ProjectEntryId),
+    }
+    let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
+    let action = if let Some((_, dragged_item)) =
+        drag_and_drop.currently_dragged::<DraggedItem>(cx.window())
+    {
+        Action::Move(dragged_item.pane.clone(), dragged_item.handle.id())
+    } else if let Some((_, project_entry)) =
+        drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window())
+    {
+        Action::Open(*project_entry)
+    } else {
+        cx.propagate_event();
+        return;
+    };
+
+    if let Some(split_direction) =
+        split_margin.and_then(|margin| drop_split_direction(event.position, event.region, margin))
+    {
+        let pane_to_split = pane.clone();
+        match action {
+            Action::Move(from, item_id_to_move) => {
+                cx.window_context().defer(move |cx| {
+                    if let Some(workspace) = workspace.upgrade(cx) {
+                        workspace.update(cx, |workspace, cx| {
+                            workspace.split_pane_with_item(
+                                pane_to_split,
+                                split_direction,
+                                from,
+                                item_id_to_move,
+                                cx,
+                            );
+                        })
+                    }
+                });
+            }
+            Action::Open(project_entry) => {
+                cx.window_context().defer(move |cx| {
+                    if let Some(workspace) = workspace.upgrade(cx) {
+                        workspace.update(cx, |workspace, cx| {
+                            if let Some(task) = workspace.split_pane_with_project_entry(
+                                pane_to_split,
+                                split_direction,
+                                project_entry,
+                                cx,
+                            ) {
+                                task.detach_and_log_err(cx);
+                            }
+                        })
+                    }
+                });
+            }
+        };
+    } else {
+        match action {
+            Action::Move(from, item_id) => {
+                if pane != &from || allow_same_pane {
+                    let pane = pane.clone();
+                    cx.window_context().defer(move |cx| {
+                        if let Some(((workspace, from), to)) = workspace
+                            .upgrade(cx)
+                            .zip(from.upgrade(cx))
+                            .zip(pane.upgrade(cx))
+                        {
+                            workspace.update(cx, |workspace, cx| {
+                                workspace.move_item(from, to, item_id, index, cx);
+                            })
+                        }
+                    });
+                } else {
+                    cx.propagate_event();
+                }
+            }
+            Action::Open(project_entry) => {
+                let pane = pane.clone();
+                cx.window_context().defer(move |cx| {
+                    if let Some(workspace) = workspace.upgrade(cx) {
+                        workspace.update(cx, |workspace, cx| {
+                            if let Some(path) =
+                                workspace.project.read(cx).path_for_entry(project_entry, cx)
+                            {
+                                workspace
+                                    .open_path(path, Some(pane), true, cx)
+                                    .detach_and_log_err(cx);
+                            }
+                        });
+                    }
+                });
+            }
+        }
+    }
+}
+
+fn drop_split_direction(
+    position: Vector2F,
+    region: RectF,
+    split_margin: f32,
+) -> Option<SplitDirection> {
+    let mut min_direction = None;
+    let mut min_distance = split_margin;
+    for direction in SplitDirection::all() {
+        let edge_distance = (direction.edge(region) - direction.axis().component(position)).abs();
+
+        if edge_distance < min_distance {
+            min_direction = Some(direction);
+            min_distance = edge_distance;
+        }
+    }
+
+    min_direction
+}
+
+fn overlay_color(cx: &AppContext) -> Color {
+    theme2::current(cx).workspace.drop_target_overlay_color
+}

crates/workspace2/src/pane_group.rs 🔗

@@ -1,23 +1,57 @@
 use crate::{AppState, FollowerState, Pane, Workspace};
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, bail, Result};
 use call2::ActiveCall;
 use collections::HashMap;
-use gpui2::{size, AnyElement, AnyView, Bounds, Handle, Pixels, Point, View, ViewContext};
+use db2::sqlez::{
+    bindable::{Bind, Column, StaticColumnCount},
+    statement::Statement,
+};
+use gpui2::{
+    point, size, AnyElement, AnyWeakView, Bounds, Model, Pixels, Point, View, ViewContext,
+};
+use parking_lot::Mutex;
 use project2::Project;
 use serde::Deserialize;
-use std::{cell::RefCell, rc::Rc, sync::Arc};
-use theme2::Theme;
+use std::sync::Arc;
+use ui::prelude::*;
 
 const HANDLE_HITBOX_SIZE: f32 = 4.0;
 const HORIZONTAL_MIN_SIZE: f32 = 80.;
 const VERTICAL_MIN_SIZE: f32 = 100.;
 
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum Axis {
     Vertical,
     Horizontal,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+impl StaticColumnCount for Axis {}
+impl Bind for Axis {
+    fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
+        match self {
+            Axis::Horizontal => "Horizontal",
+            Axis::Vertical => "Vertical",
+        }
+        .bind(statement, start_index)
+    }
+}
+
+impl Column for Axis {
+    fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
+        String::column(statement, start_index).and_then(|(axis_text, next_index)| {
+            Ok((
+                match axis_text.as_str() {
+                    "Horizontal" => Axis::Horizontal,
+                    "Vertical" => Axis::Vertical,
+                    _ => bail!("Stored serialized item kind is incorrect"),
+                },
+                next_index,
+            ))
+        })
+    }
+}
+
+#[derive(Clone, PartialEq)]
 pub struct PaneGroup {
     pub(crate) root: Member,
 }
@@ -91,19 +125,17 @@ impl PaneGroup {
 
     pub(crate) fn render(
         &self,
-        project: &Handle<Project>,
-        theme: &Theme,
+        project: &Model<Project>,
         follower_states: &HashMap<View<Pane>, FollowerState>,
-        active_call: Option<&Handle<ActiveCall>>,
+        active_call: Option<&Model<ActiveCall>>,
         active_pane: &View<Pane>,
-        zoomed: Option<&AnyView>,
+        zoomed: Option<&AnyWeakView>,
         app_state: &Arc<AppState>,
         cx: &mut ViewContext<Workspace>,
-    ) -> AnyElement<Workspace> {
+    ) -> impl Component<Workspace> {
         self.root.render(
             project,
             0,
-            theme,
             follower_states,
             active_call,
             active_pane,
@@ -120,7 +152,7 @@ impl PaneGroup {
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, PartialEq)]
 pub(crate) enum Member {
     Axis(PaneAxis),
     Pane(View<Pane>),
@@ -153,17 +185,51 @@ impl Member {
 
     pub fn render(
         &self,
-        project: &Handle<Project>,
+        project: &Model<Project>,
         basis: usize,
-        theme: &Theme,
         follower_states: &HashMap<View<Pane>, FollowerState>,
-        active_call: Option<&Handle<ActiveCall>>,
+        active_call: Option<&Model<ActiveCall>>,
         active_pane: &View<Pane>,
-        zoomed: Option<&AnyView>,
+        zoomed: Option<&AnyWeakView>,
         app_state: &Arc<AppState>,
         cx: &mut ViewContext<Workspace>,
-    ) -> AnyElement<Workspace> {
-        todo!()
+    ) -> impl Component<Workspace> {
+        match self {
+            Member::Pane(pane) => {
+                // todo!()
+                // let pane_element = if Some(pane.into()) == zoomed {
+                //     None
+                // } else {
+                //     Some(pane)
+                // };
+
+                div().child(pane.clone()).render()
+
+                //         Stack::new()
+                //             .with_child(pane_element.contained().with_border(leader_border))
+                //             .with_children(leader_status_box)
+                //             .into_any()
+
+                // let el = div()
+                //     .flex()
+                //     .flex_1()
+                //     .gap_px()
+                //     .w_full()
+                //     .h_full()
+                //     .bg(cx.theme().colors().editor)
+                //     .children();
+            }
+            Member::Axis(axis) => axis.render(
+                project,
+                basis + 1,
+                follower_states,
+                active_call,
+                active_pane,
+                zoomed,
+                app_state,
+                cx,
+            ),
+        }
 
         // enum FollowIntoExternalProject {}
 
@@ -305,18 +371,24 @@ impl Member {
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone)]
 pub(crate) struct PaneAxis {
     pub axis: Axis,
     pub members: Vec<Member>,
-    pub flexes: Rc<RefCell<Vec<f32>>>,
-    pub bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
+    pub flexes: Arc<Mutex<Vec<f32>>>,
+    pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+}
+
+impl PartialEq for PaneAxis {
+    fn eq(&self, other: &Self) -> bool {
+        todo!()
+    }
 }
 
 impl PaneAxis {
     pub fn new(axis: Axis, members: Vec<Member>) -> Self {
-        let flexes = Rc::new(RefCell::new(vec![1.; members.len()]));
-        let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()]));
+        let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
+        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
         Self {
             axis,
             members,
@@ -329,8 +401,8 @@ impl PaneAxis {
         let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
         debug_assert!(members.len() == flexes.len());
 
-        let flexes = Rc::new(RefCell::new(flexes));
-        let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()]));
+        let flexes = Arc::new(Mutex::new(flexes));
+        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
         Self {
             axis,
             members,
@@ -360,7 +432,7 @@ impl PaneAxis {
                             }
 
                             self.members.insert(idx, Member::Pane(new_pane.clone()));
-                            *self.flexes.borrow_mut() = vec![1.; self.members.len()];
+                            *self.flexes.lock() = vec![1.; self.members.len()];
                         } else {
                             *member =
                                 Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
@@ -400,12 +472,12 @@ impl PaneAxis {
         if found_pane {
             if let Some(idx) = remove_member {
                 self.members.remove(idx);
-                *self.flexes.borrow_mut() = vec![1.; self.members.len()];
+                *self.flexes.lock() = vec![1.; self.members.len()];
             }
 
             if self.members.len() == 1 {
                 let result = self.members.pop();
-                *self.flexes.borrow_mut() = vec![1.; self.members.len()];
+                *self.flexes.lock() = vec![1.; self.members.len()];
                 Ok(result)
             } else {
                 Ok(None)
@@ -431,13 +503,13 @@ impl PaneAxis {
     }
 
     fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
-        debug_assert!(self.members.len() == self.bounding_boxes.borrow().len());
+        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 
         for (idx, member) in self.members.iter().enumerate() {
             match member {
                 Member::Pane(found) => {
                     if pane == found {
-                        return self.bounding_boxes.borrow()[idx];
+                        return self.bounding_boxes.lock()[idx];
                     }
                 }
                 Member::Axis(axis) => {
@@ -451,9 +523,9 @@ impl PaneAxis {
     }
 
     fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
-        debug_assert!(self.members.len() == self.bounding_boxes.borrow().len());
+        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 
-        let bounding_boxes = self.bounding_boxes.borrow();
+        let bounding_boxes = self.bounding_boxes.lock();
 
         for (idx, member) in self.members.iter().enumerate() {
             if let Some(coordinates) = bounding_boxes[idx] {
@@ -470,17 +542,16 @@ impl PaneAxis {
 
     fn render(
         &self,
-        project: &Handle<Project>,
+        project: &Model<Project>,
         basis: usize,
-        theme: &Theme,
         follower_states: &HashMap<View<Pane>, FollowerState>,
-        active_call: Option<&Handle<ActiveCall>>,
+        active_call: Option<&Model<ActiveCall>>,
         active_pane: &View<Pane>,
-        zoomed: Option<&AnyView>,
+        zoomed: Option<&AnyWeakView>,
         app_state: &Arc<AppState>,
         cx: &mut ViewContext<Workspace>,
     ) -> AnyElement<Workspace> {
-        debug_assert!(self.members.len() == self.flexes.borrow().len());
+        debug_assert!(self.members.len() == self.flexes.lock().len());
 
         todo!()
         // let mut pane_axis = PaneAxisElement::new(
@@ -546,32 +617,32 @@ impl SplitDirection {
         [Self::Up, Self::Down, Self::Left, Self::Right]
     }
 
-    pub fn edge(&self, rect: Bounds<Pixels>) -> f32 {
+    pub fn edge(&self, rect: Bounds<Pixels>) -> Pixels {
         match self {
-            Self::Up => rect.min_y(),
-            Self::Down => rect.max_y(),
-            Self::Left => rect.min_x(),
-            Self::Right => rect.max_x(),
+            Self::Up => rect.origin.y,
+            Self::Down => rect.lower_left().y,
+            Self::Left => rect.lower_left().x,
+            Self::Right => rect.lower_right().x,
         }
     }
 
     pub fn along_edge(&self, bounds: Bounds<Pixels>, length: Pixels) -> Bounds<Pixels> {
         match self {
             Self::Up => Bounds {
-                origin: bounds.origin(),
-                size: size(bounds.width(), length),
+                origin: bounds.origin,
+                size: size(bounds.size.width, length),
             },
             Self::Down => Bounds {
-                origin: size(bounds.min_x(), bounds.max_y() - length),
-                size: size(bounds.width(), length),
+                origin: point(bounds.lower_left().x, bounds.lower_left().y - length),
+                size: size(bounds.size.width, length),
             },
             Self::Left => Bounds {
-                origin: bounds.origin(),
-                size: size(length, bounds.height()),
+                origin: bounds.origin,
+                size: size(length, bounds.size.height),
             },
             Self::Right => Bounds {
-                origin: size(bounds.max_x() - length, bounds.min_y()),
-                size: size(length, bounds.height()),
+                origin: point(bounds.lower_right().x - length, bounds.lower_left().y),
+                size: size(length, bounds.size.height),
             },
         }
     }

crates/workspace2/src/persistence.rs 🔗

@@ -0,0 +1,973 @@
+#![allow(dead_code)]
+
+pub mod model;
+
+use std::path::Path;
+
+use anyhow::{anyhow, bail, Context, Result};
+use db2::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
+use gpui2::WindowBounds;
+
+use util::{unzip_option, ResultExt};
+use uuid::Uuid;
+
+use crate::{Axis, WorkspaceId};
+
+use model::{
+    GroupId, PaneId, SerializedItem, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
+    WorkspaceLocation,
+};
+
+use self::model::DockStructure;
+
+define_connection! {
+    // Current schema shape using pseudo-rust syntax:
+    //
+    // workspaces(
+    //   workspace_id: usize, // Primary key for workspaces
+    //   workspace_location: Bincode<Vec<PathBuf>>,
+    //   dock_visible: bool, // Deprecated
+    //   dock_anchor: DockAnchor, // Deprecated
+    //   dock_pane: Option<usize>, // Deprecated
+    //   left_sidebar_open: boolean,
+    //   timestamp: String, // UTC YYYY-MM-DD HH:MM:SS
+    //   window_state: String, // WindowBounds Discriminant
+    //   window_x: Option<f32>, // WindowBounds::Fixed RectF x
+    //   window_y: Option<f32>, // WindowBounds::Fixed RectF y
+    //   window_width: Option<f32>, // WindowBounds::Fixed RectF width
+    //   window_height: Option<f32>, // WindowBounds::Fixed RectF height
+    //   display: Option<Uuid>, // Display id
+    // )
+    //
+    // pane_groups(
+    //   group_id: usize, // Primary key for pane_groups
+    //   workspace_id: usize, // References workspaces table
+    //   parent_group_id: Option<usize>, // None indicates that this is the root node
+    //   position: Optiopn<usize>, // None indicates that this is the root node
+    //   axis: Option<Axis>, // 'Vertical', 'Horizontal'
+    //   flexes: Option<Vec<f32>>, // A JSON array of floats
+    // )
+    //
+    // panes(
+    //     pane_id: usize, // Primary key for panes
+    //     workspace_id: usize, // References workspaces table
+    //     active: bool,
+    // )
+    //
+    // center_panes(
+    //     pane_id: usize, // Primary key for center_panes
+    //     parent_group_id: Option<usize>, // References pane_groups. If none, this is the root
+    //     position: Option<usize>, // None indicates this is the root
+    // )
+    //
+    // CREATE TABLE items(
+    //     item_id: usize, // This is the item's view id, so this is not unique
+    //     workspace_id: usize, // References workspaces table
+    //     pane_id: usize, // References panes table
+    //     kind: String, // Indicates which view this connects to. This is the key in the item_deserializers global
+    //     position: usize, // Position of the item in the parent pane. This is equivalent to panes' position column
+    //     active: bool, // Indicates if this item is the active one in the pane
+    // )
+    pub static ref DB: WorkspaceDb<()> =
+    &[sql!(
+        CREATE TABLE workspaces(
+            workspace_id INTEGER PRIMARY KEY,
+            workspace_location BLOB UNIQUE,
+            dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
+            dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
+            dock_pane INTEGER, // Deprecated.  Preserving so users can downgrade Zed.
+            left_sidebar_open INTEGER, // Boolean
+            timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
+            FOREIGN KEY(dock_pane) REFERENCES panes(pane_id)
+        ) STRICT;
+
+        CREATE TABLE pane_groups(
+            group_id INTEGER PRIMARY KEY,
+            workspace_id INTEGER NOT NULL,
+            parent_group_id INTEGER, // NULL indicates that this is a root node
+            position INTEGER, // NULL indicates that this is a root node
+            axis TEXT NOT NULL, // Enum: 'Vertical' / 'Horizontal'
+            FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
+            ON DELETE CASCADE
+            ON UPDATE CASCADE,
+            FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
+        ) STRICT;
+
+        CREATE TABLE panes(
+            pane_id INTEGER PRIMARY KEY,
+            workspace_id INTEGER NOT NULL,
+            active INTEGER NOT NULL, // Boolean
+            FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
+            ON DELETE CASCADE
+            ON UPDATE CASCADE
+        ) STRICT;
+
+        CREATE TABLE center_panes(
+            pane_id INTEGER PRIMARY KEY,
+            parent_group_id INTEGER, // NULL means that this is a root pane
+            position INTEGER, // NULL means that this is a root pane
+            FOREIGN KEY(pane_id) REFERENCES panes(pane_id)
+            ON DELETE CASCADE,
+            FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
+        ) STRICT;
+
+        CREATE TABLE items(
+            item_id INTEGER NOT NULL, // This is the item's view id, so this is not unique
+            workspace_id INTEGER NOT NULL,
+            pane_id INTEGER NOT NULL,
+            kind TEXT NOT NULL,
+            position INTEGER NOT NULL,
+            active INTEGER NOT NULL,
+            FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
+            ON DELETE CASCADE
+            ON UPDATE CASCADE,
+            FOREIGN KEY(pane_id) REFERENCES panes(pane_id)
+            ON DELETE CASCADE,
+            PRIMARY KEY(item_id, workspace_id)
+        ) STRICT;
+    ),
+    sql!(
+        ALTER TABLE workspaces ADD COLUMN window_state TEXT;
+        ALTER TABLE workspaces ADD COLUMN window_x REAL;
+        ALTER TABLE workspaces ADD COLUMN window_y REAL;
+        ALTER TABLE workspaces ADD COLUMN window_width REAL;
+        ALTER TABLE workspaces ADD COLUMN window_height REAL;
+        ALTER TABLE workspaces ADD COLUMN display BLOB;
+    ),
+    // Drop foreign key constraint from workspaces.dock_pane to panes table.
+    sql!(
+        CREATE TABLE workspaces_2(
+            workspace_id INTEGER PRIMARY KEY,
+            workspace_location BLOB UNIQUE,
+            dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
+            dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
+            dock_pane INTEGER, // Deprecated.  Preserving so users can downgrade Zed.
+            left_sidebar_open INTEGER, // Boolean
+            timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
+            window_state TEXT,
+            window_x REAL,
+            window_y REAL,
+            window_width REAL,
+            window_height REAL,
+            display BLOB
+        ) STRICT;
+        INSERT INTO workspaces_2 SELECT * FROM workspaces;
+        DROP TABLE workspaces;
+        ALTER TABLE workspaces_2 RENAME TO workspaces;
+    ),
+    // Add panels related information
+    sql!(
+        ALTER TABLE workspaces ADD COLUMN left_dock_visible INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN left_dock_active_panel TEXT;
+        ALTER TABLE workspaces ADD COLUMN right_dock_visible INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN right_dock_active_panel TEXT;
+        ALTER TABLE workspaces ADD COLUMN bottom_dock_visible INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN bottom_dock_active_panel TEXT;
+    ),
+    // Add panel zoom persistence
+    sql!(
+        ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool
+    ),
+    // Add pane group flex data
+    sql!(
+        ALTER TABLE pane_groups ADD COLUMN flexes TEXT;
+    )
+    ];
+}
+
+impl WorkspaceDb {
+    /// Returns a serialized workspace for the given worktree_roots. If the passed array
+    /// is empty, the most recent workspace is returned instead. If no workspace for the
+    /// passed roots is stored, returns none.
+    pub fn workspace_for_roots<P: AsRef<Path>>(
+        &self,
+        worktree_roots: &[P],
+    ) -> Option<SerializedWorkspace> {
+        let workspace_location: WorkspaceLocation = worktree_roots.into();
+
+        // 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, workspace_location, bounds, display, docks): (
+            WorkspaceId,
+            WorkspaceLocation,
+            Option<WindowBounds>,
+            Option<Uuid>,
+            DockStructure,
+        ) = self
+            .select_row_bound(sql! {
+                SELECT
+                    workspace_id,
+                    workspace_location,
+                    window_state,
+                    window_x,
+                    window_y,
+                    window_width,
+                    window_height,
+                    display,
+                    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
+                FROM workspaces
+                WHERE workspace_location = ?
+            })
+            .and_then(|mut prepared_statement| (prepared_statement)(&workspace_location))
+            .context("No workspaces found")
+            .warn_on_err()
+            .flatten()?;
+
+        Some(SerializedWorkspace {
+            id: workspace_id,
+            location: workspace_location.clone(),
+            center_group: self
+                .get_center_pane_group(workspace_id)
+                .context("Getting center group")
+                .log_err()?,
+            bounds,
+            display,
+            docks,
+        })
+    }
+
+    /// Saves a workspace using the worktree roots. Will garbage collect any workspaces
+    /// that used this workspace previously
+    pub async fn save_workspace(&self, workspace: SerializedWorkspace) {
+        self.write(move |conn| {
+            conn.with_savepoint("update_worktrees", || {
+                // Clear out panes and pane_groups
+                conn.exec_bound(sql!(
+                    DELETE FROM pane_groups WHERE workspace_id = ?1;
+                    DELETE FROM panes WHERE workspace_id = ?1;))?(workspace.id)
+                .expect("Clearing old panes");
+
+                conn.exec_bound(sql!(
+                    DELETE FROM workspaces WHERE workspace_location = ? AND workspace_id != ?
+                ))?((&workspace.location, workspace.id.clone()))
+                .context("clearing out old locations")?;
+
+                // Upsert
+                conn.exec_bound(sql!(
+                    INSERT INTO workspaces(
+                        workspace_id,
+                        workspace_location,
+                        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
+                        workspace_location = ?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, &workspace.location, workspace.docks))
+                .context("Updating workspace")?;
+
+                // Save center pane group
+                Self::save_pane_group(conn, workspace.id, &workspace.center_group, None)
+                    .context("save pane group in save workspace")?;
+
+                Ok(())
+            })
+            .log_err();
+        })
+        .await;
+    }
+
+    query! {
+        pub async fn next_id() -> Result<WorkspaceId> {
+            INSERT INTO workspaces DEFAULT VALUES RETURNING workspace_id
+        }
+    }
+
+    query! {
+        fn recent_workspaces() -> Result<Vec<(WorkspaceId, WorkspaceLocation)>> {
+            SELECT workspace_id, workspace_location
+            FROM workspaces
+            WHERE workspace_location IS NOT NULL
+            ORDER BY timestamp DESC
+        }
+    }
+
+    query! {
+        async fn delete_stale_workspace(id: WorkspaceId) -> Result<()> {
+            DELETE FROM workspaces
+            WHERE workspace_id IS ?
+        }
+    }
+
+    // Returns the recent locations which are still valid on disk and deletes ones which no longer
+    // exist.
+    pub async fn recent_workspaces_on_disk(&self) -> Result<Vec<(WorkspaceId, WorkspaceLocation)>> {
+        let mut result = Vec::new();
+        let mut delete_tasks = Vec::new();
+        for (id, location) in self.recent_workspaces()? {
+            if location.paths().iter().all(|path| path.exists())
+                && location.paths().iter().any(|path| path.is_dir())
+            {
+                result.push((id, location));
+            } else {
+                delete_tasks.push(self.delete_stale_workspace(id));
+            }
+        }
+
+        futures::future::join_all(delete_tasks).await;
+        Ok(result)
+    }
+
+    pub async fn last_workspace(&self) -> Result<Option<WorkspaceLocation>> {
+        Ok(self
+            .recent_workspaces_on_disk()
+            .await?
+            .into_iter()
+            .next()
+            .map(|(_, location)| location))
+    }
+
+    fn get_center_pane_group(&self, workspace_id: WorkspaceId) -> Result<SerializedPaneGroup> {
+        Ok(self
+            .get_pane_group(workspace_id, None)?
+            .into_iter()
+            .next()
+            .unwrap_or_else(|| {
+                SerializedPaneGroup::Pane(SerializedPane {
+                    active: true,
+                    children: vec![],
+                })
+            }))
+    }
+
+    fn get_pane_group(
+        &self,
+        workspace_id: WorkspaceId,
+        group_id: Option<GroupId>,
+    ) -> Result<Vec<SerializedPaneGroup>> {
+        type GroupKey = (Option<GroupId>, WorkspaceId);
+        type GroupOrPane = (
+            Option<GroupId>,
+            Option<Axis>,
+            Option<PaneId>,
+            Option<bool>,
+            Option<String>,
+        );
+        self.select_bound::<GroupKey, GroupOrPane>(sql!(
+            SELECT group_id, axis, pane_id, active, flexes
+                FROM (SELECT
+                        group_id,
+                        axis,
+                        NULL as pane_id,
+                        NULL as active,
+                        position,
+                        parent_group_id,
+                        workspace_id,
+                        flexes
+                      FROM pane_groups
+                    UNION
+                      SELECT
+                        NULL,
+                        NULL,
+                        center_panes.pane_id,
+                        panes.active as active,
+                        position,
+                        parent_group_id,
+                        panes.workspace_id as workspace_id,
+                        NULL
+                      FROM center_panes
+                      JOIN panes ON center_panes.pane_id = panes.pane_id)
+                WHERE parent_group_id IS ? AND workspace_id = ?
+                ORDER BY position
+        ))?((group_id, workspace_id))?
+        .into_iter()
+        .map(|(group_id, axis, pane_id, active, flexes)| {
+            if let Some((group_id, axis)) = group_id.zip(axis) {
+                let flexes = flexes
+                    .map(|flexes| serde_json::from_str::<Vec<f32>>(&flexes))
+                    .transpose()?;
+
+                Ok(SerializedPaneGroup::Group {
+                    axis,
+                    children: self.get_pane_group(workspace_id, Some(group_id))?,
+                    flexes,
+                })
+            } else if let Some((pane_id, active)) = pane_id.zip(active) {
+                Ok(SerializedPaneGroup::Pane(SerializedPane::new(
+                    self.get_items(pane_id)?,
+                    active,
+                )))
+            } else {
+                bail!("Pane Group Child was neither a pane group or a pane");
+            }
+        })
+        // Filter out panes and pane groups which don't have any children or items
+        .filter(|pane_group| match pane_group {
+            Ok(SerializedPaneGroup::Group { children, .. }) => !children.is_empty(),
+            Ok(SerializedPaneGroup::Pane(pane)) => !pane.children.is_empty(),
+            _ => true,
+        })
+        .collect::<Result<_>>()
+    }
+
+    fn save_pane_group(
+        conn: &Connection,
+        workspace_id: WorkspaceId,
+        pane_group: &SerializedPaneGroup,
+        parent: Option<(GroupId, usize)>,
+    ) -> Result<()> {
+        match pane_group {
+            SerializedPaneGroup::Group {
+                axis,
+                children,
+                flexes,
+            } => {
+                let (parent_id, position) = unzip_option(parent);
+
+                let flex_string = flexes
+                    .as_ref()
+                    .map(|flexes| serde_json::json!(flexes).to_string());
+
+                let group_id = conn.select_row_bound::<_, i64>(sql!(
+                    INSERT INTO pane_groups(
+                        workspace_id,
+                        parent_group_id,
+                        position,
+                        axis,
+                        flexes
+                    )
+                    VALUES (?, ?, ?, ?, ?)
+                    RETURNING group_id
+                ))?((
+                    workspace_id,
+                    parent_id,
+                    position,
+                    *axis,
+                    flex_string,
+                ))?
+                .ok_or_else(|| anyhow!("Couldn't retrieve group_id from inserted pane_group"))?;
+
+                for (position, group) in children.iter().enumerate() {
+                    Self::save_pane_group(conn, workspace_id, group, Some((group_id, position)))?
+                }
+
+                Ok(())
+            }
+            SerializedPaneGroup::Pane(pane) => {
+                Self::save_pane(conn, workspace_id, &pane, parent)?;
+                Ok(())
+            }
+        }
+    }
+
+    fn save_pane(
+        conn: &Connection,
+        workspace_id: WorkspaceId,
+        pane: &SerializedPane,
+        parent: Option<(GroupId, usize)>,
+    ) -> Result<PaneId> {
+        let pane_id = conn.select_row_bound::<_, i64>(sql!(
+            INSERT INTO panes(workspace_id, active)
+            VALUES (?, ?)
+            RETURNING pane_id
+        ))?((workspace_id, pane.active))?
+        .ok_or_else(|| anyhow!("Could not retrieve inserted pane_id"))?;
+
+        let (parent_id, order) = unzip_option(parent);
+        conn.exec_bound(sql!(
+            INSERT INTO center_panes(pane_id, parent_group_id, position)
+            VALUES (?, ?, ?)
+        ))?((pane_id, parent_id, order))?;
+
+        Self::save_items(conn, workspace_id, pane_id, &pane.children).context("Saving items")?;
+
+        Ok(pane_id)
+    }
+
+    fn get_items(&self, pane_id: PaneId) -> Result<Vec<SerializedItem>> {
+        Ok(self.select_bound(sql!(
+            SELECT kind, item_id, active FROM items
+            WHERE pane_id = ?
+                ORDER BY position
+        ))?(pane_id)?)
+    }
+
+    fn save_items(
+        conn: &Connection,
+        workspace_id: WorkspaceId,
+        pane_id: PaneId,
+        items: &[SerializedItem],
+    ) -> Result<()> {
+        let mut insert = conn.exec_bound(sql!(
+            INSERT INTO items(workspace_id, pane_id, position, kind, item_id, active) VALUES (?, ?, ?, ?, ?, ?)
+        )).context("Preparing insertion")?;
+        for (position, item) in items.iter().enumerate() {
+            insert((workspace_id, pane_id, position, item))?;
+        }
+
+        Ok(())
+    }
+
+    query! {
+        pub async fn update_timestamp(workspace_id: WorkspaceId) -> Result<()> {
+            UPDATE workspaces
+            SET timestamp = CURRENT_TIMESTAMP
+            WHERE workspace_id = ?
+        }
+    }
+
+    query! {
+        pub async fn set_window_bounds(workspace_id: WorkspaceId, bounds: WindowBounds, display: Uuid) -> Result<()> {
+            UPDATE workspaces
+            SET window_state = ?2,
+                window_x = ?3,
+                window_y = ?4,
+                window_width = ?5,
+                window_height = ?6,
+                display = ?7
+            WHERE workspace_id = ?1
+        }
+    }
+}
+
+// todo!()
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use db::open_test_db;
+
+//     #[gpui::test]
+//     async fn test_next_id_stability() {
+//         env_logger::try_init().ok();
+
+//         let db = WorkspaceDb(open_test_db("test_next_id_stability").await);
+
+//         db.write(|conn| {
+//             conn.migrate(
+//                 "test_table",
+//                 &[sql!(
+//                     CREATE TABLE test_table(
+//                         text TEXT,
+//                         workspace_id INTEGER,
+//                         FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
+//                         ON DELETE CASCADE
+//                     ) STRICT;
+//                 )],
+//             )
+//             .unwrap();
+//         })
+//         .await;
+
+//         let id = db.next_id().await.unwrap();
+//         // Assert the empty row got inserted
+//         assert_eq!(
+//             Some(id),
+//             db.select_row_bound::<WorkspaceId, WorkspaceId>(sql!(
+//                 SELECT workspace_id FROM workspaces WHERE workspace_id = ?
+//             ))
+//             .unwrap()(id)
+//             .unwrap()
+//         );
+
+//         db.write(move |conn| {
+//             conn.exec_bound(sql!(INSERT INTO test_table(text, workspace_id) VALUES (?, ?)))
+//                 .unwrap()(("test-text-1", id))
+//             .unwrap()
+//         })
+//         .await;
+
+//         let test_text_1 = db
+//             .select_row_bound::<_, String>(sql!(SELECT text FROM test_table WHERE workspace_id = ?))
+//             .unwrap()(1)
+//         .unwrap()
+//         .unwrap();
+//         assert_eq!(test_text_1, "test-text-1");
+//     }
+
+//     #[gpui::test]
+//     async fn test_workspace_id_stability() {
+//         env_logger::try_init().ok();
+
+//         let db = WorkspaceDb(open_test_db("test_workspace_id_stability").await);
+
+//         db.write(|conn| {
+//             conn.migrate(
+//                 "test_table",
+//                 &[sql!(
+//                     CREATE TABLE test_table(
+//                         text TEXT,
+//                         workspace_id INTEGER,
+//                         FOREIGN KEY(workspace_id)
+//                             REFERENCES workspaces(workspace_id)
+//                         ON DELETE CASCADE
+//                     ) STRICT;)],
+//             )
+//         })
+//         .await
+//         .unwrap();
+
+//         let mut workspace_1 = SerializedWorkspace {
+//             id: 1,
+//             location: (["/tmp", "/tmp2"]).into(),
+//             center_group: Default::default(),
+//             bounds: Default::default(),
+//             display: Default::default(),
+//             docks: Default::default(),
+//         };
+
+//         let workspace_2 = SerializedWorkspace {
+//             id: 2,
+//             location: (["/tmp"]).into(),
+//             center_group: Default::default(),
+//             bounds: Default::default(),
+//             display: Default::default(),
+//             docks: Default::default(),
+//         };
+
+//         db.save_workspace(workspace_1.clone()).await;
+
+//         db.write(|conn| {
+//             conn.exec_bound(sql!(INSERT INTO test_table(text, workspace_id) VALUES (?, ?)))
+//                 .unwrap()(("test-text-1", 1))
+//             .unwrap();
+//         })
+//         .await;
+
+//         db.save_workspace(workspace_2.clone()).await;
+
+//         db.write(|conn| {
+//             conn.exec_bound(sql!(INSERT INTO test_table(text, workspace_id) VALUES (?, ?)))
+//                 .unwrap()(("test-text-2", 2))
+//             .unwrap();
+//         })
+//         .await;
+
+//         workspace_1.location = (["/tmp", "/tmp3"]).into();
+//         db.save_workspace(workspace_1.clone()).await;
+//         db.save_workspace(workspace_1).await;
+//         db.save_workspace(workspace_2).await;
+
+//         let test_text_2 = db
+//             .select_row_bound::<_, String>(sql!(SELECT text FROM test_table WHERE workspace_id = ?))
+//             .unwrap()(2)
+//         .unwrap()
+//         .unwrap();
+//         assert_eq!(test_text_2, "test-text-2");
+
+//         let test_text_1 = db
+//             .select_row_bound::<_, String>(sql!(SELECT text FROM test_table WHERE workspace_id = ?))
+//             .unwrap()(1)
+//         .unwrap()
+//         .unwrap();
+//         assert_eq!(test_text_1, "test-text-1");
+//     }
+
+//     fn group(axis: gpui::Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup {
+//         SerializedPaneGroup::Group {
+//             axis,
+//             flexes: None,
+//             children,
+//         }
+//     }
+
+//     #[gpui::test]
+//     async fn test_full_workspace_serialization() {
+//         env_logger::try_init().ok();
+
+//         let db = WorkspaceDb(open_test_db("test_full_workspace_serialization").await);
+
+//         //  -----------------
+//         //  | 1,2   | 5,6   |
+//         //  | - - - |       |
+//         //  | 3,4   |       |
+//         //  -----------------
+//         let center_group = group(
+//             gpui::Axis::Horizontal,
+//             vec![
+//                 group(
+//                     gpui::Axis::Vertical,
+//                     vec![
+//                         SerializedPaneGroup::Pane(SerializedPane::new(
+//                             vec![
+//                                 SerializedItem::new("Terminal", 5, false),
+//                                 SerializedItem::new("Terminal", 6, true),
+//                             ],
+//                             false,
+//                         )),
+//                         SerializedPaneGroup::Pane(SerializedPane::new(
+//                             vec![
+//                                 SerializedItem::new("Terminal", 7, true),
+//                                 SerializedItem::new("Terminal", 8, false),
+//                             ],
+//                             false,
+//                         )),
+//                     ],
+//                 ),
+//                 SerializedPaneGroup::Pane(SerializedPane::new(
+//                     vec![
+//                         SerializedItem::new("Terminal", 9, false),
+//                         SerializedItem::new("Terminal", 10, true),
+//                     ],
+//                     false,
+//                 )),
+//             ],
+//         );
+
+//         let workspace = SerializedWorkspace {
+//             id: 5,
+//             location: (["/tmp", "/tmp2"]).into(),
+//             center_group,
+//             bounds: Default::default(),
+//             display: Default::default(),
+//             docks: Default::default(),
+//         };
+
+//         db.save_workspace(workspace.clone()).await;
+//         let round_trip_workspace = db.workspace_for_roots(&["/tmp2", "/tmp"]);
+
+//         assert_eq!(workspace, round_trip_workspace.unwrap());
+
+//         // Test guaranteed duplicate IDs
+//         db.save_workspace(workspace.clone()).await;
+//         db.save_workspace(workspace.clone()).await;
+
+//         let round_trip_workspace = db.workspace_for_roots(&["/tmp", "/tmp2"]);
+//         assert_eq!(workspace, round_trip_workspace.unwrap());
+//     }
+
+//     #[gpui::test]
+//     async fn test_workspace_assignment() {
+//         env_logger::try_init().ok();
+
+//         let db = WorkspaceDb(open_test_db("test_basic_functionality").await);
+
+//         let workspace_1 = SerializedWorkspace {
+//             id: 1,
+//             location: (["/tmp", "/tmp2"]).into(),
+//             center_group: Default::default(),
+//             bounds: Default::default(),
+//             display: Default::default(),
+//             docks: Default::default(),
+//         };
+
+//         let mut workspace_2 = SerializedWorkspace {
+//             id: 2,
+//             location: (["/tmp"]).into(),
+//             center_group: Default::default(),
+//             bounds: Default::default(),
+//             display: Default::default(),
+//             docks: Default::default(),
+//         };
+
+//         db.save_workspace(workspace_1.clone()).await;
+//         db.save_workspace(workspace_2.clone()).await;
+
+//         // Test that paths are treated as a set
+//         assert_eq!(
+//             db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
+//             workspace_1
+//         );
+//         assert_eq!(
+//             db.workspace_for_roots(&["/tmp2", "/tmp"]).unwrap(),
+//             workspace_1
+//         );
+
+//         // Make sure that other keys work
+//         assert_eq!(db.workspace_for_roots(&["/tmp"]).unwrap(), workspace_2);
+//         assert_eq!(db.workspace_for_roots(&["/tmp3", "/tmp2", "/tmp4"]), None);
+
+//         // Test 'mutate' case of updating a pre-existing id
+//         workspace_2.location = (["/tmp", "/tmp2"]).into();
+
+//         db.save_workspace(workspace_2.clone()).await;
+//         assert_eq!(
+//             db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
+//             workspace_2
+//         );
+
+//         // Test other mechanism for mutating
+//         let mut workspace_3 = SerializedWorkspace {
+//             id: 3,
+//             location: (&["/tmp", "/tmp2"]).into(),
+//             center_group: Default::default(),
+//             bounds: Default::default(),
+//             display: Default::default(),
+//             docks: Default::default(),
+//         };
+
+//         db.save_workspace(workspace_3.clone()).await;
+//         assert_eq!(
+//             db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
+//             workspace_3
+//         );
+
+//         // Make sure that updating paths differently also works
+//         workspace_3.location = (["/tmp3", "/tmp4", "/tmp2"]).into();
+//         db.save_workspace(workspace_3.clone()).await;
+//         assert_eq!(db.workspace_for_roots(&["/tmp2", "tmp"]), None);
+//         assert_eq!(
+//             db.workspace_for_roots(&["/tmp2", "/tmp3", "/tmp4"])
+//                 .unwrap(),
+//             workspace_3
+//         );
+//     }
+
+//     use crate::persistence::model::SerializedWorkspace;
+//     use crate::persistence::model::{SerializedItem, SerializedPane, SerializedPaneGroup};
+
+//     fn default_workspace<P: AsRef<Path>>(
+//         workspace_id: &[P],
+//         center_group: &SerializedPaneGroup,
+//     ) -> SerializedWorkspace {
+//         SerializedWorkspace {
+//             id: 4,
+//             location: workspace_id.into(),
+//             center_group: center_group.clone(),
+//             bounds: Default::default(),
+//             display: Default::default(),
+//             docks: Default::default(),
+//         }
+//     }
+
+//     #[gpui::test]
+//     async fn test_simple_split() {
+//         env_logger::try_init().ok();
+
+//         let db = WorkspaceDb(open_test_db("simple_split").await);
+
+//         //  -----------------
+//         //  | 1,2   | 5,6   |
+//         //  | - - - |       |
+//         //  | 3,4   |       |
+//         //  -----------------
+//         let center_pane = group(
+//             gpui::Axis::Horizontal,
+//             vec![
+//                 group(
+//                     gpui::Axis::Vertical,
+//                     vec![
+//                         SerializedPaneGroup::Pane(SerializedPane::new(
+//                             vec![
+//                                 SerializedItem::new("Terminal", 1, false),
+//                                 SerializedItem::new("Terminal", 2, true),
+//                             ],
+//                             false,
+//                         )),
+//                         SerializedPaneGroup::Pane(SerializedPane::new(
+//                             vec![
+//                                 SerializedItem::new("Terminal", 4, false),
+//                                 SerializedItem::new("Terminal", 3, true),
+//                             ],
+//                             true,
+//                         )),
+//                     ],
+//                 ),
+//                 SerializedPaneGroup::Pane(SerializedPane::new(
+//                     vec![
+//                         SerializedItem::new("Terminal", 5, true),
+//                         SerializedItem::new("Terminal", 6, false),
+//                     ],
+//                     false,
+//                 )),
+//             ],
+//         );
+
+//         let workspace = default_workspace(&["/tmp"], &center_pane);
+
+//         db.save_workspace(workspace.clone()).await;
+
+//         let new_workspace = db.workspace_for_roots(&["/tmp"]).unwrap();
+
+//         assert_eq!(workspace.center_group, new_workspace.center_group);
+//     }
+
+//     #[gpui::test]
+//     async fn test_cleanup_panes() {
+//         env_logger::try_init().ok();
+
+//         let db = WorkspaceDb(open_test_db("test_cleanup_panes").await);
+
+//         let center_pane = group(
+//             gpui::Axis::Horizontal,
+//             vec![
+//                 group(
+//                     gpui::Axis::Vertical,
+//                     vec![
+//                         SerializedPaneGroup::Pane(SerializedPane::new(
+//                             vec![
+//                                 SerializedItem::new("Terminal", 1, false),
+//                                 SerializedItem::new("Terminal", 2, true),
+//                             ],
+//                             false,
+//                         )),
+//                         SerializedPaneGroup::Pane(SerializedPane::new(
+//                             vec![
+//                                 SerializedItem::new("Terminal", 4, false),
+//                                 SerializedItem::new("Terminal", 3, true),
+//                             ],
+//                             true,
+//                         )),
+//                     ],
+//                 ),
+//                 SerializedPaneGroup::Pane(SerializedPane::new(
+//                     vec![
+//                         SerializedItem::new("Terminal", 5, false),
+//                         SerializedItem::new("Terminal", 6, true),
+//                     ],
+//                     false,
+//                 )),
+//             ],
+//         );
+
+//         let id = &["/tmp"];
+
+//         let mut workspace = default_workspace(id, &center_pane);
+
+//         db.save_workspace(workspace.clone()).await;
+
+//         workspace.center_group = group(
+//             gpui::Axis::Vertical,
+//             vec![
+//                 SerializedPaneGroup::Pane(SerializedPane::new(
+//                     vec![
+//                         SerializedItem::new("Terminal", 1, false),
+//                         SerializedItem::new("Terminal", 2, true),
+//                     ],
+//                     false,
+//                 )),
+//                 SerializedPaneGroup::Pane(SerializedPane::new(
+//                     vec![
+//                         SerializedItem::new("Terminal", 4, true),
+//                         SerializedItem::new("Terminal", 3, false),
+//                     ],
+//                     true,
+//                 )),
+//             ],
+//         );
+
+//         db.save_workspace(workspace.clone()).await;
+
+//         let new_workspace = db.workspace_for_roots(id).unwrap();
+
+//         assert_eq!(workspace.center_group, new_workspace.center_group);
+//     }
+// }

crates/workspace2/src/persistence/model.rs 🔗

@@ -7,7 +7,7 @@ use db2::sqlez::{
     bindable::{Bind, Column, StaticColumnCount},
     statement::Statement,
 };
-use gpui2::{AsyncAppContext, Handle, Task, View, WeakView, WindowBounds};
+use gpui2::{AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
 use project2::Project;
 use std::{
     path::{Path, PathBuf},
@@ -55,7 +55,7 @@ impl Column for WorkspaceLocation {
     }
 }
 
-#[derive(Debug, PartialEq, Clone)]
+#[derive(PartialEq, Clone)]
 pub struct SerializedWorkspace {
     pub id: WorkspaceId,
     pub location: WorkspaceLocation,
@@ -127,7 +127,7 @@ impl Bind for DockData {
     }
 }
 
-#[derive(Debug, PartialEq, Clone)]
+#[derive(PartialEq, Clone)]
 pub enum SerializedPaneGroup {
     Group {
         axis: Axis,
@@ -151,10 +151,10 @@ impl SerializedPaneGroup {
     #[async_recursion(?Send)]
     pub(crate) async fn deserialize(
         self,
-        project: &Handle<Project>,
+        project: &Model<Project>,
         workspace_id: WorkspaceId,
-        workspace: &WeakView<Workspace>,
-        cx: &mut AsyncAppContext,
+        workspace: WeakView<Workspace>,
+        cx: &mut AsyncWindowContext,
     ) -> Option<(Member, Option<View<Pane>>, Vec<Option<Box<dyn ItemHandle>>>)> {
         match self {
             SerializedPaneGroup::Group {
@@ -167,7 +167,7 @@ impl SerializedPaneGroup {
                 let mut items = Vec::new();
                 for child in children {
                     if let Some((new_member, active_pane, new_items)) = child
-                        .deserialize(project, workspace_id, workspace, cx)
+                        .deserialize(project, workspace_id, workspace.clone(), cx)
                         .await
                     {
                         members.push(new_member);
@@ -196,14 +196,11 @@ impl SerializedPaneGroup {
                     .log_err()?;
                 let active = serialized_pane.active;
                 let new_items = serialized_pane
-                    .deserialize_to(project, &pane, workspace_id, workspace, cx)
+                    .deserialize_to(project, &pane, workspace_id, workspace.clone(), cx)
                     .await
                     .log_err()?;
 
-                if pane
-                    .read_with(cx, |pane, _| pane.items_len() != 0)
-                    .log_err()?
-                {
+                if pane.update(cx, |pane, _| pane.items_len() != 0).log_err()? {
                     let pane = pane.upgrade()?;
                     Some((Member::Pane(pane.clone()), active.then(|| pane), new_items))
                 } else {
@@ -231,11 +228,11 @@ impl SerializedPane {
 
     pub async fn deserialize_to(
         &self,
-        project: &Handle<Project>,
+        project: &Model<Project>,
         pane: &WeakView<Pane>,
         workspace_id: WorkspaceId,
-        workspace: &WeakView<Workspace>,
-        cx: &mut AsyncAppContext,
+        workspace: WeakView<Workspace>,
+        cx: &mut AsyncWindowContext,
     ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
         let mut items = Vec::new();
         let mut active_item_index = None;
@@ -289,15 +286,15 @@ pub struct SerializedItem {
     pub active: bool,
 }
 
-impl SerializedItem {
-    pub fn new(kind: impl AsRef<str>, item_id: ItemId, active: bool) -> Self {
-        Self {
-            kind: Arc::from(kind.as_ref()),
-            item_id,
-            active,
-        }
-    }
-}
+// impl SerializedItem {
+//     pub fn new(kind: impl AsRef<str>, item_id: ItemId, active: bool) -> Self {
+//         Self {
+//             kind: Arc::from(kind.as_ref()),
+//             item_id,
+//             active,
+//         }
+//     }
+// }
 
 #[cfg(test)]
 impl Default for SerializedItem {

crates/workspace2/src/searchable.rs 🔗

@@ -0,0 +1,285 @@
+use std::{any::Any, sync::Arc};
+
+use gpui2::{AnyView, AppContext, Subscription, Task, View, ViewContext, WindowContext};
+use project2::search::SearchQuery;
+
+use crate::{
+    item::{Item, WeakItemHandle},
+    ItemHandle,
+};
+
+#[derive(Debug)]
+pub enum SearchEvent {
+    MatchesInvalidated,
+    ActiveMatchChanged,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum Direction {
+    Prev,
+    Next,
+}
+
+#[derive(Clone, Copy, Debug, Default)]
+pub struct SearchOptions {
+    pub case: bool,
+    pub word: bool,
+    pub regex: bool,
+    /// Specifies whether the item supports search & replace.
+    pub replacement: bool,
+}
+
+pub trait SearchableItem: Item {
+    type Match: Any + Sync + Send + Clone;
+
+    fn supported_options() -> SearchOptions {
+        SearchOptions {
+            case: true,
+            word: true,
+            regex: true,
+            replacement: true,
+        }
+    }
+    fn to_search_event(
+        &mut self,
+        event: &Self::Event,
+        cx: &mut ViewContext<Self>,
+    ) -> Option<SearchEvent>;
+    fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
+    fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
+    fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
+    fn activate_match(
+        &mut self,
+        index: usize,
+        matches: Vec<Self::Match>,
+        cx: &mut ViewContext<Self>,
+    );
+    fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
+    fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext<Self>);
+    fn match_index_for_direction(
+        &mut self,
+        matches: &Vec<Self::Match>,
+        current_index: usize,
+        direction: Direction,
+        count: usize,
+        _: &mut ViewContext<Self>,
+    ) -> usize {
+        match direction {
+            Direction::Prev => {
+                let count = count % matches.len();
+                if current_index >= count {
+                    current_index - count
+                } else {
+                    matches.len() - (count - current_index)
+                }
+            }
+            Direction::Next => (current_index + count) % matches.len(),
+        }
+    }
+    fn find_matches(
+        &mut self,
+        query: Arc<SearchQuery>,
+        cx: &mut ViewContext<Self>,
+    ) -> Task<Vec<Self::Match>>;
+    fn active_match_index(
+        &mut self,
+        matches: Vec<Self::Match>,
+        cx: &mut ViewContext<Self>,
+    ) -> Option<usize>;
+}
+
+pub trait SearchableItemHandle: ItemHandle {
+    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
+    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
+    fn supported_options(&self) -> SearchOptions;
+    fn subscribe_to_search_events(
+        &self,
+        cx: &mut WindowContext,
+        handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>,
+    ) -> Subscription;
+    fn clear_matches(&self, cx: &mut WindowContext);
+    fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
+    fn query_suggestion(&self, cx: &mut WindowContext) -> String;
+    fn activate_match(
+        &self,
+        index: usize,
+        matches: &Vec<Box<dyn Any + Send>>,
+        cx: &mut WindowContext,
+    );
+    fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
+    fn replace(&self, _: &Box<dyn Any + Send>, _: &SearchQuery, _: &mut WindowContext);
+    fn match_index_for_direction(
+        &self,
+        matches: &Vec<Box<dyn Any + Send>>,
+        current_index: usize,
+        direction: Direction,
+        count: usize,
+        cx: &mut WindowContext,
+    ) -> usize;
+    fn find_matches(
+        &self,
+        query: Arc<SearchQuery>,
+        cx: &mut WindowContext,
+    ) -> Task<Vec<Box<dyn Any + Send>>>;
+    fn active_match_index(
+        &self,
+        matches: &Vec<Box<dyn Any + Send>>,
+        cx: &mut WindowContext,
+    ) -> Option<usize>;
+}
+
+// todo!("here is where we need to use AnyWeakView");
+impl<T: SearchableItem> SearchableItemHandle for View<T> {
+    fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
+        // Box::new(self.downgrade())
+        todo!()
+    }
+
+    fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
+        Box::new(self.clone())
+    }
+
+    fn supported_options(&self) -> SearchOptions {
+        T::supported_options()
+    }
+
+    fn subscribe_to_search_events(
+        &self,
+        cx: &mut WindowContext,
+        handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>,
+    ) -> Subscription {
+        cx.subscribe(self, move |handle, event, cx| {
+            let search_event = handle.update(cx, |handle, cx| handle.to_search_event(event, cx));
+            if let Some(search_event) = search_event {
+                handler(search_event, cx)
+            }
+        })
+    }
+
+    fn clear_matches(&self, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.clear_matches(cx));
+    }
+    fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
+        let matches = downcast_matches(matches);
+        self.update(cx, |this, cx| this.update_matches(matches, cx));
+    }
+    fn query_suggestion(&self, cx: &mut WindowContext) -> String {
+        self.update(cx, |this, cx| this.query_suggestion(cx))
+    }
+    fn activate_match(
+        &self,
+        index: usize,
+        matches: &Vec<Box<dyn Any + Send>>,
+        cx: &mut WindowContext,
+    ) {
+        let matches = downcast_matches(matches);
+        self.update(cx, |this, cx| this.activate_match(index, matches, cx));
+    }
+
+    fn select_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
+        let matches = downcast_matches(matches);
+        self.update(cx, |this, cx| this.select_matches(matches, cx));
+    }
+
+    fn match_index_for_direction(
+        &self,
+        matches: &Vec<Box<dyn Any + Send>>,
+        current_index: usize,
+        direction: Direction,
+        count: usize,
+        cx: &mut WindowContext,
+    ) -> usize {
+        let matches = downcast_matches(matches);
+        self.update(cx, |this, cx| {
+            this.match_index_for_direction(&matches, current_index, direction, count, cx)
+        })
+    }
+    fn find_matches(
+        &self,
+        query: Arc<SearchQuery>,
+        cx: &mut WindowContext,
+    ) -> Task<Vec<Box<dyn Any + Send>>> {
+        let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
+        cx.spawn(|cx| async {
+            let matches = matches.await;
+            matches
+                .into_iter()
+                .map::<Box<dyn Any + Send>, _>(|range| Box::new(range))
+                .collect()
+        })
+    }
+    fn active_match_index(
+        &self,
+        matches: &Vec<Box<dyn Any + Send>>,
+        cx: &mut WindowContext,
+    ) -> Option<usize> {
+        let matches = downcast_matches(matches);
+        self.update(cx, |this, cx| this.active_match_index(matches, cx))
+    }
+
+    fn replace(&self, matches: &Box<dyn Any + Send>, query: &SearchQuery, cx: &mut WindowContext) {
+        let matches = matches.downcast_ref().unwrap();
+        self.update(cx, |this, cx| this.replace(matches, query, cx))
+    }
+}
+
+fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T> {
+    matches
+        .iter()
+        .map(|range| range.downcast_ref::<T>().cloned())
+        .collect::<Option<Vec<_>>>()
+        .expect(
+            "SearchableItemHandle function called with vec of matches of a different type than expected",
+        )
+}
+
+impl From<Box<dyn SearchableItemHandle>> for AnyView {
+    fn from(this: Box<dyn SearchableItemHandle>) -> Self {
+        this.to_any().clone()
+    }
+}
+
+impl From<&Box<dyn SearchableItemHandle>> for AnyView {
+    fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
+        this.to_any().clone()
+    }
+}
+
+impl PartialEq for Box<dyn SearchableItemHandle> {
+    fn eq(&self, other: &Self) -> bool {
+        self.id() == other.id()
+    }
+}
+
+impl Eq for Box<dyn SearchableItemHandle> {}
+
+pub trait WeakSearchableItemHandle: WeakItemHandle {
+    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
+
+    // fn into_any(self) -> AnyWeakView;
+}
+
+// todo!()
+// impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
+//     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
+//         Some(Box::new(self.upgrade(cx)?))
+//     }
+
+//     // fn into_any(self) -> AnyView {
+//     //     self.into_any()
+//     // }
+// }
+
+impl PartialEq for Box<dyn WeakSearchableItemHandle> {
+    fn eq(&self, other: &Self) -> bool {
+        self.id() == other.id()
+    }
+}
+
+impl Eq for Box<dyn WeakSearchableItemHandle> {}
+
+impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.id().hash(state)
+    }
+}

crates/workspace2/src/shared_screen.rs 🔗

@@ -0,0 +1,151 @@
+use crate::{
+    item::{Item, ItemEvent},
+    ItemNavHistory, WorkspaceId,
+};
+use anyhow::Result;
+use call::participant::{Frame, RemoteVideoTrack};
+use client::{proto::PeerId, User};
+use futures::StreamExt;
+use gpui::{
+    elements::*,
+    geometry::{rect::RectF, vector::vec2f},
+    platform::MouseButton,
+    AppContext, Entity, Task, View, ViewContext,
+};
+use smallvec::SmallVec;
+use std::{
+    borrow::Cow,
+    sync::{Arc, Weak},
+};
+
+pub enum Event {
+    Close,
+}
+
+pub struct SharedScreen {
+    track: Weak<RemoteVideoTrack>,
+    frame: Option<Frame>,
+    pub peer_id: PeerId,
+    user: Arc<User>,
+    nav_history: Option<ItemNavHistory>,
+    _maintain_frame: Task<Result<()>>,
+}
+
+impl SharedScreen {
+    pub fn new(
+        track: &Arc<RemoteVideoTrack>,
+        peer_id: PeerId,
+        user: Arc<User>,
+        cx: &mut ViewContext<Self>,
+    ) -> Self {
+        let mut frames = track.frames();
+        Self {
+            track: Arc::downgrade(track),
+            frame: None,
+            peer_id,
+            user,
+            nav_history: Default::default(),
+            _maintain_frame: cx.spawn(|this, mut cx| async move {
+                while let Some(frame) = frames.next().await {
+                    this.update(&mut cx, |this, cx| {
+                        this.frame = Some(frame);
+                        cx.notify();
+                    })?;
+                }
+                this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
+                Ok(())
+            }),
+        }
+    }
+}
+
+impl Entity for SharedScreen {
+    type Event = Event;
+}
+
+impl View for SharedScreen {
+    fn ui_name() -> &'static str {
+        "SharedScreen"
+    }
+
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+        enum Focus {}
+
+        let frame = self.frame.clone();
+        MouseEventHandler::new::<Focus, _>(0, cx, |_, cx| {
+            Canvas::new(move |bounds, _, _, cx| {
+                if let Some(frame) = frame.clone() {
+                    let size = constrain_size_preserving_aspect_ratio(
+                        bounds.size(),
+                        vec2f(frame.width() as f32, frame.height() as f32),
+                    );
+                    let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
+                    cx.scene().push_surface(gpui::platform::mac::Surface {
+                        bounds: RectF::new(origin, size),
+                        image_buffer: frame.image(),
+                    });
+                }
+            })
+            .contained()
+            .with_style(theme::current(cx).shared_screen)
+        })
+        .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent())
+        .into_any()
+    }
+}
+
+impl Item for SharedScreen {
+    fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
+        Some(format!("{}'s screen", self.user.github_login).into())
+    }
+    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+        if let Some(nav_history) = self.nav_history.as_mut() {
+            nav_history.push::<()>(None, cx);
+        }
+    }
+
+    fn tab_content<V: 'static>(
+        &self,
+        _: Option<usize>,
+        style: &theme::Tab,
+        _: &AppContext,
+    ) -> gpui::AnyElement<V> {
+        Flex::row()
+            .with_child(
+                Svg::new("icons/desktop.svg")
+                    .with_color(style.label.text.color)
+                    .constrained()
+                    .with_width(style.type_icon_width)
+                    .aligned()
+                    .contained()
+                    .with_margin_right(style.spacing),
+            )
+            .with_child(
+                Label::new(
+                    format!("{}'s screen", self.user.github_login),
+                    style.label.clone(),
+                )
+                .aligned(),
+            )
+            .into_any()
+    }
+
+    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
+        self.nav_history = Some(history);
+    }
+
+    fn clone_on_split(
+        &self,
+        _workspace_id: WorkspaceId,
+        cx: &mut ViewContext<Self>,
+    ) -> Option<Self> {
+        let track = self.track.upgrade()?;
+        Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
+    }
+
+    fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
+        match event {
+            Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
+        }
+    }
+}

crates/workspace2/src/status_bar.rs 🔗

@@ -0,0 +1,301 @@
+use std::any::TypeId;
+
+use crate::{ItemHandle, Pane};
+use gpui2::{
+    div, AnyView, Component, Div, ParentElement, Render, Styled, Subscription, View, ViewContext,
+    WindowContext,
+};
+use theme2::ActiveTheme;
+use util::ResultExt;
+
+pub trait StatusItemView: Render {
+    fn set_active_pane_item(
+        &mut self,
+        active_pane_item: Option<&dyn crate::ItemHandle>,
+        cx: &mut ViewContext<Self>,
+    );
+}
+
+trait StatusItemViewHandle: Send {
+    fn to_any(&self) -> AnyView;
+    fn set_active_pane_item(
+        &self,
+        active_pane_item: Option<&dyn ItemHandle>,
+        cx: &mut WindowContext,
+    );
+    fn item_type(&self) -> TypeId;
+}
+
+pub struct StatusBar {
+    left_items: Vec<Box<dyn StatusItemViewHandle>>,
+    right_items: Vec<Box<dyn StatusItemViewHandle>>,
+    active_pane: View<Pane>,
+    _observe_active_pane: Subscription,
+}
+
+impl Render for StatusBar {
+    type Element = Div<Self>;
+
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+        div()
+            .py_0p5()
+            .px_1()
+            .flex()
+            .items_center()
+            .justify_between()
+            .w_full()
+            .bg(cx.theme().colors().status_bar)
+            .child(self.render_left_tools(cx))
+            .child(self.render_right_tools(cx))
+    }
+}
+
+impl StatusBar {
+    fn render_left_tools(&self, cx: &mut ViewContext<Self>) -> impl Component<Self> {
+        div()
+            .flex()
+            .items_center()
+            .gap_1()
+            .children(self.left_items.iter().map(|item| item.to_any()))
+    }
+
+    fn render_right_tools(&self, cx: &mut ViewContext<Self>) -> impl Component<Self> {
+        div()
+            .flex()
+            .items_center()
+            .gap_2()
+            .children(self.right_items.iter().map(|item| item.to_any()))
+    }
+}
+
+// todo!()
+// impl View for StatusBar {
+//     fn ui_name() -> &'static str {
+//         "StatusBar"
+//     }
+
+//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+//         let theme = &theme::current(cx).workspace.status_bar;
+
+//         StatusBarElement {
+//             left: Flex::row()
+//                 .with_children(self.left_items.iter().map(|i| {
+//                     ChildView::new(i.as_any(), cx)
+//                         .aligned()
+//                         .contained()
+//                         .with_margin_right(theme.item_spacing)
+//                 }))
+//                 .into_any(),
+//             right: Flex::row()
+//                 .with_children(self.right_items.iter().rev().map(|i| {
+//                     ChildView::new(i.as_any(), cx)
+//                         .aligned()
+//                         .contained()
+//                         .with_margin_left(theme.item_spacing)
+//                 }))
+//                 .into_any(),
+//         }
+//         .contained()
+//         .with_style(theme.container)
+//         .constrained()
+//         .with_height(theme.height)
+//         .into_any()
+//     }
+// }
+
+impl StatusBar {
+    pub fn new(active_pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Self {
+        let mut this = Self {
+            left_items: Default::default(),
+            right_items: Default::default(),
+            active_pane: active_pane.clone(),
+            _observe_active_pane: cx
+                .observe(active_pane, |this, _, cx| this.update_active_pane_item(cx)),
+        };
+        this.update_active_pane_item(cx);
+        this
+    }
+
+    pub fn add_left_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
+    where
+        T: 'static + StatusItemView,
+    {
+        self.left_items.push(Box::new(item));
+        cx.notify();
+    }
+
+    pub fn item_of_type<T: StatusItemView>(&self) -> Option<View<T>> {
+        self.left_items
+            .iter()
+            .chain(self.right_items.iter())
+            .find_map(|item| item.to_any().clone().downcast().log_err())
+    }
+
+    pub fn position_of_item<T>(&self) -> Option<usize>
+    where
+        T: StatusItemView,
+    {
+        for (index, item) in self.left_items.iter().enumerate() {
+            if item.item_type() == TypeId::of::<T>() {
+                return Some(index);
+            }
+        }
+        for (index, item) in self.right_items.iter().enumerate() {
+            if item.item_type() == TypeId::of::<T>() {
+                return Some(index + self.left_items.len());
+            }
+        }
+        return None;
+    }
+
+    pub fn insert_item_after<T>(
+        &mut self,
+        position: usize,
+        item: View<T>,
+        cx: &mut ViewContext<Self>,
+    ) where
+        T: 'static + StatusItemView,
+    {
+        if position < self.left_items.len() {
+            self.left_items.insert(position + 1, Box::new(item))
+        } else {
+            self.right_items
+                .insert(position + 1 - self.left_items.len(), Box::new(item))
+        }
+        cx.notify()
+    }
+
+    pub fn remove_item_at(&mut self, position: usize, cx: &mut ViewContext<Self>) {
+        if position < self.left_items.len() {
+            self.left_items.remove(position);
+        } else {
+            self.right_items.remove(position - self.left_items.len());
+        }
+        cx.notify();
+    }
+
+    pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
+    where
+        T: 'static + StatusItemView,
+    {
+        self.right_items.push(Box::new(item));
+        cx.notify();
+    }
+
+    pub fn set_active_pane(&mut self, active_pane: &View<Pane>, cx: &mut ViewContext<Self>) {
+        self.active_pane = active_pane.clone();
+        self._observe_active_pane =
+            cx.observe(active_pane, |this, _, cx| this.update_active_pane_item(cx));
+        self.update_active_pane_item(cx);
+    }
+
+    fn update_active_pane_item(&mut self, cx: &mut ViewContext<Self>) {
+        let active_pane_item = self.active_pane.read(cx).active_item();
+        for item in self.left_items.iter().chain(&self.right_items) {
+            item.set_active_pane_item(active_pane_item.as_deref(), cx);
+        }
+    }
+}
+
+impl<T: StatusItemView> StatusItemViewHandle for View<T> {
+    fn to_any(&self) -> AnyView {
+        self.clone().into()
+    }
+
+    fn set_active_pane_item(
+        &self,
+        active_pane_item: Option<&dyn ItemHandle>,
+        cx: &mut WindowContext,
+    ) {
+        self.update(cx, |this, cx| {
+            this.set_active_pane_item(active_pane_item, cx)
+        });
+    }
+
+    fn item_type(&self) -> TypeId {
+        TypeId::of::<T>()
+    }
+}
+
+impl From<&dyn StatusItemViewHandle> for AnyView {
+    fn from(val: &dyn StatusItemViewHandle) -> Self {
+        val.to_any().clone()
+    }
+}
+
+// todo!()
+// struct StatusBarElement {
+//     left: AnyElement<StatusBar>,
+//     right: AnyElement<StatusBar>,
+// }
+
+// todo!()
+// impl Element<StatusBar> for StatusBarElement {
+//     type LayoutState = ();
+//     type PaintState = ();
+
+//     fn layout(
+//         &mut self,
+//         mut constraint: SizeConstraint,
+//         view: &mut StatusBar,
+//         cx: &mut ViewContext<StatusBar>,
+//     ) -> (Vector2F, Self::LayoutState) {
+//         let max_width = constraint.max.x();
+//         constraint.min = vec2f(0., constraint.min.y());
+
+//         let right_size = self.right.layout(constraint, view, cx);
+//         let constraint = SizeConstraint::new(
+//             vec2f(0., constraint.min.y()),
+//             vec2f(max_width - right_size.x(), constraint.max.y()),
+//         );
+
+//         self.left.layout(constraint, view, cx);
+
+//         (vec2f(max_width, right_size.y()), ())
+//     }
+
+//     fn paint(
+//         &mut self,
+//         bounds: RectF,
+//         visible_bounds: RectF,
+//         _: &mut Self::LayoutState,
+//         view: &mut StatusBar,
+//         cx: &mut ViewContext<StatusBar>,
+//     ) -> Self::PaintState {
+//         let origin_y = bounds.upper_right().y();
+//         let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
+
+//         let left_origin = vec2f(bounds.lower_left().x(), origin_y);
+//         self.left.paint(left_origin, visible_bounds, view, cx);
+
+//         let right_origin = vec2f(bounds.upper_right().x() - self.right.size().x(), origin_y);
+//         self.right.paint(right_origin, visible_bounds, view, cx);
+//     }
+
+//     fn rect_for_text_range(
+//         &self,
+//         _: Range<usize>,
+//         _: RectF,
+//         _: RectF,
+//         _: &Self::LayoutState,
+//         _: &Self::PaintState,
+//         _: &StatusBar,
+//         _: &ViewContext<StatusBar>,
+//     ) -> Option<RectF> {
+//         None
+//     }
+
+//     fn debug(
+//         &self,
+//         bounds: RectF,
+//         _: &Self::LayoutState,
+//         _: &Self::PaintState,
+//         _: &StatusBar,
+//         _: &ViewContext<StatusBar>,
+//     ) -> serde_json::Value {
+//         json!({
+//             "type": "StatusBarElement",
+//             "bounds": bounds.to_json()
+//         })
+//     }
+// }

crates/workspace2/src/toolbar.rs 🔗

@@ -0,0 +1,298 @@
+use crate::ItemHandle;
+use gpui2::{
+    AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext,
+};
+
+pub trait ToolbarItemView: Render + EventEmitter {
+    fn set_active_pane_item(
+        &mut self,
+        active_pane_item: Option<&dyn crate::ItemHandle>,
+        cx: &mut ViewContext<Self>,
+    ) -> ToolbarItemLocation;
+
+    fn location_for_event(
+        &self,
+        _event: &Self::Event,
+        current_location: ToolbarItemLocation,
+        _cx: &AppContext,
+    ) -> ToolbarItemLocation {
+        current_location
+    }
+
+    fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {}
+
+    /// Number of times toolbar's height will be repeated to get the effective height.
+    /// Useful when multiple rows one under each other are needed.
+    /// The rows have the same width and act as a whole when reacting to resizes and similar events.
+    fn row_count(&self, _cx: &WindowContext) -> usize {
+        1
+    }
+}
+
+trait ToolbarItemViewHandle: Send {
+    fn id(&self) -> EntityId;
+    fn to_any(&self) -> AnyView;
+    fn set_active_pane_item(
+        &self,
+        active_pane_item: Option<&dyn ItemHandle>,
+        cx: &mut WindowContext,
+    ) -> ToolbarItemLocation;
+    fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext);
+    fn row_count(&self, cx: &WindowContext) -> usize;
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ToolbarItemLocation {
+    Hidden,
+    PrimaryLeft { flex: Option<(f32, bool)> },
+    PrimaryRight { flex: Option<(f32, bool)> },
+    Secondary,
+}
+
+pub struct Toolbar {
+    active_item: Option<Box<dyn ItemHandle>>,
+    hidden: bool,
+    can_navigate: bool,
+    items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
+}
+
+// todo!()
+// impl View for Toolbar {
+//     fn ui_name() -> &'static str {
+//         "Toolbar"
+//     }
+
+//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+//         let theme = &theme::current(cx).workspace.toolbar;
+
+//         let mut primary_left_items = Vec::new();
+//         let mut primary_right_items = Vec::new();
+//         let mut secondary_item = None;
+//         let spacing = theme.item_spacing;
+//         let mut primary_items_row_count = 1;
+
+//         for (item, position) in &self.items {
+//             match *position {
+//                 ToolbarItemLocation::Hidden => {}
+
+//                 ToolbarItemLocation::PrimaryLeft { flex } => {
+//                     primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
+//                     let left_item = ChildView::new(item.as_any(), cx).aligned();
+//                     if let Some((flex, expanded)) = flex {
+//                         primary_left_items.push(left_item.flex(flex, expanded).into_any());
+//                     } else {
+//                         primary_left_items.push(left_item.into_any());
+//                     }
+//                 }
+
+//                 ToolbarItemLocation::PrimaryRight { flex } => {
+//                     primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
+//                     let right_item = ChildView::new(item.as_any(), cx).aligned().flex_float();
+//                     if let Some((flex, expanded)) = flex {
+//                         primary_right_items.push(right_item.flex(flex, expanded).into_any());
+//                     } else {
+//                         primary_right_items.push(right_item.into_any());
+//                     }
+//                 }
+
+//                 ToolbarItemLocation::Secondary => {
+//                     secondary_item = Some(
+//                         ChildView::new(item.as_any(), cx)
+//                             .constrained()
+//                             .with_height(theme.height * item.row_count(cx) as f32)
+//                             .into_any(),
+//                     );
+//                 }
+//             }
+//         }
+
+//         let container_style = theme.container;
+//         let height = theme.height * primary_items_row_count as f32;
+
+//         let mut primary_items = Flex::row().with_spacing(spacing);
+//         primary_items.extend(primary_left_items);
+//         primary_items.extend(primary_right_items);
+
+//         let mut toolbar = Flex::column();
+//         if !primary_items.is_empty() {
+//             toolbar.add_child(primary_items.constrained().with_height(height));
+//         }
+//         if let Some(secondary_item) = secondary_item {
+//             toolbar.add_child(secondary_item);
+//         }
+
+//         if toolbar.is_empty() {
+//             toolbar.into_any_named("toolbar")
+//         } else {
+//             toolbar
+//                 .contained()
+//                 .with_style(container_style)
+//                 .into_any_named("toolbar")
+//         }
+//     }
+// }
+
+// <<<<<<< HEAD
+// =======
+// #[allow(clippy::too_many_arguments)]
+// fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>)>(
+//     svg_path: &'static str,
+//     style: theme::Interactive<theme::IconButton>,
+//     nav_button_height: f32,
+//     tooltip_style: TooltipStyle,
+//     enabled: bool,
+//     spacing: f32,
+//     on_click: F,
+//     tooltip_action: A,
+//     action_name: &'static str,
+//     cx: &mut ViewContext<Toolbar>,
+// ) -> AnyElement<Toolbar> {
+//     MouseEventHandler::new::<A, _>(0, cx, |state, _| {
+//         let style = if enabled {
+//             style.style_for(state)
+//         } else {
+//             style.disabled_style()
+//         };
+//         Svg::new(svg_path)
+//             .with_color(style.color)
+//             .constrained()
+//             .with_width(style.icon_width)
+//             .aligned()
+//             .contained()
+//             .with_style(style.container)
+//             .constrained()
+//             .with_width(style.button_width)
+//             .with_height(nav_button_height)
+//             .aligned()
+//             .top()
+//     })
+//     .with_cursor_style(if enabled {
+//         CursorStyle::PointingHand
+//     } else {
+//         CursorStyle::default()
+//     })
+//     .on_click(MouseButton::Left, move |_, toolbar, cx| {
+//         on_click(toolbar, cx)
+//     })
+//     .with_tooltip::<A>(
+//         0,
+//         action_name,
+//         Some(Box::new(tooltip_action)),
+//         tooltip_style,
+//         cx,
+//     )
+//     .contained()
+//     .with_margin_right(spacing)
+//     .into_any_named("nav button")
+// }
+
+// >>>>>>> 139cbbfd3aebd0863a7d51b0c12d748764cf0b2e
+impl Toolbar {
+    pub fn new() -> Self {
+        Self {
+            active_item: None,
+            items: Default::default(),
+            hidden: false,
+            can_navigate: true,
+        }
+    }
+
+    pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut ViewContext<Self>) {
+        self.can_navigate = can_navigate;
+        cx.notify();
+    }
+
+    pub fn add_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
+    where
+        T: 'static + ToolbarItemView,
+    {
+        let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
+        cx.subscribe(&item, |this, item, event, cx| {
+            if let Some((_, current_location)) =
+                this.items.iter_mut().find(|(i, _)| i.id() == item.id())
+            {
+                let new_location = item
+                    .read(cx)
+                    .location_for_event(event, *current_location, cx);
+                if new_location != *current_location {
+                    *current_location = new_location;
+                    cx.notify();
+                }
+            }
+        })
+        .detach();
+        self.items.push((Box::new(item), location));
+        cx.notify();
+    }
+
+    pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
+        self.active_item = item.map(|item| item.boxed_clone());
+        self.hidden = self
+            .active_item
+            .as_ref()
+            .map(|item| !item.show_toolbar(cx))
+            .unwrap_or(false);
+
+        for (toolbar_item, current_location) in self.items.iter_mut() {
+            let new_location = toolbar_item.set_active_pane_item(item, cx);
+            if new_location != *current_location {
+                *current_location = new_location;
+                cx.notify();
+            }
+        }
+    }
+
+    pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext<Self>) {
+        for (toolbar_item, _) in self.items.iter_mut() {
+            toolbar_item.focus_changed(focused, cx);
+        }
+    }
+
+    pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<View<T>> {
+        self.items
+            .iter()
+            .find_map(|(item, _)| item.to_any().downcast().ok())
+    }
+
+    pub fn hidden(&self) -> bool {
+        self.hidden
+    }
+}
+
+impl<T: ToolbarItemView> ToolbarItemViewHandle for View<T> {
+    fn id(&self) -> EntityId {
+        self.entity_id()
+    }
+
+    fn to_any(&self) -> AnyView {
+        self.clone().into()
+    }
+
+    fn set_active_pane_item(
+        &self,
+        active_pane_item: Option<&dyn ItemHandle>,
+        cx: &mut WindowContext,
+    ) -> ToolbarItemLocation {
+        self.update(cx, |this, cx| {
+            this.set_active_pane_item(active_pane_item, cx)
+        })
+    }
+
+    fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| {
+            this.pane_focus_update(pane_focused, cx);
+            cx.notify();
+        });
+    }
+
+    fn row_count(&self, cx: &WindowContext) -> usize {
+        self.read(cx).row_count(cx)
+    }
+}
+
+// todo!()
+// impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle {
+//     fn from(val: &dyn ToolbarItemViewHandle) -> Self {
+//         val.as_any().clone()
+//     }
+// }

crates/workspace2/src/workspace2.rs 🔗

@@ -1,86 +1,82 @@
-// pub mod dock;
+#![allow(unused_variables, dead_code, unused_mut)]
+// todo!() this is to make transition easier.
+
+pub mod dock;
 pub mod item;
-// pub mod notifications;
+pub mod notifications;
 pub mod pane;
 pub mod pane_group;
 mod persistence;
 pub mod searchable;
 // pub mod shared_screen;
-// mod status_bar;
+mod status_bar;
 mod toolbar;
 mod workspace_settings;
 
-use anyhow::{anyhow, Result};
-// use call2::ActiveCall;
-// use client2::{
-//     proto::{self, PeerId},
-//     Client, Status, TypedEnvelope, UserStore,
-// };
-// use collections::{hash_map, HashMap, HashSet};
-// use futures::{
-//     channel::{mpsc, oneshot},
-//     future::try_join_all,
-//     FutureExt, StreamExt,
-// };
-// use gpui2::{
-//     actions,
-//     elements::*,
-//     geometry::{
-//         rect::RectF,
-//         vector::{vec2f, Vector2F},
-//     },
-//     impl_actions,
-//     platform::{
-//         CursorStyle, ModifiersChangedEvent, MouseButton, PathPromptOptions, Platform, PromptLevel,
-//         WindowBounds, WindowOptions,
-//     },
-//     AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AnyWindowHandle, AppContext, AsyncAppContext,
-//     Entity, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext,
-//     View, WeakViewHandle, WindowContext, WindowHandle,
-// };
-// use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
-// use itertools::Itertools;
-// use language2::{LanguageRegistry, Rope};
-// use node_runtime::NodeRuntime;// //
-
-use futures::channel::oneshot;
-// use crate::{
-//     notifications::{simple_message_notification::MessageNotification, NotificationTracker},
-//     persistence::model::{
-//         DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
-//     },
-// };
-// use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
-// use lazy_static::lazy_static;
-// use notifications::{NotificationHandle, NotifyResultExt};
+use crate::persistence::model::{
+    DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup,
+    SerializedWorkspace,
+};
+use anyhow::{anyhow, Context as _, Result};
+use call2::ActiveCall;
+use client2::{
+    proto::{self, PeerId},
+    Client, TypedEnvelope, UserStore,
+};
+use collections::{HashMap, HashSet};
+use dock::{Dock, DockPosition, PanelButtons};
+use futures::{
+    channel::{mpsc, oneshot},
+    future::try_join_all,
+    Future, FutureExt, StreamExt,
+};
+use gpui2::{
+    div, point, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext,
+    AsyncWindowContext, Bounds, Component, Div, EntityId, EventEmitter, GlobalPixels, Model,
+    ModelContext, ParentElement, Point, Render, Size, StatefulInteractive, Styled, Subscription,
+    Task, View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle,
+    WindowOptions,
+};
+use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
+use language2::LanguageRegistry;
+use lazy_static::lazy_static;
+use node_runtime::NodeRuntime;
+use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
 pub use pane::*;
 pub use pane_group::*;
-// use persistence::{model::SerializedItem, DB};
-// pub use persistence::{
-//     model::{ItemId, WorkspaceLocation},
-//     WorkspaceDb, DB as WORKSPACE_DB,
-// };
-// use postage::prelude::Stream;
-// use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
-// use serde::Deserialize;
-// use shared_screen::SharedScreen;
-// use status_bar::StatusBar;
-// pub use status_bar::StatusItemView;
-// use theme::{Theme, ThemeSettings};
+use persistence::{
+    model::{ItemId, WorkspaceLocation},
+    DB,
+};
+use postage::stream::Stream;
+use project2::{Project, ProjectEntryId, ProjectPath, Worktree};
+use serde::Deserialize;
+use settings2::Settings;
+use status_bar::StatusBar;
+use std::{
+    any::TypeId,
+    borrow::Cow,
+    env,
+    path::{Path, PathBuf},
+    sync::{atomic::AtomicUsize, Arc},
+    time::Duration,
+};
+use theme2::ActiveTheme;
 pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
-// use util::ResultExt;
-// pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings};
-
-// lazy_static! {
-//     static ref ZED_WINDOW_SIZE: Option<Vector2F> = env::var("ZED_WINDOW_SIZE")
-//         .ok()
-//         .as_deref()
-//         .and_then(parse_pixel_position_env_var);
-//     static ref ZED_WINDOW_POSITION: Option<Vector2F> = env::var("ZED_WINDOW_POSITION")
-//         .ok()
-//         .as_deref()
-//         .and_then(parse_pixel_position_env_var);
-// }
+use util::ResultExt;
+use uuid::Uuid;
+use workspace_settings::{AutosaveSetting, WorkspaceSettings};
+
+lazy_static! {
+    static ref ZED_WINDOW_SIZE: Option<Size<GlobalPixels>> = env::var("ZED_WINDOW_SIZE")
+        .ok()
+        .as_deref()
+        .and_then(parse_pixel_size_env_var);
+    static ref ZED_WINDOW_POSITION: Option<Point<GlobalPixels>> = env::var("ZED_WINDOW_POSITION")
+        .ok()
+        .as_deref()
+        .and_then(parse_pixel_position_env_var);
+}
 
 // pub trait Modal: View {
 //     fn has_focus(&self) -> bool;
@@ -170,13 +166,13 @@ pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
 //     pub save_intent: Option<SaveIntent>,
 // }
 
-// #[derive(Deserialize)]
-// pub struct Toast {
-//     id: usize,
-//     msg: Cow<'static, str>,
-//     #[serde(skip)]
-//     on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,
-// }
+#[derive(Deserialize)]
+pub struct Toast {
+    id: usize,
+    msg: Cow<'static, str>,
+    #[serde(skip)]
+    on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,
+}
 
 // impl Toast {
 //     pub fn new<I: Into<Cow<'static, str>>>(id: usize, msg: I) -> Self {
@@ -237,143 +233,142 @@ pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
 
 pub type WorkspaceId = i64;
 
-// pub fn init_settings(cx: &mut AppContext) {
-//     settings::register::<WorkspaceSettings>(cx);
-//     settings::register::<item::ItemSettings>(cx);
-// }
-
-// pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
-//     init_settings(cx);
-//     pane::init(cx);
-//     notifications::init(cx);
-
-//     cx.add_global_action({
-//         let app_state = Arc::downgrade(&app_state);
-//         move |_: &Open, cx: &mut AppContext| {
-//             let mut paths = cx.prompt_for_paths(PathPromptOptions {
-//                 files: true,
-//                 directories: true,
-//                 multiple: true,
-//             });
-
-//             if let Some(app_state) = app_state.upgrade() {
-//                 cx.spawn(move |mut cx| async move {
-//                     if let Some(paths) = paths.recv().await.flatten() {
-//                         cx.update(|cx| {
-//                             open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx)
-//                         });
-//                     }
-//                 })
-//                 .detach();
-//             }
-//         }
-//     });
-//     cx.add_async_action(Workspace::open);
-
-//     cx.add_async_action(Workspace::follow_next_collaborator);
-//     cx.add_async_action(Workspace::close);
-//     cx.add_async_action(Workspace::close_inactive_items_and_panes);
-//     cx.add_async_action(Workspace::close_all_items_and_panes);
-//     cx.add_global_action(Workspace::close_global);
-//     cx.add_global_action(restart);
-//     cx.add_async_action(Workspace::save_all);
-//     cx.add_action(Workspace::add_folder_to_project);
-//     cx.add_action(
-//         |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
-//             let pane = workspace.active_pane().clone();
-//             workspace.unfollow(&pane, cx);
-//         },
-//     );
-//     cx.add_action(
-//         |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext<Workspace>| {
-//             workspace
-//                 .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx)
-//                 .detach_and_log_err(cx);
-//         },
-//     );
-//     cx.add_action(
-//         |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
-//             workspace
-//                 .save_active_item(SaveIntent::SaveAs, cx)
-//                 .detach_and_log_err(cx);
-//         },
-//     );
-//     cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
-//         workspace.activate_previous_pane(cx)
-//     });
-//     cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
-//         workspace.activate_next_pane(cx)
-//     });
+pub fn init_settings(cx: &mut AppContext) {
+    WorkspaceSettings::register(cx);
+    ItemSettings::register(cx);
+}
 
-//     cx.add_action(
-//         |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| {
-//             workspace.activate_pane_in_direction(action.0, cx)
-//         },
-//     );
+pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
+    init_settings(cx);
+    pane::init(cx);
+    notifications::init(cx);
+
+    //     cx.add_global_action({
+    //         let app_state = Arc::downgrade(&app_state);
+    //         move |_: &Open, cx: &mut AppContext| {
+    //             let mut paths = cx.prompt_for_paths(PathPromptOptions {
+    //                 files: true,
+    //                 directories: true,
+    //                 multiple: true,
+    //             });
 
-//     cx.add_action(
-//         |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| {
-//             workspace.swap_pane_in_direction(action.0, cx)
-//         },
-//     );
+    //             if let Some(app_state) = app_state.upgrade() {
+    //                 cx.spawn(move |mut cx| async move {
+    //                     if let Some(paths) = paths.recv().await.flatten() {
+    //                         cx.update(|cx| {
+    //                             open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx)
+    //                         });
+    //                     }
+    //                 })
+    //                 .detach();
+    //             }
+    //         }
+    //     });
+    //     cx.add_async_action(Workspace::open);
+
+    //     cx.add_async_action(Workspace::follow_next_collaborator);
+    //     cx.add_async_action(Workspace::close);
+    //     cx.add_async_action(Workspace::close_inactive_items_and_panes);
+    //     cx.add_async_action(Workspace::close_all_items_and_panes);
+    //     cx.add_global_action(Workspace::close_global);
+    //     cx.add_global_action(restart);
+    //     cx.add_async_action(Workspace::save_all);
+    //     cx.add_action(Workspace::add_folder_to_project);
+    //     cx.add_action(
+    //         |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
+    //             let pane = workspace.active_pane().clone();
+    //             workspace.unfollow(&pane, cx);
+    //         },
+    //     );
+    //     cx.add_action(
+    //         |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext<Workspace>| {
+    //             workspace
+    //                 .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx)
+    //                 .detach_and_log_err(cx);
+    //         },
+    //     );
+    //     cx.add_action(
+    //         |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
+    //             workspace
+    //                 .save_active_item(SaveIntent::SaveAs, cx)
+    //                 .detach_and_log_err(cx);
+    //         },
+    //     );
+    //     cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
+    //         workspace.activate_previous_pane(cx)
+    //     });
+    //     cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
+    //         workspace.activate_next_pane(cx)
+    //     });
+
+    //     cx.add_action(
+    //         |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| {
+    //             workspace.activate_pane_in_direction(action.0, cx)
+    //         },
+    //     );
+
+    //     cx.add_action(
+    //         |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| {
+    //             workspace.swap_pane_in_direction(action.0, cx)
+    //         },
+    //     );
+
+    //     cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| {
+    //         workspace.toggle_dock(DockPosition::Left, cx);
+    //     });
+    //     cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| {
+    //         workspace.toggle_dock(DockPosition::Right, cx);
+    //     });
+    //     cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| {
+    //         workspace.toggle_dock(DockPosition::Bottom, cx);
+    //     });
+    //     cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| {
+    //         workspace.close_all_docks(cx);
+    //     });
+    //     cx.add_action(Workspace::activate_pane_at_index);
+    //     cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
+    //         workspace.reopen_closed_item(cx).detach();
+    //     });
+    //     cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| {
+    //         workspace
+    //             .go_back(workspace.active_pane().downgrade(), cx)
+    //             .detach();
+    //     });
+    //     cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| {
+    //         workspace
+    //             .go_forward(workspace.active_pane().downgrade(), cx)
+    //             .detach();
+    //     });
 
-//     cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| {
-//         workspace.toggle_dock(DockPosition::Left, cx);
-//     });
-//     cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| {
-//         workspace.toggle_dock(DockPosition::Right, cx);
-//     });
-//     cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| {
-//         workspace.toggle_dock(DockPosition::Bottom, cx);
-//     });
-//     cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| {
-//         workspace.close_all_docks(cx);
-//     });
-//     cx.add_action(Workspace::activate_pane_at_index);
-//     cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
-//         workspace.reopen_closed_item(cx).detach();
-//     });
-//     cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| {
-//         workspace
-//             .go_back(workspace.active_pane().downgrade(), cx)
-//             .detach();
-//     });
-//     cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| {
-//         workspace
-//             .go_forward(workspace.active_pane().downgrade(), cx)
-//             .detach();
-//     });
+    //     cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| {
+    //         cx.spawn(|workspace, mut cx| async move {
+    //             let err = install_cli::install_cli(&cx)
+    //                 .await
+    //                 .context("Failed to create CLI symlink");
 
-//     cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| {
-//         cx.spawn(|workspace, mut cx| async move {
-//             let err = install_cli::install_cli(&cx)
-//                 .await
-//                 .context("Failed to create CLI symlink");
-
-//             workspace.update(&mut cx, |workspace, cx| {
-//                 if matches!(err, Err(_)) {
-//                     err.notify_err(workspace, cx);
-//                 } else {
-//                     workspace.show_notification(1, cx, |cx| {
-//                         cx.add_view(|_| {
-//                             MessageNotification::new("Successfully installed the `zed` binary")
-//                         })
-//                     });
-//                 }
-//             })
-//         })
-//         .detach();
-//     });
-// }
+    //             workspace.update(&mut cx, |workspace, cx| {
+    //                 if matches!(err, Err(_)) {
+    //                     err.notify_err(workspace, cx);
+    //                 } else {
+    //                     workspace.show_notification(1, cx, |cx| {
+    //                         cx.build_view(|_| {
+    //                             MessageNotification::new("Successfully installed the `zed` binary")
+    //                         })
+    //                     });
+    //                 }
+    //             })
+    //         })
+    //         .detach();
+    //     });
+}
 
 type ProjectItemBuilders =
-    HashMap<TypeId, fn(Handle<Project>, AnyHandle, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>>;
+    HashMap<TypeId, fn(Model<Project>, AnyModel, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>>;
 pub fn register_project_item<I: ProjectItem>(cx: &mut AppContext) {
-    cx.update_default_global(|builders: &mut ProjectItemBuilders, _| {
-        builders.insert(TypeId::of::<I::Item>(), |project, model, cx| {
-            let item = model.downcast::<I::Item>().unwrap();
-            Box::new(cx.add_view(|cx| I::for_project_item(project, item, cx)))
-        });
+    let builders = cx.default_global::<ProjectItemBuilders>();
+    builders.insert(TypeId::of::<I::Item>(), |project, model, cx| {
+        let item = model.downcast::<I::Item>().unwrap();
+        Box::new(cx.build_view(|cx| I::for_project_item(project, item, cx)))
     });
 }
 
@@ -392,26 +387,25 @@ type FollowableItemBuilders = HashMap<
     ),
 >;
 pub fn register_followable_item<I: FollowableItem>(cx: &mut AppContext) {
-    cx.update_default_global(|builders: &mut FollowableItemBuilders, _| {
-        builders.insert(
-            TypeId::of::<I>(),
-            (
-                |pane, workspace, id, state, cx| {
-                    I::from_state_proto(pane, workspace, id, state, cx).map(|task| {
-                        cx.foreground()
-                            .spawn(async move { Ok(Box::new(task.await?) as Box<_>) })
-                    })
-                },
-                |this| Box::new(this.clone().downcast::<I>().unwrap()),
-            ),
-        );
-    });
+    let builders = cx.default_global::<FollowableItemBuilders>();
+    builders.insert(
+        TypeId::of::<I>(),
+        (
+            |pane, workspace, id, state, cx| {
+                I::from_state_proto(pane, workspace, id, state, cx).map(|task| {
+                    cx.foreground_executor()
+                        .spawn(async move { Ok(Box::new(task.await?) as Box<_>) })
+                })
+            },
+            |this| Box::new(this.clone().downcast::<I>().unwrap()),
+        ),
+    );
 }
 
 type ItemDeserializers = HashMap<
     Arc<str>,
     fn(
-        Handle<Project>,
+        Model<Project>,
         WeakView<Workspace>,
         WorkspaceId,
         ItemId,
@@ -419,13 +413,13 @@ type ItemDeserializers = HashMap<
     ) -> Task<Result<Box<dyn ItemHandle>>>,
 >;
 pub fn register_deserializable_item<I: Item>(cx: &mut AppContext) {
-    cx.update_default_global(|deserializers: &mut ItemDeserializers, _cx| {
+    cx.update_global(|deserializers: &mut ItemDeserializers, _cx| {
         if let Some(serialized_item_kind) = I::serialized_item_kind() {
             deserializers.insert(
                 Arc::from(serialized_item_kind),
                 |project, workspace, workspace_id, item_id, cx| {
                     let task = I::deserialize(project, workspace, workspace_id, item_id, cx);
-                    cx.foreground()
+                    cx.foreground_executor()
                         .spawn(async { Ok(Box::new(task.await?) as Box<_>) })
                 },
             );
@@ -436,18 +430,22 @@ pub fn register_deserializable_item<I: Item>(cx: &mut AppContext) {
 pub struct AppState {
     pub languages: Arc<LanguageRegistry>,
     pub client: Arc<Client>,
-    pub user_store: Handle<UserStore>,
-    pub workspace_store: Handle<WorkspaceStore>,
+    pub user_store: Model<UserStore>,
+    pub workspace_store: Model<WorkspaceStore>,
     pub fs: Arc<dyn fs2::Fs>,
     pub build_window_options:
-        fn(Option<WindowBounds>, Option<DisplayId>, &MainThread<AppContext>) -> WindowOptions,
-    pub initialize_workspace:
-        fn(WeakHandle<Workspace>, bool, Arc<AppState>, AsyncAppContext) -> Task<anyhow::Result<()>>,
+        fn(Option<WindowBounds>, Option<Uuid>, &mut AppContext) -> WindowOptions,
+    pub initialize_workspace: fn(
+        WeakView<Workspace>,
+        bool,
+        Arc<AppState>,
+        AsyncWindowContext,
+    ) -> Task<anyhow::Result<()>>,
     pub node_runtime: Arc<dyn NodeRuntime>,
 }
 
 pub struct WorkspaceStore {
-    workspaces: HashSet<WeakHandle<Workspace>>,
+    workspaces: HashSet<WindowHandle<Workspace>>,
     followers: Vec<Follower>,
     client: Arc<Client>,
     _subscriptions: Vec<client2::Subscription>,
@@ -459,40 +457,42 @@ struct Follower {
     peer_id: PeerId,
 }
 
-// impl AppState {
-//     #[cfg(any(test, feature = "test-support"))]
-//     pub fn test(cx: &mut AppContext) -> Arc<Self> {
-//         use node_runtime::FakeNodeRuntime;
-//         use settings::SettingsStore;
+impl AppState {
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn test(cx: &mut AppContext) -> Arc<Self> {
+        use gpui2::Context;
+        use node_runtime::FakeNodeRuntime;
+        use settings2::SettingsStore;
 
-//         if !cx.has_global::<SettingsStore>() {
-//             cx.set_global(SettingsStore::test(cx));
-//         }
+        if !cx.has_global::<SettingsStore>() {
+            let settings_store = SettingsStore::test(cx);
+            cx.set_global(settings_store);
+        }
 
-//         let fs = fs::FakeFs::new(cx.background().clone());
-//         let languages = Arc::new(LanguageRegistry::test());
-//         let http_client = util::http::FakeHttpClient::with_404_response();
-//         let client = Client::new(http_client.clone(), cx);
-//         let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
-//         let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
-
-//         theme::init((), cx);
-//         client::init(&client, cx);
-//         crate::init_settings(cx);
-
-//         Arc::new(Self {
-//             client,
-//             fs,
-//             languages,
-//             user_store,
-//             // channel_store,
-//             workspace_store,
-//             node_runtime: FakeNodeRuntime::new(),
-//             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
-//             build_window_options: |_, _, _| Default::default(),
-//         })
-//     }
-// }
+        let fs = fs2::FakeFs::new(cx.background_executor().clone());
+        let languages = Arc::new(LanguageRegistry::test());
+        let http_client = util::http::FakeHttpClient::with_404_response();
+        let client = Client::new(http_client.clone(), cx);
+        let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http_client, cx));
+        let workspace_store = cx.build_model(|cx| WorkspaceStore::new(client.clone(), cx));
+
+        // todo!()
+        // theme::init((), cx);
+        client2::init(&client, cx);
+        crate::init_settings(cx);
+
+        Arc::new(Self {
+            client,
+            fs,
+            languages,
+            user_store,
+            workspace_store,
+            node_runtime: FakeNodeRuntime::new(),
+            initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
+            build_window_options: |_, _, _| Default::default(),
+        })
+    }
+}
 
 struct DelayedDebouncedEditAction {
     task: Option<Task<()>>,
@@ -509,7 +509,7 @@ impl DelayedDebouncedEditAction {
 
     fn fire_new<F>(&mut self, delay: Duration, cx: &mut ViewContext<Workspace>, func: F)
     where
-        F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> Task<Result<()>>,
+        F: 'static + Send + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> Task<Result<()>>,
     {
         if let Some(channel) = self.cancel_channel.take() {
             _ = channel.send(());
@@ -519,8 +519,8 @@ impl DelayedDebouncedEditAction {
         self.cancel_channel = Some(sender);
 
         let previous_task = self.task.take();
-        self.task = Some(cx.spawn(|workspace, mut cx| async move {
-            let mut timer = cx.background().timer(delay).fuse();
+        self.task = Some(cx.spawn(move |workspace, mut cx| async move {
+            let mut timer = cx.background_executor().timer(delay).fuse();
             if let Some(previous_task) = previous_task {
                 previous_task.await;
             }
@@ -540,41 +540,41 @@ impl DelayedDebouncedEditAction {
     }
 }
 
-// pub enum Event {
-//     PaneAdded(View<Pane>),
-//     ContactRequestedJoin(u64),
-// }
+pub enum Event {
+    PaneAdded(View<Pane>),
+    ContactRequestedJoin(u64),
+}
 
 pub struct Workspace {
-    weak_self: WeakHandle<Self>,
+    weak_self: WeakView<Self>,
     //     modal: Option<ActiveModal>,
-    //     zoomed: Option<AnyWeakViewHandle>,
+    zoomed: Option<AnyWeakView>,
     //     zoomed_position: Option<DockPosition>,
-    //     center: PaneGroup,
-    //     left_dock: View<Dock>,
-    //     bottom_dock: View<Dock>,
-    //     right_dock: View<Dock>,
+    center: PaneGroup,
+    left_dock: View<Dock>,
+    bottom_dock: View<Dock>,
+    right_dock: View<Dock>,
     panes: Vec<View<Pane>>,
-    //     panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
-    //     active_pane: View<Pane>,
+    panes_by_item: HashMap<EntityId, WeakView<Pane>>,
+    active_pane: View<Pane>,
     last_active_center_pane: Option<WeakView<Pane>>,
-    //     last_active_view_id: Option<proto::ViewId>,
-    //     status_bar: View<StatusBar>,
+    last_active_view_id: Option<proto::ViewId>,
+    status_bar: View<StatusBar>,
     //     titlebar_item: Option<AnyViewHandle>,
-    //     notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
-    project: Handle<Project>,
-    //     follower_states: HashMap<View<Pane>, FollowerState>,
-    //     last_leaders_by_pane: HashMap<WeakViewHandle<Pane>, PeerId>,
-    //     window_edited: bool,
-    //     active_call: Option<(ModelHandle<ActiveCall>, Vec<Subscription>)>,
-    //     leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
-    //     database_id: WorkspaceId,
+    notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
+    project: Model<Project>,
+    follower_states: HashMap<View<Pane>, FollowerState>,
+    last_leaders_by_pane: HashMap<WeakView<Pane>, PeerId>,
+    window_edited: bool,
+    active_call: Option<(Model<ActiveCall>, Vec<Subscription>)>,
+    leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
+    database_id: WorkspaceId,
     app_state: Arc<AppState>,
-    //     subscriptions: Vec<Subscription>,
-    //     _apply_leader_updates: Task<Result<()>>,
-    //     _observe_current_user: Task<Result<()>>,
-    //     _schedule_serialize: Option<Task<()>>,
-    //     pane_history_timestamp: Arc<AtomicUsize>,
+    subscriptions: Vec<Subscription>,
+    _apply_leader_updates: Task<Result<()>>,
+    _observe_current_user: Task<Result<()>>,
+    _schedule_serialize: Option<Task<()>>,
+    pane_history_timestamp: Arc<AtomicUsize>,
 }
 
 // struct ActiveModal {
@@ -582,11 +582,11 @@ pub struct Workspace {
 //     previously_focused_view_id: Option<usize>,
 // }
 
-// #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-// pub struct ViewId {
-//     pub creator: PeerId,
-//     pub id: u64,
-// }
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ViewId {
+    pub creator: PeerId,
+    pub id: u64,
+}
 
 #[derive(Default)]
 struct FollowerState {
@@ -595,353 +595,362 @@ struct FollowerState {
     items_by_leader_view_id: HashMap<ViewId, Box<dyn FollowableItemHandle>>,
 }
 
-// enum WorkspaceBounds {}
+enum WorkspaceBounds {}
 
 impl Workspace {
-    //     pub fn new(
-    //         workspace_id: WorkspaceId,
-    //         project: ModelHandle<Project>,
-    //         app_state: Arc<AppState>,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Self {
-    //         cx.observe(&project, |_, _, cx| cx.notify()).detach();
-    //         cx.subscribe(&project, move |this, _, event, cx| {
-    //             match event {
-    //                 project::Event::RemoteIdChanged(_) => {
-    //                     this.update_window_title(cx);
-    //                 }
-
-    //                 project::Event::CollaboratorLeft(peer_id) => {
-    //                     this.collaborator_left(*peer_id, cx);
-    //                 }
-
-    //                 project::Event::WorktreeRemoved(_) | project::Event::WorktreeAdded => {
-    //                     this.update_window_title(cx);
-    //                     this.serialize_workspace(cx);
-    //                 }
-
-    //                 project::Event::DisconnectedFromHost => {
-    //                     this.update_window_edited(cx);
-    //                     cx.blur();
-    //                 }
-
-    //                 project::Event::Closed => {
-    //                     cx.remove_window();
-    //                 }
-
-    //                 project::Event::DeletedEntry(entry_id) => {
-    //                     for pane in this.panes.iter() {
-    //                         pane.update(cx, |pane, cx| {
-    //                             pane.handle_deleted_project_item(*entry_id, cx)
-    //                         });
-    //                     }
-    //                 }
-
-    //                 project::Event::Notification(message) => this.show_notification(0, cx, |cx| {
-    //                     cx.add_view(|_| MessageNotification::new(message.clone()))
-    //                 }),
-
-    //                 _ => {}
-    //             }
-    //             cx.notify()
-    //         })
-    //         .detach();
-
-    //         let weak_handle = cx.weak_handle();
-    //         let pane_history_timestamp = Arc::new(AtomicUsize::new(0));
-
-    //         let center_pane = cx.add_view(|cx| {
-    //             Pane::new(
-    //                 weak_handle.clone(),
-    //                 project.clone(),
-    //                 pane_history_timestamp.clone(),
-    //                 cx,
-    //             )
-    //         });
-    //         cx.subscribe(&center_pane, Self::handle_pane_event).detach();
-    //         cx.focus(&center_pane);
-    //         cx.emit(Event::PaneAdded(center_pane.clone()));
-
-    //         app_state.workspace_store.update(cx, |store, _| {
-    //             store.workspaces.insert(weak_handle.clone());
-    //         });
-
-    //         let mut current_user = app_state.user_store.read(cx).watch_current_user();
-    //         let mut connection_status = app_state.client.status();
-    //         let _observe_current_user = cx.spawn(|this, mut cx| async move {
-    //             current_user.recv().await;
-    //             connection_status.recv().await;
-    //             let mut stream =
-    //                 Stream::map(current_user, drop).merge(Stream::map(connection_status, drop));
+    pub fn new(
+        workspace_id: WorkspaceId,
+        project: Model<Project>,
+        app_state: Arc<AppState>,
+        cx: &mut ViewContext<Self>,
+    ) -> Self {
+        cx.observe(&project, |_, _, cx| cx.notify()).detach();
+        cx.subscribe(&project, move |this, _, event, cx| {
+            match event {
+                project2::Event::RemoteIdChanged(_) => {
+                    this.update_window_title(cx);
+                }
+
+                project2::Event::CollaboratorLeft(peer_id) => {
+                    this.collaborator_left(*peer_id, cx);
+                }
+
+                project2::Event::WorktreeRemoved(_) | project2::Event::WorktreeAdded => {
+                    this.update_window_title(cx);
+                    this.serialize_workspace(cx);
+                }
+
+                project2::Event::DisconnectedFromHost => {
+                    this.update_window_edited(cx);
+                    cx.blur();
+                }
+
+                project2::Event::Closed => {
+                    // cx.remove_window();
+                }
+
+                project2::Event::DeletedEntry(entry_id) => {
+                    for pane in this.panes.iter() {
+                        pane.update(cx, |pane, cx| {
+                            pane.handle_deleted_project_item(*entry_id, cx)
+                        });
+                    }
+                }
 
-    //             while stream.recv().await.is_some() {
-    //                 this.update(&mut cx, |_, cx| cx.notify())?;
-    //             }
-    //             anyhow::Ok(())
-    //         });
+                project2::Event::Notification(message) => this.show_notification(0, cx, |cx| {
+                    cx.build_view(|_| MessageNotification::new(message.clone()))
+                }),
 
-    //         // All leader updates are enqueued and then processed in a single task, so
-    //         // that each asynchronous operation can be run in order.
-    //         let (leader_updates_tx, mut leader_updates_rx) =
-    //             mpsc::unbounded::<(PeerId, proto::UpdateFollowers)>();
-    //         let _apply_leader_updates = cx.spawn(|this, mut cx| async move {
-    //             while let Some((leader_id, update)) = leader_updates_rx.next().await {
-    //                 Self::process_leader_update(&this, leader_id, update, &mut cx)
-    //                     .await
-    //                     .log_err();
-    //             }
+                _ => {}
+            }
+            cx.notify()
+        })
+        .detach();
+
+        let weak_handle = cx.view().downgrade();
+        let pane_history_timestamp = Arc::new(AtomicUsize::new(0));
+
+        let center_pane = cx.build_view(|cx| {
+            Pane::new(
+                weak_handle.clone(),
+                project.clone(),
+                pane_history_timestamp.clone(),
+                cx,
+            )
+        });
+        cx.subscribe(&center_pane, Self::handle_pane_event).detach();
+        // todo!()
+        // cx.focus(&center_pane);
+        cx.emit(Event::PaneAdded(center_pane.clone()));
+
+        let window_handle = cx.window_handle().downcast::<Workspace>().unwrap();
+        app_state.workspace_store.update(cx, |store, _| {
+            store.workspaces.insert(window_handle);
+        });
 
-    //             Ok(())
-    //         });
+        let mut current_user = app_state.user_store.read(cx).watch_current_user();
+        let mut connection_status = app_state.client.status();
+        let _observe_current_user = cx.spawn(|this, mut cx| async move {
+            current_user.next().await;
+            connection_status.next().await;
+            let mut stream =
+                Stream::map(current_user, drop).merge(Stream::map(connection_status, drop));
 
-    //         cx.emit_global(WorkspaceCreated(weak_handle.clone()));
-
-    //         let left_dock = cx.add_view(|_| Dock::new(DockPosition::Left));
-    //         let bottom_dock = cx.add_view(|_| Dock::new(DockPosition::Bottom));
-    //         let right_dock = cx.add_view(|_| Dock::new(DockPosition::Right));
-    //         let left_dock_buttons =
-    //             cx.add_view(|cx| PanelButtons::new(left_dock.clone(), weak_handle.clone(), cx));
-    //         let bottom_dock_buttons =
-    //             cx.add_view(|cx| PanelButtons::new(bottom_dock.clone(), weak_handle.clone(), cx));
-    //         let right_dock_buttons =
-    //             cx.add_view(|cx| PanelButtons::new(right_dock.clone(), weak_handle.clone(), cx));
-    //         let status_bar = cx.add_view(|cx| {
-    //             let mut status_bar = StatusBar::new(&center_pane.clone(), cx);
-    //             status_bar.add_left_item(left_dock_buttons, cx);
-    //             status_bar.add_right_item(right_dock_buttons, cx);
-    //             status_bar.add_right_item(bottom_dock_buttons, cx);
-    //             status_bar
-    //         });
+            while stream.recv().await.is_some() {
+                this.update(&mut cx, |_, cx| cx.notify())?;
+            }
+            anyhow::Ok(())
+        });
 
-    //         cx.update_default_global::<DragAndDrop<Workspace>, _, _>(|drag_and_drop, _| {
-    //             drag_and_drop.register_container(weak_handle.clone());
-    //         });
+        // All leader updates are enqueued and then processed in a single task, so
+        // that each asynchronous operation can be run in order.
+        let (leader_updates_tx, mut leader_updates_rx) =
+            mpsc::unbounded::<(PeerId, proto::UpdateFollowers)>();
+        let _apply_leader_updates = cx.spawn(|this, mut cx| async move {
+            while let Some((leader_id, update)) = leader_updates_rx.next().await {
+                Self::process_leader_update(&this, leader_id, update, &mut cx)
+                    .await
+                    .log_err();
+            }
 
-    //         let mut active_call = None;
-    //         if cx.has_global::<ModelHandle<ActiveCall>>() {
-    //             let call = cx.global::<ModelHandle<ActiveCall>>().clone();
-    //             let mut subscriptions = Vec::new();
-    //             subscriptions.push(cx.subscribe(&call, Self::on_active_call_event));
-    //             active_call = Some((call, subscriptions));
-    //         }
+            Ok(())
+        });
 
-    //         let subscriptions = vec![
-    //             cx.observe_fullscreen(|_, _, cx| cx.notify()),
-    //             cx.observe_window_activation(Self::on_window_activation_changed),
-    //             cx.observe_window_bounds(move |_, mut bounds, display, cx| {
-    //                 // Transform fixed bounds to be stored in terms of the containing display
-    //                 if let WindowBounds::Fixed(mut window_bounds) = bounds {
-    //                     if let Some(screen) = cx.platform().screen_by_id(display) {
-    //                         let screen_bounds = screen.bounds();
-    //                         window_bounds
-    //                             .set_origin_x(window_bounds.origin_x() - screen_bounds.origin_x());
-    //                         window_bounds
-    //                             .set_origin_y(window_bounds.origin_y() - screen_bounds.origin_y());
-    //                         bounds = WindowBounds::Fixed(window_bounds);
-    //                     }
-    //                 }
+        // todo!("replace with a different mechanism")
+        // cx.emit_global(WorkspaceCreated(weak_handle.clone()));
+
+        let left_dock = cx.build_view(|_| Dock::new(DockPosition::Left));
+        let bottom_dock = cx.build_view(|_| Dock::new(DockPosition::Bottom));
+        let right_dock = cx.build_view(|_| Dock::new(DockPosition::Right));
+        let left_dock_buttons =
+            cx.build_view(|cx| PanelButtons::new(left_dock.clone(), weak_handle.clone(), cx));
+        let bottom_dock_buttons =
+            cx.build_view(|cx| PanelButtons::new(bottom_dock.clone(), weak_handle.clone(), cx));
+        let right_dock_buttons =
+            cx.build_view(|cx| PanelButtons::new(right_dock.clone(), weak_handle.clone(), cx));
+        let status_bar = cx.build_view(|cx| {
+            let mut status_bar = StatusBar::new(&center_pane.clone(), cx);
+            status_bar.add_left_item(left_dock_buttons, cx);
+            status_bar.add_right_item(right_dock_buttons, cx);
+            status_bar.add_right_item(bottom_dock_buttons, cx);
+            status_bar
+        });
 
-    //                 cx.background()
-    //                     .spawn(DB.set_window_bounds(workspace_id, bounds, display))
-    //                     .detach_and_log_err(cx);
-    //             }),
-    //             cx.observe(&left_dock, |this, _, cx| {
-    //                 this.serialize_workspace(cx);
-    //                 cx.notify();
-    //             }),
-    //             cx.observe(&bottom_dock, |this, _, cx| {
-    //                 this.serialize_workspace(cx);
-    //                 cx.notify();
-    //             }),
-    //             cx.observe(&right_dock, |this, _, cx| {
-    //                 this.serialize_workspace(cx);
-    //                 cx.notify();
-    //             }),
-    //         ];
-
-    //         cx.defer(|this, cx| this.update_window_title(cx));
-    //         Workspace {
-    //             weak_self: weak_handle.clone(),
-    //             modal: None,
-    //             zoomed: None,
-    //             zoomed_position: None,
-    //             center: PaneGroup::new(center_pane.clone()),
-    //             panes: vec![center_pane.clone()],
-    //             panes_by_item: Default::default(),
-    //             active_pane: center_pane.clone(),
-    //             last_active_center_pane: Some(center_pane.downgrade()),
-    //             last_active_view_id: None,
-    //             status_bar,
-    //             titlebar_item: None,
-    //             notifications: Default::default(),
-    //             left_dock,
-    //             bottom_dock,
-    //             right_dock,
-    //             project: project.clone(),
-    //             follower_states: Default::default(),
-    //             last_leaders_by_pane: Default::default(),
-    //             window_edited: false,
-    //             active_call,
-    //             database_id: workspace_id,
-    //             app_state,
-    //             _observe_current_user,
-    //             _apply_leader_updates,
-    //             _schedule_serialize: None,
-    //             leader_updates_tx,
-    //             subscriptions,
-    //             pane_history_timestamp,
-    //         }
-    //     }
+        // todo!()
+        // cx.update_default_global::<DragAndDrop<Workspace>, _, _>(|drag_and_drop, _| {
+        //     drag_and_drop.register_container(weak_handle.clone());
+        // });
+
+        let mut active_call = None;
+        if cx.has_global::<Model<ActiveCall>>() {
+            let call = cx.global::<Model<ActiveCall>>().clone();
+            let mut subscriptions = Vec::new();
+            subscriptions.push(cx.subscribe(&call, Self::on_active_call_event));
+            active_call = Some((call, subscriptions));
+        }
 
-    //     fn new_local(
-    //         abs_paths: Vec<PathBuf>,
-    //         app_state: Arc<AppState>,
-    //         requesting_window: Option<WindowHandle<Workspace>>,
-    //         cx: &mut AppContext,
-    //     ) -> Task<(
-    //         WeakViewHandle<Workspace>,
-    //         Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
-    //     )> {
-    //         let project_handle = Project::local(
-    //             app_state.client.clone(),
-    //             app_state.node_runtime.clone(),
-    //             app_state.user_store.clone(),
-    //             app_state.languages.clone(),
-    //             app_state.fs.clone(),
-    //             cx,
-    //         );
+        let subscriptions = vec![
+            cx.observe_window_activation(Self::on_window_activation_changed),
+            cx.observe_window_bounds(move |_, cx| {
+                if let Some(display) = cx.display() {
+                    // Transform fixed bounds to be stored in terms of the containing display
+                    let mut bounds = cx.window_bounds();
+                    if let WindowBounds::Fixed(window_bounds) = &mut bounds {
+                        let display_bounds = display.bounds();
+                        window_bounds.origin.x -= display_bounds.origin.x;
+                        window_bounds.origin.y -= display_bounds.origin.y;
+                    }
 
-    //         cx.spawn(|mut cx| async move {
-    //             let serialized_workspace = persistence::DB.workspace_for_roots(&abs_paths.as_slice());
-
-    //             let paths_to_open = Arc::new(abs_paths);
-
-    //             // Get project paths for all of the abs_paths
-    //             let mut worktree_roots: HashSet<Arc<Path>> = Default::default();
-    //             let mut project_paths: Vec<(PathBuf, Option<ProjectPath>)> =
-    //                 Vec::with_capacity(paths_to_open.len());
-    //             for path in paths_to_open.iter().cloned() {
-    //                 if let Some((worktree, project_entry)) = cx
-    //                     .update(|cx| {
-    //                         Workspace::project_path_for_path(project_handle.clone(), &path, true, cx)
-    //                     })
-    //                     .await
-    //                     .log_err()
-    //                 {
-    //                     worktree_roots.insert(worktree.read_with(&mut cx, |tree, _| tree.abs_path()));
-    //                     project_paths.push((path, Some(project_entry)));
-    //                 } else {
-    //                     project_paths.push((path, None));
-    //                 }
-    //             }
+                    if let Some(display_uuid) = display.uuid().log_err() {
+                        cx.background_executor()
+                            .spawn(DB.set_window_bounds(workspace_id, bounds, display_uuid))
+                            .detach_and_log_err(cx);
+                    }
+                }
+                cx.notify();
+            }),
+            cx.observe(&left_dock, |this, _, cx| {
+                this.serialize_workspace(cx);
+                cx.notify();
+            }),
+            cx.observe(&bottom_dock, |this, _, cx| {
+                this.serialize_workspace(cx);
+                cx.notify();
+            }),
+            cx.observe(&right_dock, |this, _, cx| {
+                this.serialize_workspace(cx);
+                cx.notify();
+            }),
+        ];
+
+        cx.defer(|this, cx| this.update_window_title(cx));
+        Workspace {
+            weak_self: weak_handle.clone(),
+            // modal: None,
+            zoomed: None,
+            // zoomed_position: None,
+            center: PaneGroup::new(center_pane.clone()),
+            panes: vec![center_pane.clone()],
+            panes_by_item: Default::default(),
+            active_pane: center_pane.clone(),
+            last_active_center_pane: Some(center_pane.downgrade()),
+            last_active_view_id: None,
+            status_bar,
+            // titlebar_item: None,
+            notifications: Default::default(),
+            left_dock,
+            bottom_dock,
+            right_dock,
+            project: project.clone(),
+            follower_states: Default::default(),
+            last_leaders_by_pane: Default::default(),
+            window_edited: false,
+            active_call,
+            database_id: workspace_id,
+            app_state,
+            _observe_current_user,
+            _apply_leader_updates,
+            _schedule_serialize: None,
+            leader_updates_tx,
+            subscriptions,
+            pane_history_timestamp,
+        }
+    }
 
-    //             let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() {
-    //                 serialized_workspace.id
-    //             } else {
-    //                 DB.next_id().await.unwrap_or(0)
-    //             };
+    fn new_local(
+        abs_paths: Vec<PathBuf>,
+        app_state: Arc<AppState>,
+        _requesting_window: Option<WindowHandle<Workspace>>,
+        cx: &mut AppContext,
+    ) -> Task<
+        anyhow::Result<(
+            WindowHandle<Workspace>,
+            Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
+        )>,
+    > {
+        let project_handle = Project::local(
+            app_state.client.clone(),
+            app_state.node_runtime.clone(),
+            app_state.user_store.clone(),
+            app_state.languages.clone(),
+            app_state.fs.clone(),
+            cx,
+        );
 
-    //             let window = if let Some(window) = requesting_window {
-    //                 window.replace_root(&mut cx, |cx| {
-    //                     Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
-    //                 });
-    //                 window
-    //             } else {
-    //                 {
-    //                     let window_bounds_override = window_bounds_env_override(&cx);
-    //                     let (bounds, display) = if let Some(bounds) = window_bounds_override {
-    //                         (Some(bounds), None)
-    //                     } else {
-    //                         serialized_workspace
-    //                             .as_ref()
-    //                             .and_then(|serialized_workspace| {
-    //                                 let display = serialized_workspace.display?;
-    //                                 let mut bounds = serialized_workspace.bounds?;
-
-    //                                 // Stored bounds are relative to the containing display.
-    //                                 // So convert back to global coordinates if that screen still exists
-    //                                 if let WindowBounds::Fixed(mut window_bounds) = bounds {
-    //                                     if let Some(screen) = cx.platform().screen_by_id(display) {
-    //                                         let screen_bounds = screen.bounds();
-    //                                         window_bounds.set_origin_x(
-    //                                             window_bounds.origin_x() + screen_bounds.origin_x(),
-    //                                         );
-    //                                         window_bounds.set_origin_y(
-    //                                             window_bounds.origin_y() + screen_bounds.origin_y(),
-    //                                         );
-    //                                         bounds = WindowBounds::Fixed(window_bounds);
-    //                                     } else {
-    //                                         // Screen no longer exists. Return none here.
-    //                                         return None;
-    //                                     }
-    //                                 }
-
-    //                                 Some((bounds, display))
-    //                             })
-    //                             .unzip()
-    //                     };
-
-    //                     // Use the serialized workspace to construct the new window
-    //                     cx.add_window(
-    //                         (app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
-    //                         |cx| {
-    //                             Workspace::new(
-    //                                 workspace_id,
-    //                                 project_handle.clone(),
-    //                                 app_state.clone(),
-    //                                 cx,
-    //                             )
-    //                         },
-    //                     )
-    //                 }
-    //             };
+        cx.spawn(|mut cx| async move {
+            let serialized_workspace: Option<SerializedWorkspace> = None; //persistence::DB.workspace_for_roots(&abs_paths.as_slice());
+
+            let paths_to_open = Arc::new(abs_paths);
+
+            // Get project paths for all of the abs_paths
+            let mut worktree_roots: HashSet<Arc<Path>> = Default::default();
+            let mut project_paths: Vec<(PathBuf, Option<ProjectPath>)> =
+                Vec::with_capacity(paths_to_open.len());
+            for path in paths_to_open.iter().cloned() {
+                if let Some((worktree, project_entry)) = cx
+                    .update(|cx| {
+                        Workspace::project_path_for_path(project_handle.clone(), &path, true, cx)
+                    })?
+                    .await
+                    .log_err()
+                {
+                    worktree_roots.extend(worktree.update(&mut cx, |tree, _| tree.abs_path()).ok());
+                    project_paths.push((path, Some(project_entry)));
+                } else {
+                    project_paths.push((path, None));
+                }
+            }
 
-    //             // We haven't yielded the main thread since obtaining the window handle,
-    //             // so the window exists.
-    //             let workspace = window.root(&cx).unwrap();
+            let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() {
+                serialized_workspace.id
+            } else {
+                DB.next_id().await.unwrap_or(0)
+            };
 
-    //             (app_state.initialize_workspace)(
-    //                 workspace.downgrade(),
-    //                 serialized_workspace.is_some(),
-    //                 app_state.clone(),
-    //                 cx.clone(),
-    //             )
-    //             .await
-    //             .log_err();
+            // todo!()
+            let window = /*if let Some(window) = requesting_window {
+                cx.update_window(window.into(), |old_workspace, cx| {
+                    cx.replace_root_view(|cx| {
+                        Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
+                    });
+                });
+                window
+                } else */ {
+                let window_bounds_override = window_bounds_env_override(&cx);
+                let (bounds, display) = if let Some(bounds) = window_bounds_override {
+                    (Some(bounds), None)
+                } else {
+                    serialized_workspace
+                        .as_ref()
+                        .and_then(|serialized_workspace| {
+                            let serialized_display = serialized_workspace.display?;
+                            let mut bounds = serialized_workspace.bounds?;
+
+                            // Stored bounds are relative to the containing display.
+                            // So convert back to global coordinates if that screen still exists
+                            if let WindowBounds::Fixed(mut window_bounds) = bounds {
+                                let screen =
+                                    cx.update(|cx|
+                                        cx.displays()
+                                            .into_iter()
+                                            .find(|display| display.uuid().ok() == Some(serialized_display))
+                                    ).ok()??;
+                                let screen_bounds = screen.bounds();
+                                window_bounds.origin.x += screen_bounds.origin.x;
+                                window_bounds.origin.y += screen_bounds.origin.y;
+                                bounds = WindowBounds::Fixed(window_bounds);
+                            }
+
+                            Some((bounds, serialized_display))
+                        })
+                        .unzip()
+                };
 
-    //             window.update(&mut cx, |cx| cx.activate_window());
+                // Use the serialized workspace to construct the new window
+                let options =
+                    cx.update(|cx| (app_state.build_window_options)(bounds, display, cx))?;
+
+                cx.open_window(options, {
+                    let app_state = app_state.clone();
+                    let workspace_id = workspace_id.clone();
+                    let project_handle = project_handle.clone();
+                    move |cx| {
+                        cx.build_view(|cx| {
+                            Workspace::new(workspace_id, project_handle, app_state, cx)
+                        })
+                    }
+                })?
+            };
 
-    //             let workspace = workspace.downgrade();
-    //             notify_if_database_failed(&workspace, &mut cx);
-    //             let opened_items = open_items(
-    //                 serialized_workspace,
-    //                 &workspace,
-    //                 project_paths,
-    //                 app_state,
-    //                 cx,
-    //             )
-    //             .await
-    //             .unwrap_or_default();
+            // todo!() Ask how to do this
+            let weak_view = window.update(&mut cx, |_, cx| cx.view().downgrade())?;
+            let async_cx = window.update(&mut cx, |_, cx| cx.to_async())?;
+
+            (app_state.initialize_workspace)(
+                weak_view,
+                serialized_workspace.is_some(),
+                app_state.clone(),
+                async_cx,
+            )
+            .await
+            .log_err();
+
+            window
+                .update(&mut cx, |_, cx| cx.activate_window())
+                .log_err();
+
+            notify_if_database_failed(window, &mut cx);
+            let opened_items = window
+                .update(&mut cx, |_workspace, cx| {
+                    open_items(
+                        serialized_workspace,
+                        project_paths,
+                        app_state,
+                        cx,
+                    )
+                })?
+                .await
+                .unwrap_or_default();
 
-    //             (workspace, opened_items)
-    //         })
-    //     }
+            Ok((window, opened_items))
+        })
+    }
 
-    //     pub fn weak_handle(&self) -> WeakViewHandle<Self> {
-    //         self.weak_self.clone()
-    //     }
+    pub fn weak_handle(&self) -> WeakView<Self> {
+        self.weak_self.clone()
+    }
 
-    //     pub fn left_dock(&self) -> &View<Dock> {
-    //         &self.left_dock
-    //     }
+    pub fn left_dock(&self) -> &View<Dock> {
+        &self.left_dock
+    }
 
-    //     pub fn bottom_dock(&self) -> &View<Dock> {
-    //         &self.bottom_dock
-    //     }
+    pub fn bottom_dock(&self) -> &View<Dock> {
+        &self.bottom_dock
+    }
 
-    //     pub fn right_dock(&self) -> &View<Dock> {
-    //         &self.right_dock
-    //     }
+    pub fn right_dock(&self) -> &View<Dock> {
+        &self.right_dock
+    }
 
     //     pub fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>)
     //     where

crates/workspace2/src/workspace_settings.rs 🔗

@@ -0,0 +1,56 @@
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+use settings2::Settings;
+
+#[derive(Deserialize)]
+pub struct WorkspaceSettings {
+    pub active_pane_magnification: f32,
+    pub confirm_quit: bool,
+    pub show_call_status_icon: bool,
+    pub autosave: AutosaveSetting,
+}
+
+#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
+pub struct WorkspaceSettingsContent {
+    pub active_pane_magnification: Option<f32>,
+    pub confirm_quit: Option<bool>,
+    pub show_call_status_icon: Option<bool>,
+    pub autosave: Option<AutosaveSetting>,
+}
+
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum AutosaveSetting {
+    Off,
+    AfterDelay { milliseconds: u64 },
+    OnFocusChange,
+    OnWindowChange,
+}
+
+#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
+pub struct GitSettings {
+    pub git_gutter: Option<GitGutterSetting>,
+    pub gutter_debounce: Option<u64>,
+}
+
+#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum GitGutterSetting {
+    #[default]
+    TrackedFiles,
+    Hide,
+}
+
+impl Settings for WorkspaceSettings {
+    const KEY: Option<&'static str> = None;
+
+    type FileContent = WorkspaceSettingsContent;
+
+    fn load(
+        default_value: &Self::FileContent,
+        user_values: &[&Self::FileContent],
+        _: &mut gpui2::AppContext,
+    ) -> anyhow::Result<Self> {
+        Self::load_via_json_merge(default_value, user_values)
+    }
+}

crates/zed2/Cargo.toml 🔗

@@ -15,12 +15,12 @@ name = "Zed"
 path = "src/main.rs"
 
 [dependencies]
-ai2 = { path = "../ai2"}
+ai = { package = "ai2", path = "../ai2"}
 # audio = { path = "../audio" }
 # activity_indicator = { path = "../activity_indicator" }
 # auto_update = { path = "../auto_update" }
 # breadcrumbs = { path = "../breadcrumbs" }
-call2 = { path = "../call2" }
+call = { package = "call2", path = "../call2" }
 # channel = { path = "../channel" }
 cli = { path = "../cli" }
 # collab_ui = { path = "../collab_ui" }
@@ -28,49 +28,49 @@ collections = { path = "../collections" }
 # command_palette = { path = "../command_palette" }
 # component_test = { path = "../component_test" }
 # context_menu = { path = "../context_menu" }
-client2 = { path = "../client2" }
+client = { package = "client2", path = "../client2" }
 # clock = { path = "../clock" }
-copilot2 = { path = "../copilot2" }
+copilot = { package = "copilot2", path = "../copilot2" }
 # copilot_button = { path = "../copilot_button" }
 # diagnostics = { path = "../diagnostics" }
-db2 = { path = "../db2" }
+db = { package = "db2", path = "../db2" }
 # editor = { path = "../editor" }
 # feedback = { path = "../feedback" }
 # file_finder = { path = "../file_finder" }
 # search = { path = "../search" }
-fs2 = { path = "../fs2" }
+fs = { package = "fs2", path = "../fs2" }
 fsevent = { path = "../fsevent" }
 fuzzy = { path = "../fuzzy" }
 # go_to_line = { path = "../go_to_line" }
-gpui2 = { path = "../gpui2" }
+gpui = { package = "gpui2", path = "../gpui2" }
 install_cli = { path = "../install_cli" }
-journal2 = { path = "../journal2" }
-language2 = { path = "../language2" }
+journal = { package = "journal2", path = "../journal2" }
+language = { package = "language2", path = "../language2" }
 # language_selector = { path = "../language_selector" }
-lsp2 = { path = "../lsp2" }
+lsp = { package = "lsp2", path = "../lsp2" }
 language_tools = { path = "../language_tools" }
 node_runtime = { path = "../node_runtime" }
 # assistant = { path = "../assistant" }
 # outline = { path = "../outline" }
 # plugin_runtime = { path = "../plugin_runtime",optional = true }
-project2 = { path = "../project2" }
+project = { package = "project2", path = "../project2" }
 # project_panel = { path = "../project_panel" }
 # project_symbols = { path = "../project_symbols" }
 # quick_action_bar = { path = "../quick_action_bar" }
 # recent_projects = { path = "../recent_projects" }
-rpc2 = { path = "../rpc2" }
-settings2 = { path = "../settings2" }
-feature_flags2 = { path = "../feature_flags2" }
+rpc = { package = "rpc2", path = "../rpc2" }
+settings = { package = "settings2", path = "../settings2" }
+feature_flags = { package = "feature_flags2", path = "../feature_flags2" }
 sum_tree = { path = "../sum_tree" }
 shellexpand = "2.1.0"
-text2 = { path = "../text2" }
+text = { package = "text2", path = "../text2" }
 # terminal_view = { path = "../terminal_view" }
-theme2 = { path = "../theme2" }
+theme = { package = "theme2", path = "../theme2" }
 # theme_selector = { path = "../theme_selector" }
 util = { path = "../util" }
 # semantic_index = { path = "../semantic_index" }
 # vim = { path = "../vim" }
-# workspace = { path = "../workspace" }
+workspace2 = { path = "../workspace2" }
 # welcome = { path = "../welcome" }
 # zed-actions = {path = "../zed-actions"}
 anyhow.workspace = true
@@ -142,17 +142,17 @@ urlencoding = "2.1.2"
 uuid.workspace = true
 
 [dev-dependencies]
-call2 = { path = "../call2", features = ["test-support"] }
+call = { package = "call2", path = "../call2", features = ["test-support"] }
 # client = { path = "../client", features = ["test-support"] }
 # editor = { path = "../editor", features = ["test-support"] }
 # gpui = { path = "../gpui", features = ["test-support"] }
-gpui2 = { path = "../gpui2", features = ["test-support"] }
-language2 = { path = "../language2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
 # lsp = { path = "../lsp", features = ["test-support"] }
-project2 = { path = "../project2", features = ["test-support"] }
+project = { package = "project2", path = "../project2", features = ["test-support"] }
 # rpc = { path = "../rpc", features = ["test-support"] }
 # settings = { path = "../settings", features = ["test-support"] }
-text2 = { path = "../text2", features = ["test-support"] }
+text = { package = "text2", path = "../text2", features = ["test-support"] }
 # util = { path = "../util", features = ["test-support"] }
 # workspace = { path = "../workspace", features = ["test-support"] }
 unindent.workspace = true

crates/zed2/src/assets.rs 🔗

@@ -1,5 +1,6 @@
 use anyhow::anyhow;
-use gpui2::{AssetSource, Result, SharedString};
+
+use gpui::{AssetSource, Result, SharedString};
 use rust_embed::RustEmbed;
 
 #[derive(RustEmbed)]

crates/zed2/src/languages.rs 🔗

@@ -1,9 +1,9 @@
 use anyhow::Context;
-use gpui2::AppContext;
-pub use language2::*;
+use gpui::AppContext;
+pub use language::*;
 use node_runtime::NodeRuntime;
 use rust_embed::RustEmbed;
-use settings2::Settings;
+use settings::Settings;
 use std::{borrow::Cow, str, sync::Arc};
 use util::asset_str;
 

crates/zed2/src/languages/c.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::{anyhow, Context, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-pub use language2::*;
-use lsp2::LanguageServerBinary;
+pub use language::*;
+use lsp::LanguageServerBinary;
 use smol::fs::{self, File};
 use std::{any::Any, path::PathBuf, sync::Arc};
 use util::{
@@ -108,7 +108,7 @@ impl super::LspAdapter for CLspAdapter {
 
     async fn label_for_completion(
         &self,
-        completion: &lsp2::CompletionItem,
+        completion: &lsp::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         let label = completion
@@ -118,7 +118,7 @@ impl super::LspAdapter for CLspAdapter {
             .trim();
 
         match completion.kind {
-            Some(lsp2::CompletionItemKind::FIELD) if completion.detail.is_some() => {
+            Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
                 let detail = completion.detail.as_ref().unwrap();
                 let text = format!("{} {}", detail, label);
                 let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
@@ -129,7 +129,7 @@ impl super::LspAdapter for CLspAdapter {
                     runs,
                 });
             }
-            Some(lsp2::CompletionItemKind::CONSTANT | lsp2::CompletionItemKind::VARIABLE)
+            Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE)
                 if completion.detail.is_some() =>
             {
                 let detail = completion.detail.as_ref().unwrap();
@@ -141,7 +141,7 @@ impl super::LspAdapter for CLspAdapter {
                     runs,
                 });
             }
-            Some(lsp2::CompletionItemKind::FUNCTION | lsp2::CompletionItemKind::METHOD)
+            Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD)
                 if completion.detail.is_some() =>
             {
                 let detail = completion.detail.as_ref().unwrap();
@@ -155,13 +155,13 @@ impl super::LspAdapter for CLspAdapter {
             }
             Some(kind) => {
                 let highlight_name = match kind {
-                    lsp2::CompletionItemKind::STRUCT
-                    | lsp2::CompletionItemKind::INTERFACE
-                    | lsp2::CompletionItemKind::CLASS
-                    | lsp2::CompletionItemKind::ENUM => Some("type"),
-                    lsp2::CompletionItemKind::ENUM_MEMBER => Some("variant"),
-                    lsp2::CompletionItemKind::KEYWORD => Some("keyword"),
-                    lsp2::CompletionItemKind::VALUE | lsp2::CompletionItemKind::CONSTANT => {
+                    lsp::CompletionItemKind::STRUCT
+                    | lsp::CompletionItemKind::INTERFACE
+                    | lsp::CompletionItemKind::CLASS
+                    | lsp::CompletionItemKind::ENUM => Some("type"),
+                    lsp::CompletionItemKind::ENUM_MEMBER => Some("variant"),
+                    lsp::CompletionItemKind::KEYWORD => Some("keyword"),
+                    lsp::CompletionItemKind::VALUE | lsp::CompletionItemKind::CONSTANT => {
                         Some("constant")
                     }
                     _ => None,
@@ -186,47 +186,47 @@ impl super::LspAdapter for CLspAdapter {
     async fn label_for_symbol(
         &self,
         name: &str,
-        kind: lsp2::SymbolKind,
+        kind: lsp::SymbolKind,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         let (text, filter_range, display_range) = match kind {
-            lsp2::SymbolKind::METHOD | lsp2::SymbolKind::FUNCTION => {
+            lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
                 let text = format!("void {} () {{}}", name);
                 let filter_range = 0..name.len();
                 let display_range = 5..5 + name.len();
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::STRUCT => {
+            lsp::SymbolKind::STRUCT => {
                 let text = format!("struct {} {{}}", name);
                 let filter_range = 7..7 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::ENUM => {
+            lsp::SymbolKind::ENUM => {
                 let text = format!("enum {} {{}}", name);
                 let filter_range = 5..5 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::INTERFACE | lsp2::SymbolKind::CLASS => {
+            lsp::SymbolKind::INTERFACE | lsp::SymbolKind::CLASS => {
                 let text = format!("class {} {{}}", name);
                 let filter_range = 6..6 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::CONSTANT => {
+            lsp::SymbolKind::CONSTANT => {
                 let text = format!("const int {} = 0;", name);
                 let filter_range = 10..10 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::MODULE => {
+            lsp::SymbolKind::MODULE => {
                 let text = format!("namespace {} {{}}", name);
                 let filter_range = 10..10 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::TYPE_PARAMETER => {
+            lsp::SymbolKind::TYPE_PARAMETER => {
                 let text = format!("typename {} {{}};", name);
                 let filter_range = 9..9 + name.len();
                 let display_range = 0..filter_range.end;
@@ -273,18 +273,18 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
 
 #[cfg(test)]
 mod tests {
-    use gpui2::{Context, TestAppContext};
-    use language2::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
-    use settings2::SettingsStore;
+    use gpui::{Context, TestAppContext};
+    use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
+    use settings::SettingsStore;
     use std::num::NonZeroU32;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_c_autoindent(cx: &mut TestAppContext) {
         // cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
         cx.update(|cx| {
             let test_settings = SettingsStore::test(cx);
             cx.set_global(test_settings);
-            language2::init(cx);
+            language::init(cx);
             cx.update_global::<SettingsStore, _>(|store, cx| {
                 store.update_user_settings::<AllLanguageSettings>(cx, |s| {
                     s.defaults.tab_size = NonZeroU32::new(2);

crates/zed2/src/languages/css.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
 use smol::fs;

crates/zed2/src/languages/elixir.rs 🔗

@@ -1,12 +1,12 @@
 use anyhow::{anyhow, bail, Context, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use gpui2::{AsyncAppContext, Task};
-pub use language2::*;
-use lsp2::{CompletionItemKind, LanguageServerBinary, SymbolKind};
+use gpui::{AsyncAppContext, Task};
+pub use language::*;
+use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
 use schemars::JsonSchema;
 use serde_derive::{Deserialize, Serialize};
-use settings2::Settings;
+use settings::Settings;
 use smol::fs::{self, File};
 use std::{
     any::Any,
@@ -54,7 +54,7 @@ impl Settings for ElixirSettings {
     fn load(
         default_value: &Self::FileContent,
         user_values: &[&Self::FileContent],
-        _: &mut gpui2::AppContext,
+        _: &mut gpui::AppContext,
     ) -> Result<Self>
     where
         Self: Sized,
@@ -200,7 +200,7 @@ impl LspAdapter for ElixirLspAdapter {
 
     async fn label_for_completion(
         &self,
-        completion: &lsp2::CompletionItem,
+        completion: &lsp::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         match completion.kind.zip(completion.detail.as_ref()) {
@@ -404,7 +404,7 @@ impl LspAdapter for NextLspAdapter {
 
     async fn label_for_completion(
         &self,
-        completion: &lsp2::CompletionItem,
+        completion: &lsp::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         label_for_completion_elixir(completion, language)
@@ -506,7 +506,7 @@ impl LspAdapter for LocalLspAdapter {
 
     async fn label_for_completion(
         &self,
-        completion: &lsp2::CompletionItem,
+        completion: &lsp::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         label_for_completion_elixir(completion, language)
@@ -523,7 +523,7 @@ impl LspAdapter for LocalLspAdapter {
 }
 
 fn label_for_completion_elixir(
-    completion: &lsp2::CompletionItem,
+    completion: &lsp::CompletionItem,
     language: &Arc<Language>,
 ) -> Option<CodeLabel> {
     return Some(CodeLabel {

crates/zed2/src/languages/go.rs 🔗

@@ -1,10 +1,10 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use gpui2::{AsyncAppContext, Task};
-pub use language2::*;
+use gpui::{AsyncAppContext, Task};
+pub use language::*;
 use lazy_static::lazy_static;
-use lsp2::LanguageServerBinary;
+use lsp::LanguageServerBinary;
 use regex::Regex;
 use smol::{fs, process};
 use std::{
@@ -170,7 +170,7 @@ impl super::LspAdapter for GoLspAdapter {
 
     async fn label_for_completion(
         &self,
-        completion: &lsp2::CompletionItem,
+        completion: &lsp::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         let label = &completion.label;
@@ -181,7 +181,7 @@ impl super::LspAdapter for GoLspAdapter {
         let name_offset = label.rfind('.').unwrap_or(0);
 
         match completion.kind.zip(completion.detail.as_ref()) {
-            Some((lsp2::CompletionItemKind::MODULE, detail)) => {
+            Some((lsp::CompletionItemKind::MODULE, detail)) => {
                 let text = format!("{label} {detail}");
                 let source = Rope::from(format!("import {text}").as_str());
                 let runs = language.highlight_text(&source, 7..7 + text.len());
@@ -192,7 +192,7 @@ impl super::LspAdapter for GoLspAdapter {
                 });
             }
             Some((
-                lsp2::CompletionItemKind::CONSTANT | lsp2::CompletionItemKind::VARIABLE,
+                lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE,
                 detail,
             )) => {
                 let text = format!("{label} {detail}");
@@ -208,7 +208,7 @@ impl super::LspAdapter for GoLspAdapter {
                     filter_range: 0..label.len(),
                 });
             }
-            Some((lsp2::CompletionItemKind::STRUCT, _)) => {
+            Some((lsp::CompletionItemKind::STRUCT, _)) => {
                 let text = format!("{label} struct {{}}");
                 let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
                 let runs = adjust_runs(
@@ -221,7 +221,7 @@ impl super::LspAdapter for GoLspAdapter {
                     filter_range: 0..label.len(),
                 });
             }
-            Some((lsp2::CompletionItemKind::INTERFACE, _)) => {
+            Some((lsp::CompletionItemKind::INTERFACE, _)) => {
                 let text = format!("{label} interface {{}}");
                 let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
                 let runs = adjust_runs(
@@ -234,7 +234,7 @@ impl super::LspAdapter for GoLspAdapter {
                     filter_range: 0..label.len(),
                 });
             }
-            Some((lsp2::CompletionItemKind::FIELD, detail)) => {
+            Some((lsp::CompletionItemKind::FIELD, detail)) => {
                 let text = format!("{label} {detail}");
                 let source =
                     Rope::from(format!("type T struct {{ {} }}", &text[name_offset..]).as_str());
@@ -248,10 +248,7 @@ impl super::LspAdapter for GoLspAdapter {
                     filter_range: 0..label.len(),
                 });
             }
-            Some((
-                lsp2::CompletionItemKind::FUNCTION | lsp2::CompletionItemKind::METHOD,
-                detail,
-            )) => {
+            Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {
                 if let Some(signature) = detail.strip_prefix("func") {
                     let text = format!("{label}{signature}");
                     let source = Rope::from(format!("func {} {{}}", &text[name_offset..]).as_str());
@@ -274,47 +271,47 @@ impl super::LspAdapter for GoLspAdapter {
     async fn label_for_symbol(
         &self,
         name: &str,
-        kind: lsp2::SymbolKind,
+        kind: lsp::SymbolKind,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         let (text, filter_range, display_range) = match kind {
-            lsp2::SymbolKind::METHOD | lsp2::SymbolKind::FUNCTION => {
+            lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
                 let text = format!("func {} () {{}}", name);
                 let filter_range = 5..5 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::STRUCT => {
+            lsp::SymbolKind::STRUCT => {
                 let text = format!("type {} struct {{}}", name);
                 let filter_range = 5..5 + name.len();
                 let display_range = 0..text.len();
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::INTERFACE => {
+            lsp::SymbolKind::INTERFACE => {
                 let text = format!("type {} interface {{}}", name);
                 let filter_range = 5..5 + name.len();
                 let display_range = 0..text.len();
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::CLASS => {
+            lsp::SymbolKind::CLASS => {
                 let text = format!("type {} T", name);
                 let filter_range = 5..5 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::CONSTANT => {
+            lsp::SymbolKind::CONSTANT => {
                 let text = format!("const {} = nil", name);
                 let filter_range = 6..6 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::VARIABLE => {
+            lsp::SymbolKind::VARIABLE => {
                 let text = format!("var {} = nil", name);
                 let filter_range = 4..4 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::MODULE => {
+            lsp::SymbolKind::MODULE => {
                 let text = format!("package {}", name);
                 let filter_range = 8..8 + name.len();
                 let display_range = 0..filter_range.end;
@@ -375,10 +372,10 @@ fn adjust_runs(
 mod tests {
     use super::*;
     use crate::languages::language;
-    use gpui2::Hsla;
-    use theme2::SyntaxTheme;
+    use gpui::Hsla;
+    use theme::SyntaxTheme;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_go_label_for_completion() {
         let language = language(
             "go",
@@ -405,8 +402,8 @@ mod tests {
 
         assert_eq!(
             language
-                .label_for_completion(&lsp2::CompletionItem {
-                    kind: Some(lsp2::CompletionItemKind::FUNCTION),
+                .label_for_completion(&lsp::CompletionItem {
+                    kind: Some(lsp::CompletionItemKind::FUNCTION),
                     label: "Hello".to_string(),
                     detail: Some("func(a B) c.D".to_string()),
                     ..Default::default()
@@ -426,8 +423,8 @@ mod tests {
         // Nested methods
         assert_eq!(
             language
-                .label_for_completion(&lsp2::CompletionItem {
-                    kind: Some(lsp2::CompletionItemKind::METHOD),
+                .label_for_completion(&lsp::CompletionItem {
+                    kind: Some(lsp::CompletionItemKind::METHOD),
                     label: "one.two.Three".to_string(),
                     detail: Some("func() [3]interface{}".to_string()),
                     ..Default::default()
@@ -447,8 +444,8 @@ mod tests {
         // Nested fields
         assert_eq!(
             language
-                .label_for_completion(&lsp2::CompletionItem {
-                    kind: Some(lsp2::CompletionItemKind::FIELD),
+                .label_for_completion(&lsp::CompletionItem {
+                    kind: Some(lsp::CompletionItemKind::FIELD),
                     label: "two.Three".to_string(),
                     detail: Some("a.Bcd".to_string()),
                     ..Default::default()

crates/zed2/src/languages/html.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
 use smol::fs;

crates/zed2/src/languages/json.rs 🔗

@@ -1,14 +1,14 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use collections::HashMap;
-use feature_flags2::FeatureFlagAppExt;
+use feature_flags::FeatureFlagAppExt;
 use futures::{future::BoxFuture, FutureExt, StreamExt};
-use gpui2::AppContext;
-use language2::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use gpui::AppContext;
+use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
-use settings2::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
+use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
 use smol::fs;
 use std::{
     any::Any,

crates/zed2/src/languages/lua.rs 🔗

@@ -3,8 +3,8 @@ use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
 use async_trait::async_trait;
 use futures::{io::BufReader, StreamExt};
-use language2::{LanguageServerName, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use language::{LanguageServerName, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use smol::fs;
 use std::{any::Any, env::consts, path::PathBuf};
 use util::{

crates/zed2/src/languages/php.rs 🔗

@@ -3,8 +3,8 @@ use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use collections::HashMap;
 
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 
 use smol::{fs, stream::StreamExt};
@@ -91,9 +91,9 @@ impl LspAdapter for IntelephenseLspAdapter {
 
     async fn label_for_completion(
         &self,
-        _item: &lsp2::CompletionItem,
-        _language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
+        _item: &lsp::CompletionItem,
+        _language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
         None
     }
 

crates/zed2/src/languages/python.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::Result;
 use async_trait::async_trait;
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use smol::fs;
 use std::{
@@ -81,7 +81,7 @@ impl LspAdapter for PythonLspAdapter {
         get_cached_server_binary(container_dir, &*self.node).await
     }
 
-    async fn process_completion(&self, item: &mut lsp2::CompletionItem) {
+    async fn process_completion(&self, item: &mut lsp::CompletionItem) {
         // Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
         // Where `XX` is the sorting category, `YYYY` is based on most recent usage,
         // and `name` is the symbol name itself.
@@ -104,19 +104,19 @@ impl LspAdapter for PythonLspAdapter {
 
     async fn label_for_completion(
         &self,
-        item: &lsp2::CompletionItem,
-        language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
+        item: &lsp::CompletionItem,
+        language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
         let label = &item.label;
         let grammar = language.grammar()?;
         let highlight_id = match item.kind? {
-            lsp2::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
-            lsp2::CompletionItemKind::FUNCTION => grammar.highlight_id_for_name("function")?,
-            lsp2::CompletionItemKind::CLASS => grammar.highlight_id_for_name("type")?,
-            lsp2::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
+            lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
+            lsp::CompletionItemKind::FUNCTION => grammar.highlight_id_for_name("function")?,
+            lsp::CompletionItemKind::CLASS => grammar.highlight_id_for_name("type")?,
+            lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
             _ => return None,
         };
-        Some(language2::CodeLabel {
+        Some(language::CodeLabel {
             text: label.clone(),
             runs: vec![(0..label.len(), highlight_id)],
             filter_range: 0..label.len(),
@@ -126,23 +126,23 @@ impl LspAdapter for PythonLspAdapter {
     async fn label_for_symbol(
         &self,
         name: &str,
-        kind: lsp2::SymbolKind,
-        language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
+        kind: lsp::SymbolKind,
+        language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
         let (text, filter_range, display_range) = match kind {
-            lsp2::SymbolKind::METHOD | lsp2::SymbolKind::FUNCTION => {
+            lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
                 let text = format!("def {}():\n", name);
                 let filter_range = 4..4 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::CLASS => {
+            lsp::SymbolKind::CLASS => {
                 let text = format!("class {}:", name);
                 let filter_range = 6..6 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::CONSTANT => {
+            lsp::SymbolKind::CONSTANT => {
                 let text = format!("{} = 0", name);
                 let filter_range = 0..name.len();
                 let display_range = 0..filter_range.end;
@@ -151,7 +151,7 @@ impl LspAdapter for PythonLspAdapter {
             _ => return None,
         };
 
-        Some(language2::CodeLabel {
+        Some(language::CodeLabel {
             runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
             text: text[display_range].to_string(),
             filter_range,
@@ -177,12 +177,12 @@ async fn get_cached_server_binary(
 
 #[cfg(test)]
 mod tests {
-    use gpui2::{Context, ModelContext, TestAppContext};
-    use language2::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
-    use settings2::SettingsStore;
+    use gpui::{Context, ModelContext, TestAppContext};
+    use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
+    use settings::SettingsStore;
     use std::num::NonZeroU32;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_python_autoindent(cx: &mut TestAppContext) {
         // cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
         let language =
@@ -190,7 +190,7 @@ mod tests {
         cx.update(|cx| {
             let test_settings = SettingsStore::test(cx);
             cx.set_global(test_settings);
-            language2::init(cx);
+            language::init(cx);
             cx.update_global::<SettingsStore, _>(|store, cx| {
                 store.update_user_settings::<AllLanguageSettings>(cx, |s| {
                     s.defaults.tab_size = NonZeroU32::new(2);

crates/zed2/src/languages/ruby.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use std::{any::Any, path::PathBuf, sync::Arc};
 
 pub struct RubyLanguageServer;
@@ -53,25 +53,25 @@ impl LspAdapter for RubyLanguageServer {
 
     async fn label_for_completion(
         &self,
-        item: &lsp2::CompletionItem,
-        language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
+        item: &lsp::CompletionItem,
+        language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
         let label = &item.label;
         let grammar = language.grammar()?;
         let highlight_id = match item.kind? {
-            lsp2::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
-            lsp2::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
-            lsp2::CompletionItemKind::CLASS | lsp2::CompletionItemKind::MODULE => {
+            lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?,
+            lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
+            lsp::CompletionItemKind::CLASS | lsp::CompletionItemKind::MODULE => {
                 grammar.highlight_id_for_name("type")?
             }
-            lsp2::CompletionItemKind::KEYWORD => {
+            lsp::CompletionItemKind::KEYWORD => {
                 if label.starts_with(':') {
                     grammar.highlight_id_for_name("string.special.symbol")?
                 } else {
                     grammar.highlight_id_for_name("keyword")?
                 }
             }
-            lsp2::CompletionItemKind::VARIABLE => {
+            lsp::CompletionItemKind::VARIABLE => {
                 if label.starts_with('@') {
                     grammar.highlight_id_for_name("property")?
                 } else {
@@ -80,7 +80,7 @@ impl LspAdapter for RubyLanguageServer {
             }
             _ => return None,
         };
-        Some(language2::CodeLabel {
+        Some(language::CodeLabel {
             text: label.clone(),
             runs: vec![(0..label.len(), highlight_id)],
             filter_range: 0..label.len(),
@@ -90,12 +90,12 @@ impl LspAdapter for RubyLanguageServer {
     async fn label_for_symbol(
         &self,
         label: &str,
-        kind: lsp2::SymbolKind,
-        language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
+        kind: lsp::SymbolKind,
+        language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
         let grammar = language.grammar()?;
         match kind {
-            lsp2::SymbolKind::METHOD => {
+            lsp::SymbolKind::METHOD => {
                 let mut parts = label.split('#');
                 let classes = parts.next()?;
                 let method = parts.next()?;
@@ -120,21 +120,21 @@ impl LspAdapter for RubyLanguageServer {
                 ix += 1;
                 let end_ix = ix + method.len();
                 runs.push((ix..end_ix, method_id));
-                Some(language2::CodeLabel {
+                Some(language::CodeLabel {
                     text: label.to_string(),
                     runs,
                     filter_range: 0..label.len(),
                 })
             }
-            lsp2::SymbolKind::CONSTANT => {
+            lsp::SymbolKind::CONSTANT => {
                 let constant_id = grammar.highlight_id_for_name("constant")?;
-                Some(language2::CodeLabel {
+                Some(language::CodeLabel {
                     text: label.to_string(),
                     runs: vec![(0..label.len(), constant_id)],
                     filter_range: 0..label.len(),
                 })
             }
-            lsp2::SymbolKind::CLASS | lsp2::SymbolKind::MODULE => {
+            lsp::SymbolKind::CLASS | lsp::SymbolKind::MODULE => {
                 let class_id = grammar.highlight_id_for_name("type")?;
 
                 let mut ix = 0;
@@ -148,7 +148,7 @@ impl LspAdapter for RubyLanguageServer {
                     ix = end_ix;
                 }
 
-                Some(language2::CodeLabel {
+                Some(language::CodeLabel {
                     text: label.to_string(),
                     runs,
                     filter_range: 0..label.len(),

crates/zed2/src/languages/rust.rs 🔗

@@ -2,9 +2,9 @@ use anyhow::{anyhow, Result};
 use async_compression::futures::bufread::GzipDecoder;
 use async_trait::async_trait;
 use futures::{io::BufReader, StreamExt};
-pub use language2::*;
+pub use language::*;
 use lazy_static::lazy_static;
-use lsp2::LanguageServerBinary;
+use lsp::LanguageServerBinary;
 use regex::Regex;
 use smol::fs::{self, File};
 use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc};
@@ -106,7 +106,7 @@ impl LspAdapter for RustLspAdapter {
         Some("rust-analyzer/flycheck".into())
     }
 
-    fn process_diagnostics(&self, params: &mut lsp2::PublishDiagnosticsParams) {
+    fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
         lazy_static! {
             static ref REGEX: Regex = Regex::new("(?m)`([^`]+)\n`$").unwrap();
         }
@@ -128,11 +128,11 @@ impl LspAdapter for RustLspAdapter {
 
     async fn label_for_completion(
         &self,
-        completion: &lsp2::CompletionItem,
+        completion: &lsp::CompletionItem,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         match completion.kind {
-            Some(lsp2::CompletionItemKind::FIELD) if completion.detail.is_some() => {
+            Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
                 let detail = completion.detail.as_ref().unwrap();
                 let name = &completion.label;
                 let text = format!("{}: {}", name, detail);
@@ -144,9 +144,9 @@ impl LspAdapter for RustLspAdapter {
                     filter_range: 0..name.len(),
                 });
             }
-            Some(lsp2::CompletionItemKind::CONSTANT | lsp2::CompletionItemKind::VARIABLE)
+            Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE)
                 if completion.detail.is_some()
-                    && completion.insert_text_format != Some(lsp2::InsertTextFormat::SNIPPET) =>
+                    && completion.insert_text_format != Some(lsp::InsertTextFormat::SNIPPET) =>
             {
                 let detail = completion.detail.as_ref().unwrap();
                 let name = &completion.label;
@@ -159,7 +159,7 @@ impl LspAdapter for RustLspAdapter {
                     filter_range: 0..name.len(),
                 });
             }
-            Some(lsp2::CompletionItemKind::FUNCTION | lsp2::CompletionItemKind::METHOD)
+            Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD)
                 if completion.detail.is_some() =>
             {
                 lazy_static! {
@@ -188,12 +188,12 @@ impl LspAdapter for RustLspAdapter {
             }
             Some(kind) => {
                 let highlight_name = match kind {
-                    lsp2::CompletionItemKind::STRUCT
-                    | lsp2::CompletionItemKind::INTERFACE
-                    | lsp2::CompletionItemKind::ENUM => Some("type"),
-                    lsp2::CompletionItemKind::ENUM_MEMBER => Some("variant"),
-                    lsp2::CompletionItemKind::KEYWORD => Some("keyword"),
-                    lsp2::CompletionItemKind::VALUE | lsp2::CompletionItemKind::CONSTANT => {
+                    lsp::CompletionItemKind::STRUCT
+                    | lsp::CompletionItemKind::INTERFACE
+                    | lsp::CompletionItemKind::ENUM => Some("type"),
+                    lsp::CompletionItemKind::ENUM_MEMBER => Some("variant"),
+                    lsp::CompletionItemKind::KEYWORD => Some("keyword"),
+                    lsp::CompletionItemKind::VALUE | lsp::CompletionItemKind::CONSTANT => {
                         Some("constant")
                     }
                     _ => None,
@@ -214,47 +214,47 @@ impl LspAdapter for RustLspAdapter {
     async fn label_for_symbol(
         &self,
         name: &str,
-        kind: lsp2::SymbolKind,
+        kind: lsp::SymbolKind,
         language: &Arc<Language>,
     ) -> Option<CodeLabel> {
         let (text, filter_range, display_range) = match kind {
-            lsp2::SymbolKind::METHOD | lsp2::SymbolKind::FUNCTION => {
+            lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
                 let text = format!("fn {} () {{}}", name);
                 let filter_range = 3..3 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::STRUCT => {
+            lsp::SymbolKind::STRUCT => {
                 let text = format!("struct {} {{}}", name);
                 let filter_range = 7..7 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::ENUM => {
+            lsp::SymbolKind::ENUM => {
                 let text = format!("enum {} {{}}", name);
                 let filter_range = 5..5 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::INTERFACE => {
+            lsp::SymbolKind::INTERFACE => {
                 let text = format!("trait {} {{}}", name);
                 let filter_range = 6..6 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::CONSTANT => {
+            lsp::SymbolKind::CONSTANT => {
                 let text = format!("const {}: () = ();", name);
                 let filter_range = 6..6 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::MODULE => {
+            lsp::SymbolKind::MODULE => {
                 let text = format!("mod {} {{}}", name);
                 let filter_range = 4..4 + name.len();
                 let display_range = 0..filter_range.end;
                 (text, filter_range, display_range)
             }
-            lsp2::SymbolKind::TYPE_PARAMETER => {
+            lsp::SymbolKind::TYPE_PARAMETER => {
                 let text = format!("type {} {{}}", name);
                 let filter_range = 5..5 + name.len();
                 let display_range = 0..filter_range.end;
@@ -294,29 +294,29 @@ mod tests {
 
     use super::*;
     use crate::languages::language;
-    use gpui2::{Context, Hsla, TestAppContext};
-    use language2::language_settings::AllLanguageSettings;
-    use settings2::SettingsStore;
-    use theme2::SyntaxTheme;
+    use gpui::{Context, Hsla, TestAppContext};
+    use language::language_settings::AllLanguageSettings;
+    use settings::SettingsStore;
+    use theme::SyntaxTheme;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_process_rust_diagnostics() {
-        let mut params = lsp2::PublishDiagnosticsParams {
-            uri: lsp2::Url::from_file_path("/a").unwrap(),
+        let mut params = lsp::PublishDiagnosticsParams {
+            uri: lsp::Url::from_file_path("/a").unwrap(),
             version: None,
             diagnostics: vec![
                 // no newlines
-                lsp2::Diagnostic {
+                lsp::Diagnostic {
                     message: "use of moved value `a`".to_string(),
                     ..Default::default()
                 },
                 // newline at the end of a code span
-                lsp2::Diagnostic {
+                lsp::Diagnostic {
                     message: "consider importing this struct: `use b::c;\n`".to_string(),
                     ..Default::default()
                 },
                 // code span starting right after a newline
-                lsp2::Diagnostic {
+                lsp::Diagnostic {
                     message: "cannot borrow `self.d` as mutable\n`self` is a `&` reference"
                         .to_string(),
                     ..Default::default()
@@ -340,7 +340,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_rust_label_for_completion() {
         let language = language(
             "rust",
@@ -365,8 +365,8 @@ mod tests {
 
         assert_eq!(
             language
-                .label_for_completion(&lsp2::CompletionItem {
-                    kind: Some(lsp2::CompletionItemKind::FUNCTION),
+                .label_for_completion(&lsp::CompletionItem {
+                    kind: Some(lsp::CompletionItemKind::FUNCTION),
                     label: "hello(…)".to_string(),
                     detail: Some("fn(&mut Option<T>) -> Vec<T>".to_string()),
                     ..Default::default()
@@ -387,8 +387,8 @@ mod tests {
         );
         assert_eq!(
             language
-                .label_for_completion(&lsp2::CompletionItem {
-                    kind: Some(lsp2::CompletionItemKind::FUNCTION),
+                .label_for_completion(&lsp::CompletionItem {
+                    kind: Some(lsp::CompletionItemKind::FUNCTION),
                     label: "hello(…)".to_string(),
                     detail: Some("async fn(&mut Option<T>) -> Vec<T>".to_string()),
                     ..Default::default()
@@ -409,8 +409,8 @@ mod tests {
         );
         assert_eq!(
             language
-                .label_for_completion(&lsp2::CompletionItem {
-                    kind: Some(lsp2::CompletionItemKind::FIELD),
+                .label_for_completion(&lsp::CompletionItem {
+                    kind: Some(lsp::CompletionItemKind::FIELD),
                     label: "len".to_string(),
                     detail: Some("usize".to_string()),
                     ..Default::default()
@@ -425,8 +425,8 @@ mod tests {
 
         assert_eq!(
             language
-                .label_for_completion(&lsp2::CompletionItem {
-                    kind: Some(lsp2::CompletionItemKind::FUNCTION),
+                .label_for_completion(&lsp::CompletionItem {
+                    kind: Some(lsp::CompletionItemKind::FUNCTION),
                     label: "hello(…)".to_string(),
                     detail: Some("fn(&mut Option<T>) -> Vec<T>".to_string()),
                     ..Default::default()
@@ -447,7 +447,7 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_rust_label_for_symbol() {
         let language = language(
             "rust",
@@ -471,7 +471,7 @@ mod tests {
 
         assert_eq!(
             language
-                .label_for_symbol("hello", lsp2::SymbolKind::FUNCTION)
+                .label_for_symbol("hello", lsp::SymbolKind::FUNCTION)
                 .await,
             Some(CodeLabel {
                 text: "fn hello".to_string(),
@@ -482,7 +482,7 @@ mod tests {
 
         assert_eq!(
             language
-                .label_for_symbol("World", lsp2::SymbolKind::TYPE_PARAMETER)
+                .label_for_symbol("World", lsp::SymbolKind::TYPE_PARAMETER)
                 .await,
             Some(CodeLabel {
                 text: "type World".to_string(),
@@ -492,13 +492,13 @@ mod tests {
         );
     }
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_rust_autoindent(cx: &mut TestAppContext) {
         // cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
         cx.update(|cx| {
             let test_settings = SettingsStore::test(cx);
             cx.set_global(test_settings);
-            language2::init(cx);
+            language::init(cx);
             cx.update_global::<SettingsStore, _>(|store, cx| {
                 store.update_user_settings::<AllLanguageSettings>(cx, |s| {
                     s.defaults.tab_size = NonZeroU32::new(2);

crates/zed2/src/languages/svelte.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::json;
 use smol::fs;

crates/zed2/src/languages/tailwind.rs 🔗

@@ -5,9 +5,9 @@ use futures::{
     future::{self, BoxFuture},
     FutureExt, StreamExt,
 };
-use gpui2::AppContext;
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::LanguageServerBinary;
+use gpui::AppContext;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::{json, Value};
 use smol::fs;

crates/zed2/src/languages/typescript.rs 🔗

@@ -3,9 +3,9 @@ use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
 use async_trait::async_trait;
 use futures::{future::BoxFuture, FutureExt};
-use gpui2::AppContext;
-use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
-use lsp2::{CodeActionKind, LanguageServerBinary};
+use gpui::AppContext;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::{CodeActionKind, LanguageServerBinary};
 use node_runtime::NodeRuntime;
 use serde_json::{json, Value};
 use smol::{fs, io::BufReader, stream::StreamExt};
@@ -129,10 +129,10 @@ impl LspAdapter for TypeScriptLspAdapter {
 
     async fn label_for_completion(
         &self,
-        item: &lsp2::CompletionItem,
-        language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
-        use lsp2::CompletionItemKind as Kind;
+        item: &lsp::CompletionItem,
+        language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
+        use lsp::CompletionItemKind as Kind;
         let len = item.label.len();
         let grammar = language.grammar()?;
         let highlight_id = match item.kind? {
@@ -149,7 +149,7 @@ impl LspAdapter for TypeScriptLspAdapter {
             None => item.label.clone(),
         };
 
-        Some(language2::CodeLabel {
+        Some(language::CodeLabel {
             text,
             runs: vec![(0..len, highlight_id)],
             filter_range: 0..len,
@@ -300,9 +300,9 @@ impl LspAdapter for EsLintLspAdapter {
 
     async fn label_for_completion(
         &self,
-        _item: &lsp2::CompletionItem,
-        _language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
+        _item: &lsp::CompletionItem,
+        _language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
         None
     }
 
@@ -335,10 +335,10 @@ async fn get_cached_eslint_server_binary(
 
 #[cfg(test)]
 mod tests {
-    use gpui2::{Context, TestAppContext};
+    use gpui::{Context, TestAppContext};
     use unindent::Unindent;
 
-    #[gpui2::test]
+    #[gpui::test]
     async fn test_outline(cx: &mut TestAppContext) {
         let language = crate::languages::language(
             "typescript",
@@ -363,7 +363,7 @@ mod tests {
         .unindent();
 
         let buffer = cx.build_model(|cx| {
-            language2::Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
+            language::Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
         });
         let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
         assert_eq!(

crates/zed2/src/languages/vue.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::{anyhow, ensure, Result};
 use async_trait::async_trait;
 use futures::StreamExt;
-pub use language2::*;
-use lsp2::{CodeActionKind, LanguageServerBinary};
+pub use language::*;
+use lsp::{CodeActionKind, LanguageServerBinary};
 use node_runtime::NodeRuntime;
 use parking_lot::Mutex;
 use serde_json::Value;
@@ -148,10 +148,10 @@ impl super::LspAdapter for VueLspAdapter {
 
     async fn label_for_completion(
         &self,
-        item: &lsp2::CompletionItem,
-        language: &Arc<language2::Language>,
-    ) -> Option<language2::CodeLabel> {
-        use lsp2::CompletionItemKind as Kind;
+        item: &lsp::CompletionItem,
+        language: &Arc<language::Language>,
+    ) -> Option<language::CodeLabel> {
+        use lsp::CompletionItemKind as Kind;
         let len = item.label.len();
         let grammar = language.grammar()?;
         let highlight_id = match item.kind? {
@@ -171,7 +171,7 @@ impl super::LspAdapter for VueLspAdapter {
             None => item.label.clone(),
         };
 
-        Some(language2::CodeLabel {
+        Some(language::CodeLabel {
             text,
             runs: vec![(0..len, highlight_id)],
             filter_range: 0..len,

crates/zed2/src/languages/yaml.rs 🔗

@@ -1,11 +1,11 @@
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use futures::{future::BoxFuture, FutureExt, StreamExt};
-use gpui2::AppContext;
-use language2::{
+use gpui::AppContext;
+use language::{
     language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate,
 };
-use lsp2::LanguageServerBinary;
+use lsp::LanguageServerBinary;
 use node_runtime::NodeRuntime;
 use serde_json::Value;
 use smol::fs;

crates/zed2/src/main.rs 🔗

@@ -1,3 +1,6 @@
+#![allow(unused_variables, dead_code, unused_mut)]
+// todo!() this is to make transition easier.
+
 // Allow binary to be called Zed for a nice application menu when running executable directly
 #![allow(non_snake_case)]
 
@@ -8,19 +11,19 @@ use cli::{
     ipc::{self, IpcSender},
     CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
 };
-use client2::UserStore;
-use db2::kvp::KEY_VALUE_STORE;
-use fs2::RealFs;
+use client::UserStore;
+use db::kvp::KEY_VALUE_STORE;
+use fs::RealFs;
 use futures::{channel::mpsc, SinkExt, StreamExt};
-use gpui2::{App, AppContext, AsyncAppContext, Context, SemanticVersion, Task};
+use gpui::{Action, App, AppContext, AsyncAppContext, Context, SemanticVersion, Task};
 use isahc::{prelude::Configurable, Request};
-use language2::LanguageRegistry;
+use language::LanguageRegistry;
 use log::LevelFilter;
 
 use node_runtime::RealNodeRuntime;
 use parking_lot::Mutex;
 use serde::{Deserialize, Serialize};
-use settings2::{
+use settings::{
     default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore,
 };
 use simplelog::ConfigBuilder;
@@ -40,13 +43,15 @@ use std::{
     time::{SystemTime, UNIX_EPOCH},
 };
 use util::{
+    async_maybe,
     channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
     http::{self, HttpClient},
     paths, ResultExt,
 };
 use uuid::Uuid;
-use zed2::languages;
-use zed2::{ensure_only_instance, AppState, Assets, IsOnlyInstance};
+use workspace2::{AppState, WorkspaceStore};
+use zed2::{build_window_options, initialize_workspace, languages};
+use zed2::{ensure_only_instance, Assets, IsOnlyInstance};
 
 mod open_listener;
 
@@ -114,7 +119,7 @@ fn main() {
         handle_settings_file_changes(user_settings_file_rx, cx);
         // handle_keymap_file_changes(user_keymap_file_rx, cx);
 
-        let client = client2::Client::new(http.clone(), cx);
+        let client = client::Client::new(http.clone(), cx);
         let mut languages = LanguageRegistry::new(login_shell_env_loaded);
         let copilot_language_server_id = languages.next_language_server_id();
         languages.set_executor(cx.background_executor().clone());
@@ -122,19 +127,19 @@ fn main() {
         let languages = Arc::new(languages);
         let node_runtime = RealNodeRuntime::new(http.clone());
 
-        language2::init(cx);
+        language::init(cx);
         languages::init(languages.clone(), node_runtime.clone(), cx);
         let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
-        // let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
+        let workspace_store = cx.build_model(|cx| WorkspaceStore::new(client.clone(), cx));
 
         cx.set_global(client.clone());
 
-        theme2::init(cx);
+        theme::init(cx);
         // context_menu::init(cx);
-        project2::Project::init(&client, cx);
-        client2::init(&client, cx);
+        project::Project::init(&client, cx);
+        client::init(&client, cx);
         // command_palette::init(cx);
-        language2::init(cx);
+        language::init(cx);
         // editor::init(cx);
         // go_to_line::init(cx);
         // file_finder::init(cx);
@@ -147,7 +152,7 @@ fn main() {
         // semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);
         // vim::init(cx);
         // terminal_view::init(cx);
-        copilot2::init(
+        copilot::init(
             copilot_language_server_id,
             http.clone(),
             node_runtime.clone(),
@@ -170,26 +175,23 @@ fn main() {
 
         // client.telemetry().start(installation_id, session_id, cx);
 
-        // todo!("app_state")
-        let app_state = Arc::new(AppState { client, user_store });
-        // let app_state = Arc::new(AppState {
-        //     languages,
-        //     client: client.clone(),
-        //     user_store,
-        //     fs,
-        //     build_window_options,
-        //     initialize_workspace,
-        //     background_actions,
-        //     workspace_store,
-        //     node_runtime,
-        // });
-        // cx.set_global(Arc::downgrade(&app_state));
+        let app_state = Arc::new(AppState {
+            languages,
+            client: client.clone(),
+            user_store,
+            fs,
+            build_window_options,
+            initialize_workspace,
+            // background_actions: todo!("ask Mikayla"),
+            workspace_store,
+            node_runtime,
+        });
+        cx.set_global(Arc::downgrade(&app_state));
 
         // audio::init(Assets, cx);
         // auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx);
 
-        // todo!("workspace")
-        // workspace::init(app_state.clone(), cx);
+        workspace2::init(app_state.clone(), cx);
         // recent_projects::init(cx);
 
         // journal2::init(app_state.clone(), cx);
@@ -197,7 +199,7 @@ fn main() {
         // theme_selector::init(cx);
         // activity_indicator::init(cx);
         // language_tools::init(cx);
-        call2::init(app_state.client.clone(), app_state.user_store.clone(), cx);
+        call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
         // collab_ui::init(&app_state, cx);
         // feedback::init(cx);
         // welcome::init(cx);
@@ -320,22 +322,30 @@ async fn installation_id() -> Result<String> {
     }
 }
 
-async fn restore_or_create_workspace(_app_state: &Arc<AppState>, mut _cx: AsyncAppContext) {
-    todo!("workspace")
-    // if let Some(location) = workspace::last_opened_workspace_paths().await {
-    //     cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))
-    //         .await
-    //         .log_err();
-    // } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
-    //     cx.update(|cx| show_welcome_experience(app_state, cx));
-    // } else {
-    //     cx.update(|cx| {
-    //         workspace::open_new(app_state, cx, |workspace, cx| {
-    //             Editor::new_file(workspace, &Default::default(), cx)
-    //         })
-    //         .detach();
-    //     });
-    // }
+async fn restore_or_create_workspace(app_state: &Arc<AppState>, mut cx: AsyncAppContext) {
+    async_maybe!({
+        if let Some(location) = workspace2::last_opened_workspace_paths().await {
+            cx.update(|cx| workspace2::open_paths(location.paths().as_ref(), app_state, None, cx))?
+                .await
+                .log_err();
+        } else if matches!(KEY_VALUE_STORE.read_kvp("******* THIS IS A BAD KEY PLEASE UNCOMMENT BELOW TO FIX THIS VERY LONG LINE *******"), Ok(None)) {
+            // todo!(welcome)
+            //} else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
+            //todo!()
+            // cx.update(|cx| show_welcome_experience(app_state, cx));
+        } else {
+            cx.update(|cx| {
+                workspace2::open_new(app_state, cx, |workspace, cx| {
+                    // todo!(editor)
+                    // Editor::new_file(workspace, &Default::default(), cx)
+                })
+                .detach();
+            })?;
+        }
+        anyhow::Ok(())
+    })
+    .await
+    .log_err();
 }
 
 fn init_paths() {
@@ -444,7 +454,7 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
             std::process::exit(-1);
         }
 
-        let app_version = client2::ZED_APP_VERSION
+        let app_version = client::ZED_APP_VERSION
             .or(app_metadata.app_version)
             .map_or("dev".to_string(), |v| v.to_string());
 
@@ -512,11 +522,11 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
 }
 
 fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
-    let telemetry_settings = *client2::TelemetrySettings::get_global(cx);
+    let telemetry_settings = *client::TelemetrySettings::get_global(cx);
 
     cx.background_executor()
         .spawn(async move {
-            let panic_report_url = format!("{}/api/panic", &*client2::ZED_SERVER_URL);
+            let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL);
             let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
             while let Some(child) = children.next().await {
                 let child = child?;
@@ -559,7 +569,7 @@ fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
                     if let Some(panic) = panic {
                         let body = serde_json::to_string(&PanicRequest {
                             panic,
-                            token: client2::ZED_SECRET_CLIENT_TOKEN.into(),
+                            token: client::ZED_SECRET_CLIENT_TOKEN.into(),
                         })
                         .unwrap();
 
@@ -919,11 +929,13 @@ async fn handle_cli_connection(
     }
 }
 
-// pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
-//     &[
-//         ("Go to file", &file_finder::Toggle),
-//         ("Open command palette", &command_palette::Toggle),
-//         ("Open recent projects", &recent_projects::OpenRecent),
-//         ("Change your settings", &zed_actions::OpenSettings),
-//     ]
-// }
+pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
+    // &[
+    //     ("Go to file", &file_finder::Toggle),
+    //     ("Open command palette", &command_palette::Toggle),
+    //     ("Open recent projects", &recent_projects::OpenRecent),
+    //     ("Change your settings", &zed_actions::OpenSettings),
+    // ]
+    // todo!()
+    &[]
+}

crates/zed2/src/zed2.rs 🔗

@@ -1,11 +1,17 @@
+#![allow(unused_variables, dead_code, unused_mut)]
+// todo!() this is to make transition easier.
+
 mod assets;
 pub mod languages;
 mod only_instance;
 mod open_listener;
 
 pub use assets::*;
-use client2::{Client, UserStore};
-use gpui2::{AsyncAppContext, Model};
+use collections::HashMap;
+use gpui::{
+    point, px, AppContext, AsyncAppContext, AsyncWindowContext, Point, Task, TitlebarOptions,
+    WeakView, WindowBounds, WindowKind, WindowOptions,
+};
 pub use only_instance::*;
 pub use open_listener::*;
 
@@ -14,8 +20,14 @@ use cli::{
     ipc::{self, IpcSender},
     CliRequest, CliResponse, IpcHandshake,
 };
-use futures::{channel::mpsc, SinkExt, StreamExt};
-use std::{sync::Arc, thread};
+use futures::{
+    channel::{mpsc, oneshot},
+    FutureExt, SinkExt, StreamExt,
+};
+use std::{path::Path, sync::Arc, thread, time::Duration};
+use util::{paths::PathLikeWithPosition, ResultExt};
+use uuid::Uuid;
+use workspace2::{AppState, Workspace};
 
 pub fn connect_to_cli(
     server_name: &str,
@@ -46,163 +58,350 @@ pub fn connect_to_cli(
     Ok((async_request_rx, response_tx))
 }
 
-pub struct AppState {
-    pub client: Arc<Client>,
-    pub user_store: Model<UserStore>,
-}
-
 pub async fn handle_cli_connection(
-    (mut requests, _responses): (mpsc::Receiver<CliRequest>, IpcSender<CliResponse>),
-    _app_state: Arc<AppState>,
-    mut _cx: AsyncAppContext,
+    (mut requests, responses): (mpsc::Receiver<CliRequest>, IpcSender<CliResponse>),
+    app_state: Arc<AppState>,
+    mut cx: AsyncAppContext,
 ) {
     if let Some(request) = requests.next().await {
         match request {
-            CliRequest::Open { paths: _, wait: _ } => {
-                // let mut caret_positions = HashMap::new();
-
-                // let paths = if paths.is_empty() {
-                // todo!()
-                // workspace::last_opened_workspace_paths()
-                //     .await
-                //     .map(|location| location.paths().to_vec())
-                //     .unwrap_or_default()
-                // } else {
-                //     paths
-                //         .into_iter()
-                //         .filter_map(|path_with_position_string| {
-                //             let path_with_position = PathLikeWithPosition::parse_str(
-                //                 &path_with_position_string,
-                //                 |path_str| {
-                //                     Ok::<_, std::convert::Infallible>(
-                //                         Path::new(path_str).to_path_buf(),
-                //                     )
-                //                 },
-                //             )
-                //             .expect("Infallible");
-                //             let path = path_with_position.path_like;
-                //             if let Some(row) = path_with_position.row {
-                //                 if path.is_file() {
-                //                     let row = row.saturating_sub(1);
-                //                     let col =
-                //                         path_with_position.column.unwrap_or(0).saturating_sub(1);
-                //                     caret_positions.insert(path.clone(), Point::new(row, col));
-                //                 }
-                //             }
-                //             Some(path)
-                //         })
-                //         .collect()
-                // };
-
-                // let mut errored = false;
-                // todo!("workspace")
-                // match cx
-                //     .update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
-                //     .await
-                // {
-                //     Ok((workspace, items)) => {
-                //         let mut item_release_futures = Vec::new();
-
-                //         for (item, path) in items.into_iter().zip(&paths) {
-                //             match item {
-                //                 Some(Ok(item)) => {
-                //                     if let Some(point) = caret_positions.remove(path) {
-                //                         if let Some(active_editor) = item.downcast::<Editor>() {
-                //                             active_editor
-                //                                 .downgrade()
-                //                                 .update(&mut cx, |editor, cx| {
-                //                                     let snapshot =
-                //                                         editor.snapshot(cx).display_snapshot;
-                //                                     let point = snapshot
-                //                                         .buffer_snapshot
-                //                                         .clip_point(point, Bias::Left);
-                //                                     editor.change_selections(
-                //                                         Some(Autoscroll::center()),
-                //                                         cx,
-                //                                         |s| s.select_ranges([point..point]),
-                //                                     );
-                //                                 })
-                //                                 .log_err();
-                //                         }
-                //                     }
-
-                //                     let released = oneshot::channel();
-                //                     cx.update(|cx| {
-                //                         item.on_release(
-                //                             cx,
-                //                             Box::new(move |_| {
-                //                                 let _ = released.0.send(());
-                //                             }),
-                //                         )
-                //                         .detach();
-                //                     });
-                //                     item_release_futures.push(released.1);
-                //                 }
-                //                 Some(Err(err)) => {
-                //                     responses
-                //                         .send(CliResponse::Stderr {
-                //                             message: format!("error opening {:?}: {}", path, err),
-                //                         })
-                //                         .log_err();
-                //                     errored = true;
-                //                 }
-                //                 None => {}
-                //             }
-                //         }
-
-                //         if wait {
-                //             let background = cx.background();
-                //             let wait = async move {
-                //                 if paths.is_empty() {
-                //                     let (done_tx, done_rx) = oneshot::channel();
-                //                     if let Some(workspace) = workspace.upgrade(&cx) {
-                //                         let _subscription = cx.update(|cx| {
-                //                             cx.observe_release(&workspace, move |_, _| {
-                //                                 let _ = done_tx.send(());
-                //                             })
-                //                         });
-                //                         drop(workspace);
-                //                         let _ = done_rx.await;
-                //                     }
-                //                 } else {
-                //                     let _ =
-                //                         futures::future::try_join_all(item_release_futures).await;
-                //                 };
-                //             }
-                //             .fuse();
-                //             futures::pin_mut!(wait);
-
-                //             loop {
-                //                 // Repeatedly check if CLI is still open to avoid wasting resources
-                //                 // waiting for files or workspaces to close.
-                //                 let mut timer = background.timer(Duration::from_secs(1)).fuse();
-                //                 futures::select_biased! {
-                //                     _ = wait => break,
-                //                     _ = timer => {
-                //                         if responses.send(CliResponse::Ping).is_err() {
-                //                             break;
-                //                         }
-                //                     }
-                //                 }
-                //             }
-                //         }
-                //     }
-                //     Err(error) => {
-                //         errored = true;
-                //         responses
-                //             .send(CliResponse::Stderr {
-                //                 message: format!("error opening {:?}: {}", paths, error),
-                //             })
-                //             .log_err();
-                //     }
-                // }
-
-                // responses
-                //     .send(CliResponse::Exit {
-                //         status: i32::from(errored),
-                //     })
-                //     .log_err();
+            CliRequest::Open { paths, wait } => {
+                let mut caret_positions = HashMap::default();
+
+                let paths = if paths.is_empty() {
+                    todo!()
+                    //     workspace::last_opened_workspace_paths()
+                    //         .await
+                    //         .map(|location| location.paths().to_vec())
+                    //         .unwrap_or_default()
+                } else {
+                    paths
+                        .into_iter()
+                        .filter_map(|path_with_position_string| {
+                            let path_with_position = PathLikeWithPosition::parse_str(
+                                &path_with_position_string,
+                                |path_str| {
+                                    Ok::<_, std::convert::Infallible>(
+                                        Path::new(path_str).to_path_buf(),
+                                    )
+                                },
+                            )
+                            .expect("Infallible");
+                            let path = path_with_position.path_like;
+                            if let Some(row) = path_with_position.row {
+                                if path.is_file() {
+                                    let row = row.saturating_sub(1);
+                                    let col =
+                                        path_with_position.column.unwrap_or(0).saturating_sub(1);
+                                    caret_positions.insert(path.clone(), Point::new(row, col));
+                                }
+                            }
+                            Some(path)
+                        })
+                        .collect::<Vec<_>>()
+                };
+
+                let mut errored = false;
+
+                if let Some(open_paths_task) = cx
+                    .update(|cx| workspace2::open_paths(&paths, &app_state, None, cx))
+                    .log_err()
+                {
+                    match open_paths_task.await {
+                        Ok((workspace, items)) => {
+                            let mut item_release_futures = Vec::new();
+
+                            for (item, path) in items.into_iter().zip(&paths) {
+                                match item {
+                                    Some(Ok(mut item)) => {
+                                        if let Some(point) = caret_positions.remove(path) {
+                                            todo!()
+                                            // if let Some(active_editor) = item.downcast::<Editor>() {
+                                            //     active_editor
+                                            //         .downgrade()
+                                            //         .update(&mut cx, |editor, cx| {
+                                            //             let snapshot =
+                                            //                 editor.snapshot(cx).display_snapshot;
+                                            //             let point = snapshot
+                                            //                 .buffer_snapshot
+                                            //                 .clip_point(point, Bias::Left);
+                                            //             editor.change_selections(
+                                            //                 Some(Autoscroll::center()),
+                                            //                 cx,
+                                            //                 |s| s.select_ranges([point..point]),
+                                            //             );
+                                            //         })
+                                            //         .log_err();
+                                            // }
+                                        }
+
+                                        let released = oneshot::channel();
+                                        cx.update(move |cx| {
+                                            item.on_release(
+                                                cx,
+                                                Box::new(move |_| {
+                                                    let _ = released.0.send(());
+                                                }),
+                                            )
+                                            .detach();
+                                        })
+                                        .ok();
+                                        item_release_futures.push(released.1);
+                                    }
+                                    Some(Err(err)) => {
+                                        responses
+                                            .send(CliResponse::Stderr {
+                                                message: format!(
+                                                    "error opening {:?}: {}",
+                                                    path, err
+                                                ),
+                                            })
+                                            .log_err();
+                                        errored = true;
+                                    }
+                                    None => {}
+                                }
+                            }
+
+                            if wait {
+                                let executor = cx.background_executor().clone();
+                                let wait = async move {
+                                    if paths.is_empty() {
+                                        let (done_tx, done_rx) = oneshot::channel();
+                                        let _subscription =
+                                            workspace.update(&mut cx, move |_, cx| {
+                                                cx.on_release(|_, _| {
+                                                    let _ = done_tx.send(());
+                                                })
+                                            });
+                                        let _ = done_rx.await;
+                                    } else {
+                                        let _ = futures::future::try_join_all(item_release_futures)
+                                            .await;
+                                    };
+                                }
+                                .fuse();
+                                futures::pin_mut!(wait);
+
+                                loop {
+                                    // Repeatedly check if CLI is still open to avoid wasting resources
+                                    // waiting for files or workspaces to close.
+                                    let mut timer = executor.timer(Duration::from_secs(1)).fuse();
+                                    futures::select_biased! {
+                                        _ = wait => break,
+                                        _ = timer => {
+                                            if responses.send(CliResponse::Ping).is_err() {
+                                                break;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        Err(error) => {
+                            errored = true;
+                            responses
+                                .send(CliResponse::Stderr {
+                                    message: format!("error opening {:?}: {}", paths, error),
+                                })
+                                .log_err();
+                        }
+                    }
+
+                    responses
+                        .send(CliResponse::Exit {
+                            status: i32::from(errored),
+                        })
+                        .log_err();
+                }
             }
         }
     }
 }
+
+pub fn build_window_options(
+    bounds: Option<WindowBounds>,
+    display_uuid: Option<Uuid>,
+    cx: &mut AppContext,
+) -> WindowOptions {
+    let bounds = bounds.unwrap_or(WindowBounds::Maximized);
+    let display = display_uuid.and_then(|uuid| {
+        cx.displays()
+            .into_iter()
+            .find(|display| display.uuid().ok() == Some(uuid))
+    });
+
+    WindowOptions {
+        bounds,
+        titlebar: Some(TitlebarOptions {
+            title: None,
+            appears_transparent: true,
+            traffic_light_position: Some(point(px(8.), px(8.))),
+        }),
+        center: false,
+        focus: false,
+        show: false,
+        kind: WindowKind::Normal,
+        is_movable: true,
+        display_id: display.map(|display| display.id()),
+    }
+}
+
+pub fn initialize_workspace(
+    workspace_handle: WeakView<Workspace>,
+    was_deserialized: bool,
+    app_state: Arc<AppState>,
+    cx: AsyncWindowContext,
+) -> Task<Result<()>> {
+    cx.spawn(|mut cx| async move {
+        workspace_handle.update(&mut cx, |workspace, cx| {
+            let workspace_handle = cx.view();
+            cx.subscribe(&workspace_handle, {
+                move |workspace, _, event, cx| {
+                    if let workspace2::Event::PaneAdded(pane) = event {
+                        pane.update(cx, |pane, cx| {
+                            // todo!()
+                            // pane.toolbar().update(cx, |toolbar, cx| {
+                            //     let breadcrumbs = cx.add_view(|_| Breadcrumbs::new(workspace));
+                            //     toolbar.add_item(breadcrumbs, cx);
+                            //     let buffer_search_bar = cx.add_view(BufferSearchBar::new);
+                            //     toolbar.add_item(buffer_search_bar.clone(), cx);
+                            //     let quick_action_bar = cx.add_view(|_| {
+                            //         QuickActionBar::new(buffer_search_bar, workspace)
+                            //     });
+                            //     toolbar.add_item(quick_action_bar, cx);
+                            //     let diagnostic_editor_controls =
+                            //         cx.add_view(|_| diagnostics2::ToolbarControls::new());
+                            //     toolbar.add_item(diagnostic_editor_controls, cx);
+                            //     let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
+                            //     toolbar.add_item(project_search_bar, cx);
+                            //     let submit_feedback_button =
+                            //         cx.add_view(|_| SubmitFeedbackButton::new());
+                            //     toolbar.add_item(submit_feedback_button, cx);
+                            //     let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new());
+                            //     toolbar.add_item(feedback_info_text, cx);
+                            //     let lsp_log_item =
+                            //         cx.add_view(|_| language_tools::LspLogToolbarItemView::new());
+                            //     toolbar.add_item(lsp_log_item, cx);
+                            //     let syntax_tree_item = cx
+                            //         .add_view(|_| language_tools::SyntaxTreeToolbarItemView::new());
+                            //     toolbar.add_item(syntax_tree_item, cx);
+                            // })
+                        });
+                    }
+                }
+            })
+            .detach();
+
+            //     cx.emit(workspace2::Event::PaneAdded(
+            //         workspace.active_pane().clone(),
+            //     ));
+
+            //     let collab_titlebar_item =
+            //         cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx));
+            //     workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx);
+
+            //     let copilot =
+            //         cx.add_view(|cx| copilot_button::CopilotButton::new(app_state.fs.clone(), cx));
+            //     let diagnostic_summary =
+            //         cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx));
+            //     let activity_indicator = activity_indicator::ActivityIndicator::new(
+            //         workspace,
+            //         app_state.languages.clone(),
+            //         cx,
+            //     );
+            //     let active_buffer_language =
+            //         cx.add_view(|_| language_selector::ActiveBufferLanguage::new(workspace));
+            //     let vim_mode_indicator = cx.add_view(|cx| vim::ModeIndicator::new(cx));
+            //     let feedback_button = cx.add_view(|_| {
+            //         feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace)
+            //     });
+            //     let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new());
+            workspace.status_bar().update(cx, |status_bar, cx| {
+                // status_bar.add_left_item(diagnostic_summary, cx);
+                // status_bar.add_left_item(activity_indicator, cx);
+
+                // status_bar.add_right_item(feedback_button, cx);
+                // status_bar.add_right_item(copilot, cx);
+                // status_bar.add_right_item(active_buffer_language, cx);
+                // status_bar.add_right_item(vim_mode_indicator, cx);
+                // status_bar.add_right_item(cursor_position, cx);
+            });
+
+            //     auto_update::notify_of_any_new_update(cx.weak_handle(), cx);
+
+            //     vim::observe_keystrokes(cx);
+
+            //     cx.on_window_should_close(|workspace, cx| {
+            //         if let Some(task) = workspace.close(&Default::default(), cx) {
+            //             task.detach_and_log_err(cx);
+            //         }
+            //         false
+            //     });
+            // })?;
+
+            // let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
+            // let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
+            // let assistant_panel = AssistantPanel::load(workspace_handle.clone(), cx.clone());
+            // let channels_panel =
+            //     collab_ui::collab_panel::CollabPanel::load(workspace_handle.clone(), cx.clone());
+            // let chat_panel =
+            //     collab_ui::chat_panel::ChatPanel::load(workspace_handle.clone(), cx.clone());
+            // let notification_panel = collab_ui::notification_panel::NotificationPanel::load(
+            //     workspace_handle.clone(),
+            //     cx.clone(),
+            // );
+            // let (
+            //     project_panel,
+            //     terminal_panel,
+            //     assistant_panel,
+            //     channels_panel,
+            //     chat_panel,
+            //     notification_panel,
+            // ) = futures::try_join!(
+            //     project_panel,
+            //     terminal_panel,
+            //     assistant_panel,
+            //     channels_panel,
+            //     chat_panel,
+            //     notification_panel,
+            // )?;
+            // workspace_handle.update(&mut cx, |workspace, cx| {
+            //     let project_panel_position = project_panel.position(cx);
+            //     workspace.add_panel_with_extra_event_handler(
+            //         project_panel,
+            //         cx,
+            //         |workspace, _, event, cx| match event {
+            //             project_panel::Event::NewSearchInDirectory { dir_entry } => {
+            //                 search::ProjectSearchView::new_search_in_directory(workspace, dir_entry, cx)
+            //             }
+            //             project_panel::Event::ActivatePanel => {
+            //                 workspace.focus_panel::<ProjectPanel>(cx);
+            //             }
+            //             _ => {}
+            //         },
+            //     );
+            //     workspace.add_panel(terminal_panel, cx);
+            //     workspace.add_panel(assistant_panel, cx);
+            //     workspace.add_panel(channels_panel, cx);
+            //     workspace.add_panel(chat_panel, cx);
+            //     workspace.add_panel(notification_panel, cx);
+
+            //     if !was_deserialized
+            //         && workspace
+            //             .project()
+            //             .read(cx)
+            //             .visible_worktrees(cx)
+            //             .any(|tree| {
+            //                 tree.read(cx)
+            //                     .root_entry()
+            //                     .map_or(false, |entry| entry.is_dir())
+            //             })
+            //     {
+            //         workspace.toggle_dock(project_panel_position, cx);
+            //     }
+            //     cx.focus_self();
+        })?;
+        Ok(())
+    })
+}