diff --git a/Cargo.lock b/Cargo.lock index 6e26d387e6..e69eeca9e2 100644 --- a/Cargo.lock +++ b/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]] diff --git a/Cargo.toml b/Cargo.toml index faae9871f8..d54e683b11 100644 --- a/Cargo.toml +++ b/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", diff --git a/crates/ai2/Cargo.toml b/crates/ai2/Cargo.toml index 4f06840e8e..aee265db6e 100644 --- a/crates/ai2/Cargo.toml +++ b/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"] } diff --git a/crates/ai2/src/auth.rs b/crates/ai2/src/auth.rs index 995f20d39c..baa1fe7b83 100644 --- a/crates/ai2/src/auth.rs +++ b/crates/ai2/src/auth.rs @@ -1,4 +1,4 @@ -use gpui2::AppContext; +use gpui::AppContext; #[derive(Clone, Debug)] pub enum ProviderCredential { diff --git a/crates/ai2/src/embedding.rs b/crates/ai2/src/embedding.rs index 7ea4786178..6768b7ce7b 100644 --- a/crates/ai2/src/embedding.rs +++ b/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.]) diff --git a/crates/ai2/src/prompts/base.rs b/crates/ai2/src/prompts/base.rs index 29091d0f5b..75bad00154 100644 --- a/crates/ai2/src/prompts/base.rs +++ b/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; diff --git a/crates/ai2/src/prompts/file_context.rs b/crates/ai2/src/prompts/file_context.rs index 4a741beb24..f108a62f6f 100644 --- a/crates/ai2/src/prompts/file_context.rs +++ b/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; diff --git a/crates/ai2/src/prompts/repository_context.rs b/crates/ai2/src/prompts/repository_context.rs index 1bb75de7d2..0d831c2cb2 100644 --- a/crates/ai2/src/prompts/repository_context.rs +++ b/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 { diff --git a/crates/ai2/src/providers/open_ai/completion.rs b/crates/ai2/src/providers/open_ai/completion.rs index bf9dc704a2..3e49fc5290 100644 --- a/crates/ai2/src/providers/open_ai/completion.rs +++ b/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}; diff --git a/crates/ai2/src/providers/open_ai/embedding.rs b/crates/ai2/src/providers/open_ai/embedding.rs index 27a01328f3..8f62c8dc0d 100644 --- a/crates/ai2/src/providers/open_ai/embedding.rs +++ b/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}; diff --git a/crates/ai2/src/test.rs b/crates/ai2/src/test.rs index b061a47139..3d59febbe9 100644 --- a/crates/ai2/src/test.rs +++ b/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::{ diff --git a/crates/audio2/Cargo.toml b/crates/audio2/Cargo.toml index 298142dbef..3688f108f4 100644 --- a/crates/audio2/Cargo.toml +++ b/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" } diff --git a/crates/audio2/src/assets.rs b/crates/audio2/src/assets.rs index 66e0bf5aa5..b58e1f6aee 100644 --- a/crates/audio2/src/assets.rs +++ b/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, diff --git a/crates/audio2/src/audio2.rs b/crates/audio2/src/audio2.rs index 286b07aba1..9264ed25d6 100644 --- a/crates/audio2/src/audio2.rs +++ b/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; diff --git a/crates/call2/Cargo.toml b/crates/call2/Cargo.toml index e918ada3e8..9e13463680 100644 --- a/crates/call2/Cargo.toml +++ b/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"] } diff --git a/crates/call2/src/call2.rs b/crates/call2/src/call2.rs index 9383f9845f..477931919d 100644 --- a/crates/call2/src/call2.rs +++ b/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, user_store: Model, - _subscriptions: Vec, + _subscriptions: Vec, } impl EventEmitter for ActiveCall { diff --git a/crates/call2/src/call_settings.rs b/crates/call2/src/call_settings.rs index c83ed73980..9375feedf0 100644 --- a/crates/call2/src/call_settings.rs +++ b/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 { diff --git a/crates/call2/src/participant.rs b/crates/call2/src/participant.rs index 9fe212e776..f62d103f17 100644 --- a/crates/call2/src/participant.rs +++ b/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>, - pub audio_tracks: HashMap>, + pub video_tracks: HashMap>, + pub audio_tracks: HashMap>, } diff --git a/crates/call2/src/room.rs b/crates/call2/src/room.rs index deeec1df24..a46269a508 100644 --- a/crates/call2/src/room.rs +++ b/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, user_store: Model, follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec>, - client_subscriptions: Vec, - _subscriptions: Vec, + client_subscriptions: Vec, + _subscriptions: Vec, room_update_completed_tx: watch::Sender>, room_update_completed_rx: watch::Receiver>, pending_room_update: Option>, @@ -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 { 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) { + pub fn set_display_sources(&self, sources: Vec) { self.live_kit .as_ref() .unwrap() @@ -1514,7 +1514,7 @@ impl Room { } struct LiveKitRoom { - room: Arc, + room: Arc, 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. diff --git a/crates/client2/Cargo.toml b/crates/client2/Cargo.toml index 8a6edbb428..45e1f618d2 100644 --- a/crates/client2/Cargo.toml +++ b/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"] } diff --git a/crates/client2/src/client2.rs b/crates/client2/src/client2.rs index b933b62a6f..6494e0350b 100644 --- a/crates/client2/src/client2.rs +++ b/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, } -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)); diff --git a/crates/client2/src/telemetry.rs b/crates/client2/src/telemetry.rs index 0ef5f0d140..3723f7b906 100644 --- a/crates/client2/src/telemetry.rs +++ b/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, diff --git a/crates/client2/src/test.rs b/crates/client2/src/test.rs index 61f94580c3..5462799103 100644 --- a/crates/client2/src/test.rs +++ b/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, }; diff --git a/crates/client2/src/user.rs b/crates/client2/src/user.rs index 2a8cf34af4..baf3a19dad 100644 --- a/crates/client2/src/user.rs +++ b/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; diff --git a/crates/copilot2/Cargo.toml b/crates/copilot2/Cargo.toml index f83824d808..2021194607 100644 --- a/crates/copilot2/Cargo.toml +++ b/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"] } diff --git a/crates/copilot2/src/copilot2.rs b/crates/copilot2/src/copilot2.rs index 3b059775cd..6b1190a5bf 100644 --- a/crates/copilot2/src/copilot2.rs +++ b/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>, } @@ -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::DidChangeTextDocumentParams { - text_document: lsp2::VersionedTextDocumentIdentifier::new( + .notify::( + lsp::DidChangeTextDocumentParams { + text_document: lsp::VersionedTextDocumentIdentifier::new( buffer.uri.clone(), buffer.snapshot_version, ), @@ -280,7 +280,7 @@ pub struct Copilot { server: CopilotServer, buffers: HashSet>, 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::DidOpenTextDocumentParams { - text_document: lsp2::TextDocumentItem { + .notify::( + 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, - event: &language2::Event, + event: &language::Event, cx: &mut ModelContext, ) -> 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::DidSaveTextDocumentParams { - text_document: lsp2::TextDocumentIdentifier::new( + .notify::( + 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::DidCloseTextDocumentParams { - text_document: lsp2::TextDocumentIdentifier::new(old_uri), + .notify::( + lsp::DidCloseTextDocumentParams { + text_document: lsp::TextDocumentIdentifier::new(old_uri), }, )?; server .lsp - .notify::( - lsp2::DidOpenTextDocumentParams { - text_document: lsp2::TextDocumentItem::new( + .notify::( + 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::DidCloseTextDocumentParams { - text_document: lsp2::TextDocumentIdentifier::new(buffer.uri), + .notify::( + lsp::DidCloseTextDocumentParams { + text_document: lsp::TextDocumentIdentifier::new(buffer.uri), }, ) .log_err(); @@ -798,7 +798,7 @@ impl Copilot { ) -> Task>> 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>) -> String { } } -fn uri_for_buffer(buffer: &Model, cx: &AppContext) -> lsp2::Url { +fn uri_for_buffer(buffer: &Model, 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() } diff --git a/crates/copilot2/src/request.rs b/crates/copilot2/src/request.rs index fee92051dc..0f9a478b91 100644 --- a/crates/copilot2/src/request.rs +++ b/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, } -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, } -impl lsp2::request::Request for NotifyRejected { +impl lsp::request::Request for NotifyRejected { type Params = NotifyRejectedParams; type Result = String; const METHOD: &'static str = "notifyRejected"; diff --git a/crates/db2/Cargo.toml b/crates/db2/Cargo.toml index 6ef8ec0874..c73e6314c5 100644 --- a/crates/db2/Cargo.toml +++ b/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 diff --git a/crates/db2/src/db2.rs b/crates/db2/src/db2.rs index e052d59d12..573845aa2e 100644 --- a/crates/db2/src/db2.rs +++ b/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 {} diff --git a/crates/db2/src/kvp.rs b/crates/db2/src/kvp.rs index b4445e3586..0b0cdd9aa1 100644 --- a/crates/db2/src/kvp.rs +++ b/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); diff --git a/crates/feature_flags2/Cargo.toml b/crates/feature_flags2/Cargo.toml index ad77330ac3..8ae39b31db 100644 --- a/crates/feature_flags2/Cargo.toml +++ b/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 diff --git a/crates/feature_flags2/src/feature_flags2.rs b/crates/feature_flags2/src/feature_flags2.rs index 446a2867e5..23167796ec 100644 --- a/crates/feature_flags2/src/feature_flags2.rs +++ b/crates/feature_flags2/src/feature_flags2.rs @@ -1,4 +1,4 @@ -use gpui2::{AppContext, Subscription, ViewContext}; +use gpui::{AppContext, Subscription, ViewContext}; #[derive(Default)] struct FeatureFlags { diff --git a/crates/fs2/Cargo.toml b/crates/fs2/Cargo.toml index 36f4e9c9c9..636def05ec 100644 --- a/crates/fs2/Cargo.toml +++ b/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"] diff --git a/crates/fs2/src/fs2.rs b/crates/fs2/src/fs2.rs index 82b5aead07..350a33b208 100644 --- a/crates/fs2/src/fs2.rs +++ b/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, - 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 { + pub fn new(executor: gpui::BackgroundExecutor) -> Arc { 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( diff --git a/crates/fuzzy2/Cargo.toml b/crates/fuzzy2/Cargo.toml index 5b92a27a27..a112554a39 100644 --- a/crates/fuzzy2/Cargo.toml +++ b/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" } diff --git a/crates/fuzzy2/src/paths.rs b/crates/fuzzy2/src/paths.rs index 4990b9e5b5..e982195158 100644 --- a/crates/fuzzy2/src/paths.rs +++ b/crates/fuzzy2/src/paths.rs @@ -1,4 +1,4 @@ -use gpui2::BackgroundExecutor; +use gpui::BackgroundExecutor; use std::{ borrow::Cow, cmp::{self, Ordering}, diff --git a/crates/fuzzy2/src/strings.rs b/crates/fuzzy2/src/strings.rs index 7c71496a13..085362dd2c 100644 --- a/crates/fuzzy2/src/strings.rs +++ b/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}, diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 5a6e360802..da4d59daed 100644 --- a/crates/gpui2/src/app.rs +++ b/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>); +/// Temporary(?) wrapper around RefCell to help us debug any double borrows. +/// Strongly consider removing after stabilization. +pub struct AppCell { + app: RefCell, +} + +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); /// 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) -> anyhow::Result>; -type FrameCallback = Box; +pub(crate) type FrameCallback = Box; type Handler = Box bool + 'static>; type Listener = Box bool + 'static>; type QuitHandler = Box LocalBoxFuture<'static, ()> + 'static>; type ReleaseListener = Box; +// struct FrameConsumer { +// next_frame_callbacks: Vec, +// task: Task<()>, +// display_linker +// } + pub struct AppContext { - this: Weak>, + this: Weak, pub(crate) platform: Rc, app_metadata: AppMetadata, text_system: Arc, @@ -127,6 +158,7 @@ pub struct AppContext { pending_updates: usize, pub(crate) active_drag: Option, pub(crate) next_frame_callbacks: HashMap>, + pub(crate) frame_consumers: HashMap>, pub(crate) background_executor: BackgroundExecutor, pub(crate) foreground_executor: ForegroundExecutor, pub(crate) svg_renderer: SvgRenderer, @@ -157,7 +189,7 @@ impl AppContext { platform: Rc, asset_source: Arc, http_client: Arc, - ) -> Rc> { + ) -> Rc { 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) }) diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index 4bbab43446..e3ae78d78f 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/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>, + pub(crate) app: Weak, 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)) } diff --git a/crates/gpui2/src/app/test_context.rs b/crates/gpui2/src/app/test_context.rs index aaf42dd4a2..e731dccc6e 100644 --- a/crates/gpui2/src/app/test_context.rs +++ b/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>, + pub app: Rc, pub background_executor: BackgroundExecutor, pub foreground_executor: ForegroundExecutor, } diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index 747e573ea5..a35436d74e 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/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() diff --git a/crates/gpui2/src/platform.rs b/crates/gpui2/src/platform.rs index cdce67d8c1..a4be21ddf3 100644 --- a/crates/gpui2/src/platform.rs +++ b/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, + callback: Box, ); fn start_display_link(&self, display_id: DisplayId); fn stop_display_link(&self, display_id: DisplayId); diff --git a/crates/gpui2/src/platform/mac/display_linker.rs b/crates/gpui2/src/platform/mac/display_linker.rs index 6d8235a381..b63cf24e26 100644 --- a/crates/gpui2/src/platform/mac/display_linker.rs +++ b/crates/gpui2/src/platform/mac/display_linker.rs @@ -26,13 +26,13 @@ impl MacDisplayLinker { } } -type OutputCallback = Mutex>; +type OutputCallback = Mutex>; impl MacDisplayLinker { pub fn set_output_callback( &mut self, display_id: DisplayId, - output_callback: Box, + output_callback: Box, ) { if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } { let callback = Arc::new(Mutex::new(output_callback)); diff --git a/crates/gpui2/src/platform/mac/platform.rs b/crates/gpui2/src/platform/mac/platform.rs index fdc7fd6ae5..7065c02e87 100644 --- a/crates/gpui2/src/platform/mac/platform.rs +++ b/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, + callback: Box, ) { self.0 .lock() diff --git a/crates/gpui2/src/platform/mac/shaders.metal b/crates/gpui2/src/platform/mac/shaders.metal index 444842d9b2..0a3a2b2129 100644 --- a/crates/gpui2/src/platform/mac/shaders.metal +++ b/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 = diff --git a/crates/gpui2/src/platform/mac/window.rs b/crates/gpui2/src/platform/mac/window.rs index 52dcf31603..affeab57c7 100644 --- a/crates/gpui2/src/platform/mac/window.rs +++ b/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 bool>) { diff --git a/crates/gpui2/src/platform/test/platform.rs b/crates/gpui2/src/platform/test/platform.rs index 524611b620..b4f3c739e6 100644 --- a/crates/gpui2/src/platform/test/platform.rs +++ b/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, + _callback: Box, ) { unimplemented!() } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index f1335b3cff..cebf546217 100644 --- a/crates/gpui2/src/window.rs +++ b/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, display_id: DisplayId, sprite_atlas: Arc, @@ -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::>(); - for callback in callbacks { + .collect::>() + { callback(cx); } + }) + .ok(); - if cx.next_frame_callbacks.get(&display_id).unwrap().is_empty() { + // Flush effects, then stop the display link if no new next_frame_callbacks have been added. + + 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> { + 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. diff --git a/crates/gpui2_macros/src/test.rs b/crates/gpui2_macros/src/test.rs index f7e45a90f9..acaaee597b 100644 --- a/crates/gpui2_macros/src/test.rs +++ b/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| { diff --git a/crates/install_cli2/Cargo.toml b/crates/install_cli2/Cargo.toml index 0dd1b907fd..3310e7fbc8 100644 --- a/crates/install_cli2/Cargo.toml +++ b/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" } diff --git a/crates/install_cli2/src/install_cli2.rs b/crates/install_cli2/src/install_cli2.rs index e24a48ef07..7938d60210 100644 --- a/crates/install_cli2/src/install_cli2.rs +++ b/crates/install_cli2/src/install_cli2.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Result}; -use gpui2::AsyncAppContext; +use gpui::AsyncAppContext; use std::path::Path; use util::ResultExt; diff --git a/crates/journal2/Cargo.toml b/crates/journal2/Cargo.toml index 8da2f51a62..f43d90fc85 100644 --- a/crates/journal2/Cargo.toml +++ b/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" } diff --git a/crates/journal2/src/journal2.rs b/crates/journal2/src/journal2.rs index d875cb3834..fa6e05cca7 100644 --- a/crates/journal2/src/journal2.rs +++ b/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; diff --git a/crates/language2/Cargo.toml b/crates/language2/Cargo.toml index 1e49d0890f..4fca16bcb5 100644 --- a/crates/language2/Cargo.toml +++ b/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 diff --git a/crates/language2/src/buffer.rs b/crates/language2/src/buffer.rs index 3999f275f2..36c1f39e1c 100644 --- a/crates/language2/src/buffer.rs +++ b/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, - 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) } } diff --git a/crates/language2/src/buffer_tests.rs b/crates/language2/src/buffer_tests.rs index 16306fe2ce..c0bd068973 100644 --- a/crates/language2/src/buffer_tests.rs +++ b/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, query: &'a str, - cx: &'a gpui2::TestAppContext, + cx: &'a gpui::TestAppContext, ) -> Vec<(&'a str, Vec)> { 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, cx: &mut gpui2::TestAppContext) -> String { +fn get_tree_sexp(buffer: &Model, cx: &mut gpui::TestAppContext) -> String { buffer.update(cx, |buffer, _| { let snapshot = buffer.snapshot(); let layers = snapshot.syntax.layers(buffer.as_text_snapshot()); diff --git a/crates/language2/src/diagnostic_set.rs b/crates/language2/src/diagnostic_set.rs index 5247af285e..f269fce88d 100644 --- a/crates/language2/src/diagnostic_set.rs +++ b/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 DiagnosticEntry { // 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() diff --git a/crates/language2/src/highlight_map.rs b/crates/language2/src/highlight_map.rs index b394d0446e..aeeda546bf 100644 --- a/crates/language2/src/highlight_map.rs +++ b/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() { diff --git a/crates/language2/src/language2.rs b/crates/language2/src/language2.rs index ea13dc185f..381284659b 100644 --- a/crates/language2/src/language2.rs +++ b/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, ) -> Option { 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, ) -> Option { self.adapter.label_for_symbol(name, kind, language).await @@ -289,13 +289,13 @@ pub trait LspAdapter: 'static + Send + Sync { container_dir: PathBuf, ) -> Option; - 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, ) -> Option { None @@ -304,7 +304,7 @@ pub trait LspAdapter: 'static + Send + Sync { async fn label_for_symbol( &self, _: &str, - _: lsp2::SymbolKind, + _: lsp::SymbolKind, _: &Arc, ) -> Option { None @@ -476,8 +476,8 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result, D pub struct FakeLspAdapter { pub name: &'static str, pub initialization_options: Option, - pub capabilities: lsp2::ServerCapabilities, - pub initializer: Option>, + pub capabilities: lsp::ServerCapabilities, + pub initializer: Option>, pub disk_based_diagnostics_progress_token: Option, pub disk_based_diagnostics_sources: Vec, pub prettier_plugins: Vec<&'static str>, @@ -532,7 +532,7 @@ pub struct Language { #[cfg(any(test, feature = "test-support"))] fake_adapter: Option<( - mpsc::UnboundedSender, + mpsc::UnboundedSender, Arc, )>, } @@ -649,7 +649,7 @@ struct LanguageRegistryState { pub struct PendingLanguageServer { pub server_id: LanguageServerId, - pub task: Task>, + pub task: Task>, pub container_dir: Option>, } @@ -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::() + .try_receive_notification::() .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, - ) -> mpsc::UnboundedReceiver { + ) -> mpsc::UnboundedReceiver { 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, completion: &mut lsp2::CompletionItem) { + pub async fn process_completion(self: &Arc, 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, - completion: &lsp2::CompletionItem, + completion: &lsp::CompletionItem, ) -> Option { self.adapters .first() @@ -1521,7 +1521,7 @@ impl Language { pub async fn label_for_symbol( self: &Arc, name: &str, - kind: lsp2::SymbolKind, + kind: lsp::SymbolKind, ) -> Option { 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 { unreachable!(); } - fn process_diagnostics(&self, _: &mut lsp2::PublishDiagnosticsParams) {} + fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} async fn disk_based_diagnostic_sources(&self) -> Vec { self.disk_based_diagnostics_sources.clone() @@ -1824,22 +1824,22 @@ fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option)]) } } -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 { +pub fn point_from_lsp(point: lsp::Position) -> Unclipped { Unclipped(PointUtf16::new(point.line, point.character)) } -pub fn range_to_lsp(range: Range) -> lsp2::Range { - lsp2::Range { +pub fn range_to_lsp(range: Range) -> 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> { +pub fn range_from_lsp(range: lsp::Range) -> Range> { 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> { #[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()); diff --git a/crates/language2/src/language_settings.rs b/crates/language2/src/language_settings.rs index 4816e506db..49977f690c 100644 --- a/crates/language2/src/language_settings.rs +++ b/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::(); diff --git a/crates/language2/src/outline.rs b/crates/language2/src/outline.rs index 94dfaa0e11..4bcbdcd27f 100644 --- a/crates/language2/src/outline.rs +++ b/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 Outline { 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 { diff --git a/crates/language2/src/proto.rs b/crates/language2/src/proto.rs index f90bb94742..c4abe39d47 100644 --- a/crates/language2/src/proto.rs +++ b/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::*; diff --git a/crates/language2/src/syntax_map/syntax_map_tests.rs b/crates/language2/src/syntax_map/syntax_map_tests.rs index 732ed7e936..bd50608122 100644 --- a/crates/language2/src/syntax_map/syntax_map_tests.rs +++ b/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#"
@@ -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 diff --git a/crates/live_kit_client2/Cargo.toml b/crates/live_kit_client2/Cargo.toml index b5b45a8d45..5adb711948 100644 --- a/crates/live_kit_client2/Cargo.toml +++ b/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" diff --git a/crates/lsp2/Cargo.toml b/crates/lsp2/Cargo.toml index a32dd2b6b2..12993eedb6 100644 --- a/crates/lsp2/Cargo.toml +++ b/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" } diff --git a/crates/lsp2/src/lsp2.rs b/crates/lsp2/src/lsp2.rs index 8871c8eaef..356d029c58 100644 --- a/crates/lsp2/src/lsp2.rs +++ b/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>, { 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::(); @@ -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()); diff --git a/crates/menu2/Cargo.toml b/crates/menu2/Cargo.toml index c366de6866..9bf61db82c 100644 --- a/crates/menu2/Cargo.toml +++ b/crates/menu2/Cargo.toml @@ -9,4 +9,4 @@ path = "src/menu2.rs" doctest = false [dependencies] -gpui2 = { path = "../gpui2" } +gpui = { package = "gpui2", path = "../gpui2" } diff --git a/crates/multi_buffer2/Cargo.toml b/crates/multi_buffer2/Cargo.toml index 4c56bab9dc..a57ef29531 100644 --- a/crates/multi_buffer2/Cargo.toml +++ b/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 diff --git a/crates/multi_buffer2/src/anchor.rs b/crates/multi_buffer2/src/anchor.rs index fa65bfc800..39a8182da1 100644 --- a/crates/multi_buffer2/src/anchor.rs +++ b/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}, diff --git a/crates/multi_buffer2/src/multi_buffer2.rs b/crates/multi_buffer2/src/multi_buffer2.rs index b5a7ced517..df33f98b4b 100644 --- a/crates/multi_buffer2/src/multi_buffer2.rs +++ b/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, predecessor: ExcerptId, - excerpts: Vec<(ExcerptId, ExcerptRange)>, + excerpts: Vec<(ExcerptId, ExcerptRange)>, }, ExcerptsRemoved { ids: Vec, @@ -130,7 +130,7 @@ struct BufferState { last_file_update_count: usize, last_git_diff_update_count: usize, excerpts: Vec, - _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) where - T: IntoIterator, &'a language2::Transaction)>, + T: IntoIterator, &'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, language2::Anchor)> { + ) -> Option<(Model, 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, - event: &language2::Event, + event: &language::Event, cx: &mut ModelContext, ) { 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 { + pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model { 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( excerpts: [(&str, Vec>); COUNT], - cx: &mut gpui2::AppContext, + cx: &mut gpui::AppContext, ) -> Model { 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, cx: &mut gpui2::AppContext) -> Model { + pub fn build_from_buffer(buffer: Model, cx: &mut gpui::AppContext) -> Model { cx.build_model(|cx| Self::singleton(buffer, cx)) } - pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui2::AppContext) -> Model { + pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model { 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, ) where - T: IntoIterator, &'a language2::Transaction)>, + T: IntoIterator, &'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::>(), &[]); } - #[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); diff --git a/crates/prettier2/Cargo.toml b/crates/prettier2/Cargo.toml index b98124f72c..de229dcc70 100644 --- a/crates/prettier2/Cargo.toml +++ b/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"] } diff --git a/crates/prettier2/src/prettier2.rs b/crates/prettier2/src/prettier2.rs index 6d9664b234..d9b6f9eab7 100644 --- a/crates/prettier2/src/prettier2.rs +++ b/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, cx: AsyncAppContext, ) -> anyhow::Result { - 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"; diff --git a/crates/project2/Cargo.toml b/crates/project2/Cargo.toml index f1c0716d58..7aae9fb007 100644 --- a/crates/project2/Cargo.toml +++ b/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 diff --git a/crates/project2/src/lsp_command.rs b/crates/project2/src/lsp_command.rs index 9e6a96e15e..cc1821d3ff 100644 --- a/crates/project2/src/lsp_command.rs +++ b/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, cx: &AppContext, - ) -> ::Params; + ) -> ::Params; async fn response_from_lsp( self, - message: ::Result, + message: ::Result, project: Model, buffer: Model, server_id: LanguageServerId, @@ -140,8 +140,8 @@ pub(crate) struct FormattingOptions { tab_size: u32, } -impl From for FormattingOptions { - fn from(value: lsp2::FormattingOptions) -> Self { +impl From for FormattingOptions { + fn from(value: lsp::FormattingOptions) -> Self { Self { tab_size: value.tab_size, } @@ -151,11 +151,11 @@ impl From for FormattingOptions { #[async_trait(?Send)] impl LspCommand for PrepareRename { type Response = Option>; - 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, _: &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, + message: Option, _: Model, buffer: Model, _: LanguageServerId, @@ -187,8 +187,8 @@ impl LspCommand for PrepareRename { ) -> Result>> { 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, _: &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, + message: Option, project: Model, buffer: Model, 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; - 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, _: &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, + message: Option, project: Model, buffer: Model, 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; - 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, _: &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, + message: Option, project: Model, buffer: Model, 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, + message: Option, project: Model, buffer: Model, 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; - 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, _: &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>, + locations: Option>, project: Model, buffer: Model, 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; - 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, _: &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>, + lsp_highlights: Option>, _: Model, buffer: Model, _: 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; - 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, _: &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, + message: Option, _: Model, buffer: Model, _: LanguageServerId, @@ -1144,15 +1144,13 @@ impl LspCommand for GetHover { ) })?; - fn hover_blocks_from_marked_string( - marked_string: lsp2::MarkedString, - ) -> Option { + fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option { 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; - 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, _: &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, + completions: Option, _: Model, buffer: Model, 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; - 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, _: &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, + actions: Option, _: Model, _: Model, 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 { 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; - 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, _: &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>, + message: Option>, project: Model, buffer: Model, 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, server_id: LanguageServerId, resolve_state: ResolveState, @@ -1832,8 +1828,8 @@ impl InlayHints { cx: &mut AsyncAppContext, ) -> anyhow::Result { 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 { 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::>(&lsp_resolve_state.value) + serde_json::from_str::>(&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; - 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, }, } } @@ -2217,10 +2213,10 @@ impl LspCommand for InlayHints { buffer: &Buffer, _: &Arc, _: &AppContext, - ) -> lsp2::InlayHintParams { - lsp2::InlayHintParams { - text_document: lsp2::TextDocumentIdentifier { - uri: lsp2::Url::from_file_path(path).unwrap(), + ) -> lsp::InlayHintParams { + lsp::InlayHintParams { + text_document: lsp::TextDocumentIdentifier { + uri: lsp::Url::from_file_path(path).unwrap(), }, range: range_to_lsp(self.range.to_point_utf16(buffer)), work_done_progress_params: Default::default(), @@ -2229,7 +2225,7 @@ impl LspCommand for InlayHints { async fn response_from_lsp( self, - message: Option>, + message: Option>, project: Model, buffer: Model, server_id: LanguageServerId, @@ -2278,8 +2274,8 @@ impl LspCommand for InlayHints { proto::InlayHints { 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()), } } @@ -2292,11 +2288,11 @@ impl LspCommand for InlayHints { ) -> Result { let start = message .start - .and_then(language2::proto::deserialize_anchor) + .and_then(language::proto::deserialize_anchor) .context("invalid start")?; let end = message .end - .and_then(language2::proto::deserialize_anchor) + .and_then(language::proto::deserialize_anchor) .context("invalid end")?; buffer .update(&mut cx, |buffer, _| { diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index 05434b93b4..65d1aba820 100644 --- a/crates/project2/src/project2.rs +++ b/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, last_workspace_edits_by_language_server: HashMap, - client: Arc, + client: Arc, next_entry_id: Arc, join_project_response_message_id: u32, next_diagnostic_group_id: usize, @@ -131,8 +131,8 @@ pub struct Project { fs: Arc, client_state: Option, collaborators: HashMap, - client_subscriptions: Vec, - _subscriptions: Vec, + client_subscriptions: Vec, + _subscriptions: Vec, next_buffer_id: u64, opened_buffer: (watch::Sender<()>, watch::Receiver<()>), shared_buffers: HashMap>, @@ -158,8 +158,8 @@ pub struct Project { _maintain_buffer_languages: Task<()>, _maintain_workspace_config: Task>, terminals: Terminals, - copilot_lsp_subscription: Option, - copilot_log_subscription: Option, + copilot_lsp_subscription: Option, + copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, node: Option>, #[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, - pub range: Range, + pub range: Range, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct InlayHint { - pub position: language2::Anchor, + pub position: language::Anchor, pub label: InlayHintLabel, pub kind: Option, pub padding_left: bool, @@ -369,7 +369,7 @@ pub struct InlayHint { #[derive(Debug, Clone, PartialEq, Eq)] pub enum ResolveState { Resolved, - CanResolve(LanguageServerId, Option), + CanResolve(LanguageServerId, Option), Resolving, } @@ -392,7 +392,7 @@ pub enum InlayHintLabel { pub struct InlayHintLabelPart { pub value: String, pub tooltip: Option, - 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, + pub range: Range, 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>, pub signature: [u8; 32], } @@ -453,7 +453,7 @@ pub enum HoverBlockKind { #[derive(Debug)] pub struct Hover { pub contents: Vec, - pub range: Option>, + pub range: Option>, pub language: Option>, } @@ -464,7 +464,7 @@ impl Hover { } #[derive(Default)] -pub struct ProjectTransaction(pub HashMap, language2::Transaction>); +pub struct ProjectTransaction(pub HashMap, language::Transaction>); impl DiagnosticSummary { fn new<'a, T: 'a>(diagnostics: impl IntoIterator>) -> Self { @@ -859,12 +859,12 @@ impl Project { pub async fn test( fs: Arc, root_paths: impl IntoIterator, - cx: &mut gpui2::TestAppContext, + cx: &mut gpui::TestAppContext, ) -> Model { 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, @@ -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::DidCloseTextDocumentParams { - text_document: lsp2::TextDocumentIdentifier::new(uri.clone()), + .notify::( + 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::DidOpenTextDocumentParams { - text_document: lsp2::TextDocumentItem::new( + .notify::( + 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::DidCloseTextDocumentParams { - text_document: lsp2::TextDocumentIdentifier::new(file_url.clone()), + .notify::( + 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::DidChangeTextDocumentParams { - text_document: lsp2::VersionedTextDocumentIdentifier::new( + .notify::( + 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::DidSaveTextDocumentParams { + .notify::( + 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::DidChangeConfigurationParams { + .notify::( + lsp::DidChangeConfigurationParams { settings: workspace_config.clone(), }, ) @@ -2992,7 +2990,7 @@ impl Project { let language_server = pending_server.task.await?; language_server - .on_notification::({ + .on_notification::({ let adapter = adapter.clone(); let this = this.clone(); move |mut params, mut cx| { @@ -3015,7 +3013,7 @@ impl Project { .detach(); language_server - .on_request::({ + .on_request::({ 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::({ + .on_request::({ 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::({ + .on_request::({ let this = this.clone(); move |params, mut cx| { let this = this.clone(); @@ -3090,7 +3088,7 @@ impl Project { .detach(); language_server - .on_request::({ + .on_request::({ let adapter = adapter.clone(); let this = this.clone(); move |params, cx| { @@ -3106,7 +3104,7 @@ impl Project { .detach(); language_server - .on_request::({ + .on_request::({ 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::(move |params, mut cx| { + .on_notification::(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::DidChangeConfigurationParams { + .notify::( + 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::DidOpenTextDocumentParams { - text_document: lsp2::TextDocumentItem::new( + let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap(); + language_server.notify::( + 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, cx: &mut ModelContext, ) { 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, - params: lsp2::ApplyWorkspaceEditParams, + params: lsp::ApplyWorkspaceEditParams, server_id: LanguageServerId, adapter: Arc, mut cx: AsyncAppContext, - ) -> Result { + ) -> Result { 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, ) -> Result<()> { @@ -3822,8 +3820,8 @@ impl Project { for diagnostic in ¶ms.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, 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::DocumentFormattingParams { + .request::(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::DocumentRangeFormattingParams { + .request::(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::WorkspaceSymbolParams { + .request::( + 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::>() } - 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::(completion.lsp_completion) + .request::(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::(action.lsp_action) + .request::(action.lsp_action) .await?; } else { let actions = this @@ -5017,7 +5015,7 @@ impl Project { })?; let result = lang_server - .request::(lsp2::ExecuteCommandParams { + .request::(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, buffer_to_edit: Model, - edits: Vec, + edits: Vec, push_to_history: bool, _: Arc, language_server: Arc, @@ -5167,7 +5165,7 @@ impl Project { async fn deserialize_workspace_edit( this: Model, - edit: lsp2::WorkspaceEdit, + edit: lsp::WorkspaceEdit, push_to_history: bool, lsp_adapter: Arc, language_server: Arc, @@ -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::( + let resolve_task = lang_server.request::( InlayHints::project_to_lsp_hint(hint, &buffer_snapshot), ); let resolved_hint = resolve_task @@ -5846,8 +5844,8 @@ impl Project { cx: &mut ModelContext, ) -> Task> where - ::Result: Send, - ::Params: Send, + ::Result: Send, + ::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::(params) + .notify::(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::, _>>()?; 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); + as Arc); } 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::>>()?; 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 { 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<::Response> where - ::Params: Send, - ::Result: Send, + ::Params: Send, + ::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); } @@ -7930,7 +7926,7 @@ impl Project { let buffer = buffer.upgrade()?; Some(proto::BufferVersion { id: *id, - version: language2::proto::serialize_version(&buffer.read(cx).version), + version: language::proto::serialize_version(&buffer.read(cx).version), }) }) .collect(); @@ -7956,7 +7952,7 @@ impl Project { .map(|buffer| { let client = client.clone(); 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) { let operations = buffer.read(cx).serialize_ops(Some(remote_version), cx); @@ -8192,7 +8188,7 @@ impl Project { fn edits_from_lsp( &mut self, buffer: &Model, - lsp_edits: impl 'static + Send + IntoIterator, + lsp_edits: impl 'static + Send + IntoIterator, server_id: LanguageServerId, version: Option, cx: &mut ModelContext, @@ -8669,10 +8665,10 @@ impl Project { } cx.spawn(move |_| async move { - let prettier_wrapper_path = default_prettier_dir.join(prettier2::PRETTIER_SERVER_FILE); + let prettier_wrapper_path = default_prettier_dir.join(prettier::PRETTIER_SERVER_FILE); // method creates parent directory if it doesn't exist - fs.save(&prettier_wrapper_path, &text::Rope::from(prettier2::PRETTIER_SERVER_JS), text::LineEnding::Unix).await - .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier2::PRETTIER_SERVER_FILE))?; + fs.save(&prettier_wrapper_path, &text::Rope::from(prettier::PRETTIER_SERVER_JS), text::LineEnding::Unix).await + .with_context(|| format!("writing {} file at {prettier_wrapper_path:?}", prettier::PRETTIER_SERVER_FILE))?; let packages_to_versions = future::try_join_all( plugins_to_install @@ -8714,19 +8710,19 @@ impl Project { fn subscribe_for_copilot_events( copilot: &Model, cx: &mut ModelContext<'_, Project>, -) -> gpui2::Subscription { +) -> gpui::Subscription { cx.subscribe( copilot, |project, copilot, copilot_event, cx| match copilot_event { - copilot2::Event::CopilotLanguageServerStarted => { + copilot::Event::CopilotLanguageServerStarted => { match copilot.read(cx).language_server() { Some((name, copilot_server)) => { // Another event wants to re-add the server that was already added and subscribed to, avoid doing it again. - if !copilot_server.has_notification_handler::() { + if !copilot_server.has_notification_handler::() { let new_server_id = copilot_server.server_id(); let weak_project = cx.weak_model(); let copilot_log_subscription = copilot_server - .on_notification::( + .on_notification::( move |params, mut cx| { weak_project.update(&mut cx, |_, cx| { cx.emit(Event::LanguageServerLog( @@ -8796,7 +8792,7 @@ pub struct PathMatchCandidateSet { pub include_root_name: bool, } -impl<'a> fuzzy2::PathMatchCandidateSet<'a> for PathMatchCandidateSet { +impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet { type Candidates = PathMatchCandidateSetIter<'a>; fn id(&self) -> usize { @@ -8833,12 +8829,12 @@ pub struct PathMatchCandidateSetIter<'a> { } impl<'a> Iterator for PathMatchCandidateSetIter<'a> { - type Item = fuzzy2::PathMatchCandidate<'a>; + type Item = fuzzy::PathMatchCandidate<'a>; fn next(&mut self) -> Option { self.traversal.next().map(|entry| { if let EntryKind::File(char_bag) = entry.kind { - fuzzy2::PathMatchCandidate { + fuzzy::PathMatchCandidate { path: &entry.path, char_bag, } @@ -8958,18 +8954,18 @@ async fn wait_for_loading_buffer( } } -fn include_text(server: &lsp2::LanguageServer) -> bool { +fn include_text(server: &lsp::LanguageServer) -> bool { server .capabilities() .text_document_sync .as_ref() .and_then(|sync| match sync { - lsp2::TextDocumentSyncCapability::Kind(_) => None, - lsp2::TextDocumentSyncCapability::Options(options) => options.save.as_ref(), + lsp::TextDocumentSyncCapability::Kind(_) => None, + lsp::TextDocumentSyncCapability::Options(options) => options.save.as_ref(), }) .and_then(|save_options| match save_options { - lsp2::TextDocumentSyncSaveOptions::Supported(_) => None, - lsp2::TextDocumentSyncSaveOptions::SaveOptions(options) => options.include_text, + lsp::TextDocumentSyncSaveOptions::Supported(_) => None, + lsp::TextDocumentSyncSaveOptions::SaveOptions(options) => options.include_text, }) .unwrap_or(false) } diff --git a/crates/project2/src/project_settings.rs b/crates/project2/src/project_settings.rs index b85226f7cc..028a564b9c 100644 --- a/crates/project2/src/project_settings.rs +++ b/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)] diff --git a/crates/project2/src/project_tests.rs b/crates/project2/src/project_tests.rs index 5a2f82c375..490b3a0788 100644 --- a/crates/project2/src/project_tests.rs +++ b/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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::(|_, _| future::ready(Ok(()))); + .handle_request::(|_, _| future::ready(Ok(()))); let mut json_shutdown_requests = fake_json_server - .handle_request::(|_, _| future::ready(Ok(()))); + .handle_request::(|_, _| 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::() + .receive_notification::() .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::() + .receive_notification::() .await .text_document, fake_json_server - .receive_notification::() + .receive_notification::() .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::() + .receive_notification::() .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::RegistrationParams { - registrations: vec![lsp2::Registration { + .request::(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::({ + fake_server.handle_notification::({ 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::PublishDiagnosticsParams { + fake_server.notify::(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::PublishDiagnosticsParams { + fake_server.notify::(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::PublishDiagnosticsParams { + fake_server.notify::(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::PublishDiagnosticsParams { + fake_server.notify::(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::PublishDiagnosticsParams { - uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(), + fake_server.notify::(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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::() + .receive_notification::() .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::PublishDiagnosticsParams { - uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(), + fake_server.notify::(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::PublishDiagnosticsParams { - uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(), + fake_server.notify::(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::() + .receive_notification::() .await; assert!( change_notification_2.text_document.version > change_notification_1.text_document.version ); // Handle out-of-order diagnostics - fake_server.notify::(lsp2::PublishDiagnosticsParams { - uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(), + fake_server.notify::(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::() + .receive_notification::() .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( 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::(|params, _| async move { + fake_server.handle_request::(|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)), ), ))) }); @@ -2323,8 +2274,8 @@ async fn test_definition(cx: &mut gpui2::TestAppContext) { } } -#[gpui2::test] -async fn test_completions_without_edit_ranges(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) { init_test(cx); let mut language = Language::new( @@ -2337,8 +2288,8 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui2::TestAppContext) { ); let mut fake_language_servers = language .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { - capabilities: lsp2::ServerCapabilities { - completion_provider: Some(lsp2::CompletionOptions { + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { trigger_characters: Some(vec![":".to_string()]), ..Default::default() }), @@ -2373,9 +2324,9 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui2::TestAppContext) { }); fake_server - .handle_request::(|_, _| async move { - Ok(Some(lsp2::CompletionResponse::Array(vec![ - lsp2::CompletionItem { + .handle_request::(|_, _| async move { + Ok(Some(lsp::CompletionResponse::Array(vec![ + lsp::CompletionItem { label: "fullyQualifiedName?".into(), insert_text: Some("fullyQualifiedName".into()), ..Default::default() @@ -2400,9 +2351,9 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui2::TestAppContext) { }); fake_server - .handle_request::(|_, _| async move { - Ok(Some(lsp2::CompletionResponse::Array(vec![ - lsp2::CompletionItem { + .handle_request::(|_, _| async move { + Ok(Some(lsp::CompletionResponse::Array(vec![ + lsp::CompletionItem { label: "component".into(), ..Default::default() }, @@ -2420,8 +2371,8 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui2::TestAppContext) { ); } -#[gpui2::test] -async fn test_completions_with_carriage_returns(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) { init_test(cx); let mut language = Language::new( @@ -2434,8 +2385,8 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui2::TestAppContext) ); let mut fake_language_servers = language .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { - capabilities: lsp2::ServerCapabilities { - completion_provider: Some(lsp2::CompletionOptions { + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { trigger_characters: Some(vec![":".to_string()]), ..Default::default() }), @@ -2470,9 +2421,9 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui2::TestAppContext) }); fake_server - .handle_request::(|_, _| async move { - Ok(Some(lsp2::CompletionResponse::Array(vec![ - lsp2::CompletionItem { + .handle_request::(|_, _| async move { + Ok(Some(lsp::CompletionResponse::Array(vec![ + lsp::CompletionItem { label: "fullyQualifiedName?".into(), insert_text: Some("fully\rQualified\r\nName".into()), ..Default::default() @@ -2486,8 +2437,8 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui2::TestAppContext) assert_eq!(completions[0].new_text, "fully\nQualified\nName"); } -#[gpui2::test(iterations = 10)] -async fn test_apply_code_actions_with_commands(cx: &mut gpui2::TestAppContext) { +#[gpui::test(iterations = 10)] +async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { init_test(cx); let mut language = Language::new( @@ -2521,18 +2472,18 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui2::TestAppContext) { // Language server returns code actions that contain commands, and not edits. let actions = project.update(cx, |project, cx| project.code_actions(&buffer, 0..0, cx)); fake_server - .handle_request::(|_, _| async move { + .handle_request::(|_, _| async move { Ok(Some(vec![ - lsp2::CodeActionOrCommand::CodeAction(lsp2::CodeAction { + lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction { title: "The code action".into(), - command: Some(lsp2::Command { + command: Some(lsp::Command { title: "The command".into(), command: "_the/command".into(), arguments: Some(vec![json!("the-argument")]), }), ..Default::default() }), - lsp2::CodeActionOrCommand::CodeAction(lsp2::CodeAction { + lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction { title: "two".into(), ..Default::default() }), @@ -2548,31 +2499,31 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui2::TestAppContext) { // Resolving the code action does not populate its edits. In absence of // edits, we must execute the given command. - fake_server.handle_request::( + fake_server.handle_request::( |action, _| async move { Ok(action) }, ); // While executing the command, the language server sends the editor // a `workspaceEdit` request. fake_server - .handle_request::({ + .handle_request::({ let fake = fake_server.clone(); move |params, _| { assert_eq!(params.command, "_the/command"); let fake = fake.clone(); async move { fake.server - .request::( - lsp2::ApplyWorkspaceEditParams { + .request::( + lsp::ApplyWorkspaceEditParams { label: None, - edit: lsp2::WorkspaceEdit { + edit: lsp::WorkspaceEdit { changes: Some( [( - lsp2::Url::from_file_path("/dir/a.ts").unwrap(), - vec![lsp2::TextEdit { - range: lsp2::Range::new( - lsp2::Position::new(0, 0), - lsp2::Position::new(0, 0), + lsp::Url::from_file_path("/dir/a.ts").unwrap(), + vec![lsp::TextEdit { + range: lsp::Range::new( + lsp::Position::new(0, 0), + lsp::Position::new(0, 0), ), new_text: "X".into(), }], @@ -2604,8 +2555,8 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui2::TestAppContext) { }); } -#[gpui2::test(iterations = 10)] -async fn test_save_file(cx: &mut gpui2::TestAppContext) { +#[gpui::test(iterations = 10)] +async fn test_save_file(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -2636,8 +2587,8 @@ async fn test_save_file(cx: &mut gpui2::TestAppContext) { assert_eq!(new_text, buffer.update(cx, |buffer, _| buffer.text())); } -#[gpui2::test] -async fn test_save_in_single_file_worktree(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -2667,8 +2618,8 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui2::TestAppContext) { assert_eq!(new_text, buffer.update(cx, |buffer, _| buffer.text())); } -#[gpui2::test] -async fn test_save_as(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_save_as(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -2726,8 +2677,8 @@ async fn test_save_as(cx: &mut gpui2::TestAppContext) { assert_eq!(opened_buffer, buffer); } -#[gpui2::test(retries = 5)] -async fn test_rescan_and_remote_updates(cx: &mut gpui2::TestAppContext) { +#[gpui::test(retries = 5)] +async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) { init_test(cx); cx.executor().allow_parking(); @@ -2748,11 +2699,11 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui2::TestAppContext) { let project = Project::test(Arc::new(RealFs), [dir.path()], cx).await; let rpc = project.update(cx, |p, _| p.client.clone()); - let buffer_for_path = |path: &'static str, cx: &mut gpui2::TestAppContext| { + let buffer_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| { let buffer = project.update(cx, |p, cx| p.open_local_buffer(dir.path().join(path), cx)); async move { buffer.await.unwrap() } }; - let id_for_path = |path: &'static str, cx: &mut gpui2::TestAppContext| { + let id_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| { project.update(cx, |project, cx| { let tree = project.worktrees().next().unwrap(); tree.read(cx) @@ -2875,8 +2826,8 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui2::TestAppContext) { }); } -#[gpui2::test(iterations = 10)] -async fn test_buffer_identity_across_renames(cx: &mut gpui2::TestAppContext) { +#[gpui::test(iterations = 10)] +async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -2894,7 +2845,7 @@ async fn test_buffer_identity_across_renames(cx: &mut gpui2::TestAppContext) { let tree = project.update(cx, |project, _| project.worktrees().next().unwrap()); let tree_id = tree.update(cx, |tree, _| tree.id()); - let id_for_path = |path: &'static str, cx: &mut gpui2::TestAppContext| { + let id_for_path = |path: &'static str, cx: &mut gpui::TestAppContext| { project.update(cx, |project, cx| { let tree = project.worktrees().next().unwrap(); tree.read(cx) @@ -2926,8 +2877,8 @@ async fn test_buffer_identity_across_renames(cx: &mut gpui2::TestAppContext) { buffer.update(cx, |buffer, _| assert!(!buffer.is_dirty())); } -#[gpui2::test] -async fn test_buffer_deduping(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -2972,8 +2923,8 @@ async fn test_buffer_deduping(cx: &mut gpui2::TestAppContext) { assert_eq!(buffer_a_3.entity_id(), buffer_a_id); } -#[gpui2::test] -async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -3018,7 +2969,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) { assert!(buffer.is_dirty()); assert_eq!( *events.lock(), - &[language2::Event::Edited, language2::Event::DirtyChanged] + &[language::Event::Edited, language::Event::DirtyChanged] ); events.lock().clear(); buffer.did_save( @@ -3032,7 +2983,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) { // after saving, the buffer is not dirty, and emits a saved event. buffer1.update(cx, |buffer, cx| { assert!(!buffer.is_dirty()); - assert_eq!(*events.lock(), &[language2::Event::Saved]); + assert_eq!(*events.lock(), &[language::Event::Saved]); events.lock().clear(); buffer.edit([(1..1, "B")], None, cx); @@ -3046,9 +2997,9 @@ async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) { assert_eq!( *events.lock(), &[ - language2::Event::Edited, - language2::Event::DirtyChanged, - language2::Event::Edited, + language::Event::Edited, + language::Event::DirtyChanged, + language::Event::Edited, ], ); events.lock().clear(); @@ -3062,7 +3013,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) { assert_eq!( *events.lock(), - &[language2::Event::Edited, language2::Event::DirtyChanged] + &[language::Event::Edited, language::Event::DirtyChanged] ); // When a file is deleted, the buffer is considered dirty. @@ -3087,8 +3038,8 @@ async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) { assert_eq!( *events.lock(), &[ - language2::Event::DirtyChanged, - language2::Event::FileHandleChanged + language::Event::DirtyChanged, + language::Event::FileHandleChanged ] ); @@ -3114,12 +3065,12 @@ async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) { .await .unwrap(); cx.executor().run_until_parked(); - assert_eq!(*events.lock(), &[language2::Event::FileHandleChanged]); + assert_eq!(*events.lock(), &[language::Event::FileHandleChanged]); cx.update(|cx| assert!(buffer3.read(cx).is_dirty())); } -#[gpui2::test] -async fn test_buffer_file_changes_on_disk(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { init_test(cx); let initial_contents = "aaa\nbbbbb\nc\n"; @@ -3199,8 +3150,8 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui2::TestAppContext) { }); } -#[gpui2::test] -async fn test_buffer_line_endings(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -3261,8 +3212,8 @@ async fn test_buffer_line_endings(cx: &mut gpui2::TestAppContext) { ); } -#[gpui2::test] -async fn test_grouped_diagnostics(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -3288,62 +3239,56 @@ async fn test_grouped_diagnostics(cx: &mut gpui2::TestAppContext) { .unwrap(); let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap(); - let message = lsp2::PublishDiagnosticsParams { + let message = lsp::PublishDiagnosticsParams { uri: buffer_uri.clone(), diagnostics: vec![ - lsp2::Diagnostic { - range: lsp2::Range::new(lsp2::Position::new(1, 8), lsp2::Position::new(1, 9)), + lsp::Diagnostic { + range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)), severity: Some(DiagnosticSeverity::WARNING), message: "error 1".to_string(), - related_information: Some(vec![lsp2::DiagnosticRelatedInformation { - location: lsp2::Location { + related_information: Some(vec![lsp::DiagnosticRelatedInformation { + location: lsp::Location { uri: buffer_uri.clone(), - range: lsp2::Range::new( - lsp2::Position::new(1, 8), - lsp2::Position::new(1, 9), - ), + range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)), }, message: "error 1 hint 1".to_string(), }]), ..Default::default() }, - lsp2::Diagnostic { - range: lsp2::Range::new(lsp2::Position::new(1, 8), lsp2::Position::new(1, 9)), + lsp::Diagnostic { + range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)), severity: Some(DiagnosticSeverity::HINT), message: "error 1 hint 1".to_string(), - related_information: Some(vec![lsp2::DiagnosticRelatedInformation { - location: lsp2::Location { + related_information: Some(vec![lsp::DiagnosticRelatedInformation { + location: lsp::Location { uri: buffer_uri.clone(), - range: lsp2::Range::new( - lsp2::Position::new(1, 8), - lsp2::Position::new(1, 9), - ), + range: lsp::Range::new(lsp::Position::new(1, 8), lsp::Position::new(1, 9)), }, message: "original diagnostic".to_string(), }]), ..Default::default() }, - lsp2::Diagnostic { - range: lsp2::Range::new(lsp2::Position::new(2, 8), lsp2::Position::new(2, 17)), + lsp::Diagnostic { + range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)), severity: Some(DiagnosticSeverity::ERROR), message: "error 2".to_string(), related_information: Some(vec![ - lsp2::DiagnosticRelatedInformation { - location: lsp2::Location { + lsp::DiagnosticRelatedInformation { + location: lsp::Location { uri: buffer_uri.clone(), - range: lsp2::Range::new( - lsp2::Position::new(1, 13), - lsp2::Position::new(1, 15), + range: lsp::Range::new( + lsp::Position::new(1, 13), + lsp::Position::new(1, 15), ), }, message: "error 2 hint 1".to_string(), }, - lsp2::DiagnosticRelatedInformation { - location: lsp2::Location { + lsp::DiagnosticRelatedInformation { + location: lsp::Location { uri: buffer_uri.clone(), - range: lsp2::Range::new( - lsp2::Position::new(1, 13), - lsp2::Position::new(1, 15), + range: lsp::Range::new( + lsp::Position::new(1, 13), + lsp::Position::new(1, 15), ), }, message: "error 2 hint 2".to_string(), @@ -3351,33 +3296,27 @@ async fn test_grouped_diagnostics(cx: &mut gpui2::TestAppContext) { ]), ..Default::default() }, - lsp2::Diagnostic { - range: lsp2::Range::new(lsp2::Position::new(1, 13), lsp2::Position::new(1, 15)), + lsp::Diagnostic { + range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)), severity: Some(DiagnosticSeverity::HINT), message: "error 2 hint 1".to_string(), - related_information: Some(vec![lsp2::DiagnosticRelatedInformation { - location: lsp2::Location { + related_information: Some(vec![lsp::DiagnosticRelatedInformation { + location: lsp::Location { uri: buffer_uri.clone(), - range: lsp2::Range::new( - lsp2::Position::new(2, 8), - lsp2::Position::new(2, 17), - ), + range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)), }, message: "original diagnostic".to_string(), }]), ..Default::default() }, - lsp2::Diagnostic { - range: lsp2::Range::new(lsp2::Position::new(1, 13), lsp2::Position::new(1, 15)), + lsp::Diagnostic { + range: lsp::Range::new(lsp::Position::new(1, 13), lsp::Position::new(1, 15)), severity: Some(DiagnosticSeverity::HINT), message: "error 2 hint 2".to_string(), - related_information: Some(vec![lsp2::DiagnosticRelatedInformation { - location: lsp2::Location { + related_information: Some(vec![lsp::DiagnosticRelatedInformation { + location: lsp::Location { uri: buffer_uri, - range: lsp2::Range::new( - lsp2::Position::new(2, 8), - lsp2::Position::new(2, 17), - ), + range: lsp::Range::new(lsp::Position::new(2, 8), lsp::Position::new(2, 17)), }, message: "original diagnostic".to_string(), }]), @@ -3515,8 +3454,8 @@ async fn test_grouped_diagnostics(cx: &mut gpui2::TestAppContext) { ); } -#[gpui2::test] -async fn test_rename(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_rename(cx: &mut gpui::TestAppContext) { init_test(cx); let mut language = Language::new( @@ -3529,8 +3468,8 @@ async fn test_rename(cx: &mut gpui2::TestAppContext) { ); let mut fake_servers = language .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { - capabilities: lsp2::ServerCapabilities { - rename_provider: Some(lsp2::OneOf::Right(lsp2::RenameOptions { + capabilities: lsp::ServerCapabilities { + rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions { prepare_provider: Some(true), work_done_progress_options: Default::default(), })), @@ -3565,12 +3504,12 @@ async fn test_rename(cx: &mut gpui2::TestAppContext) { project.prepare_rename(buffer.clone(), 7, cx) }); fake_server - .handle_request::(|params, _| async move { + .handle_request::(|params, _| async move { assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs"); - assert_eq!(params.position, lsp2::Position::new(0, 7)); - Ok(Some(lsp2::PrepareRenameResponse::Range(lsp2::Range::new( - lsp2::Position::new(0, 6), - lsp2::Position::new(0, 9), + assert_eq!(params.position, lsp::Position::new(0, 7)); + Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new( + lsp::Position::new(0, 6), + lsp::Position::new(0, 9), )))) }) .next() @@ -3584,43 +3523,40 @@ async fn test_rename(cx: &mut gpui2::TestAppContext) { project.perform_rename(buffer.clone(), 7, "THREE".to_string(), true, cx) }); fake_server - .handle_request::(|params, _| async move { + .handle_request::(|params, _| async move { assert_eq!( params.text_document_position.text_document.uri.as_str(), "file:///dir/one.rs" ); assert_eq!( params.text_document_position.position, - lsp2::Position::new(0, 7) + lsp::Position::new(0, 7) ); assert_eq!(params.new_name, "THREE"); - Ok(Some(lsp2::WorkspaceEdit { + Ok(Some(lsp::WorkspaceEdit { changes: Some( [ ( - lsp2::Url::from_file_path("/dir/one.rs").unwrap(), - vec![lsp2::TextEdit::new( - lsp2::Range::new( - lsp2::Position::new(0, 6), - lsp2::Position::new(0, 9), - ), + lsp::Url::from_file_path("/dir/one.rs").unwrap(), + vec![lsp::TextEdit::new( + lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), "THREE".to_string(), )], ), ( - lsp2::Url::from_file_path("/dir/two.rs").unwrap(), + lsp::Url::from_file_path("/dir/two.rs").unwrap(), vec![ - lsp2::TextEdit::new( - lsp2::Range::new( - lsp2::Position::new(0, 24), - lsp2::Position::new(0, 27), + lsp::TextEdit::new( + lsp::Range::new( + lsp::Position::new(0, 24), + lsp::Position::new(0, 27), ), "THREE".to_string(), ), - lsp2::TextEdit::new( - lsp2::Range::new( - lsp2::Position::new(0, 35), - lsp2::Position::new(0, 38), + lsp::TextEdit::new( + lsp::Range::new( + lsp::Position::new(0, 35), + lsp::Position::new(0, 38), ), "THREE".to_string(), ), @@ -3656,8 +3592,8 @@ async fn test_rename(cx: &mut gpui2::TestAppContext) { ); } -#[gpui2::test] -async fn test_search(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_search(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor().clone()); @@ -3713,8 +3649,8 @@ async fn test_search(cx: &mut gpui2::TestAppContext) { ); } -#[gpui2::test] -async fn test_search_with_inclusions(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { init_test(cx); let search_query = "file"; @@ -3825,8 +3761,8 @@ async fn test_search_with_inclusions(cx: &mut gpui2::TestAppContext) { ); } -#[gpui2::test] -async fn test_search_with_exclusions(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { init_test(cx); let search_query = "file"; @@ -3936,8 +3872,8 @@ async fn test_search_with_exclusions(cx: &mut gpui2::TestAppContext) { ); } -#[gpui2::test] -async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui2::TestAppContext) { +#[gpui::test] +async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContext) { init_test(cx); let search_query = "file"; @@ -4057,7 +3993,7 @@ fn test_glob_literal_prefix() { async fn search( project: &Model, query: SearchQuery, - cx: &mut gpui2::TestAppContext, + cx: &mut gpui::TestAppContext, ) -> Result>>> { let mut search_rx = project.update(cx, |project, cx| project.search(query, cx)); let mut result = HashMap::default(); @@ -4079,7 +4015,7 @@ async fn search( .collect()) } -fn init_test(cx: &mut gpui2::TestAppContext) { +fn init_test(cx: &mut gpui::TestAppContext) { if std::env::var("RUST_LOG").is_ok() { env_logger::init(); } @@ -4087,7 +4023,7 @@ fn init_test(cx: &mut gpui2::TestAppContext) { cx.update(|cx| { let settings_store = SettingsStore::test(cx); cx.set_global(settings_store); - language2::init(cx); + language::init(cx); Project::init_settings(cx); }); } diff --git a/crates/project2/src/search.rs b/crates/project2/src/search.rs index 9af13c7732..46dd30c8a0 100644 --- a/crates/project2/src/search.rs +++ b/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::{ diff --git a/crates/project2/src/terminals.rs b/crates/project2/src/terminals.rs index ce89914dc6..1bf69aa8b5 100644 --- a/crates/project2/src/terminals.rs +++ b/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>, + pub(crate) local_handles: Vec>, } impl Project { @@ -121,7 +121,7 @@ impl Project { } } - pub fn local_terminal_handles(&self) -> &Vec> { + pub fn local_terminal_handles(&self) -> &Vec> { &self.terminals.local_handles } } diff --git a/crates/project2/src/worktree.rs b/crates/project2/src/worktree.rs index 060fefe6b3..937a549a31 100644 --- a/crates/project2/src/worktree.rs +++ b/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, cx: &AppContext, ) -> Result { @@ -2740,7 +2740,7 @@ impl File { }) } - pub fn from_dyn(file: Option<&Arc>) -> Option<&Self> { + pub fn from_dyn(file: Option<&Arc>) -> Option<&Self> { file.and_then(|f| f.as_any().downcast_ref()) } @@ -2818,7 +2818,7 @@ pub type UpdatedGitRepositoriesSet = Arc<[(Arc, GitRepositoryChange)]>; impl Entry { fn new( path: Arc, - 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 { #[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"; diff --git a/crates/rpc2/Cargo.toml b/crates/rpc2/Cargo.toml index f108af3d3f..0995029b30 100644 --- a/crates/rpc2/Cargo.toml +++ b/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 diff --git a/crates/rpc2/src/conn.rs b/crates/rpc2/src/conn.rs index ec3c5b68cf..ae5c9fd226 100644 --- a/crates/rpc2/src/conn.rs +++ b/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) { use std::sync::{ atomic::{AtomicBool, Ordering::SeqCst}, @@ -53,7 +53,7 @@ impl Connection { #[allow(clippy::type_complexity)] fn channel( killed: Arc, - executor: gpui2::BackgroundExecutor, + executor: gpui::BackgroundExecutor, ) -> ( Box>, Box>>, diff --git a/crates/rpc2/src/peer.rs b/crates/rpc2/src/peer.rs index 104ab1b421..80a2ab4378 100644 --- a/crates/rpc2/src/peer.rs +++ b/crates/rpc2/src/peer.rs @@ -342,7 +342,7 @@ impl Peer { pub fn add_test_connection( self: &Arc, connection: Connection, - executor: gpui2::BackgroundExecutor, + executor: gpui::BackgroundExecutor, ) -> ( ConnectionId, impl Future> + 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()); diff --git a/crates/rpc2/src/proto.rs b/crates/rpc2/src/proto.rs index c1a7af3e4d..f0d7937f6f 100644 --- a/crates/rpc2/src/proto.rs +++ b/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, diff --git a/crates/settings2/Cargo.toml b/crates/settings2/Cargo.toml index b455b1e38a..0a4051cbb3 100644 --- a/crates/settings2/Cargo.toml +++ b/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 diff --git a/crates/settings2/src/keymap_file.rs b/crates/settings2/src/keymap_file.rs index d0a32131b5..e51bd76e5e 100644 --- a/crates/settings2/src/keymap_file.rs +++ b/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 { +fn no_action() -> Box { todo!() } diff --git a/crates/settings2/src/settings_file.rs b/crates/settings2/src/settings_file.rs index 002c9daf12..c623ae9caf 100644 --- a/crates/settings2/src/settings_file.rs +++ b/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}; diff --git a/crates/settings2/src/settings_store.rs b/crates/settings2/src/settings_store.rs index e2c370bcac..3317a50f52 100644 --- a/crates/settings2/src/settings_store.rs +++ b/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::(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::(cx); diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index bd97ef1cab..4e2c439db0 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/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))) }, diff --git a/crates/terminal2/Cargo.toml b/crates/terminal2/Cargo.toml index 3ca5dc9aba..e37b949881 100644 --- a/crates/terminal2/Cargo.toml +++ b/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" } diff --git a/crates/terminal2/src/mappings/colors.rs b/crates/terminal2/src/mappings/colors.rs index 99b66b9e14..fc3557b4e8 100644 --- a/crates/terminal2/src/mappings/colors.rs +++ b/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) -> AlacRgb { diff --git a/crates/terminal2/src/mappings/keys.rs b/crates/terminal2/src/mappings/keys.rs index 0009d39e13..f8a26fbe2b 100644 --- a/crates/terminal2/src/mappings/keys.rs +++ b/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::*; diff --git a/crates/terminal2/src/mappings/mouse.rs b/crates/terminal2/src/mappings/mouse.rs index 28ad510fcd..eac6ad17ff 100644 --- a/crates/terminal2/src/mappings/mouse.rs +++ b/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 { pub fn mouse_button_report( point: AlacPoint, - button: gpui2::MouseButton, + button: gpui::MouseButton, modifiers: Modifiers, pressed: bool, mode: TermMode, diff --git a/crates/terminal2/src/terminal2.rs b/crates/terminal2/src/terminal2.rs index adc5dd3511..ba5c4815f2 100644 --- a/crates/terminal2/src/terminal2.rs +++ b/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}; diff --git a/crates/terminal2/src/terminal_settings.rs b/crates/terminal2/src/terminal_settings.rs index 1be9ac5000..1d1e1cea2a 100644 --- a/crates/terminal2/src/terminal_settings.rs +++ b/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; diff --git a/crates/text2/Cargo.toml b/crates/text2/Cargo.toml index 6891fef680..7c12d22adf 100644 --- a/crates/text2/Cargo.toml +++ b/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 diff --git a/crates/text2/src/locator.rs b/crates/text2/src/locator.rs index 27fdb34cde..07b73ace05 100644 --- a/crates/text2/src/locator.rs +++ b/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(); diff --git a/crates/text2/src/patch.rs b/crates/text2/src/patch.rs index 20e4a4d889..f10acbc2d3 100644 --- a/crates/text2/src/patch.rs +++ b/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")) diff --git a/crates/text2/src/tests.rs b/crates/text2/src/tests.rs index 96248285ea..7e26e0a296 100644 --- a/crates/text2/src/tests.rs +++ b/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")) diff --git a/crates/theme2/Cargo.toml b/crates/theme2/Cargo.toml index 6b273e5042..a051468b00 100644 --- a/crates/theme2/Cargo.toml +++ b/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"] } diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index 471c6e2360..c2fc31d542 100644 --- a/crates/theme2/src/colors.rs +++ b/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); diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index ddb563d237..55ce096a4c 100644 --- a/crates/theme2/src/default_colors.rs +++ b/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}, diff --git a/crates/theme2/src/registry.rs b/crates/theme2/src/registry.rs index f30f5ead91..c1bba121e1 100644 --- a/crates/theme2/src/registry.rs +++ b/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 { diff --git a/crates/theme2/src/scale.rs b/crates/theme2/src/scale.rs index bd28e43db9..22d191bd4a 100644 --- a/crates/theme2/src/scale.rs +++ b/crates/theme2/src/scale.rs @@ -1,4 +1,4 @@ -use gpui2::{AppContext, Hsla, SharedString}; +use gpui::{AppContext, Hsla, SharedString}; use crate::{ActiveTheme, Appearance}; diff --git a/crates/theme2/src/settings.rs b/crates/theme2/src/settings.rs index c8d2b52273..5e8f9de873 100644 --- a/crates/theme2/src/settings.rs +++ b/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; diff --git a/crates/theme2/src/syntax.rs b/crates/theme2/src/syntax.rs index a8127f0c44..3a068349fb 100644 --- a/crates/theme2/src/syntax.rs +++ b/crates/theme2/src/syntax.rs @@ -1,4 +1,4 @@ -use gpui2::{HighlightStyle, Hsla}; +use gpui::{HighlightStyle, Hsla}; #[derive(Clone, Default)] pub struct SyntaxTheme { diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 9cd5fe75c0..707d275730 100644 --- a/crates/theme2/src/theme2.rs +++ b/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 { diff --git a/crates/ui2/src/elements/avatar.rs b/crates/ui2/src/elements/avatar.rs index ff92021b11..ff574a2042 100644 --- a/crates/ui2/src/elements/avatar.rs +++ b/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), + // ) } } } diff --git a/crates/ui2/src/elements/player.rs b/crates/ui2/src/elements/player.rs index 8e3ad5c3a8..c7b7ade1c1 100644 --- a/crates/ui2/src/elements/player.rs +++ b/crates/ui2/src/elements/player.rs @@ -139,11 +139,11 @@ impl Player { } pub fn cursor_color(&self, cx: &mut ViewContext) -> 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(&self, cx: &mut ViewContext) -> 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 { diff --git a/crates/workspace2/Cargo.toml b/crates/workspace2/Cargo.toml new file mode 100644 index 0000000000..5072f2b8f9 --- /dev/null +++ b/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 diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs new file mode 100644 index 0000000000..9da9123a2f --- /dev/null +++ b/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); + fn size(&self, cx: &WindowContext) -> f32; + fn set_size(&mut self, size: Option, cx: &mut ViewContext); + fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>; + fn icon_tooltip(&self) -> (String, Option>); + fn icon_label(&self, _: &WindowContext) -> Option { + 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) {} + fn set_active(&mut self, _active: bool, _cx: &mut ViewContext) {} + 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, cx: &mut WindowContext); + fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>; + fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option>); + fn icon_label(&self, cx: &WindowContext) -> Option; + fn has_focus(&self, cx: &WindowContext) -> bool; + fn to_any(&self) -> AnyView; +} + +impl PanelHandle for View +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, 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>) { + self.read(cx).icon_tooltip() + } + + fn icon_label(&self, cx: &WindowContext) -> Option { + 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, + 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, + // todo!() + // context_menu: View, + _subscriptions: [Subscription; 2], +} + +pub struct PanelButtons { + dock: View, + workspace: WeakView, +} + +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(&self) -> Option> { + // self.panel_entries + // .iter() + // .find_map(|entry| entry.panel.as_any().clone().downcast()) + // } + + // pub fn panel_index_for_type(&self) -> Option { + // self.panel_entries + // .iter() + // .position(|entry| entry.panel.as_any().is::()) + // } + + pub fn panel_index_for_ui_name(&self, _ui_name: &str, _cx: &AppContext) -> Option { + 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) { + 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) { + // 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) { + // for entry in &mut self.panel_entries { + // if entry.panel.is_zoomed(cx) { + // entry.panel.set_zoomed(false, cx); + // } + // } + // } + + // pub(crate) fn add_panel(&mut self, panel: View, cx: &mut ViewContext) { + // 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(&mut self, panel: &View, cx: &mut ViewContext) { + // 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) { + 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> { + let entry = self.visible_entry()?; + Some(&entry.panel) + } + + pub fn active_panel(&self) -> Option<&Arc> { + 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> { + // 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 { + // 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 { + // 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, cx: &mut ViewContext) { + // 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 { + // todo!() + // if let Some(active_entry) = self.visible_entry() { + // Empty::new() + // .into_any() + // .contained() + // .with_style(self.style(cx)) + // .resizable::( + // 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) -> AnyElement { +// 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::( +// 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) { +// 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, + workspace: WeakView, + cx: &mut ViewContext, + ) -> 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::Element { +// todo!("") +// } + +// fn ui_name() -> &'static str { +// "PanelButtons" +// } + +// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { +// let theme = &settings::get::(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::>(); +// 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::(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::( +// 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; + + fn render(&mut self, cx: &mut ViewContext) -> 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, + ) { + // 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; + + fn render(&mut self, _cx: &mut ViewContext) -> 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.position = position; + cx.emit(TestPanelEvent::PositionChanged); + } + + fn size(&self, _: &WindowContext) -> f32 { + self.size + } + + fn set_size(&mut self, size: Option, _: &mut ViewContext) { + 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>) { + ("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.zoomed = zoomed; + } + + fn set_active(&mut self, active: bool, _cx: &mut ViewContext) { + 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) + } + } +} diff --git a/crates/workspace2/src/item.rs b/crates/workspace2/src/item.rs index 90d08d6c4a..c2d5c25781 100644 --- a/crates/workspace2/src/item.rs +++ b/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; +use smallvec::SmallVec; +use std::{ + any::{Any, TypeId}, + ops::Range, + path::PathBuf, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; +use theme2::ThemeVariant; -// #[derive(Deserialize)] -// pub struct ItemSettings { -// pub git_status: bool, -// pub close_position: ClosePosition, -// } +#[derive(Deserialize)] +pub struct ItemSettings { + pub git_status: bool, + pub close_position: ClosePosition, +} -// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] -// #[serde(rename_all = "lowercase")] -// pub enum ClosePosition { -// Left, -// #[default] -// Right, -// } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "lowercase")] +pub enum ClosePosition { + Left, + #[default] + Right, +} -// impl ClosePosition { -// pub fn right(&self) -> bool { -// match self { -// ClosePosition::Left => false, -// ClosePosition::Right => true, -// } -// } -// } +impl ClosePosition { + pub fn right(&self) -> bool { + match self { + ClosePosition::Left => false, + ClosePosition::Right => true, + } + } +} -// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] -// pub struct ItemSettingsContent { -// git_status: Option, -// close_position: Option, -// } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct ItemSettingsContent { + git_status: Option, + close_position: Option, +} -// impl Setting for ItemSettings { -// const KEY: Option<&'static str> = Some("tabs"); +impl Settings for ItemSettings { + const KEY: Option<&'static str> = Some("tabs"); -// type FileContent = ItemSettingsContent; + type FileContent = ItemSettingsContent; -// fn load( -// default_value: &Self::FileContent, -// user_values: &[&Self::FileContent], -// _: &gpui2::AppContext, -// ) -> anyhow::Result { -// Self::load_via_json_merge(default_value, user_values) -// } -// } + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &mut AppContext, + ) -> Result { + 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, HighlightStyle)>>, } -pub trait Item: EventEmitter + Sized { - // fn deactivated(&mut self, _: &mut ViewContext) {} - // fn workspace_deactivated(&mut self, _: &mut ViewContext) {} - // fn navigate(&mut self, _: Box, _: &mut ViewContext) -> bool { - // false - // } +pub trait Item: Render + EventEmitter + Send { + fn deactivated(&mut self, _: &mut ViewContext) {} + fn workspace_deactivated(&mut self, _: &mut ViewContext) {} + fn navigate(&mut self, _: Box, _: &mut ViewContext) -> bool { + false + } fn tab_tooltip_text(&self, _: &AppContext) -> Option { None } @@ -117,132 +109,104 @@ pub trait Item: EventEmitter + Sized { fn is_singleton(&self, _cx: &AppContext) -> bool { false } - // fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext) {} - fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext) -> Option + fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext) {} + fn clone_on_split( + &self, + _workspace_id: WorkspaceId, + _: &mut ViewContext, + ) -> Option> 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, - // _cx: &mut ViewContext, - // ) -> Task> { - // unimplemented!("save() must be implemented if can_save() returns true") - // } - // fn save_as( - // &mut self, - // _project: Handle, - // _abs_path: PathBuf, - // _cx: &mut ViewContext, - // ) -> Task> { - // unimplemented!("save_as() must be implemented if can_save() returns true") - // } - // fn reload( - // &mut self, - // _project: Handle, - // _cx: &mut ViewContext, - // ) -> Task> { - // 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, _cx: &mut ViewContext) -> Task> { + unimplemented!("save() must be implemented if can_save() returns true") + } + fn save_as( + &mut self, + _project: Model, + _abs_path: PathBuf, + _cx: &mut ViewContext, + ) -> Task> { + unimplemented!("save_as() must be implemented if can_save() returns true") + } + fn reload( + &mut self, + _project: Model, + _cx: &mut ViewContext, + ) -> Task> { + 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 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, - // _: &'a AppContext, - // ) -> Option<&AnyViewHandle> { - // if TypeId::of::() == type_id { - // Some(self_handle) - // } else { - // None - // } - // } + fn act_as_type<'a>( + &'a self, + type_id: TypeId, + self_handle: &'a View, + _: &'a AppContext, + ) -> Option { + if TypeId::of::() == type_id { + Some(self_handle.clone().into()) + } else { + None + } + } - // fn as_searchable(&self, _: &View) -> Option> { - // None - // } + fn as_searchable(&self, _: &View) -> Option> { + None + } - // fn breadcrumb_location(&self) -> ToolbarItemLocation { - // ToolbarItemLocation::Hidden - // } + fn breadcrumb_location(&self) -> ToolbarItemLocation { + ToolbarItemLocation::Hidden + } - // fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option> { - // None - // } + fn breadcrumbs(&self, _theme: &ThemeVariant, _cx: &AppContext) -> Option> { + None + } - // fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext) {} + fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext) {} - // fn serialized_item_kind() -> Option<&'static str> { - // None - // } + fn serialized_item_kind() -> Option<&'static str> { + None + } - // fn deserialize( - // _project: Handle, - // _workspace: WeakViewHandle, - // _workspace_id: WorkspaceId, - // _item_id: ItemId, - // _cx: &mut ViewContext, - // ) -> Task>> { - // 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 { - // None - // } + fn deserialize( + _project: Model, + _workspace: WeakView, + _workspace_id: WorkspaceId, + _item_id: ItemId, + _cx: &mut ViewContext, + ) -> Task>> { + 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> { + None + } } -use std::{ - any::Any, - cell::RefCell, - ops::Range, - path::PathBuf, - rc::Rc, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - time::Duration, -}; - -use gpui2::{ - AnyElement, AnyWindowHandle, AppContext, EventEmitter, Handle, HighlightStyle, Pixels, Point, - SharedString, Task, View, ViewContext, VisualContext, WindowContext, -}; -use project2::{Project, ProjectEntryId, ProjectPath}; -use smallvec::SmallVec; - -use crate::{ - pane::{self, Pane}, - searchable::SearchableItemHandle, - workspace_settings::{AutosaveSetting, WorkspaceSettings}, - DelayedDebouncedEditAction, FollowableItemBuilders, ToolbarItemLocation, Workspace, - WorkspaceId, -}; - pub trait ItemHandle: 'static + Send { fn subscribe_to_item_events( &self, @@ -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, 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, cx: &mut WindowContext) -> Task>; + fn save(&self, project: Model, cx: &mut WindowContext) -> Task>; fn save_as( &self, - project: Handle, + project: Model, abs_path: PathBuf, cx: &mut WindowContext, ) -> Task>; - fn reload(&self, project: Handle, cx: &mut WindowContext) -> Task>; - // fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>; todo!() + fn reload(&self, project: Model, cx: &mut WindowContext) -> Task>; + fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option; fn to_followable_item_handle(&self, cx: &AppContext) -> Option>; fn on_release( - &self, + &mut self, cx: &mut AppContext, - callback: Box, + callback: Box, ) -> gpui2::Subscription; fn to_searchable_item_handle(&self, cx: &AppContext) -> Option>; fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation; - fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option>; + fn breadcrumbs(&self, theme: &ThemeVariant, cx: &AppContext) -> Option>; fn serialized_item_kind(&self) -> Option<&'static str>; fn show_toolbar(&self, cx: &AppContext) -> bool; fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option>; } -pub trait WeakItemHandle { - fn id(&self) -> usize; - fn window(&self) -> AnyWindowHandle; - fn upgrade(&self, cx: &AppContext) -> Option>; +pub trait WeakItemHandle: Send + Sync { + fn id(&self) -> EntityId; + fn upgrade(&self) -> Option>; } -// todo!() -// impl dyn ItemHandle { -// pub fn downcast(&self) -> Option> { -// self.as_any().clone().downcast() -// } +impl dyn ItemHandle { + pub fn downcast(&self) -> Option> { + self.to_any().downcast().ok() + } -// pub fn act_as(&self, cx: &AppContext) -> Option> { -// self.act_as_type(TypeId::of::(), cx) -// .and_then(|t| t.clone().downcast()) -// } -// } + pub fn act_as(&self, cx: &AppContext) -> Option> { + self.act_as_type(TypeId::of::(), cx) + .and_then(|t| t.downcast().ok()) + } +} impl ItemHandle for View { fn subscribe_to_item_events( @@ -399,10 +360,8 @@ impl ItemHandle for View { workspace_id: WorkspaceId, cx: &mut WindowContext, ) -> Option> { - self.update(cx, |item, cx| { - cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx)) - }) - .map(|handle| Box::new(handle) as Box) + self.update(cx, |item, cx| item.clone_on_split(workspace_id, cx)) + .map(|handle| Box::new(handle) as Box) } fn added_to_pane( @@ -439,109 +398,107 @@ impl ItemHandle for View { .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 pending_update = Arc::new(Mutex::new(None)); + let pending_update_scheduled = Arc::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 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; - }; + 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, + // ); + // } + // }); + } + } - 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); + 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 ItemHandle for View { 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 ItemHandle for View { self.read(cx).can_save(cx) } - fn save(&self, project: Handle, cx: &mut WindowContext) -> Task> { + fn save(&self, project: Model, cx: &mut WindowContext) -> Task> { self.update(cx, |item, cx| item.save(project, cx)) } fn save_as( &self, - project: Handle, + project: Model, abs_path: PathBuf, cx: &mut WindowContext, ) -> Task> { self.update(cx, |item, cx| item.save_as(project, abs_path, cx)) } - fn reload(&self, project: Handle, cx: &mut WindowContext) -> Task> { + fn reload(&self, project: Model, cx: &mut WindowContext) -> Task> { 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 { + self.read(cx).act_as_type(type_id, self, cx) + } fn to_followable_item_handle(&self, cx: &AppContext) -> Option> { if cx.has_global::() { let builders = cx.global::(); - 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, + callback: Box, ) -> gpui2::Subscription { cx.observe_release(self, move |_, cx| callback(cx)) } @@ -635,7 +585,7 @@ impl ItemHandle for View { self.read(cx).breadcrumb_location() } - fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option> { + fn breadcrumbs(&self, theme: &ThemeVariant, cx: &AppContext) -> Option> { self.read(cx).breadcrumbs(theme, cx) } @@ -652,17 +602,17 @@ impl ItemHandle for View { } } -// impl From> for AnyViewHandle { -// fn from(val: Box) -> Self { -// val.as_any().clone() -// } -// } +impl From> for AnyView { + fn from(val: Box) -> Self { + val.to_any() + } +} -// impl From<&Box> for AnyViewHandle { -// fn from(val: &Box) -> Self { -// val.as_any().clone() -// } -// } +impl From<&Box> for AnyView { + fn from(val: &Box) -> Self { + val.to_any() + } +} impl Clone for Box { fn clone(&self) -> Box { @@ -670,26 +620,22 @@ impl Clone for Box { } } -// impl WeakItemHandle for WeakViewHandle { -// fn id(&self) -> usize { -// self.id() -// } +impl WeakItemHandle for WeakView { + fn id(&self) -> EntityId { + self.entity_id() + } -// fn window(&self) -> AnyWindowHandle { -// self.window() -// } - -// fn upgrade(&self, cx: &AppContext) -> Option> { -// self.upgrade(cx).map(|v| Box::new(v) as Box) -// } -// } + fn upgrade(&self) -> Option> { + self.upgrade().map(|v| Box::new(v) as Box) + } +} pub trait ProjectItem: Item { type Item: project2::Item; fn for_project_item( - project: Handle, - item: Handle, + project: Model, + item: Model, cx: &mut ViewContext, ) -> Self where @@ -714,7 +660,7 @@ pub trait FollowableItem: Item { ) -> bool; fn apply_update_proto( &mut self, - project: &Handle, + project: &Model, message: proto::update_view::Variant, cx: &mut ViewContext, ) -> Task>; @@ -736,7 +682,7 @@ pub trait FollowableItemHandle: ItemHandle { ) -> bool; fn apply_update_proto( &self, - project: &Handle, + project: &Model, message: proto::update_view::Variant, cx: &mut WindowContext, ) -> Task>; @@ -744,65 +690,65 @@ pub trait FollowableItemHandle: ItemHandle { fn is_project_item(&self, cx: &AppContext) -> bool; } -// impl FollowableItemHandle for View { -// fn remote_id(&self, client: &Arc, cx: &AppContext) -> Option { -// self.read(cx).remote_id().or_else(|| { -// client.peer_id().map(|creator| ViewId { -// creator, -// id: self.id() as u64, -// }) -// }) -// } +impl FollowableItemHandle for View { + fn remote_id(&self, client: &Arc, cx: &AppContext) -> Option { + 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, 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, 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 { -// self.read(cx).to_state_proto(cx) -// } + fn to_state_proto(&self, cx: &AppContext) -> Option { + self.read(cx).to_state_proto(cx) + } -// fn add_event_to_update_proto( -// &self, -// event: &dyn Any, -// update: &mut Option, -// 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, + 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, -// message: proto::update_view::Variant, -// cx: &mut WindowContext, -// ) -> Task> { -// self.update(cx, |this, cx| this.apply_update_proto(project, message, cx)) -// } + fn apply_update_proto( + &self, + project: &Model, + message: proto::update_view::Variant, + cx: &mut WindowContext, + ) -> Task> { + 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>, +// pub project_items: Vec>, // pub nav_history: Option, // pub tab_descriptions: Option>, // pub tab_detail: Cell>, @@ -869,7 +815,7 @@ pub trait FollowableItemHandle: ItemHandle { // } // impl TestProjectItem { -// pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Handle { +// pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Model { // 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 { +// pub fn new_untitled(cx: &mut AppContext) -> Model { // 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]) -> Self { +// pub fn with_project_items(mut self, items: &[Model]) -> 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, +// _: Model, // _: &mut ViewContext, // ) -> Task> { // self.save_count += 1; @@ -1055,7 +1001,7 @@ pub trait FollowableItemHandle: ItemHandle { // fn save_as( // &mut self, -// _: Handle, +// _: Model, // _: std::path::PathBuf, // _: &mut ViewContext, // ) -> Task> { @@ -1066,7 +1012,7 @@ pub trait FollowableItemHandle: ItemHandle { // fn reload( // &mut self, -// _: Handle, +// _: Model, // _: &mut ViewContext, // ) -> Task> { // self.reload_count += 1; @@ -1083,7 +1029,7 @@ pub trait FollowableItemHandle: ItemHandle { // } // fn deserialize( -// _project: Handle, +// _project: Model, // _workspace: WeakViewHandle, // workspace_id: WorkspaceId, // _item_id: ItemId, diff --git a/crates/workspace2/src/notifications.rs b/crates/workspace2/src/notifications.rs new file mode 100644 index 0000000000..9922bcdd26 --- /dev/null +++ b/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 NotificationHandle for View { + 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>, +} + +impl std::ops::Deref for NotificationTracker { + type Target = HashMap>; + + 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( + &self, + id: usize, + cx: &ViewContext, + ) -> bool { + cx.global::() + .get(&TypeId::of::()) + .map(|ids| ids.contains(&id)) + .unwrap_or(false) + } + + pub fn show_notification_once( + &mut self, + id: usize, + cx: &mut ViewContext, + build_notification: impl FnOnce(&mut ViewContext) -> View, + ) { + if !self.has_shown_notification_once::(id, cx) { + let tracker = cx.global_mut::(); + let entry = tracker.entry(TypeId::of::()).or_default(); + entry.push(id); + self.show_notification::(id, cx, build_notification) + } + } + + pub fn show_notification( + &mut self, + id: usize, + cx: &mut ViewContext, + build_notification: impl FnOnce(&mut ViewContext) -> View, + ) { + let type_id = TypeId::of::(); + 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(¬ification, 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(&mut self, id: usize, cx: &mut ViewContext) { + let type_id = TypeId::of::(); + + self.dismiss_notification_internal(type_id, id, cx) + } + + pub fn show_toast(&mut self, toast: Toast, cx: &mut ViewContext) { + todo!() + // self.dismiss_notification::(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) { + todo!() + // self.dismiss_notification::(id, cx); + } + + fn dismiss_notification_internal( + &mut self, + type_id: TypeId, + id: usize, + cx: &mut ViewContext, + ) { + 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>>(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| { + // cx.platform().open_url(open_action.0.as_ref()); + // }, + // ) + // } + + enum NotificationMessage { + Text(Cow<'static, str>), + Element(fn(TextStyle, &AppContext) -> AnyElement), + } + + pub struct MessageNotification { + message: NotificationMessage, + on_click: Option) + Send + Sync>>, + click_message: Option>, + } + + pub enum MessageNotificationEvent { + Dismiss, + } + + impl EventEmitter for MessageNotification { + type Event = MessageNotificationEvent; + } + + impl MessageNotification { + pub fn new(message: S) -> MessageNotification + where + S: Into>, + { + Self { + message: NotificationMessage::Text(message.into()), + on_click: None, + click_message: None, + } + } + + // todo!() + // pub fn new_element( + // message: fn(TextStyle, &AppContext) -> AnyElement, + // ) -> MessageNotification { + // Self { + // message: NotificationMessage::Element(message), + // on_click: None, + // click_message: None, + // } + // } + + // pub fn with_click_message(mut self, message: S) -> Self + // where + // S: Into>, + // { + // self.click_message = Some(message.into()); + // self + // } + + // pub fn on_click(mut self, on_click: F) -> Self + // where + // F: 'static + Fn(&mut ViewContext), + // { + // self.on_click = Some(Arc::new(on_click)); + // self + // } + + // pub fn dismiss(&mut self, _: &CancelMessageNotification, cx: &mut ViewContext) { + // cx.emit(MessageNotificationEvent::Dismiss); + // } + } + + impl Render for MessageNotification { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + todo!() + } + } + // todo!() + // impl View for MessageNotification { + // fn ui_name() -> &'static str { + // "MessageNotification" + // } + + // fn render(&mut self, cx: &mut gpui2::ViewContext) -> gpui::AnyElement { + // 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::(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::( + // 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, + ) -> Option; +} + +impl NotifyResultExt for Result +where + E: std::fmt::Debug, +{ + type Ok = T; + + fn notify_err(self, workspace: &mut Workspace, cx: &mut ViewContext) -> Option { + 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 + } + } + } +} diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index e0eb1b7ec2..b30ec0b7f8 100644 --- a/crates/workspace2/src/pane.rs +++ b/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, +// pub pane: WeakView, // } // #[derive(Clone, PartialEq)] // pub struct CloseItemsToTheLeftById { // pub item_id: usize, -// pub pane: WeakViewHandle, +// pub pane: WeakView, // } // #[derive(Clone, PartialEq)] // pub struct CloseItemsToTheRightById { // pub item_id: usize, -// pub pane: WeakViewHandle, +// pub pane: WeakView, // } // #[derive(Clone, PartialEq, Debug, Deserialize, Default)] @@ -96,6 +81,7 @@ pub enum SaveIntent { // pub save_intent: Option, // } +// todo!() // actions!( // pane, // [ @@ -118,40 +104,40 @@ pub enum SaveIntent { // impl_actions!(pane, [ActivateItem, CloseActiveItem, CloseAllItems]); -// const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; +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)); -// } +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 }, 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>, - // activation_history: Vec, - // zoomed: bool, - // active_item_index: usize, + activation_history: Vec, + zoomed: bool, + active_item_index: usize, // last_focused_view_by_item: HashMap, - // autoscroll: bool, + autoscroll: bool, nav_history: NavHistory, - // toolbar: ViewHandle, + toolbar: View, // tab_bar_context_menu: TabBarContextMenu, // tab_context_menu: ViewHandle, - // workspace: WeakViewHandle, - project: Handle, - // has_focus: bool, + workspace: WeakView, + project: Model, + has_focus: bool, // can_drop: Rc, &WindowContext) -> bool>, // can_split: bool, // render_tab_bar_buttons: Rc) -> AnyElement>, @@ -196,18 +191,18 @@ pub struct Pane { pub struct ItemNavHistory { history: NavHistory, - item: Rc, + item: Arc, } #[derive(Clone)] -pub struct NavHistory(Rc>); +pub struct NavHistory(Arc>); struct NavHistoryState { mode: NavigationMode, backward_stack: VecDeque, forward_stack: VecDeque, closed_stack: VecDeque, - paths_by_item: HashMap)>, + paths_by_item: HashMap)>, pane: WeakView, next_timestamp: Arc, } @@ -229,14 +224,14 @@ impl Default for NavigationMode { } pub struct NavigationEntry { - pub item: Rc, - pub data: Option>, + pub item: Arc, + pub data: Option>, pub timestamp: usize, } // pub struct DraggedItem { // pub handle: Box, -// pub pane: WeakViewHandle, +// pub pane: WeakView, // } // pub enum ReorderBehavior { @@ -315,114 +310,119 @@ pub struct NavigationEntry { // .into_any_named("nav button") // } +impl EventEmitter for Pane { + type Event = Event; +} + impl Pane { - // pub fn new( - // workspace: WeakViewHandle, - // project: ModelHandle, - // next_timestamp: Arc, - // cx: &mut ViewContext, - // ) -> 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) - // }); + pub fn new( + workspace: WeakView, + project: Model, + next_timestamp: Arc, + cx: &mut ViewContext, + ) -> 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) + // }); - // 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"; - // } + 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() - // }), - // } - // } + // 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 { + // pub(crate) fn workspace(&self) -> &WeakView { // &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(&self, item: &ViewHandle) -> ItemNavHistory { - // ItemNavHistory { - // history: self.nav_history.clone(), - // item: Rc::new(item.downgrade()), - // } - // } + pub fn nav_history_for_item(&self, item: &View) -> 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.toolbar.update(cx, |_, cx| cx.notify()); - // } + fn history_updated(&mut self, cx: &mut ViewContext) { + 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> + DoubleEndedIterator { - // self.items.iter() - // } + pub fn items(&self) -> impl Iterator> + DoubleEndedIterator { + self.items.iter() + } // pub fn items_of_type(&self) -> impl '_ + Iterator> { // self.items @@ -629,9 +629,9 @@ impl Pane { // .filter_map(|item| item.as_any().clone().downcast()) // } - // pub fn active_item(&self) -> Option> { - // self.items.get(self.active_item_index).cloned() - // } + pub fn active_item(&self) -> Option> { + self.items.get(self.active_item_index).cloned() + } // pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option { // self.items @@ -653,9 +653,9 @@ impl Pane { // }) // } - // pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option { - // self.items.iter().position(|i| i.id() == item.id()) - // } + pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option { + self.items.iter().position(|i| i.id() == item.id()) + } // pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { // // 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, - // ) -> Task> { - // 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, + ) -> Task> { + 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, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; // } - // pub fn close_inactive_items( - // &mut self, - // _: &CloseInactiveItems, - // cx: &mut ViewContext, - // ) -> Option>> { - // 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, + // ) -> Option>> { + // 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, + // ) -> Option>> { + // 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, + // ) -> Task> { + // 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, + // ) -> Option>> { + // 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, + // ) -> Task> { + // 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, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; // } - // pub fn close_clean_items( - // &mut self, - // _: &CloseCleanItems, - // cx: &mut ViewContext, - // ) -> Option>> { - // 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) - // })) - // } + // Some( + // self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| { + // true + // }), + // ) + // } - // pub fn close_items_to_the_left( - // &mut self, - // _: &CloseItemsToTheLeft, - // cx: &mut ViewContext, - // ) -> Option>> { - // 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(super) fn file_names_for_prompt( + items: &mut dyn Iterator>, + 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_to_the_left_by_id( - // &mut self, - // item_id: usize, - // cx: &mut ViewContext, - // ) -> Task> { - // 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( + &mut self, + cx: &mut ViewContext, + mut save_intent: SaveIntent, + should_close: impl 'static + Fn(EntityId) -> bool, + ) -> Task> { + // 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_to_the_right( - // &mut self, - // _: &CloseItemsToTheRight, - // cx: &mut ViewContext, - // ) -> Option>> { - // 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)) - // } + // 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)); - // pub fn close_items_to_the_right_by_id( - // &mut self, - // item_id: usize, - // cx: &mut ViewContext, - // ) -> Task> { - // 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) - // }) - // } + 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; + }; - // pub fn close_all_items( - // &mut self, - // action: &CloseAllItems, - // cx: &mut ViewContext, - // ) -> Option>> { - // if self.items.is_empty() { - // return None; - // } + // 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)); - // Some( - // self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| { - // true - // }), - // ) - // } + if should_save + && !Self::save_item( + project.clone(), + &pane, + item_ix, + &*item, + save_intent, + &mut cx, + ) + .await? + { + break; + } - // pub(super) fn file_names_for_prompt( - // items: &mut dyn Iterator>, - // 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 - // ) - // } + // 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); + } + })?; + } - // pub fn close_items( - // &mut self, - // cx: &mut ViewContext, - // mut save_intent: SaveIntent, - // should_close: impl 'static + Fn(usize) -> bool, - // ) -> Task> { - // // 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()); - // } - // } - // } + pane.update(&mut cx, |_, cx| cx.notify())?; + Ok(()) + }) + } - // // 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)); + pub fn remove_item( + &mut self, + item_index: usize, + activate_pane: bool, + cx: &mut ViewContext, + ) { + self.activation_history + .retain(|&history_entry| history_entry != self.items[item_index].id()); - // 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; - // }; + 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)); - // // 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)); + let should_activate = activate_pane || self.has_focus; + self.activate_item(index_to_activate, should_activate, should_activate, cx); + } - // if should_save - // && !Self::save_item( - // project.clone(), - // &pane, - // item_ix, - // &*item, - // save_intent, - // &mut cx, - // ) - // .await? - // { - // break; - // } + let item = self.items.remove(item_index); - // // 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); - // } - // })?; - // } + cx.emit(Event::RemoveItem { item_id: item.id() }); + if self.items.is_empty() { + item.deactivated(cx); + self.update_toolbar(cx); + cx.emit(Event::Remove); + } - // pane.update(&mut cx, |_, cx| cx.notify())?; - // Ok(()) - // }) - // } + if item_index < self.active_item_index { + self.active_item_index -= 1; + } - // pub fn remove_item( - // &mut self, - // item_index: usize, - // activate_pane: bool, - // cx: &mut ViewContext, - // ) { - // self.activation_history - // .retain(|&history_entry| history_entry != self.items[item_index].id()); + self.nav_history.set_mode(NavigationMode::ClosingItem); + item.deactivated(cx); + self.nav_history.set_mode(NavigationMode::Normal); - // 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)); + 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()); - // let should_activate = activate_pane || self.has_focus; - // self.activate_item(index_to_activate, should_activate, should_activate, cx); - // } + 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()); + } - // let item = self.items.remove(item_index); + if self.items.is_empty() && self.zoomed { + cx.emit(Event::ZoomOut); + } - // 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.notify(); + } - // if item_index < self.active_item_index { - // self.active_item_index -= 1; - // } + pub async fn save_item( + project: Model, + pane: &WeakView, + item_ix: usize, + item: &dyn ItemHandle, + save_intent: SaveIntent, + cx: &mut AsyncWindowContext, + ) -> Result { + const CONFLICT_MESSAGE: &str = + "This file has changed on disk since you started editing it. Do you want to overwrite it?"; - // self.nav_history.set_mode(NavigationMode::ClosingItem); - // item.deactivated(cx); - // self.nav_history.set_mode(NavigationMode::Normal); + if save_intent == SaveIntent::Skip { + return Ok(true); + } - // 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()); + 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), + ) + })?; - // 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()); - // } + // when saving a single buffer, we ignore whether or not it's dirty. + if save_intent == SaveIntent::Save { + is_dirty = true; + } - // if self.items.is_empty() && self.zoomed { - // cx.emit(Event::ZoomOut); - // } + if save_intent == SaveIntent::SaveAs { + is_dirty = true; + has_conflict = false; + can_save = false; + } - // cx.notify(); - // } + if save_intent == SaveIntent::Overwrite { + has_conflict = false; + } - // pub async fn save_item( - // project: ModelHandle, - // pane: &WeakViewHandle, - // item_ix: usize, - // item: &dyn ItemHandle, - // save_intent: SaveIntent, - // cx: &mut AsyncAppContext, - // ) -> Result { - // const CONFLICT_MESSAGE: &str = - // "This file has changed on disk since you started editing it. Do you want to overwrite it?"; + 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 save_intent == SaveIntent::Skip { - // return 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 (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 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) + } - // // when saving a single buffer, we ignore whether or not it's dirty. - // if save_intent == SaveIntent::Save { - // is_dirty = 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 + } - // if save_intent == SaveIntent::SaveAs { - // is_dirty = true; - // has_conflict = false; - // can_save = false; - // } + pub fn autosave_item( + item: &dyn ItemHandle, + project: Model, + cx: &mut WindowContext, + ) -> Task> { + if Self::can_autosave_item(item, cx) { + item.save(project, cx) + } else { + Task::ready(Ok(())) + } + } - // 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::(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 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) - // } - - // 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, - // cx: &mut WindowContext, - // ) -> Task> { - // 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) { - // if let Some(active_item) = self.active_item() { - // cx.focus(active_item.as_any()); - // } - // } + pub fn focus_active_item(&mut self, cx: &mut ViewContext) { + 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) { // cx.emit(Event::Split(direction)); @@ -1317,38 +1314,178 @@ impl Pane { // }); // } - // pub fn toolbar(&self) -> &ViewHandle { - // &self.toolbar - // } + pub fn toolbar(&self) -> &View { + &self.toolbar + } - // pub fn handle_deleted_project_item( - // &mut self, - // entry_id: ProjectEntryId, - // cx: &mut ViewContext, - // ) -> Option<()> { - // let (item_index_to_delete, item_id) = self.items().enumerate().find_map(|(i, item)| { - // if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [entry_id] { - // Some((i, item.id())) - // } else { - // None - // } - // })?; + pub fn handle_deleted_project_item( + &mut self, + entry_id: ProjectEntryId, + cx: &mut ViewContext, + ) -> Option<()> { + let (item_index_to_delete, item_id) = self.items().enumerate().find_map(|(i, item)| { + if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [entry_id] { + Some((i, item.id())) + } else { + None + } + })?; - // self.remove_item(item_index_to_delete, false, cx); - // self.nav_history.remove_item(item_id); + self.remove_item(item_index_to_delete, false, cx); + self.nav_history.remove_item(item_id); - // Some(()) - // } + Some(()) + } - // fn update_toolbar(&mut self, cx: &mut ViewContext) { - // let active_item = self - // .items - // .get(self.active_item_index) - // .map(|item| item.as_ref()); - // self.toolbar.update(cx, |toolbar, cx| { - // toolbar.set_active_item(active_item, cx); - // }); - // } + fn update_toolbar(&mut self, cx: &mut ViewContext) { + let active_item = self + .items + .get(self.active_item_index) + .map(|item| item.as_ref()); + self.toolbar.update(cx, |toolbar, cx| { + toolbar.set_active_item(active_item, cx); + }); + } + + fn render_tab( + &self, + ix: usize, + item: &Box, + detail: usize, + cx: &mut ViewContext<'_, Pane>, + ) -> impl Component { + let label = item.tab_content(Some(detail), cx); + let close_icon = || IconElement::new(Icon::Close).color(IconColor::Muted); + + let (tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index { + false => ( + cx.theme().colors().tab_inactive, + cx.theme().colors().ghost_element_hover, + cx.theme().colors().ghost_element_active, + ), + true => ( + cx.theme().colors().tab_active, + cx.theme().colors().element_hover, + cx.theme().colors().element_active, + ), + }; + + let close_right = ItemSettings::get_global(cx).close_position.right(); + + div() + .id(item.id()) + // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) + // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) + // .on_drop(|_view, state: View, cx| { + // eprintln!("{:?}", state.read(cx)); + // }) + .px_2() + .py_0p5() + .flex() + .items_center() + .justify_center() + .bg(tab_bg) + .hover(|h| h.bg(tab_hover_bg)) + .active(|a| a.bg(tab_active_bg)) + .child( + div() + .px_1() + .flex() + .items_center() + .gap_1p5() + .children(if item.has_conflict(cx) { + Some( + IconElement::new(Icon::ExclamationTriangle) + .size(ui::IconSize::Small) + .color(IconColor::Warning), + ) + } else if item.is_dirty(cx) { + Some( + IconElement::new(Icon::ExclamationTriangle) + .size(ui::IconSize::Small) + .color(IconColor::Info), + ) + } else { + None + }) + .children(if !close_right { + Some(close_icon()) + } else { + None + }) + .child(label) + .children(if close_right { + Some(close_icon()) + } else { + None + }), + ) + } + + fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl Component { + div() + .group("tab_bar") + .id("tab_bar") + .w_full() + .flex() + .bg(cx.theme().colors().tab_bar) + // Left Side + .child( + div() + .relative() + .px_1() + .flex() + .flex_none() + .gap_2() + // Nav Buttons + .child( + div() + .right_0() + .flex() + .items_center() + .gap_px() + .child(IconButton::new("navigate_backward", Icon::ArrowLeft).state( + InteractionState::Enabled.if_enabled(self.can_navigate_backward()), + )) + .child(IconButton::new("navigate_forward", Icon::ArrowRight).state( + InteractionState::Enabled.if_enabled(self.can_navigate_forward()), + )), + ), + ) + .child( + div().w_0().flex_1().h_full().child( + div().id("tabs").flex().overflow_x_scroll().children( + self.items + .iter() + .enumerate() + .zip(self.tab_details(cx)) + .map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)), + ), + ), + ) + // Right Side + .child( + div() + // We only use absolute here since we don't + // have opacity or `hidden()` yet + .absolute() + .neg_top_7() + .px_1() + .flex() + .flex_none() + .gap_2() + .group_hover("tab_bar", |this| this.top_0()) + // Nav Buttons + .child( + div() + .flex() + .items_center() + .gap_px() + .child(IconButton::new("plus", Icon::Plus)) + .child(IconButton::new("split", Icon::Split)), + ), + ) + } // fn render_tabs(&mut self, cx: &mut ViewContext) -> impl Element { // let theme = theme::current(cx).clone(); @@ -1505,46 +1642,46 @@ impl Pane { // row // } - // fn tab_details(&self, cx: &AppContext) -> Vec { - // let mut tab_details = (0..self.items.len()).map(|_| 0).collect::>(); + fn tab_details(&self, cx: &AppContext) -> Vec { + let mut tab_details = self.items.iter().map(|_| 0).collect::>(); - // let mut tab_descriptions = HashMap::default(); - // let mut done = false; - // while !done { - // done = true; + let mut tab_descriptions = HashMap::default(); + let mut done = false; + while !done { + done = true; - // // Store item indices by their tab description. - // for (ix, (item, detail)) in self.items.iter().zip(&tab_details).enumerate() { - // if let Some(description) = item.tab_description(*detail, cx) { - // if *detail == 0 - // || Some(&description) != item.tab_description(detail - 1, cx).as_ref() - // { - // tab_descriptions - // .entry(description) - // .or_insert(Vec::new()) - // .push(ix); - // } - // } - // } + // Store item indices by their tab description. + for (ix, (item, detail)) in self.items.iter().zip(&tab_details).enumerate() { + if let Some(description) = item.tab_description(*detail, cx) { + if *detail == 0 + || Some(&description) != item.tab_description(detail - 1, cx).as_ref() + { + tab_descriptions + .entry(description) + .or_insert(Vec::new()) + .push(ix); + } + } + } - // // If two or more items have the same tab description, increase their level - // // of detail and try again. - // for (_, item_ixs) in tab_descriptions.drain() { - // if item_ixs.len() > 1 { - // done = false; - // for ix in item_ixs { - // tab_details[ix] += 1; - // } - // } - // } - // } + // If two or more items have the same tab description, increase eir level + // of detail and try again. + for (_, item_ixs) in tab_descriptions.drain() { + if item_ixs.len() > 1 { + done = false; + for ix in item_ixs { + tab_details[ix] += 1; + } + } + } + } - // tab_details - // } + tab_details + } // fn render_tab( // item: &Box, - // pane: WeakViewHandle, + // pane: WeakView, // first: bool, // detail: Option, // hovered: bool, @@ -1557,7 +1694,7 @@ impl Pane { // fn render_dragged_tab( // item: &Box, - // pane: WeakViewHandle, + // pane: WeakView, // first: bool, // detail: Option, // hovered: bool, @@ -1571,7 +1708,7 @@ impl Pane { // fn render_tab_with_title( // title: AnyElement, // item: &Box, - // pane: WeakViewHandle, + // pane: WeakView, // first: bool, // hovered: bool, // tab_style: &theme::Tab, @@ -1736,398 +1873,406 @@ impl Pane { // pub fn is_zoomed(&self) -> bool { // self.zoomed // } - // } +} - // impl Entity for Pane { - // type Event = Event; - // } +// impl Entity for Pane { +// type Event = Event; +// } - // impl View for Pane { - // fn ui_name() -> &'static str { - // "Pane" - // } +impl Render for Pane { + type Element = Div; - // fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - // enum MouseNavigationHandler {} + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + v_stack() + .child(self.render_tab_bar(cx)) + .child(div() /* toolbar */) + .child(if let Some(item) = self.active_item() { + item.to_any().render() + } else { + // todo!() + div().child("Empty Pane").render() + }) - // MouseEventHandler::new::(0, cx, |_, cx| { - // let active_item_index = self.active_item_index; + // enum MouseNavigationHandler {} - // if let Some(active_item) = self.active_item() { - // Flex::column() - // .with_child({ - // let theme = theme::current(cx).clone(); + // MouseEventHandler::new::(0, cx, |_, cx| { + // let active_item_index = self.active_item_index; - // let mut stack = Stack::new(); + // if let Some(active_item) = self.active_item() { + // Flex::column() + // .with_child({ + // let theme = theme::current(cx).clone(); - // enum TabBarEventHandler {} - // stack.add_child( - // MouseEventHandler::new::(0, cx, |_, _| { - // Empty::new() - // .contained() - // .with_style(theme.workspace.tab_bar.container) - // }) - // .on_down( - // MouseButton::Left, - // move |_, this, cx| { - // this.activate_item(active_item_index, true, true, cx); - // }, - // ), - // ); - // let tooltip_style = theme.tooltip.clone(); - // let tab_bar_theme = theme.workspace.tab_bar.clone(); + // let mut stack = Stack::new(); - // let nav_button_height = tab_bar_theme.height; - // let button_style = tab_bar_theme.nav_button; - // let border_for_nav_buttons = tab_bar_theme - // .tab_style(false, false) - // .container - // .border - // .clone(); + // enum TabBarEventHandler {} + // stack.add_child( + // MouseEventHandler::new::(0, cx, |_, _| { + // Empty::new() + // .contained() + // .with_style(theme.workspace.tab_bar.container) + // }) + // .on_down( + // MouseButton::Left, + // move |_, this, cx| { + // this.activate_item(active_item_index, true, true, cx); + // }, + // ), + // ); + // let tooltip_style = theme.tooltip.clone(); + // let tab_bar_theme = theme.workspace.tab_bar.clone(); - // let mut tab_row = Flex::row() - // .with_child(nav_button( - // "icons/arrow_left.svg", - // button_style.clone(), - // nav_button_height, - // tooltip_style.clone(), - // self.can_navigate_backward(), - // { - // move |pane, cx| { - // if let Some(workspace) = pane.workspace.upgrade(cx) { - // let pane = cx.weak_handle(); - // cx.window_context().defer(move |cx| { - // workspace.update(cx, |workspace, cx| { - // workspace - // .go_back(pane, cx) - // .detach_and_log_err(cx) - // }) - // }) - // } - // } - // }, - // super::GoBack, - // "Go Back", - // cx, - // )) - // .with_child( - // nav_button( - // "icons/arrow_right.svg", - // button_style.clone(), - // nav_button_height, - // tooltip_style, - // self.can_navigate_forward(), - // { - // move |pane, cx| { - // if let Some(workspace) = pane.workspace.upgrade(cx) { - // let pane = cx.weak_handle(); - // cx.window_context().defer(move |cx| { - // workspace.update(cx, |workspace, cx| { - // workspace - // .go_forward(pane, cx) - // .detach_and_log_err(cx) - // }) - // }) - // } - // } - // }, - // super::GoForward, - // "Go Forward", - // cx, - // ) - // .contained() - // .with_border(border_for_nav_buttons), - // ) - // .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs")); + // let nav_button_height = tab_bar_theme.height; + // let button_style = tab_bar_theme.nav_button; + // let border_for_nav_buttons = tab_bar_theme + // .tab_style(false, false) + // .container + // .border + // .clone(); - // if self.has_focus { - // let render_tab_bar_buttons = self.render_tab_bar_buttons.clone(); - // tab_row.add_child( - // (render_tab_bar_buttons)(self, cx) - // .contained() - // .with_style(theme.workspace.tab_bar.pane_button_container) - // .flex(1., false) - // .into_any(), - // ) - // } + // let mut tab_row = Flex::row() + // .with_child(nav_button( + // "icons/arrow_left.svg", + // button_style.clone(), + // nav_button_height, + // tooltip_style.clone(), + // self.can_navigate_backward(), + // { + // move |pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace + // .go_back(pane, cx) + // .detach_and_log_err(cx) + // }) + // }) + // } + // } + // }, + // super::GoBack, + // "Go Back", + // cx, + // )) + // .with_child( + // nav_button( + // "icons/arrow_right.svg", + // button_style.clone(), + // nav_button_height, + // tooltip_style, + // self.can_navigate_forward(), + // { + // move |pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace + // .go_forward(pane, cx) + // .detach_and_log_err(cx) + // }) + // }) + // } + // } + // }, + // super::GoForward, + // "Go Forward", + // cx, + // ) + // .contained() + // .with_border(border_for_nav_buttons), + // ) + // .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs")); - // stack.add_child(tab_row); - // stack - // .constrained() - // .with_height(theme.workspace.tab_bar.height) - // .flex(1., false) - // .into_any_named("tab bar") - // }) - // .with_child({ - // enum PaneContentTabDropTarget {} - // dragged_item_receiver::( - // self, - // 0, - // self.active_item_index + 1, - // !self.can_split, - // if self.can_split { Some(100.) } else { None }, - // cx, - // { - // let toolbar = self.toolbar.clone(); - // let toolbar_hidden = toolbar.read(cx).hidden(); - // move |_, cx| { - // Flex::column() - // .with_children( - // (!toolbar_hidden) - // .then(|| ChildView::new(&toolbar, cx).expanded()), - // ) - // .with_child( - // ChildView::new(active_item.as_any(), cx).flex(1., true), - // ) - // } - // }, - // ) - // .flex(1., true) - // }) - // .with_child(ChildView::new(&self.tab_context_menu, cx)) - // .into_any() - // } else { - // enum EmptyPane {} - // let theme = theme::current(cx).clone(); + // if self.has_focus { + // let render_tab_bar_buttons = self.render_tab_bar_buttons.clone(); + // tab_row.add_child( + // (render_tab_bar_buttons)(self, cx) + // .contained() + // .with_style(theme.workspace.tab_bar.pane_button_container) + // .flex(1., false) + // .into_any(), + // ) + // } - // dragged_item_receiver::(self, 0, 0, false, None, cx, |_, cx| { - // self.render_blank_pane(&theme, cx) - // }) - // .on_down(MouseButton::Left, |_, _, cx| { - // cx.focus_parent(); - // }) - // .into_any() - // } - // }) - // .on_down( - // MouseButton::Navigate(NavigationDirection::Back), - // move |_, pane, cx| { - // if let Some(workspace) = pane.workspace.upgrade(cx) { - // let pane = cx.weak_handle(); - // cx.window_context().defer(move |cx| { - // workspace.update(cx, |workspace, cx| { - // workspace.go_back(pane, cx).detach_and_log_err(cx) - // }) - // }) - // } - // }, - // ) - // .on_down(MouseButton::Navigate(NavigationDirection::Forward), { - // move |_, pane, cx| { - // if let Some(workspace) = pane.workspace.upgrade(cx) { - // let pane = cx.weak_handle(); - // cx.window_context().defer(move |cx| { - // workspace.update(cx, |workspace, cx| { - // workspace.go_forward(pane, cx).detach_and_log_err(cx) - // }) - // }) - // } - // } - // }) - // .into_any_named("pane") - // } + // stack.add_child(tab_row); + // stack + // .constrained() + // .with_height(theme.workspace.tab_bar.height) + // .flex(1., false) + // .into_any_named("tab bar") + // }) + // .with_child({ + // enum PaneContentTabDropTarget {} + // dragged_item_receiver::( + // self, + // 0, + // self.active_item_index + 1, + // !self.can_split, + // if self.can_split { Some(100.) } else { None }, + // cx, + // { + // let toolbar = self.toolbar.clone(); + // let toolbar_hidden = toolbar.read(cx).hidden(); + // move |_, cx| { + // Flex::column() + // .with_children( + // (!toolbar_hidden) + // .then(|| ChildView::new(&toolbar, cx).expanded()), + // ) + // .with_child( + // ChildView::new(active_item.as_any(), cx).flex(1., true), + // ) + // } + // }, + // ) + // .flex(1., true) + // }) + // .with_child(ChildView::new(&self.tab_context_menu, cx)) + // .into_any() + // } else { + // enum EmptyPane {} + // let theme = theme::current(cx).clone(); - // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { - // if !self.has_focus { - // self.has_focus = true; - // cx.emit(Event::Focus); - // cx.notify(); - // } + // dragged_item_receiver::(self, 0, 0, false, None, cx, |_, cx| { + // self.render_blank_pane(&theme, cx) + // }) + // .on_down(MouseButton::Left, |_, _, cx| { + // cx.focus_parent(); + // }) + // .into_any() + // } + // }) + // .on_down( + // MouseButton::Navigate(NavigationDirection::Back), + // move |_, pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace.go_back(pane, cx).detach_and_log_err(cx) + // }) + // }) + // } + // }, + // ) + // .on_down(MouseButton::Navigate(NavigationDirection::Forward), { + // move |_, pane, cx| { + // if let Some(workspace) = pane.workspace.upgrade(cx) { + // let pane = cx.weak_handle(); + // cx.window_context().defer(move |cx| { + // workspace.update(cx, |workspace, cx| { + // workspace.go_forward(pane, cx).detach_and_log_err(cx) + // }) + // }) + // } + // } + // }) + // .into_any_named("pane") + } - // self.toolbar.update(cx, |toolbar, cx| { - // toolbar.focus_changed(true, cx); - // }); - - // if let Some(active_item) = self.active_item() { - // if cx.is_self_focused() { - // // Pane was focused directly. We need to either focus a view inside the active item, - // // or focus the active item itself - // if let Some(weak_last_focused_view) = - // self.last_focused_view_by_item.get(&active_item.id()) - // { - // if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) { - // cx.focus(&last_focused_view); - // return; - // } else { - // self.last_focused_view_by_item.remove(&active_item.id()); - // } - // } - - // cx.focus(active_item.as_any()); - // } else if focused != self.tab_bar_context_menu.handle { - // self.last_focused_view_by_item - // .insert(active_item.id(), focused.downgrade()); - // } - // } - // } - - // fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - // self.has_focus = false; - // self.toolbar.update(cx, |toolbar, cx| { - // toolbar.focus_changed(false, cx); - // }); + // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { + // if !self.has_focus { + // self.has_focus = true; + // cx.emit(Event::Focus); // cx.notify(); // } - // fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) { - // Self::reset_to_default_keymap_context(keymap); + // self.toolbar.update(cx, |toolbar, cx| { + // toolbar.focus_changed(true, cx); + // }); + + // if let Some(active_item) = self.active_item() { + // if cx.is_self_focused() { + // // Pane was focused directly. We need to either focus a view inside the active item, + // // or focus the active item itself + // if let Some(weak_last_focused_view) = + // self.last_focused_view_by_item.get(&active_item.id()) + // { + // if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) { + // cx.focus(&last_focused_view); + // return; + // } else { + // self.last_focused_view_by_item.remove(&active_item.id()); + // } + // } + + // cx.focus(active_item.as_any()); + // } else if focused != self.tab_bar_context_menu.handle { + // self.last_focused_view_by_item + // .insert(active_item.id(), focused.downgrade()); + // } // } // } - // impl ItemNavHistory { - // pub fn push(&mut self, data: Option, cx: &mut WindowContext) { - // self.history.push(data, self.item.clone(), cx); - // } - - // pub fn pop_backward(&mut self, cx: &mut WindowContext) -> Option { - // self.history.pop(NavigationMode::GoingBack, cx) - // } - - // pub fn pop_forward(&mut self, cx: &mut WindowContext) -> Option { - // self.history.pop(NavigationMode::GoingForward, cx) - // } + // fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + // self.has_focus = false; + // self.toolbar.update(cx, |toolbar, cx| { + // toolbar.focus_changed(false, cx); + // }); + // cx.notify(); // } - // impl NavHistory { - // pub fn for_each_entry( - // &self, - // cx: &AppContext, - // mut f: impl FnMut(&NavigationEntry, (ProjectPath, Option)), - // ) { - // let borrowed_history = self.0.borrow(); - // borrowed_history - // .forward_stack - // .iter() - // .chain(borrowed_history.backward_stack.iter()) - // .chain(borrowed_history.closed_stack.iter()) - // .for_each(|entry| { - // if let Some(project_and_abs_path) = - // borrowed_history.paths_by_item.get(&entry.item.id()) - // { - // f(entry, project_and_abs_path.clone()); - // } else if let Some(item) = entry.item.upgrade(cx) { - // if let Some(path) = item.project_path(cx) { - // f(entry, (path, None)); - // } - // } - // }) - // } - - // pub fn set_mode(&mut self, mode: NavigationMode) { - // self.0.borrow_mut().mode = mode; - // } - - pub fn mode(&self) -> NavigationMode { - self.0.borrow().mode - } - - // pub fn disable(&mut self) { - // self.0.borrow_mut().mode = NavigationMode::Disabled; - // } - - // pub fn enable(&mut self) { - // self.0.borrow_mut().mode = NavigationMode::Normal; - // } - - // pub fn pop(&mut self, mode: NavigationMode, cx: &mut WindowContext) -> Option { - // let mut state = self.0.borrow_mut(); - // let entry = match mode { - // NavigationMode::Normal | NavigationMode::Disabled | NavigationMode::ClosingItem => { - // return None - // } - // NavigationMode::GoingBack => &mut state.backward_stack, - // NavigationMode::GoingForward => &mut state.forward_stack, - // NavigationMode::ReopeningClosedItem => &mut state.closed_stack, - // } - // .pop_back(); - // if entry.is_some() { - // state.did_update(cx); - // } - // entry - // } - - // pub fn push( - // &mut self, - // data: Option, - // item: Rc, - // cx: &mut WindowContext, - // ) { - // let state = &mut *self.0.borrow_mut(); - // match state.mode { - // NavigationMode::Disabled => {} - // NavigationMode::Normal | NavigationMode::ReopeningClosedItem => { - // if state.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { - // state.backward_stack.pop_front(); - // } - // state.backward_stack.push_back(NavigationEntry { - // item, - // data: data.map(|data| Box::new(data) as Box), - // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), - // }); - // state.forward_stack.clear(); - // } - // NavigationMode::GoingBack => { - // if state.forward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { - // state.forward_stack.pop_front(); - // } - // state.forward_stack.push_back(NavigationEntry { - // item, - // data: data.map(|data| Box::new(data) as Box), - // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), - // }); - // } - // NavigationMode::GoingForward => { - // if state.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { - // state.backward_stack.pop_front(); - // } - // state.backward_stack.push_back(NavigationEntry { - // item, - // data: data.map(|data| Box::new(data) as Box), - // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), - // }); - // } - // NavigationMode::ClosingItem => { - // if state.closed_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { - // state.closed_stack.pop_front(); - // } - // state.closed_stack.push_back(NavigationEntry { - // item, - // data: data.map(|data| Box::new(data) as Box), - // timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), - // }); - // } - // } - // state.did_update(cx); - // } - - // pub fn remove_item(&mut self, item_id: usize) { - // let mut state = self.0.borrow_mut(); - // state.paths_by_item.remove(&item_id); - // state - // .backward_stack - // .retain(|entry| entry.item.id() != item_id); - // state - // .forward_stack - // .retain(|entry| entry.item.id() != item_id); - // state - // .closed_stack - // .retain(|entry| entry.item.id() != item_id); - // } - - // pub fn path_for_item(&self, item_id: usize) -> Option<(ProjectPath, Option)> { - // self.0.borrow().paths_by_item.get(&item_id).cloned() - // } + // fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) { + // Self::reset_to_default_keymap_context(keymap); + // } } -// impl NavHistoryState { -// pub fn did_update(&self, cx: &mut WindowContext) { -// if let Some(pane) = self.pane.upgrade(cx) { -// cx.defer(move |cx| { -// pane.update(cx, |pane, cx| pane.history_updated(cx)); -// }); -// } -// } -// } +impl ItemNavHistory { + pub fn push(&mut self, data: Option, cx: &mut WindowContext) { + self.history.push(data, self.item.clone(), cx); + } + + pub fn pop_backward(&mut self, cx: &mut WindowContext) -> Option { + self.history.pop(NavigationMode::GoingBack, cx) + } + + pub fn pop_forward(&mut self, cx: &mut WindowContext) -> Option { + self.history.pop(NavigationMode::GoingForward, cx) + } +} + +impl NavHistory { + pub fn for_each_entry( + &self, + cx: &AppContext, + mut f: impl FnMut(&NavigationEntry, (ProjectPath, Option)), + ) { + let borrowed_history = self.0.lock(); + borrowed_history + .forward_stack + .iter() + .chain(borrowed_history.backward_stack.iter()) + .chain(borrowed_history.closed_stack.iter()) + .for_each(|entry| { + if let Some(project_and_abs_path) = + borrowed_history.paths_by_item.get(&entry.item.id()) + { + f(entry, project_and_abs_path.clone()); + } else if let Some(item) = entry.item.upgrade() { + if let Some(path) = item.project_path(cx) { + f(entry, (path, None)); + } + } + }) + } + + pub fn set_mode(&mut self, mode: NavigationMode) { + self.0.lock().mode = mode; + } + + pub fn mode(&self) -> NavigationMode { + self.0.lock().mode + } + + pub fn disable(&mut self) { + self.0.lock().mode = NavigationMode::Disabled; + } + + pub fn enable(&mut self) { + self.0.lock().mode = NavigationMode::Normal; + } + + pub fn pop(&mut self, mode: NavigationMode, cx: &mut WindowContext) -> Option { + let mut state = self.0.lock(); + let entry = match mode { + NavigationMode::Normal | NavigationMode::Disabled | NavigationMode::ClosingItem => { + return None + } + NavigationMode::GoingBack => &mut state.backward_stack, + NavigationMode::GoingForward => &mut state.forward_stack, + NavigationMode::ReopeningClosedItem => &mut state.closed_stack, + } + .pop_back(); + if entry.is_some() { + state.did_update(cx); + } + entry + } + + pub fn push( + &mut self, + data: Option, + item: Arc, + cx: &mut WindowContext, + ) { + let state = &mut *self.0.lock(); + match state.mode { + NavigationMode::Disabled => {} + NavigationMode::Normal | NavigationMode::ReopeningClosedItem => { + if state.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + state.backward_stack.pop_front(); + } + state.backward_stack.push_back(NavigationEntry { + item, + data: data.map(|data| Box::new(data) as Box), + timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + }); + state.forward_stack.clear(); + } + NavigationMode::GoingBack => { + if state.forward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + state.forward_stack.pop_front(); + } + state.forward_stack.push_back(NavigationEntry { + item, + data: data.map(|data| Box::new(data) as Box), + timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + }); + } + NavigationMode::GoingForward => { + if state.backward_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + state.backward_stack.pop_front(); + } + state.backward_stack.push_back(NavigationEntry { + item, + data: data.map(|data| Box::new(data) as Box), + timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + }); + } + NavigationMode::ClosingItem => { + if state.closed_stack.len() >= MAX_NAVIGATION_HISTORY_LEN { + state.closed_stack.pop_front(); + } + state.closed_stack.push_back(NavigationEntry { + item, + data: data.map(|data| Box::new(data) as Box), + timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst), + }); + } + } + state.did_update(cx); + } + + pub fn remove_item(&mut self, item_id: EntityId) { + let mut state = self.0.lock(); + state.paths_by_item.remove(&item_id); + state + .backward_stack + .retain(|entry| entry.item.id() != item_id); + state + .forward_stack + .retain(|entry| entry.item.id() != item_id); + state + .closed_stack + .retain(|entry| entry.item.id() != item_id); + } + + pub fn path_for_item(&self, item_id: EntityId) -> Option<(ProjectPath, Option)> { + self.0.lock().paths_by_item.get(&item_id).cloned() + } +} + +impl NavHistoryState { + pub fn did_update(&self, cx: &mut WindowContext) { + if let Some(pane) = self.pane.upgrade() { + cx.defer(move |cx| { + pane.update(cx, |pane, cx| pane.history_updated(cx)); + }); + } + } +} // pub struct PaneBackdrop { // child_view: usize, @@ -2221,14 +2366,14 @@ impl Pane { // } // } -// fn dirty_message_for(buffer_path: Option) -> String { -// let path = buffer_path -// .as_ref() -// .and_then(|p| p.path.to_str()) -// .unwrap_or(&"This buffer"); -// let path = truncate_and_remove_front(path, 80); -// format!("{path} contains unsaved edits. Do you want to save it?") -// } +fn dirty_message_for(buffer_path: Option) -> String { + let path = buffer_path + .as_ref() + .and_then(|p| p.path.to_str()) + .unwrap_or(&"This buffer"); + let path = truncate_and_remove_front(path, 80); + format!("{path} contains unsaved edits. Do you want to save it?") +} // todo!("uncomment tests") // #[cfg(test)] @@ -2752,3 +2897,16 @@ impl Pane { // }) // } // } + +#[derive(Clone, Debug)] +struct DraggedTab { + title: String, +} + +impl Render for DraggedTab { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div().w_8().h_4().bg(gpui2::red()) + } +} diff --git a/crates/workspace2/src/pane/dragged_item_receiver.rs b/crates/workspace2/src/pane/dragged_item_receiver.rs new file mode 100644 index 0000000000..292529e787 --- /dev/null +++ b/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( + pane: &Pane, + region_id: usize, + drop_index: usize, + allow_same_pane: bool, + split_margin: Option, + cx: &mut ViewContext, + render_child: F, +) -> MouseEventHandler +where + Tag: 'static, + D: Element, + F: FnOnce(&mut MouseState, &mut ViewContext) -> D, +{ + let drag_and_drop = cx.global::>(); + let drag_position = if (pane.can_drop)(drag_and_drop, cx) { + drag_and_drop + .currently_dragged::(cx.window()) + .map(|(drag_position, _)| drag_position) + .or_else(|| { + drag_and_drop + .currently_dragged::(cx.window()) + .map(|(drag_position, _)| drag_position) + }) + } else { + None + }; + + let mut handler = MouseEventHandler::above::(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::>(); + + if drag_and_drop + .currently_dragged::(cx.window()) + .is_some() + || drag_and_drop + .currently_dragged::(cx.window()) + .is_some() + { + cx.notify(); + } else { + cx.propagate_event(); + } + }) + } + + handler +} + +pub fn handle_dropped_item( + event: MouseUp, + workspace: WeakViewHandle, + pane: &WeakViewHandle, + index: usize, + allow_same_pane: bool, + split_margin: Option, + cx: &mut EventContext, +) { + enum Action { + Move(WeakViewHandle, usize), + Open(ProjectEntryId), + } + let drag_and_drop = cx.global::>(); + let action = if let Some((_, dragged_item)) = + drag_and_drop.currently_dragged::(cx.window()) + { + Action::Move(dragged_item.pane.clone(), dragged_item.handle.id()) + } else if let Some((_, project_entry)) = + drag_and_drop.currently_dragged::(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 { + 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 +} diff --git a/crates/workspace2/src/pane_group.rs b/crates/workspace2/src/pane_group.rs index f226f7fc43..e521c51bda 100644 --- a/crates/workspace2/src/pane_group.rs +++ b/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 { + 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, - theme: &Theme, + project: &Model, follower_states: &HashMap, FollowerState>, - active_call: Option<&Handle>, + active_call: Option<&Model>, active_pane: &View, - zoomed: Option<&AnyView>, + zoomed: Option<&AnyWeakView>, app_state: &Arc, cx: &mut ViewContext, - ) -> AnyElement { + ) -> impl Component { 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), @@ -153,17 +185,51 @@ impl Member { pub fn render( &self, - project: &Handle, + project: &Model, basis: usize, - theme: &Theme, follower_states: &HashMap, FollowerState>, - active_call: Option<&Handle>, + active_call: Option<&Model>, active_pane: &View, - zoomed: Option<&AnyView>, + zoomed: Option<&AnyWeakView>, app_state: &Arc, cx: &mut ViewContext, - ) -> AnyElement { - todo!() + ) -> impl Component { + 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, - pub flexes: Rc>>, - pub bounding_boxes: Rc>>>>, + pub flexes: Arc>>, + pub bounding_boxes: Arc>>>>, +} + +impl PartialEq for PaneAxis { + fn eq(&self, other: &Self) -> bool { + todo!() + } } impl PaneAxis { pub fn new(axis: Axis, members: Vec) -> 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) -> Option> { - 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) -> Option<&View> { - 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: &Model, basis: usize, - theme: &Theme, follower_states: &HashMap, FollowerState>, - active_call: Option<&Handle>, + active_call: Option<&Model>, active_pane: &View, - zoomed: Option<&AnyView>, + zoomed: Option<&AnyWeakView>, app_state: &Arc, cx: &mut ViewContext, ) -> AnyElement { - 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) -> f32 { + pub fn edge(&self, rect: Bounds) -> 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, length: Pixels) -> Bounds { 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), }, } } diff --git a/crates/workspace2/src/persistence.rs b/crates/workspace2/src/persistence.rs new file mode 100644 index 0000000000..435518271d --- /dev/null +++ b/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>, + // dock_visible: bool, // Deprecated + // dock_anchor: DockAnchor, // Deprecated + // dock_pane: Option, // Deprecated + // left_sidebar_open: boolean, + // timestamp: String, // UTC YYYY-MM-DD HH:MM:SS + // window_state: String, // WindowBounds Discriminant + // window_x: Option, // WindowBounds::Fixed RectF x + // window_y: Option, // WindowBounds::Fixed RectF y + // window_width: Option, // WindowBounds::Fixed RectF width + // window_height: Option, // WindowBounds::Fixed RectF height + // display: Option, // Display id + // ) + // + // pane_groups( + // group_id: usize, // Primary key for pane_groups + // workspace_id: usize, // References workspaces table + // parent_group_id: Option, // None indicates that this is the root node + // position: Optiopn, // None indicates that this is the root node + // axis: Option, // 'Vertical', 'Horizontal' + // flexes: Option>, // 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, // References pane_groups. If none, this is the root + // position: Option, // 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>( + &self, + worktree_roots: &[P], + ) -> Option { + 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, + Option, + 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 { + INSERT INTO workspaces DEFAULT VALUES RETURNING workspace_id + } + } + + query! { + fn recent_workspaces() -> Result> { + 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> { + 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> { + Ok(self + .recent_workspaces_on_disk() + .await? + .into_iter() + .next() + .map(|(_, location)| location)) + } + + fn get_center_pane_group(&self, workspace_id: WorkspaceId) -> Result { + 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, + ) -> Result> { + type GroupKey = (Option, WorkspaceId); + type GroupOrPane = ( + Option, + Option, + Option, + Option, + Option, + ); + self.select_bound::(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::>(&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::>() + } + + 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 { + 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> { + 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::(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::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>( +// 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"], ¢er_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, ¢er_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); +// } +// } diff --git a/crates/workspace2/src/persistence/model.rs b/crates/workspace2/src/persistence/model.rs index 2e28dabffb..de4518f68e 100644 --- a/crates/workspace2/src/persistence/model.rs +++ b/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: &Model, workspace_id: WorkspaceId, - workspace: &WeakView, - cx: &mut AsyncAppContext, + workspace: WeakView, + cx: &mut AsyncWindowContext, ) -> Option<(Member, Option>, Vec>>)> { 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: &Model, pane: &WeakView, workspace_id: WorkspaceId, - workspace: &WeakView, - cx: &mut AsyncAppContext, + workspace: WeakView, + cx: &mut AsyncWindowContext, ) -> Result>>> { 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, item_id: ItemId, active: bool) -> Self { - Self { - kind: Arc::from(kind.as_ref()), - item_id, - active, - } - } -} +// impl SerializedItem { +// pub fn new(kind: impl AsRef, item_id: ItemId, active: bool) -> Self { +// Self { +// kind: Arc::from(kind.as_ref()), +// item_id, +// active, +// } +// } +// } #[cfg(test)] impl Default for SerializedItem { diff --git a/crates/workspace2/src/searchable.rs b/crates/workspace2/src/searchable.rs new file mode 100644 index 0000000000..3935423635 --- /dev/null +++ b/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, + ) -> Option; + fn clear_matches(&mut self, cx: &mut ViewContext); + fn update_matches(&mut self, matches: Vec, cx: &mut ViewContext); + fn query_suggestion(&mut self, cx: &mut ViewContext) -> String; + fn activate_match( + &mut self, + index: usize, + matches: Vec, + cx: &mut ViewContext, + ); + fn select_matches(&mut self, matches: Vec, cx: &mut ViewContext); + fn replace(&mut self, _: &Self::Match, _: &SearchQuery, _: &mut ViewContext); + fn match_index_for_direction( + &mut self, + matches: &Vec, + current_index: usize, + direction: Direction, + count: usize, + _: &mut ViewContext, + ) -> 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, + cx: &mut ViewContext, + ) -> Task>; + fn active_match_index( + &mut self, + matches: Vec, + cx: &mut ViewContext, + ) -> Option; +} + +pub trait SearchableItemHandle: ItemHandle { + fn downgrade(&self) -> Box; + fn boxed_clone(&self) -> Box; + fn supported_options(&self) -> SearchOptions; + fn subscribe_to_search_events( + &self, + cx: &mut WindowContext, + handler: Box, + ) -> Subscription; + fn clear_matches(&self, cx: &mut WindowContext); + fn update_matches(&self, matches: &Vec>, cx: &mut WindowContext); + fn query_suggestion(&self, cx: &mut WindowContext) -> String; + fn activate_match( + &self, + index: usize, + matches: &Vec>, + cx: &mut WindowContext, + ); + fn select_matches(&self, matches: &Vec>, cx: &mut WindowContext); + fn replace(&self, _: &Box, _: &SearchQuery, _: &mut WindowContext); + fn match_index_for_direction( + &self, + matches: &Vec>, + current_index: usize, + direction: Direction, + count: usize, + cx: &mut WindowContext, + ) -> usize; + fn find_matches( + &self, + query: Arc, + cx: &mut WindowContext, + ) -> Task>>; + fn active_match_index( + &self, + matches: &Vec>, + cx: &mut WindowContext, + ) -> Option; +} + +// todo!("here is where we need to use AnyWeakView"); +impl SearchableItemHandle for View { + fn downgrade(&self) -> Box { + // Box::new(self.downgrade()) + todo!() + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn supported_options(&self) -> SearchOptions { + T::supported_options() + } + + fn subscribe_to_search_events( + &self, + cx: &mut WindowContext, + handler: Box, + ) -> 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>, 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>, + 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>, 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>, + 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, + cx: &mut WindowContext, + ) -> Task>> { + let matches = self.update(cx, |this, cx| this.find_matches(query, cx)); + cx.spawn(|cx| async { + let matches = matches.await; + matches + .into_iter() + .map::, _>(|range| Box::new(range)) + .collect() + }) + } + fn active_match_index( + &self, + matches: &Vec>, + cx: &mut WindowContext, + ) -> Option { + let matches = downcast_matches(matches); + self.update(cx, |this, cx| this.active_match_index(matches, cx)) + } + + fn replace(&self, matches: &Box, query: &SearchQuery, cx: &mut WindowContext) { + let matches = matches.downcast_ref().unwrap(); + self.update(cx, |this, cx| this.replace(matches, query, cx)) + } +} + +fn downcast_matches(matches: &Vec>) -> Vec { + matches + .iter() + .map(|range| range.downcast_ref::().cloned()) + .collect::>>() + .expect( + "SearchableItemHandle function called with vec of matches of a different type than expected", + ) +} + +impl From> for AnyView { + fn from(this: Box) -> Self { + this.to_any().clone() + } +} + +impl From<&Box> for AnyView { + fn from(this: &Box) -> Self { + this.to_any().clone() + } +} + +impl PartialEq for Box { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } +} + +impl Eq for Box {} + +pub trait WeakSearchableItemHandle: WeakItemHandle { + fn upgrade(&self, cx: &AppContext) -> Option>; + + // fn into_any(self) -> AnyWeakView; +} + +// todo!() +// impl WeakSearchableItemHandle for WeakView { +// fn upgrade(&self, cx: &AppContext) -> Option> { +// Some(Box::new(self.upgrade(cx)?)) +// } + +// // fn into_any(self) -> AnyView { +// // self.into_any() +// // } +// } + +impl PartialEq for Box { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } +} + +impl Eq for Box {} + +impl std::hash::Hash for Box { + fn hash(&self, state: &mut H) { + self.id().hash(state) + } +} diff --git a/crates/workspace2/src/shared_screen.rs b/crates/workspace2/src/shared_screen.rs new file mode 100644 index 0000000000..b99c5f3ab9 --- /dev/null +++ b/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, + frame: Option, + pub peer_id: PeerId, + user: Arc, + nav_history: Option, + _maintain_frame: Task>, +} + +impl SharedScreen { + pub fn new( + track: &Arc, + peer_id: PeerId, + user: Arc, + cx: &mut ViewContext, + ) -> 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) -> AnyElement { + enum Focus {} + + let frame = self.frame.clone(); + MouseEventHandler::new::(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> { + Some(format!("{}'s screen", self.user.github_login).into()) + } + fn deactivated(&mut self, cx: &mut ViewContext) { + if let Some(nav_history) = self.nav_history.as_mut() { + nav_history.push::<()>(None, cx); + } + } + + fn tab_content( + &self, + _: Option, + style: &theme::Tab, + _: &AppContext, + ) -> gpui::AnyElement { + 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.nav_history = Some(history); + } + + fn clone_on_split( + &self, + _workspace_id: WorkspaceId, + cx: &mut ViewContext, + ) -> Option { + 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), + } + } +} diff --git a/crates/workspace2/src/status_bar.rs b/crates/workspace2/src/status_bar.rs new file mode 100644 index 0000000000..c2f78d9ad6 --- /dev/null +++ b/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, + ); +} + +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>, + right_items: Vec>, + active_pane: View, + _observe_active_pane: Subscription, +} + +impl Render for StatusBar { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> 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) -> impl Component { + div() + .flex() + .items_center() + .gap_1() + .children(self.left_items.iter().map(|item| item.to_any())) + } + + fn render_right_tools(&self, cx: &mut ViewContext) -> impl Component { + 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) -> AnyElement { +// 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, cx: &mut ViewContext) -> 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(&mut self, item: View, cx: &mut ViewContext) + where + T: 'static + StatusItemView, + { + self.left_items.push(Box::new(item)); + cx.notify(); + } + + pub fn item_of_type(&self) -> Option> { + self.left_items + .iter() + .chain(self.right_items.iter()) + .find_map(|item| item.to_any().clone().downcast().log_err()) + } + + pub fn position_of_item(&self) -> Option + where + T: StatusItemView, + { + for (index, item) in self.left_items.iter().enumerate() { + if item.item_type() == TypeId::of::() { + return Some(index); + } + } + for (index, item) in self.right_items.iter().enumerate() { + if item.item_type() == TypeId::of::() { + return Some(index + self.left_items.len()); + } + } + return None; + } + + pub fn insert_item_after( + &mut self, + position: usize, + item: View, + cx: &mut ViewContext, + ) 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) { + 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(&mut self, item: View, cx: &mut ViewContext) + where + T: 'static + StatusItemView, + { + self.right_items.push(Box::new(item)); + cx.notify(); + } + + pub fn set_active_pane(&mut self, active_pane: &View, cx: &mut ViewContext) { + 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) { + 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 StatusItemViewHandle for View { + 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::() + } +} + +impl From<&dyn StatusItemViewHandle> for AnyView { + fn from(val: &dyn StatusItemViewHandle) -> Self { + val.to_any().clone() + } +} + +// todo!() +// struct StatusBarElement { +// left: AnyElement, +// right: AnyElement, +// } + +// todo!() +// impl Element for StatusBarElement { +// type LayoutState = (); +// type PaintState = (); + +// fn layout( +// &mut self, +// mut constraint: SizeConstraint, +// view: &mut StatusBar, +// cx: &mut ViewContext, +// ) -> (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, +// ) -> 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, +// _: RectF, +// _: RectF, +// _: &Self::LayoutState, +// _: &Self::PaintState, +// _: &StatusBar, +// _: &ViewContext, +// ) -> Option { +// None +// } + +// fn debug( +// &self, +// bounds: RectF, +// _: &Self::LayoutState, +// _: &Self::PaintState, +// _: &StatusBar, +// _: &ViewContext, +// ) -> serde_json::Value { +// json!({ +// "type": "StatusBarElement", +// "bounds": bounds.to_json() +// }) +// } +// } diff --git a/crates/workspace2/src/toolbar.rs b/crates/workspace2/src/toolbar.rs new file mode 100644 index 0000000000..c3d1e520c7 --- /dev/null +++ b/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, + ) -> 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) {} + + /// 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>, + hidden: bool, + can_navigate: bool, + items: Vec<(Box, ToolbarItemLocation)>, +} + +// todo!() +// impl View for Toolbar { +// fn ui_name() -> &'static str { +// "Toolbar" +// } + +// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { +// 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)>( +// svg_path: &'static str, +// style: theme::Interactive, +// nav_button_height: f32, +// tooltip_style: TooltipStyle, +// enabled: bool, +// spacing: f32, +// on_click: F, +// tooltip_action: A, +// action_name: &'static str, +// cx: &mut ViewContext, +// ) -> AnyElement { +// MouseEventHandler::new::(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::( +// 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.can_navigate = can_navigate; + cx.notify(); + } + + pub fn add_item(&mut self, item: View, cx: &mut ViewContext) + 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.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) { + for (toolbar_item, _) in self.items.iter_mut() { + toolbar_item.focus_changed(focused, cx); + } + } + + pub fn item_of_type(&self) -> Option> { + self.items + .iter() + .find_map(|(item, _)| item.to_any().downcast().ok()) + } + + pub fn hidden(&self) -> bool { + self.hidden + } +} + +impl ToolbarItemViewHandle for View { + 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() +// } +// } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 3731094b26..3d9b86a051 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/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}; +use util::ResultExt; +use uuid::Uuid; +use workspace_settings::{AutosaveSetting, WorkspaceSettings}; -// lazy_static! { -// static ref ZED_WINDOW_SIZE: Option = env::var("ZED_WINDOW_SIZE") -// .ok() -// .as_deref() -// .and_then(parse_pixel_position_env_var); -// static ref ZED_WINDOW_POSITION: Option = env::var("ZED_WINDOW_POSITION") -// .ok() -// .as_deref() -// .and_then(parse_pixel_position_env_var); -// } +lazy_static! { + static ref ZED_WINDOW_SIZE: Option> = env::var("ZED_WINDOW_SIZE") + .ok() + .as_deref() + .and_then(parse_pixel_size_env_var); + static ref ZED_WINDOW_POSITION: Option> = 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, // } -// #[derive(Deserialize)] -// pub struct Toast { -// id: usize, -// msg: Cow<'static, str>, -// #[serde(skip)] -// on_click: Option<(Cow<'static, str>, Arc)>, -// } +#[derive(Deserialize)] +pub struct Toast { + id: usize, + msg: Cow<'static, str>, + #[serde(skip)] + on_click: Option<(Cow<'static, str>, Arc)>, +} // impl Toast { // pub fn new>>(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::(cx); -// settings::register::(cx); -// } +pub fn init_settings(cx: &mut AppContext) { + WorkspaceSettings::register(cx); + ItemSettings::register(cx); +} -// pub fn init(app_state: Arc, cx: &mut AppContext) { -// init_settings(cx); -// pane::init(cx); -// notifications::init(cx); +pub fn init(app_state: Arc, 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_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); + // 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| { -// let pane = workspace.active_pane().clone(); -// workspace.unfollow(&pane, cx); -// }, -// ); -// cx.add_action( -// |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext| { -// 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 -// .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_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| { + // let pane = workspace.active_pane().clone(); + // workspace.unfollow(&pane, cx); + // }, + // ); + // cx.add_action( + // |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext| { + // 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 + // .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: &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, 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, AnyHandle, &mut ViewContext) -> Box>; + HashMap, AnyModel, &mut ViewContext) -> Box>; pub fn register_project_item(cx: &mut AppContext) { - cx.update_default_global(|builders: &mut ProjectItemBuilders, _| { - builders.insert(TypeId::of::(), |project, model, cx| { - let item = model.downcast::().unwrap(); - Box::new(cx.add_view(|cx| I::for_project_item(project, item, cx))) - }); + let builders = cx.default_global::(); + builders.insert(TypeId::of::(), |project, model, cx| { + let item = model.downcast::().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(cx: &mut AppContext) { - cx.update_default_global(|builders: &mut FollowableItemBuilders, _| { - builders.insert( - TypeId::of::(), - ( - |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::().unwrap()), - ), - ); - }); + let builders = cx.default_global::(); + builders.insert( + TypeId::of::(), + ( + |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::().unwrap()), + ), + ); } type ItemDeserializers = HashMap< Arc, fn( - Handle, + Model, WeakView, WorkspaceId, ItemId, @@ -419,13 +413,13 @@ type ItemDeserializers = HashMap< ) -> Task>>, >; pub fn register_deserializable_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(cx: &mut AppContext) { pub struct AppState { pub languages: Arc, pub client: Arc, - pub user_store: Handle, - pub workspace_store: Handle, + pub user_store: Model, + pub workspace_store: Model, pub fs: Arc, pub build_window_options: - fn(Option, Option, &MainThread) -> WindowOptions, - pub initialize_workspace: - fn(WeakHandle, bool, Arc, AsyncAppContext) -> Task>, + fn(Option, Option, &mut AppContext) -> WindowOptions, + pub initialize_workspace: fn( + WeakView, + bool, + Arc, + AsyncWindowContext, + ) -> Task>, pub node_runtime: Arc, } pub struct WorkspaceStore { - workspaces: HashSet>, + workspaces: HashSet>, followers: Vec, client: Arc, _subscriptions: Vec, @@ -459,40 +457,42 @@ struct Follower { peer_id: PeerId, } -// impl AppState { -// #[cfg(any(test, feature = "test-support"))] -// pub fn test(cx: &mut AppContext) -> Arc { -// use node_runtime::FakeNodeRuntime; -// use settings::SettingsStore; +impl AppState { + #[cfg(any(test, feature = "test-support"))] + pub fn test(cx: &mut AppContext) -> Arc { + use gpui2::Context; + use node_runtime::FakeNodeRuntime; + use settings2::SettingsStore; -// if !cx.has_global::() { -// cx.set_global(SettingsStore::test(cx)); -// } + if !cx.has_global::() { + 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)); + 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)); -// theme::init((), cx); -// client::init(&client, cx); -// crate::init_settings(cx); + // todo!() + // theme::init((), cx); + client2::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(), -// }) -// } -// } + 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>, @@ -509,7 +509,7 @@ impl DelayedDebouncedEditAction { fn fire_new(&mut self, delay: Duration, cx: &mut ViewContext, func: F) where - F: 'static + FnOnce(&mut Workspace, &mut ViewContext) -> Task>, + F: 'static + Send + FnOnce(&mut Workspace, &mut ViewContext) -> Task>, { 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), -// ContactRequestedJoin(u64), -// } +pub enum Event { + PaneAdded(View), + ContactRequestedJoin(u64), +} pub struct Workspace { - weak_self: WeakHandle, + weak_self: WeakView, // modal: Option, - // zoomed: Option, + zoomed: Option, // zoomed_position: Option, - // center: PaneGroup, - // left_dock: View, - // bottom_dock: View, - // right_dock: View, + center: PaneGroup, + left_dock: View, + bottom_dock: View, + right_dock: View, panes: Vec>, - // panes_by_item: HashMap>, - // active_pane: View, + panes_by_item: HashMap>, + active_pane: View, last_active_center_pane: Option>, - // last_active_view_id: Option, - // status_bar: View, + last_active_view_id: Option, + status_bar: View, // titlebar_item: Option, - // notifications: Vec<(TypeId, usize, Box)>, - project: Handle, - // follower_states: HashMap, FollowerState>, - // last_leaders_by_pane: HashMap, PeerId>, - // window_edited: bool, - // active_call: Option<(ModelHandle, Vec)>, - // leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>, - // database_id: WorkspaceId, + notifications: Vec<(TypeId, usize, Box)>, + project: Model, + follower_states: HashMap, FollowerState>, + last_leaders_by_pane: HashMap, PeerId>, + window_edited: bool, + active_call: Option<(Model, Vec)>, + leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>, + database_id: WorkspaceId, app_state: Arc, - // subscriptions: Vec, - // _apply_leader_updates: Task>, - // _observe_current_user: Task>, - // _schedule_serialize: Option>, - // pane_history_timestamp: Arc, + subscriptions: Vec, + _apply_leader_updates: Task>, + _observe_current_user: Task>, + _schedule_serialize: Option>, + pane_history_timestamp: Arc, } // struct ActiveModal { @@ -582,11 +582,11 @@ pub struct Workspace { // previously_focused_view_id: Option, // } -// #[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>, } -// enum WorkspaceBounds {} +enum WorkspaceBounds {} impl Workspace { - // pub fn new( - // workspace_id: WorkspaceId, - // project: ModelHandle, - // app_state: Arc, - // cx: &mut ViewContext, - // ) -> 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); - // } + pub fn new( + workspace_id: WorkspaceId, + project: Model, + app_state: Arc, + cx: &mut ViewContext, + ) -> 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); + } - // project::Event::CollaboratorLeft(peer_id) => { - // this.collaborator_left(*peer_id, cx); - // } + project2::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); - // } + project2::Event::WorktreeRemoved(_) | project2::Event::WorktreeAdded => { + this.update_window_title(cx); + this.serialize_workspace(cx); + } - // project::Event::DisconnectedFromHost => { - // this.update_window_edited(cx); - // cx.blur(); - // } + project2::Event::DisconnectedFromHost => { + this.update_window_edited(cx); + cx.blur(); + } - // project::Event::Closed => { - // cx.remove_window(); - // } + project2::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) - // }); - // } - // } + project2::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())) - // }), + project2::Event::Notification(message) => this.show_notification(0, cx, |cx| { + cx.build_view(|_| MessageNotification::new(message.clone())) + }), - // _ => {} - // } - // cx.notify() - // }) - // .detach(); + _ => {} + } + cx.notify() + }) + .detach(); - // let weak_handle = cx.weak_handle(); - // let pane_history_timestamp = Arc::new(AtomicUsize::new(0)); + let weak_handle = cx.view().downgrade(); + 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(¢er_pane, Self::handle_pane_event).detach(); - // cx.focus(¢er_pane); - // cx.emit(Event::PaneAdded(center_pane.clone())); + let center_pane = cx.build_view(|cx| { + Pane::new( + weak_handle.clone(), + project.clone(), + pane_history_timestamp.clone(), + cx, + ) + }); + cx.subscribe(¢er_pane, Self::handle_pane_event).detach(); + // todo!() + // cx.focus(¢er_pane); + cx.emit(Event::PaneAdded(center_pane.clone())); - // app_state.workspace_store.update(cx, |store, _| { - // store.workspaces.insert(weak_handle.clone()); - // }); + let window_handle = cx.window_handle().downcast::().unwrap(); + app_state.workspace_store.update(cx, |store, _| { + store.workspaces.insert(window_handle); + }); - // 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)); + 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)); - // while stream.recv().await.is_some() { - // this.update(&mut cx, |_, cx| cx.notify())?; - // } - // anyhow::Ok(()) - // }); + while stream.recv().await.is_some() { + this.update(&mut cx, |_, cx| cx.notify())?; + } + anyhow::Ok(()) + }); - // // 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(); - // } + // 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(); + } - // Ok(()) - // }); + Ok(()) + }); - // cx.emit_global(WorkspaceCreated(weak_handle.clone())); + // todo!("replace with a different mechanism") + // 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(¢er_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 - // }); + 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(¢er_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.update_default_global::, _, _>(|drag_and_drop, _| { - // drag_and_drop.register_container(weak_handle.clone()); - // }); + // todo!() + // cx.update_default_global::, _, _>(|drag_and_drop, _| { + // drag_and_drop.register_container(weak_handle.clone()); + // }); - // let mut active_call = None; - // if cx.has_global::>() { - // let call = cx.global::>().clone(); - // let mut subscriptions = Vec::new(); - // subscriptions.push(cx.subscribe(&call, Self::on_active_call_event)); - // active_call = Some((call, subscriptions)); - // } + let mut active_call = None; + if cx.has_global::>() { + let call = cx.global::>().clone(); + let mut subscriptions = Vec::new(); + subscriptions.push(cx.subscribe(&call, Self::on_active_call_event)); + active_call = Some((call, subscriptions)); + } - // 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); - // } - // } + 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.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(); - // }), - // ]; + 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, - // } - // } + 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, + } + } - // fn new_local( - // abs_paths: Vec, - // app_state: Arc, - // requesting_window: Option>, - // cx: &mut AppContext, - // ) -> Task<( - // WeakViewHandle, - // Vec, 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, - // ); + fn new_local( + abs_paths: Vec, + app_state: Arc, + _requesting_window: Option>, + cx: &mut AppContext, + ) -> Task< + anyhow::Result<( + WindowHandle, + Vec, 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, + ); - // cx.spawn(|mut cx| async move { - // let serialized_workspace = persistence::DB.workspace_for_roots(&abs_paths.as_slice()); + cx.spawn(|mut cx| async move { + let serialized_workspace: Option = None; //persistence::DB.workspace_for_roots(&abs_paths.as_slice()); - // let paths_to_open = Arc::new(abs_paths); + let paths_to_open = Arc::new(abs_paths); - // // Get project paths for all of the abs_paths - // let mut worktree_roots: HashSet> = Default::default(); - // let mut project_paths: Vec<(PathBuf, Option)> = - // 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)); - // } - // } + // Get project paths for all of the abs_paths + let mut worktree_roots: HashSet> = Default::default(); + let mut project_paths: Vec<(PathBuf, Option)> = + 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)); + } + } - // let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() { - // serialized_workspace.id - // } else { - // DB.next_id().await.unwrap_or(0) - // }; + let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() { + serialized_workspace.id + } else { + DB.next_id().await.unwrap_or(0) + }; - // 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?; + // 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 { - // 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; - // } - // } + // 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, display)) - // }) - // .unzip() - // }; + Some((bounds, serialized_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, - // ) - // }, - // ) - // } - // }; + // Use the serialized workspace to construct the new window + let options = + cx.update(|cx| (app_state.build_window_options)(bounds, display, cx))?; - // // We haven't yielded the main thread since obtaining the window handle, - // // so the window exists. - // let workspace = window.root(&cx).unwrap(); + 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) + }) + } + })? + }; - // (app_state.initialize_workspace)( - // workspace.downgrade(), - // serialized_workspace.is_some(), - // app_state.clone(), - // cx.clone(), - // ) - // .await - // .log_err(); + // 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())?; - // window.update(&mut cx, |cx| cx.activate_window()); + (app_state.initialize_workspace)( + weak_view, + serialized_workspace.is_some(), + app_state.clone(), + async_cx, + ) + .await + .log_err(); - // 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(); + window + .update(&mut cx, |_, cx| cx.activate_window()) + .log_err(); - // (workspace, opened_items) - // }) - // } + 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(); - // pub fn weak_handle(&self) -> WeakViewHandle { - // self.weak_self.clone() - // } + Ok((window, opened_items)) + }) + } - // pub fn left_dock(&self) -> &View { - // &self.left_dock - // } + pub fn weak_handle(&self) -> WeakView { + self.weak_self.clone() + } - // pub fn bottom_dock(&self) -> &View { - // &self.bottom_dock - // } + pub fn left_dock(&self) -> &View { + &self.left_dock + } - // pub fn right_dock(&self) -> &View { - // &self.right_dock - // } + pub fn bottom_dock(&self) -> &View { + &self.bottom_dock + } + + pub fn right_dock(&self) -> &View { + &self.right_dock + } // pub fn add_panel(&mut self, panel: View, cx: &mut ViewContext) // where @@ -1034,19 +1043,19 @@ impl Workspace { // dock.update(cx, |dock, cx| dock.add_panel(panel, cx)); // } - // pub fn status_bar(&self) -> &View { - // &self.status_bar - // } + pub fn status_bar(&self) -> &View { + &self.status_bar + } - // pub fn app_state(&self) -> &Arc { - // &self.app_state - // } + pub fn app_state(&self) -> &Arc { + &self.app_state + } - // pub fn user_store(&self) -> &ModelHandle { - // &self.app_state.user_store - // } + pub fn user_store(&self) -> &Model { + &self.app_state.user_store + } - pub fn project(&self) -> &Handle { + pub fn project(&self) -> &Model { &self.project } @@ -1108,7 +1117,7 @@ impl Workspace { // fn navigate_history( // &mut self, - // pane: WeakViewHandle, + // pane: WeakView, // mode: NavigationMode, // cx: &mut ViewContext, // ) -> Task> { @@ -1193,7 +1202,7 @@ impl Workspace { // pub fn go_back( // &mut self, - // pane: WeakViewHandle, + // pane: WeakView, // cx: &mut ViewContext, // ) -> Task> { // self.navigate_history(pane, NavigationMode::GoingBack, cx) @@ -1201,7 +1210,7 @@ impl Workspace { // pub fn go_forward( // &mut self, - // pane: WeakViewHandle, + // pane: WeakView, // cx: &mut ViewContext, // ) -> Task> { // self.navigate_history(pane, NavigationMode::GoingForward, cx) @@ -1498,13 +1507,13 @@ impl Workspace { visible: bool, cx: &mut ViewContext, ) -> Task, anyhow::Error>>>> { - log::info!("open paths {:?}", abs_paths); + log::info!("open paths {abs_paths:?}"); let fs = self.app_state.fs.clone(); // Sort the paths to ensure we add worktrees for parents before their children. abs_paths.sort_unstable(); - cx.spawn(|this, mut cx| async move { + cx.spawn(move |this, mut cx| async move { let mut tasks = Vec::with_capacity(abs_paths.len()); for abs_path in &abs_paths { let project_path = match this @@ -1523,45 +1532,41 @@ impl Workspace { }; let this = this.clone(); - let task = cx.spawn(|mut cx| { - let fs = fs.clone(); - let abs_path = abs_path.clone(); - async move { - let (worktree, project_path) = project_path?; - if fs.is_file(&abs_path).await { - Some( - this.update(&mut cx, |this, cx| { - this.open_path(project_path, None, true, cx) - }) - .log_err()? - .await, - ) - } else { - this.update(&mut cx, |workspace, cx| { - let worktree = worktree.read(cx); - let worktree_abs_path = worktree.abs_path(); - let entry_id = if abs_path == worktree_abs_path.as_ref() { - worktree.root_entry() - } else { - abs_path - .strip_prefix(worktree_abs_path.as_ref()) - .ok() - .and_then(|relative_path| { - worktree.entry_for_path(relative_path) - }) - } - .map(|entry| entry.id); - if let Some(entry_id) = entry_id { - workspace.project.update(cx, |_, cx| { - cx.emit(project2::Event::ActiveEntryChanged(Some( - entry_id, - ))); - }) - } + let abs_path = abs_path.clone(); + let fs = fs.clone(); + let task = cx.spawn(move |mut cx| async move { + let (worktree, project_path) = project_path?; + if fs.is_file(&abs_path).await { + Some( + this.update(&mut cx, |this, cx| { + this.open_path(project_path, None, true, cx) }) - .log_err()?; - None - } + .log_err()? + .await, + ) + } else { + this.update(&mut cx, |workspace, cx| { + let worktree = worktree.read(cx); + let worktree_abs_path = worktree.abs_path(); + let entry_id = if abs_path == worktree_abs_path.as_ref() { + worktree.root_entry() + } else { + abs_path + .strip_prefix(worktree_abs_path.as_ref()) + .ok() + .and_then(|relative_path| { + worktree.entry_for_path(relative_path) + }) + } + .map(|entry| entry.id); + if let Some(entry_id) = entry_id { + workspace.project.update(cx, |_, cx| { + cx.emit(project2::Event::ActiveEntryChanged(Some(entry_id))); + }) + } + }) + .log_err()?; + None } }); tasks.push(task); @@ -1592,15 +1597,15 @@ impl Workspace { // } fn project_path_for_path( - project: Handle, + project: Model, abs_path: &Path, visible: bool, cx: &mut AppContext, - ) -> Task, ProjectPath)>> { + ) -> Task, ProjectPath)>> { let entry = project.update(cx, |project, cx| { project.find_or_create_local_worktree(abs_path, visible, cx) }); - cx.spawn(|cx| async move { + cx.spawn(|mut cx| async move { let (worktree, path) = entry.await?; let worktree_id = worktree.update(&mut cx, |t, _| t.id())?; Ok(( @@ -1617,7 +1622,7 @@ impl Workspace { // pub fn toggle_modal( // &mut self, // cx: &mut ViewContext, - // add_view: F, + // build_view: F, // ) -> Option> // where // V: 'static + Modal, @@ -1633,7 +1638,7 @@ impl Workspace { // cx.focus_self(); // Some(already_open_modal) // } else { - // let modal = add_view(self, cx); + // let modal = build_view(self, cx); // cx.subscribe(&modal, |this, _, event, cx| { // if V::dismiss_on_event(event) { // this.dismiss_modal(cx); @@ -1670,12 +1675,12 @@ impl Workspace { // } // } - // pub fn items<'a>( - // &'a self, - // cx: &'a AppContext, - // ) -> impl 'a + Iterator> { - // self.panes.iter().flat_map(|pane| pane.read(cx).items()) - // } + pub fn items<'a>( + &'a self, + cx: &'a AppContext, + ) -> impl 'a + Iterator> { + self.panes.iter().flat_map(|pane| pane.read(cx).items()) + } // pub fn item_of_type(&self, cx: &AppContext) -> Option> { // self.items_of_type(cx).max_by_key(|item| item.id()) @@ -1690,9 +1695,9 @@ impl Workspace { // .flat_map(|pane| pane.read(cx).items_of_type()) // } - // pub fn active_item(&self, cx: &AppContext) -> Option> { - // self.active_pane().read(cx).active_item() - // } + pub fn active_item(&self, cx: &AppContext) -> Option> { + self.active_pane().read(cx).active_item() + } // fn active_project_path(&self, cx: &ViewContext) -> Option { // self.active_item(cx).and_then(|item| item.project_path(cx)) @@ -1957,21 +1962,23 @@ impl Workspace { // cx.notify(); // } - // fn add_pane(&mut self, cx: &mut ViewContext) -> View { - // let pane = cx.add_view(|cx| { - // Pane::new( - // self.weak_handle(), - // self.project.clone(), - // self.pane_history_timestamp.clone(), - // cx, - // ) - // }); - // cx.subscribe(&pane, Self::handle_pane_event).detach(); - // self.panes.push(pane.clone()); - // cx.focus(&pane); - // cx.emit(Event::PaneAdded(pane.clone())); - // pane - // } + fn add_pane(&mut self, _cx: &mut ViewContext) -> View { + todo!() + // let pane = cx.build_view(|cx| { + // Pane::new( + // self.weak_handle(), + // self.project.clone(), + // self.pane_history_timestamp.clone(), + // cx, + // ) + // }); + // cx.subscribe(&pane, Self::handle_pane_event).detach(); + // self.panes.push(pane.clone()); + // todo!() + // cx.focus(&pane); + // cx.emit(Event::PaneAdded(pane.clone())); + // pane + } // pub fn add_item_to_center( // &mut self, @@ -2069,7 +2076,7 @@ impl Workspace { }); let task = self.load_path(path.into(), cx); - cx.spawn(|_, mut cx| async move { + cx.spawn(move |_, mut cx| async move { let (project_entry_id, build_item) = task.await?; pane.update(&mut cx, |pane, cx| { pane.open_item(project_entry_id, focus_item, cx, build_item) @@ -2116,19 +2123,19 @@ impl Workspace { ) -> Task< Result<( ProjectEntryId, - impl 'static + FnOnce(&mut ViewContext) -> Box, + impl 'static + Send + FnOnce(&mut ViewContext) -> Box, )>, > { let project = self.project().clone(); let project_item = project.update(cx, |project, cx| project.open_path(path, cx)); cx.spawn(|_, mut cx| async move { let (project_entry_id, project_item) = project_item.await?; - let build_item = cx.update(|cx| { + let build_item = cx.update(|_, cx| { cx.default_global::() - .get(&project_item.model_type()) + .get(&project_item.entity_type()) .ok_or_else(|| anyhow!("no item builder for project item")) .cloned() - })?; + })??; let build_item = move |cx: &mut ViewContext| build_item(project, project_item, cx); Ok((project_entry_id, build_item)) @@ -2154,7 +2161,7 @@ impl Workspace { // return item; // } - // let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); + // let item = cx.build_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); // self.add_item(Box::new(item.clone()), cx); // item // } @@ -2178,7 +2185,7 @@ impl Workspace { // return item; // } - // let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); + // let item = cx.build_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); // self.split_item(SplitDirection::Right, Box::new(item.clone()), cx); // item // } @@ -2303,64 +2310,65 @@ impl Workspace { // cx.notify(); // } - // fn handle_pane_event( - // &mut self, - // pane: View, - // event: &pane::Event, - // cx: &mut ViewContext, - // ) { - // match event { - // pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx), - // pane::Event::Split(direction) => { - // self.split_and_clone(pane, *direction, cx); - // } - // pane::Event::Remove => self.remove_pane(pane, cx), - // pane::Event::ActivateItem { local } => { - // if *local { - // self.unfollow(&pane, cx); - // } - // if &pane == self.active_pane() { - // self.active_item_path_changed(cx); - // } - // } - // pane::Event::ChangeItemTitle => { - // if pane == self.active_pane { - // self.active_item_path_changed(cx); - // } - // self.update_window_edited(cx); - // } - // pane::Event::RemoveItem { item_id } => { - // self.update_window_edited(cx); - // if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) { - // if entry.get().id() == pane.id() { - // entry.remove(); - // } - // } - // } - // pane::Event::Focus => { - // self.handle_pane_focused(pane.clone(), cx); - // } - // pane::Event::ZoomIn => { - // if pane == self.active_pane { - // pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); - // if pane.read(cx).has_focus() { - // self.zoomed = Some(pane.downgrade().into_any()); - // self.zoomed_position = None; - // } - // cx.notify(); - // } - // } - // pane::Event::ZoomOut => { - // pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); - // if self.zoomed_position.is_none() { - // self.zoomed = None; - // } - // cx.notify(); - // } - // } + fn handle_pane_event( + &mut self, + _pane: View, + _event: &pane::Event, + _cx: &mut ViewContext, + ) { + todo!() + // match event { + // pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx), + // pane::Event::Split(direction) => { + // self.split_and_clone(pane, *direction, cx); + // } + // pane::Event::Remove => self.remove_pane(pane, cx), + // pane::Event::ActivateItem { local } => { + // if *local { + // self.unfollow(&pane, cx); + // } + // if &pane == self.active_pane() { + // self.active_item_path_changed(cx); + // } + // } + // pane::Event::ChangeItemTitle => { + // if pane == self.active_pane { + // self.active_item_path_changed(cx); + // } + // self.update_window_edited(cx); + // } + // pane::Event::RemoveItem { item_id } => { + // self.update_window_edited(cx); + // if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) { + // if entry.get().id() == pane.id() { + // entry.remove(); + // } + // } + // } + // pane::Event::Focus => { + // self.handle_pane_focused(pane.clone(), cx); + // } + // pane::Event::ZoomIn => { + // if pane == self.active_pane { + // pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); + // if pane.read(cx).has_focus() { + // self.zoomed = Some(pane.downgrade().into_any()); + // self.zoomed_position = None; + // } + // cx.notify(); + // } + // } + // pane::Event::ZoomOut => { + // pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + // if self.zoomed_position.is_none() { + // self.zoomed = None; + // } + // cx.notify(); + // } + // } - // self.serialize_workspace(cx); - // } + // self.serialize_workspace(cx); + } // pub fn split_pane( // &mut self, @@ -2397,9 +2405,9 @@ impl Workspace { // pub fn split_pane_with_item( // &mut self, - // pane_to_split: WeakViewHandle, + // pane_to_split: WeakView, // split_direction: SplitDirection, - // from: WeakViewHandle, + // from: WeakView, // item_id_to_move: usize, // cx: &mut ViewContext, // ) { @@ -2420,7 +2428,7 @@ impl Workspace { // pub fn split_pane_with_project_entry( // &mut self, - // pane_to_split: WeakViewHandle, + // pane_to_split: WeakView, // split_direction: SplitDirection, // project_entry: ProjectEntryId, // cx: &mut ViewContext, @@ -2489,27 +2497,27 @@ impl Workspace { // } // } - // pub fn panes(&self) -> &[View] { - // &self.panes - // } + pub fn panes(&self) -> &[View] { + &self.panes + } - // pub fn active_pane(&self) -> &View { - // &self.active_pane - // } + pub fn active_pane(&self) -> &View { + &self.active_pane + } - // fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext) { - // self.follower_states.retain(|_, state| { - // if state.leader_id == peer_id { - // for item in state.items_by_leader_view_id.values() { - // item.set_leader_peer_id(None, cx); - // } - // false - // } else { - // true - // } - // }); - // cx.notify(); - // } + fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext) { + self.follower_states.retain(|_, state| { + if state.leader_id == peer_id { + for item in state.items_by_leader_view_id.values() { + item.set_leader_peer_id(None, cx); + } + false + } else { + true + } + }); + cx.notify(); + } // fn start_following( // &mut self, @@ -2650,37 +2658,33 @@ impl Workspace { // self.start_following(leader_id, cx) // } - // pub fn unfollow( - // &mut self, - // pane: &View, - // cx: &mut ViewContext, - // ) -> Option { - // let state = self.follower_states.remove(pane)?; - // let leader_id = state.leader_id; - // for (_, item) in state.items_by_leader_view_id { - // item.set_leader_peer_id(None, cx); - // } + pub fn unfollow(&mut self, pane: &View, cx: &mut ViewContext) -> Option { + let state = self.follower_states.remove(pane)?; + let leader_id = state.leader_id; + for (_, item) in state.items_by_leader_view_id { + item.set_leader_peer_id(None, cx); + } - // if self - // .follower_states - // .values() - // .all(|state| state.leader_id != state.leader_id) - // { - // let project_id = self.project.read(cx).remote_id(); - // let room_id = self.active_call()?.read(cx).room()?.read(cx).id(); - // self.app_state - // .client - // .send(proto::Unfollow { - // room_id, - // project_id, - // leader_id: Some(leader_id), - // }) - // .log_err(); - // } + if self + .follower_states + .values() + .all(|state| state.leader_id != state.leader_id) + { + let project_id = self.project.read(cx).remote_id(); + let room_id = self.active_call()?.read(cx).room()?.read(cx).id(); + self.app_state + .client + .send(proto::Unfollow { + room_id, + project_id, + leader_id: Some(leader_id), + }) + .log_err(); + } - // cx.notify(); - // Some(leader_id) - // } + cx.notify(); + Some(leader_id) + } // pub fn is_being_followed(&self, peer_id: PeerId) -> bool { // self.follower_states @@ -2688,100 +2692,85 @@ impl Workspace { // .any(|state| state.leader_id == peer_id) // } - // fn render_titlebar(&self, theme: &Theme, cx: &mut ViewContext) -> AnyElement { - // // TODO: There should be a better system in place for this - // // (https://github.com/zed-industries/zed/issues/1290) - // let is_fullscreen = cx.window_is_fullscreen(); - // let container_theme = if is_fullscreen { - // let mut container_theme = theme.titlebar.container; - // container_theme.padding.left = container_theme.padding.right; - // container_theme - // } else { - // theme.titlebar.container - // }; + fn render_titlebar(&self, cx: &mut ViewContext) -> impl Component { + div() + .bg(cx.theme().colors().title_bar) + .when( + !matches!(cx.window_bounds(), WindowBounds::Fullscreen), + |s| s.pl_20(), + ) + .id("titlebar") + .on_click(|_, event, cx| { + if event.up.click_count == 2 { + cx.zoom_window(); + } + }) + .child("Collab title bar Item") // self.titlebar_item + } - // enum TitleBar {} - // MouseEventHandler::new::(0, cx, |_, cx| { - // Stack::new() - // .with_children( - // self.titlebar_item - // .as_ref() - // .map(|item| ChildView::new(item, cx)), - // ) - // .contained() - // .with_style(container_theme) - // }) - // .on_click(MouseButton::Left, |event, _, cx| { - // if event.click_count == 2 { - // cx.zoom_window(); - // } - // }) - // .constrained() - // .with_height(theme.titlebar.height) - // .into_any_named("titlebar") - // } + // fn active_item_path_changed(&mut self, cx: &mut ViewContext) { + // let active_entry = self.active_project_path(cx); + // self.project + // .update(cx, |project, cx| project.set_active_path(active_entry, cx)); + // self.update_window_title(cx); + // } - // fn active_item_path_changed(&mut self, cx: &mut ViewContext) { - // let active_entry = self.active_project_path(cx); - // self.project - // .update(cx, |project, cx| project.set_active_path(active_entry, cx)); - // self.update_window_title(cx); - // } + fn update_window_title(&mut self, cx: &mut ViewContext) { + let project = self.project().read(cx); + let mut title = String::new(); - // fn update_window_title(&mut self, cx: &mut ViewContext) { - // let project = self.project().read(cx); - // let mut title = String::new(); + if let Some(path) = self.active_item(cx).and_then(|item| item.project_path(cx)) { + let filename = path + .path + .file_name() + .map(|s| s.to_string_lossy()) + .or_else(|| { + Some(Cow::Borrowed( + project + .worktree_for_id(path.worktree_id, cx)? + .read(cx) + .root_name(), + )) + }); - // if let Some(path) = self.active_item(cx).and_then(|item| item.project_path(cx)) { - // let filename = path - // .path - // .file_name() - // .map(|s| s.to_string_lossy()) - // .or_else(|| { - // Some(Cow::Borrowed( - // project - // .worktree_for_id(path.worktree_id, cx)? - // .read(cx) - // .root_name(), - // )) - // }); + if let Some(filename) = filename { + title.push_str(filename.as_ref()); + title.push_str(" — "); + } + } - // if let Some(filename) = filename { - // title.push_str(filename.as_ref()); - // title.push_str(" — "); - // } - // } + for (i, name) in project.worktree_root_names(cx).enumerate() { + if i > 0 { + title.push_str(", "); + } + title.push_str(name); + } - // for (i, name) in project.worktree_root_names(cx).enumerate() { - // if i > 0 { - // title.push_str(", "); - // } - // title.push_str(name); - // } + if title.is_empty() { + title = "empty project".to_string(); + } - // if title.is_empty() { - // title = "empty project".to_string(); - // } + if project.is_remote() { + title.push_str(" ↙"); + } else if project.is_shared() { + title.push_str(" ↗"); + } - // if project.is_remote() { - // title.push_str(" ↙"); - // } else if project.is_shared() { - // title.push_str(" ↗"); - // } + // todo!() + // cx.set_window_title(&title); + } - // cx.set_window_title(&title); - // } - - // fn update_window_edited(&mut self, cx: &mut ViewContext) { - // let is_edited = !self.project.read(cx).is_read_only() - // && self - // .items(cx) - // .any(|item| item.has_conflict(cx) || item.is_dirty(cx)); - // if is_edited != self.window_edited { - // self.window_edited = is_edited; - // cx.set_window_edited(self.window_edited) - // } - // } + fn update_window_edited(&mut self, cx: &mut ViewContext) { + let is_edited = !self.project.read(cx).is_read_only() + && self + .items(cx) + .any(|item| item.has_conflict(cx) || item.is_dirty(cx)); + if is_edited != self.window_edited { + self.window_edited = is_edited; + todo!() + // cx.set_window_edited(self.window_edited) + } + } // fn render_disconnected_overlay( // &self, @@ -2838,298 +2827,300 @@ impl Workspace { // // RPC handlers - // fn handle_follow( - // &mut self, - // follower_project_id: Option, - // cx: &mut ViewContext, - // ) -> proto::FollowResponse { - // let client = &self.app_state.client; - // let project_id = self.project.read(cx).remote_id(); + fn handle_follow( + &mut self, + _follower_project_id: Option, + _cx: &mut ViewContext, + ) -> proto::FollowResponse { + todo!() - // let active_view_id = self.active_item(cx).and_then(|i| { - // Some( - // i.to_followable_item_handle(cx)? - // .remote_id(client, cx)? - // .to_proto(), - // ) - // }); + // let client = &self.app_state.client; + // let project_id = self.project.read(cx).remote_id(); - // cx.notify(); + // let active_view_id = self.active_item(cx).and_then(|i| { + // Some( + // i.to_followable_item_handle(cx)? + // .remote_id(client, cx)? + // .to_proto(), + // ) + // }); - // self.last_active_view_id = active_view_id.clone(); - // proto::FollowResponse { - // active_view_id, - // views: self - // .panes() - // .iter() - // .flat_map(|pane| { - // let leader_id = self.leader_for_pane(pane); - // pane.read(cx).items().filter_map({ - // let cx = &cx; - // move |item| { - // let item = item.to_followable_item_handle(cx)?; - // if (project_id.is_none() || project_id != follower_project_id) - // && item.is_project_item(cx) - // { - // return None; - // } - // let id = item.remote_id(client, cx)?.to_proto(); - // let variant = item.to_state_proto(cx)?; - // Some(proto::View { - // id: Some(id), - // leader_id, - // variant: Some(variant), - // }) - // } - // }) - // }) - // .collect(), - // } - // } + // cx.notify(); - // fn handle_update_followers( - // &mut self, - // leader_id: PeerId, - // message: proto::UpdateFollowers, - // _cx: &mut ViewContext, - // ) { - // self.leader_updates_tx - // .unbounded_send((leader_id, message)) - // .ok(); - // } + // self.last_active_view_id = active_view_id.clone(); + // proto::FollowResponse { + // active_view_id, + // views: self + // .panes() + // .iter() + // .flat_map(|pane| { + // let leader_id = self.leader_for_pane(pane); + // pane.read(cx).items().filter_map({ + // let cx = &cx; + // move |item| { + // let item = item.to_followable_item_handle(cx)?; + // if (project_id.is_none() || project_id != follower_project_id) + // && item.is_project_item(cx) + // { + // return None; + // } + // let id = item.remote_id(client, cx)?.to_proto(); + // let variant = item.to_state_proto(cx)?; + // Some(proto::View { + // id: Some(id), + // leader_id, + // variant: Some(variant), + // }) + // } + // }) + // }) + // .collect(), + // } + } - // async fn process_leader_update( - // this: &WeakViewHandle, - // leader_id: PeerId, - // update: proto::UpdateFollowers, - // cx: &mut AsyncAppContext, - // ) -> Result<()> { - // match update.variant.ok_or_else(|| anyhow!("invalid update"))? { - // proto::update_followers::Variant::UpdateActiveView(update_active_view) => { - // this.update(cx, |this, _| { - // for (_, state) in &mut this.follower_states { - // if state.leader_id == leader_id { - // state.active_view_id = - // if let Some(active_view_id) = update_active_view.id.clone() { - // Some(ViewId::from_proto(active_view_id)?) - // } else { - // None - // }; - // } - // } - // anyhow::Ok(()) - // })??; - // } - // proto::update_followers::Variant::UpdateView(update_view) => { - // let variant = update_view - // .variant - // .ok_or_else(|| anyhow!("missing update view variant"))?; - // let id = update_view - // .id - // .ok_or_else(|| anyhow!("missing update view id"))?; - // let mut tasks = Vec::new(); - // this.update(cx, |this, cx| { - // let project = this.project.clone(); - // for (_, state) in &mut this.follower_states { - // if state.leader_id == leader_id { - // let view_id = ViewId::from_proto(id.clone())?; - // if let Some(item) = state.items_by_leader_view_id.get(&view_id) { - // tasks.push(item.apply_update_proto(&project, variant.clone(), cx)); - // } - // } - // } - // anyhow::Ok(()) - // })??; - // try_join_all(tasks).await.log_err(); - // } - // proto::update_followers::Variant::CreateView(view) => { - // let panes = this.read_with(cx, |this, _| { - // this.follower_states - // .iter() - // .filter_map(|(pane, state)| (state.leader_id == leader_id).then_some(pane)) - // .cloned() - // .collect() - // })?; - // Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], cx).await?; - // } - // } - // this.update(cx, |this, cx| this.leader_updated(leader_id, cx))?; - // Ok(()) - // } + fn handle_update_followers( + &mut self, + leader_id: PeerId, + message: proto::UpdateFollowers, + _cx: &mut ViewContext, + ) { + self.leader_updates_tx + .unbounded_send((leader_id, message)) + .ok(); + } - // async fn add_views_from_leader( - // this: WeakViewHandle, - // leader_id: PeerId, - // panes: Vec>, - // views: Vec, - // cx: &mut AsyncAppContext, - // ) -> Result<()> { - // let this = this - // .upgrade(cx) - // .ok_or_else(|| anyhow!("workspace dropped"))?; + async fn process_leader_update( + this: &WeakView, + leader_id: PeerId, + update: proto::UpdateFollowers, + cx: &mut AsyncWindowContext, + ) -> Result<()> { + match update.variant.ok_or_else(|| anyhow!("invalid update"))? { + proto::update_followers::Variant::UpdateActiveView(update_active_view) => { + this.update(cx, |this, _| { + for (_, state) in &mut this.follower_states { + if state.leader_id == leader_id { + state.active_view_id = + if let Some(active_view_id) = update_active_view.id.clone() { + Some(ViewId::from_proto(active_view_id)?) + } else { + None + }; + } + } + anyhow::Ok(()) + })??; + } + proto::update_followers::Variant::UpdateView(update_view) => { + let variant = update_view + .variant + .ok_or_else(|| anyhow!("missing update view variant"))?; + let id = update_view + .id + .ok_or_else(|| anyhow!("missing update view id"))?; + let mut tasks = Vec::new(); + this.update(cx, |this, cx| { + let project = this.project.clone(); + for (_, state) in &mut this.follower_states { + if state.leader_id == leader_id { + let view_id = ViewId::from_proto(id.clone())?; + if let Some(item) = state.items_by_leader_view_id.get(&view_id) { + tasks.push(item.apply_update_proto(&project, variant.clone(), cx)); + } + } + } + anyhow::Ok(()) + })??; + try_join_all(tasks).await.log_err(); + } + proto::update_followers::Variant::CreateView(view) => { + let panes = this.update(cx, |this, _| { + this.follower_states + .iter() + .filter_map(|(pane, state)| (state.leader_id == leader_id).then_some(pane)) + .cloned() + .collect() + })?; + Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], cx).await?; + } + } + this.update(cx, |this, cx| this.leader_updated(leader_id, cx))?; + Ok(()) + } - // let item_builders = cx.update(|cx| { - // cx.default_global::() - // .values() - // .map(|b| b.0) - // .collect::>() - // }); + async fn add_views_from_leader( + this: WeakView, + leader_id: PeerId, + panes: Vec>, + views: Vec, + cx: &mut AsyncWindowContext, + ) -> Result<()> { + let this = this.upgrade().context("workspace dropped")?; - // let mut item_tasks_by_pane = HashMap::default(); - // for pane in panes { - // let mut item_tasks = Vec::new(); - // let mut leader_view_ids = Vec::new(); - // for view in &views { - // let Some(id) = &view.id else { continue }; - // let id = ViewId::from_proto(id.clone())?; - // let mut variant = view.variant.clone(); - // if variant.is_none() { - // Err(anyhow!("missing view variant"))?; - // } - // for build_item in &item_builders { - // let task = cx - // .update(|cx| build_item(pane.clone(), this.clone(), id, &mut variant, cx)); - // if let Some(task) = task { - // item_tasks.push(task); - // leader_view_ids.push(id); - // break; - // } else { - // assert!(variant.is_some()); - // } - // } - // } + let item_builders = cx.update(|_, cx| { + cx.default_global::() + .values() + .map(|b| b.0) + .collect::>() + })?; - // item_tasks_by_pane.insert(pane, (item_tasks, leader_view_ids)); - // } + let mut item_tasks_by_pane = HashMap::default(); + for pane in panes { + let mut item_tasks = Vec::new(); + let mut leader_view_ids = Vec::new(); + for view in &views { + let Some(id) = &view.id else { continue }; + let id = ViewId::from_proto(id.clone())?; + let mut variant = view.variant.clone(); + if variant.is_none() { + Err(anyhow!("missing view variant"))?; + } + for build_item in &item_builders { + let task = cx.update(|_, cx| { + build_item(pane.clone(), this.clone(), id, &mut variant, cx) + })?; + if let Some(task) = task { + item_tasks.push(task); + leader_view_ids.push(id); + break; + } else { + assert!(variant.is_some()); + } + } + } - // for (pane, (item_tasks, leader_view_ids)) in item_tasks_by_pane { - // let items = futures::future::try_join_all(item_tasks).await?; - // this.update(cx, |this, cx| { - // let state = this.follower_states.get_mut(&pane)?; - // for (id, item) in leader_view_ids.into_iter().zip(items) { - // item.set_leader_peer_id(Some(leader_id), cx); - // state.items_by_leader_view_id.insert(id, item); - // } + item_tasks_by_pane.insert(pane, (item_tasks, leader_view_ids)); + } - // Some(()) - // }); - // } - // Ok(()) - // } + for (pane, (item_tasks, leader_view_ids)) in item_tasks_by_pane { + let items = futures::future::try_join_all(item_tasks).await?; + this.update(cx, |this, cx| { + let state = this.follower_states.get_mut(&pane)?; + for (id, item) in leader_view_ids.into_iter().zip(items) { + item.set_leader_peer_id(Some(leader_id), cx); + state.items_by_leader_view_id.insert(id, item); + } - // fn update_active_view_for_followers(&mut self, cx: &AppContext) { - // let mut is_project_item = true; - // let mut update = proto::UpdateActiveView::default(); - // if self.active_pane.read(cx).has_focus() { - // let item = self - // .active_item(cx) - // .and_then(|item| item.to_followable_item_handle(cx)); - // if let Some(item) = item { - // is_project_item = item.is_project_item(cx); - // update = proto::UpdateActiveView { - // id: item - // .remote_id(&self.app_state.client, cx) - // .map(|id| id.to_proto()), - // leader_id: self.leader_for_pane(&self.active_pane), - // }; - // } - // } + Some(()) + })?; + } + Ok(()) + } - // if update.id != self.last_active_view_id { - // self.last_active_view_id = update.id.clone(); - // self.update_followers( - // is_project_item, - // proto::update_followers::Variant::UpdateActiveView(update), - // cx, - // ); - // } - // } + fn update_active_view_for_followers(&mut self, cx: &mut ViewContext) { + let mut is_project_item = true; + let mut update = proto::UpdateActiveView::default(); + if self.active_pane.read(cx).has_focus() { + let item = self + .active_item(cx) + .and_then(|item| item.to_followable_item_handle(cx)); + if let Some(item) = item { + is_project_item = item.is_project_item(cx); + update = proto::UpdateActiveView { + id: item + .remote_id(&self.app_state.client, cx) + .map(|id| id.to_proto()), + leader_id: self.leader_for_pane(&self.active_pane), + }; + } + } - // fn update_followers( - // &self, - // project_only: bool, - // update: proto::update_followers::Variant, - // cx: &AppContext, - // ) -> Option<()> { - // let project_id = if project_only { - // self.project.read(cx).remote_id() - // } else { - // None - // }; - // self.app_state().workspace_store.read_with(cx, |store, cx| { - // store.update_followers(project_id, update, cx) - // }) - // } + if update.id != self.last_active_view_id { + self.last_active_view_id = update.id.clone(); + self.update_followers( + is_project_item, + proto::update_followers::Variant::UpdateActiveView(update), + cx, + ); + } + } - // pub fn leader_for_pane(&self, pane: &View) -> Option { - // self.follower_states.get(pane).map(|state| state.leader_id) - // } + fn update_followers( + &self, + project_only: bool, + update: proto::update_followers::Variant, + cx: &mut WindowContext, + ) -> Option<()> { + let project_id = if project_only { + self.project.read(cx).remote_id() + } else { + None + }; + self.app_state().workspace_store.update(cx, |store, cx| { + store.update_followers(project_id, update, cx) + }) + } - // fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext) -> Option<()> { - // cx.notify(); + pub fn leader_for_pane(&self, pane: &View) -> Option { + self.follower_states.get(pane).map(|state| state.leader_id) + } - // let call = self.active_call()?; - // let room = call.read(cx).room()?.read(cx); - // let participant = room.remote_participant_for_peer_id(leader_id)?; - // let mut items_to_activate = Vec::new(); + fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext) -> Option<()> { + cx.notify(); - // let leader_in_this_app; - // let leader_in_this_project; - // match participant.location { - // call::ParticipantLocation::SharedProject { project_id } => { - // leader_in_this_app = true; - // leader_in_this_project = Some(project_id) == self.project.read(cx).remote_id(); - // } - // call::ParticipantLocation::UnsharedProject => { - // leader_in_this_app = true; - // leader_in_this_project = false; - // } - // call::ParticipantLocation::External => { - // leader_in_this_app = false; - // leader_in_this_project = false; - // } - // }; + let call = self.active_call()?; + let room = call.read(cx).room()?.read(cx); + let participant = room.remote_participant_for_peer_id(leader_id)?; + let mut items_to_activate = Vec::new(); - // for (pane, state) in &self.follower_states { - // if state.leader_id != leader_id { - // continue; - // } - // if let (Some(active_view_id), true) = (state.active_view_id, leader_in_this_app) { - // if let Some(item) = state.items_by_leader_view_id.get(&active_view_id) { - // if leader_in_this_project || !item.is_project_item(cx) { - // items_to_activate.push((pane.clone(), item.boxed_clone())); - // } - // } else { - // log::warn!( - // "unknown view id {:?} for leader {:?}", - // active_view_id, - // leader_id - // ); - // } - // continue; - // } - // if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) { - // items_to_activate.push((pane.clone(), Box::new(shared_screen))); - // } - // } + let leader_in_this_app; + let leader_in_this_project; + match participant.location { + call2::ParticipantLocation::SharedProject { project_id } => { + leader_in_this_app = true; + leader_in_this_project = Some(project_id) == self.project.read(cx).remote_id(); + } + call2::ParticipantLocation::UnsharedProject => { + leader_in_this_app = true; + leader_in_this_project = false; + } + call2::ParticipantLocation::External => { + leader_in_this_app = false; + leader_in_this_project = false; + } + }; - // for (pane, item) in items_to_activate { - // let pane_was_focused = pane.read(cx).has_focus(); - // if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) { - // pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx)); - // } else { - // pane.update(cx, |pane, cx| { - // pane.add_item(item.boxed_clone(), false, false, None, cx) - // }); - // } + for (pane, state) in &self.follower_states { + if state.leader_id != leader_id { + continue; + } + if let (Some(active_view_id), true) = (state.active_view_id, leader_in_this_app) { + if let Some(item) = state.items_by_leader_view_id.get(&active_view_id) { + if leader_in_this_project || !item.is_project_item(cx) { + items_to_activate.push((pane.clone(), item.boxed_clone())); + } + } else { + log::warn!( + "unknown view id {:?} for leader {:?}", + active_view_id, + leader_id + ); + } + continue; + } + // todo!() + // if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) { + // items_to_activate.push((pane.clone(), Box::new(shared_screen))); + // } + } - // if pane_was_focused { - // pane.update(cx, |pane, cx| pane.focus_active_item(cx)); - // } - // } + for (pane, item) in items_to_activate { + let pane_was_focused = pane.read(cx).has_focus(); + if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) { + pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx)); + } else { + pane.update(cx, |pane, cx| { + pane.add_item(item.boxed_clone(), false, false, None, cx) + }); + } - // None - // } + if pane_was_focused { + pane.update(cx, |pane, cx| pane.focus_active_item(cx)); + } + } + + None + } // fn shared_screen_for_peer( // &self, @@ -3149,95 +3140,98 @@ impl Workspace { // } // } - // Some(cx.add_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx))) + // Some(cx.build_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx))) // } - // pub fn on_window_activation_changed(&mut self, active: bool, cx: &mut ViewContext) { - // if active { - // self.update_active_view_for_followers(cx); - // cx.background() - // .spawn(persistence::DB.update_timestamp(self.database_id())) - // .detach(); - // } else { - // for pane in &self.panes { - // pane.update(cx, |pane, cx| { - // if let Some(item) = pane.active_item() { - // item.workspace_deactivated(cx); - // } - // if matches!( - // settings::get::(cx).autosave, - // AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange - // ) { - // for item in pane.items() { - // Pane::autosave_item(item.as_ref(), self.project.clone(), cx) - // .detach_and_log_err(cx); - // } - // } - // }); - // } - // } - // } + pub fn on_window_activation_changed(&mut self, cx: &mut ViewContext) { + if cx.is_window_active() { + self.update_active_view_for_followers(cx); + cx.background_executor() + .spawn(persistence::DB.update_timestamp(self.database_id())) + .detach(); + } else { + for pane in &self.panes { + pane.update(cx, |pane, cx| { + if let Some(item) = pane.active_item() { + item.workspace_deactivated(cx); + } + if matches!( + WorkspaceSettings::get_global(cx).autosave, + AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange + ) { + for item in pane.items() { + Pane::autosave_item(item.as_ref(), self.project.clone(), cx) + .detach_and_log_err(cx); + } + } + }); + } + } + } - // fn active_call(&self) -> Option<&ModelHandle> { - // self.active_call.as_ref().map(|(call, _)| call) - // } + fn active_call(&self) -> Option<&Model> { + self.active_call.as_ref().map(|(call, _)| call) + } - // fn on_active_call_event( - // &mut self, - // _: ModelHandle, - // event: &call::room::Event, - // cx: &mut ViewContext, - // ) { - // match event { - // call::room::Event::ParticipantLocationChanged { participant_id } - // | call::room::Event::RemoteVideoTracksChanged { participant_id } => { - // self.leader_updated(*participant_id, cx); - // } - // _ => {} - // } - // } + fn on_active_call_event( + &mut self, + _: Model, + event: &call2::room::Event, + cx: &mut ViewContext, + ) { + match event { + call2::room::Event::ParticipantLocationChanged { participant_id } + | call2::room::Event::RemoteVideoTracksChanged { participant_id } => { + self.leader_updated(*participant_id, cx); + } + _ => {} + } + } - // pub fn database_id(&self) -> WorkspaceId { - // self.database_id - // } + pub fn database_id(&self) -> WorkspaceId { + self.database_id + } - // fn location(&self, cx: &AppContext) -> Option { - // let project = self.project().read(cx); + fn location(&self, cx: &AppContext) -> Option { + let project = self.project().read(cx); - // if project.is_local() { - // Some( - // project - // .visible_worktrees(cx) - // .map(|worktree| worktree.read(cx).abs_path()) - // .collect::>() - // .into(), - // ) - // } else { - // None - // } - // } + if project.is_local() { + Some( + project + .visible_worktrees(cx) + .map(|worktree| worktree.read(cx).abs_path()) + .collect::>() + .into(), + ) + } else { + None + } + } - // fn remove_panes(&mut self, member: Member, cx: &mut ViewContext) { - // match member { - // Member::Axis(PaneAxis { members, .. }) => { - // for child in members.iter() { - // self.remove_panes(child.clone(), cx) - // } - // } - // Member::Pane(pane) => { - // self.force_remove_pane(&pane, cx); - // } - // } - // } + fn remove_panes(&mut self, member: Member, cx: &mut ViewContext) { + match member { + Member::Axis(PaneAxis { members, .. }) => { + for child in members.iter() { + self.remove_panes(child.clone(), cx) + } + } + Member::Pane(pane) => { + self.force_remove_pane(&pane, cx); + } + } + } - // fn force_remove_pane(&mut self, pane: &View, cx: &mut ViewContext) { - // self.panes.retain(|p| p != pane); - // cx.focus(self.panes.last().unwrap()); - // if self.last_active_center_pane == Some(pane.downgrade()) { - // self.last_active_center_pane = None; - // } - // cx.notify(); - // } + fn force_remove_pane(&mut self, pane: &View, cx: &mut ViewContext) { + self.panes.retain(|p| p != pane); + if true { + todo!() + // cx.focus(self.panes.last().unwrap()); + } + if self.last_active_center_pane == Some(pane.downgrade()) { + self.last_active_center_pane = None; + } + cx.notify(); + } // fn schedule_serialize(&mut self, cx: &mut ViewContext) { // self._schedule_serialize = Some(cx.spawn(|this, cx| async move { @@ -3247,263 +3241,262 @@ impl Workspace { // })); // } - // fn serialize_workspace(&self, cx: &ViewContext) { - // fn serialize_pane_handle( - // pane_handle: &View, - // cx: &AppContext, - // ) -> SerializedPane { - // let (items, active) = { - // let pane = pane_handle.read(cx); - // let active_item_id = pane.active_item().map(|item| item.id()); - // ( - // pane.items() - // .filter_map(|item_handle| { - // Some(SerializedItem { - // kind: Arc::from(item_handle.serialized_item_kind()?), - // item_id: item_handle.id(), - // active: Some(item_handle.id()) == active_item_id, - // }) - // }) - // .collect::>(), - // pane.has_focus(), - // ) - // }; + fn serialize_workspace(&self, cx: &mut ViewContext) { + fn serialize_pane_handle(pane_handle: &View, cx: &AppContext) -> SerializedPane { + let (items, active) = { + let pane = pane_handle.read(cx); + let active_item_id = pane.active_item().map(|item| item.id()); + ( + pane.items() + .filter_map(|item_handle| { + Some(SerializedItem { + kind: Arc::from(item_handle.serialized_item_kind()?), + item_id: item_handle.id().as_u64() as usize, + active: Some(item_handle.id()) == active_item_id, + }) + }) + .collect::>(), + pane.has_focus(), + ) + }; - // SerializedPane::new(items, active) - // } + SerializedPane::new(items, active) + } - // fn build_serialized_pane_group( - // pane_group: &Member, - // cx: &AppContext, - // ) -> SerializedPaneGroup { - // match pane_group { - // Member::Axis(PaneAxis { - // axis, - // members, - // flexes, - // bounding_boxes: _, - // }) => SerializedPaneGroup::Group { - // axis: *axis, - // children: members - // .iter() - // .map(|member| build_serialized_pane_group(member, cx)) - // .collect::>(), - // flexes: Some(flexes.borrow().clone()), - // }, - // Member::Pane(pane_handle) => { - // SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx)) - // } - // } - // } + fn build_serialized_pane_group( + pane_group: &Member, + cx: &AppContext, + ) -> SerializedPaneGroup { + match pane_group { + Member::Axis(PaneAxis { + axis, + members, + flexes, + bounding_boxes: _, + }) => SerializedPaneGroup::Group { + axis: *axis, + children: members + .iter() + .map(|member| build_serialized_pane_group(member, cx)) + .collect::>(), + flexes: Some(flexes.lock().clone()), + }, + Member::Pane(pane_handle) => { + SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx)) + } + } + } - // fn build_serialized_docks(this: &Workspace, cx: &ViewContext) -> DockStructure { - // let left_dock = this.left_dock.read(cx); - // let left_visible = left_dock.is_open(); - // let left_active_panel = left_dock.visible_panel().and_then(|panel| { - // Some( - // cx.view_ui_name(panel.as_any().window(), panel.id())? - // .to_string(), - // ) - // }); - // let left_dock_zoom = left_dock - // .visible_panel() - // .map(|panel| panel.is_zoomed(cx)) - // .unwrap_or(false); + fn build_serialized_docks( + this: &Workspace, + cx: &mut ViewContext, + ) -> DockStructure { + let left_dock = this.left_dock.read(cx); + let left_visible = left_dock.is_open(); + let left_active_panel = left_dock + .visible_panel() + .and_then(|panel| Some(panel.persistent_name(cx).to_string())); + let left_dock_zoom = left_dock + .visible_panel() + .map(|panel| panel.is_zoomed(cx)) + .unwrap_or(false); - // let right_dock = this.right_dock.read(cx); - // let right_visible = right_dock.is_open(); - // let right_active_panel = right_dock.visible_panel().and_then(|panel| { - // Some( - // cx.view_ui_name(panel.as_any().window(), panel.id())? - // .to_string(), - // ) - // }); - // let right_dock_zoom = right_dock - // .visible_panel() - // .map(|panel| panel.is_zoomed(cx)) - // .unwrap_or(false); + let right_dock = this.right_dock.read(cx); + let right_visible = right_dock.is_open(); + let right_active_panel = right_dock + .visible_panel() + .and_then(|panel| Some(panel.persistent_name(cx).to_string())); + let right_dock_zoom = right_dock + .visible_panel() + .map(|panel| panel.is_zoomed(cx)) + .unwrap_or(false); - // let bottom_dock = this.bottom_dock.read(cx); - // let bottom_visible = bottom_dock.is_open(); - // let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| { - // Some( - // cx.view_ui_name(panel.as_any().window(), panel.id())? - // .to_string(), - // ) - // }); - // let bottom_dock_zoom = bottom_dock - // .visible_panel() - // .map(|panel| panel.is_zoomed(cx)) - // .unwrap_or(false); + let bottom_dock = this.bottom_dock.read(cx); + let bottom_visible = bottom_dock.is_open(); + let bottom_active_panel = bottom_dock + .visible_panel() + .and_then(|panel| Some(panel.persistent_name(cx).to_string())); + let bottom_dock_zoom = bottom_dock + .visible_panel() + .map(|panel| panel.is_zoomed(cx)) + .unwrap_or(false); - // DockStructure { - // left: DockData { - // visible: left_visible, - // active_panel: left_active_panel, - // zoom: left_dock_zoom, - // }, - // right: DockData { - // visible: right_visible, - // active_panel: right_active_panel, - // zoom: right_dock_zoom, - // }, - // bottom: DockData { - // visible: bottom_visible, - // active_panel: bottom_active_panel, - // zoom: bottom_dock_zoom, - // }, - // } - // } + DockStructure { + left: DockData { + visible: left_visible, + active_panel: left_active_panel, + zoom: left_dock_zoom, + }, + right: DockData { + visible: right_visible, + active_panel: right_active_panel, + zoom: right_dock_zoom, + }, + bottom: DockData { + visible: bottom_visible, + active_panel: bottom_active_panel, + zoom: bottom_dock_zoom, + }, + } + } - // if let Some(location) = self.location(cx) { - // // Load bearing special case: - // // - with_local_workspace() relies on this to not have other stuff open - // // when you open your log - // if !location.paths().is_empty() { - // let center_group = build_serialized_pane_group(&self.center.root, cx); - // let docks = build_serialized_docks(self, cx); + if let Some(location) = self.location(cx) { + // Load bearing special case: + // - with_local_workspace() relies on this to not have other stuff open + // when you open your log + if !location.paths().is_empty() { + let center_group = build_serialized_pane_group(&self.center.root, cx); + let docks = build_serialized_docks(self, cx); - // let serialized_workspace = SerializedWorkspace { - // id: self.database_id, - // location, - // center_group, - // bounds: Default::default(), - // display: Default::default(), - // docks, - // }; + let serialized_workspace = SerializedWorkspace { + id: self.database_id, + location, + center_group, + bounds: Default::default(), + display: Default::default(), + docks, + }; - // cx.background() - // .spawn(persistence::DB.save_workspace(serialized_workspace)) - // .detach(); - // } - // } - // } + cx.spawn(|_, _| persistence::DB.save_workspace(serialized_workspace)) + .detach(); + } + } + } - // pub(crate) fn load_workspace( - // workspace: WeakViewHandle, - // serialized_workspace: SerializedWorkspace, - // paths_to_open: Vec>, - // cx: &mut AppContext, - // ) -> Task>>>> { - // cx.spawn(|mut cx| async move { - // let (project, old_center_pane) = workspace.read_with(&cx, |workspace, _| { - // ( - // workspace.project().clone(), - // workspace.last_active_center_pane.clone(), - // ) - // })?; + pub(crate) fn load_workspace( + serialized_workspace: SerializedWorkspace, + paths_to_open: Vec>, + cx: &mut ViewContext, + ) -> Task>>>> { + cx.spawn(|workspace, mut cx| async move { + let (project, old_center_pane) = workspace.update(&mut cx, |workspace, _| { + ( + workspace.project().clone(), + workspace.last_active_center_pane.clone(), + ) + })?; - // let mut center_group = None; - // let mut center_items = None; - // // Traverse the splits tree and add to things - // if let Some((group, active_pane, items)) = serialized_workspace - // .center_group - // .deserialize(&project, serialized_workspace.id, &workspace, &mut cx) - // .await - // { - // center_items = Some(items); - // center_group = Some((group, active_pane)) - // } + let mut center_group = None; + let mut center_items = None; - // let mut items_by_project_path = cx.read(|cx| { - // center_items - // .unwrap_or_default() - // .into_iter() - // .filter_map(|item| { - // let item = item?; - // let project_path = item.project_path(cx)?; - // Some((project_path, item)) - // }) - // .collect::>() - // }); + // Traverse the splits tree and add to things + if let Some((group, active_pane, items)) = serialized_workspace + .center_group + .deserialize( + &project, + serialized_workspace.id, + workspace.clone(), + &mut cx, + ) + .await + { + center_items = Some(items); + center_group = Some((group, active_pane)) + } - // let opened_items = paths_to_open - // .into_iter() - // .map(|path_to_open| { - // path_to_open - // .and_then(|path_to_open| items_by_project_path.remove(&path_to_open)) - // }) - // .collect::>(); + let mut items_by_project_path = cx.update(|_, cx| { + center_items + .unwrap_or_default() + .into_iter() + .filter_map(|item| { + let item = item?; + let project_path = item.project_path(cx)?; + Some((project_path, item)) + }) + .collect::>() + })?; - // // Remove old panes from workspace panes list - // workspace.update(&mut cx, |workspace, cx| { - // if let Some((center_group, active_pane)) = center_group { - // workspace.remove_panes(workspace.center.root.clone(), cx); + let opened_items = paths_to_open + .into_iter() + .map(|path_to_open| { + path_to_open + .and_then(|path_to_open| items_by_project_path.remove(&path_to_open)) + }) + .collect::>(); - // // Swap workspace center group - // workspace.center = PaneGroup::with_root(center_group); + // Remove old panes from workspace panes list + workspace.update(&mut cx, |workspace, cx| { + if let Some((center_group, active_pane)) = center_group { + workspace.remove_panes(workspace.center.root.clone(), cx); - // // Change the focus to the workspace first so that we retrigger focus in on the pane. - // cx.focus_self(); + // Swap workspace center group + workspace.center = PaneGroup::with_root(center_group); - // if let Some(active_pane) = active_pane { - // cx.focus(&active_pane); - // } else { - // cx.focus(workspace.panes.last().unwrap()); - // } - // } else { - // let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade(cx)); - // if let Some(old_center_handle) = old_center_handle { - // cx.focus(&old_center_handle) - // } else { - // cx.focus_self() - // } - // } + // Change the focus to the workspace first so that we retrigger focus in on the pane. + // todo!() + // cx.focus_self(); + // if let Some(active_pane) = active_pane { + // cx.focus(&active_pane); + // } else { + // cx.focus(workspace.panes.last().unwrap()); + // } + } else { + // todo!() + // let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade()); + // if let Some(old_center_handle) = old_center_handle { + // cx.focus(&old_center_handle) + // } else { + // cx.focus_self() + // } + } - // let docks = serialized_workspace.docks; - // workspace.left_dock.update(cx, |dock, cx| { - // dock.set_open(docks.left.visible, cx); - // if let Some(active_panel) = docks.left.active_panel { - // if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { - // dock.activate_panel(ix, cx); - // } - // } - // dock.active_panel() - // .map(|panel| panel.set_zoomed(docks.left.zoom, cx)); - // if docks.left.visible && docks.left.zoom { - // cx.focus_self() - // } - // }); - // // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something - // workspace.right_dock.update(cx, |dock, cx| { - // dock.set_open(docks.right.visible, cx); - // if let Some(active_panel) = docks.right.active_panel { - // if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { - // dock.activate_panel(ix, cx); - // } - // } - // dock.active_panel() - // .map(|panel| panel.set_zoomed(docks.right.zoom, cx)); + let docks = serialized_workspace.docks; + workspace.left_dock.update(cx, |dock, cx| { + dock.set_open(docks.left.visible, cx); + if let Some(active_panel) = docks.left.active_panel { + if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { + dock.activate_panel(ix, cx); + } + } + dock.active_panel() + .map(|panel| panel.set_zoomed(docks.left.zoom, cx)); + if docks.left.visible && docks.left.zoom { + // todo!() + // cx.focus_self() + } + }); + // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something + workspace.right_dock.update(cx, |dock, cx| { + dock.set_open(docks.right.visible, cx); + if let Some(active_panel) = docks.right.active_panel { + if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { + dock.activate_panel(ix, cx); + } + } + dock.active_panel() + .map(|panel| panel.set_zoomed(docks.right.zoom, cx)); - // if docks.right.visible && docks.right.zoom { - // cx.focus_self() - // } - // }); - // workspace.bottom_dock.update(cx, |dock, cx| { - // dock.set_open(docks.bottom.visible, cx); - // if let Some(active_panel) = docks.bottom.active_panel { - // if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { - // dock.activate_panel(ix, cx); - // } - // } + if docks.right.visible && docks.right.zoom { + // todo!() + // cx.focus_self() + } + }); + workspace.bottom_dock.update(cx, |dock, cx| { + dock.set_open(docks.bottom.visible, cx); + if let Some(active_panel) = docks.bottom.active_panel { + if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) { + dock.activate_panel(ix, cx); + } + } - // dock.active_panel() - // .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx)); + dock.active_panel() + .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx)); - // if docks.bottom.visible && docks.bottom.zoom { - // cx.focus_self() - // } - // }); + if docks.bottom.visible && docks.bottom.zoom { + // todo!() + // cx.focus_self() + } + }); - // cx.notify(); - // })?; + cx.notify(); + })?; - // // Serialize ourself to make sure our timestamps and any pane / item changes are replicated - // workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))?; + // Serialize ourself to make sure our timestamps and any pane / item changes are replicated + workspace.update(&mut cx, |workspace, cx| workspace.serialize_workspace(cx))?; - // Ok(opened_items) - // }) - // } + Ok(opened_items) + }) + } // #[cfg(any(test, feature = "test-support"))] // pub fn test_new(project: ModelHandle, cx: &mut ViewContext) -> Self { @@ -3556,209 +3549,372 @@ impl Workspace { // ) // } // } - - // fn window_bounds_env_override(cx: &AsyncAppContext) -> Option { - // ZED_WINDOW_POSITION - // .zip(*ZED_WINDOW_SIZE) - // .map(|(position, size)| { - // WindowBounds::Fixed(RectF::new( - // cx.platform().screens()[0].bounds().origin() + position, - // size, - // )) - // }) - // } - - // async fn open_items( - // serialized_workspace: Option, - // workspace: &WeakViewHandle, - // mut project_paths_to_open: Vec<(PathBuf, Option)>, - // app_state: Arc, - // mut cx: AsyncAppContext, - // ) -> Result>>>> { - // let mut opened_items = Vec::with_capacity(project_paths_to_open.len()); - - // if let Some(serialized_workspace) = serialized_workspace { - // let workspace = workspace.clone(); - // let restored_items = cx - // .update(|cx| { - // Workspace::load_workspace( - // workspace, - // serialized_workspace, - // project_paths_to_open - // .iter() - // .map(|(_, project_path)| project_path) - // .cloned() - // .collect(), - // cx, - // ) - // }) - // .await?; - - // let restored_project_paths = cx.read(|cx| { - // restored_items - // .iter() - // .filter_map(|item| item.as_ref()?.project_path(cx)) - // .collect::>() - // }); - - // for restored_item in restored_items { - // opened_items.push(restored_item.map(Ok)); - // } - - // project_paths_to_open - // .iter_mut() - // .for_each(|(_, project_path)| { - // if let Some(project_path_to_open) = project_path { - // if restored_project_paths.contains(project_path_to_open) { - // *project_path = None; - // } - // } - // }); - // } else { - // for _ in 0..project_paths_to_open.len() { - // opened_items.push(None); - // } - // } - // assert!(opened_items.len() == project_paths_to_open.len()); - - // let tasks = - // project_paths_to_open - // .into_iter() - // .enumerate() - // .map(|(i, (abs_path, project_path))| { - // let workspace = workspace.clone(); - // cx.spawn(|mut cx| { - // let fs = app_state.fs.clone(); - // async move { - // let file_project_path = project_path?; - // if fs.is_file(&abs_path).await { - // Some(( - // i, - // workspace - // .update(&mut cx, |workspace, cx| { - // workspace.open_path(file_project_path, None, true, cx) - // }) - // .log_err()? - // .await, - // )) - // } else { - // None - // } - // } - // }) - // }); - - // for maybe_opened_path in futures::future::join_all(tasks.into_iter()) - // .await - // .into_iter() - // { - // if let Some((i, path_open_result)) = maybe_opened_path { - // opened_items[i] = Some(path_open_result); - // } - // } - - // Ok(opened_items) - // } - - // fn notify_of_new_dock(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { - // const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system"; - // const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key"; - // const MESSAGE_ID: usize = 2; - - // if workspace - // .read_with(cx, |workspace, cx| { - // workspace.has_shown_notification_once::(MESSAGE_ID, cx) - // }) - // .unwrap_or(false) - // { - // return; - // } - - // if db::kvp::KEY_VALUE_STORE - // .read_kvp(NEW_DOCK_HINT_KEY) - // .ok() - // .flatten() - // .is_some() - // { - // if !workspace - // .read_with(cx, |workspace, cx| { - // workspace.has_shown_notification_once::(MESSAGE_ID, cx) - // }) - // .unwrap_or(false) - // { - // cx.update(|cx| { - // cx.update_global::(|tracker, _| { - // let entry = tracker - // .entry(TypeId::of::()) - // .or_default(); - // if !entry.contains(&MESSAGE_ID) { - // entry.push(MESSAGE_ID); - // } - // }); - // }); - // } - - // return; - // } - - // cx.spawn(|_| async move { - // db::kvp::KEY_VALUE_STORE - // .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string()) - // .await - // .ok(); - // }) - // .detach(); - - // workspace - // .update(cx, |workspace, cx| { - // workspace.show_notification_once(2, cx, |cx| { - // cx.add_view(|_| { - // MessageNotification::new_element(|text, _| { - // Text::new( - // "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.", - // text, - // ) - // .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| { - // let code_span_background_color = settings::get::(cx) - // .theme - // .editor - // .document_highlight_read_background; - - // cx.scene().push_quad(gpui::Quad { - // bounds, - // background: Some(code_span_background_color), - // border: Default::default(), - // corner_radii: (2.0).into(), - // }) - // }) - // .into_any() - // }) - // .with_click_message("Read more about the new panel system") - // .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) - // }) - // }) - // }) - // .ok(); } -// fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { -// const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml"; +fn window_bounds_env_override(cx: &AsyncAppContext) -> Option { + let display_origin = cx + .update(|cx| Some(cx.displays().first()?.bounds().origin)) + .ok()??; + ZED_WINDOW_POSITION + .zip(*ZED_WINDOW_SIZE) + .map(|(position, size)| { + WindowBounds::Fixed(Bounds { + origin: display_origin + position, + size, + }) + }) +} + +fn open_items( + serialized_workspace: Option, + mut project_paths_to_open: Vec<(PathBuf, Option)>, + app_state: Arc, + cx: &mut ViewContext, +) -> impl 'static + Future>>>>> { + let restored_items = serialized_workspace.map(|serialized_workspace| { + Workspace::load_workspace( + serialized_workspace, + project_paths_to_open + .iter() + .map(|(_, project_path)| project_path) + .cloned() + .collect(), + cx, + ) + }); + + cx.spawn(|workspace, mut cx| async move { + let mut opened_items = Vec::with_capacity(project_paths_to_open.len()); + + if let Some(restored_items) = restored_items { + let restored_items = restored_items.await?; + + let restored_project_paths = restored_items + .iter() + .filter_map(|item| { + cx.update(|_, cx| item.as_ref()?.project_path(cx)) + .ok() + .flatten() + }) + .collect::>(); + + for restored_item in restored_items { + opened_items.push(restored_item.map(Ok)); + } + + project_paths_to_open + .iter_mut() + .for_each(|(_, project_path)| { + if let Some(project_path_to_open) = project_path { + if restored_project_paths.contains(project_path_to_open) { + *project_path = None; + } + } + }); + } else { + for _ in 0..project_paths_to_open.len() { + opened_items.push(None); + } + } + assert!(opened_items.len() == project_paths_to_open.len()); + + let tasks = + project_paths_to_open + .into_iter() + .enumerate() + .map(|(i, (abs_path, project_path))| { + let workspace = workspace.clone(); + cx.spawn(|mut cx| { + let fs = app_state.fs.clone(); + async move { + let file_project_path = project_path?; + if fs.is_file(&abs_path).await { + Some(( + i, + workspace + .update(&mut cx, |workspace, cx| { + workspace.open_path(file_project_path, None, true, cx) + }) + .log_err()? + .await, + )) + } else { + None + } + } + }) + }); + + let tasks = tasks.collect::>(); + + let tasks = futures::future::join_all(tasks.into_iter()); + for maybe_opened_path in tasks.await.into_iter() { + if let Some((i, path_open_result)) = maybe_opened_path { + opened_items[i] = Some(path_open_result); + } + } + + Ok(opened_items) + }) +} + +// fn notify_of_new_dock(workspace: &WeakView, cx: &mut AsyncAppContext) { +// const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system"; +// const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key"; +// const MESSAGE_ID: usize = 2; + +// if workspace +// .read_with(cx, |workspace, cx| { +// workspace.has_shown_notification_once::(MESSAGE_ID, cx) +// }) +// .unwrap_or(false) +// { +// return; +// } + +// if db::kvp::KEY_VALUE_STORE +// .read_kvp(NEW_DOCK_HINT_KEY) +// .ok() +// .flatten() +// .is_some() +// { +// if !workspace +// .read_with(cx, |workspace, cx| { +// workspace.has_shown_notification_once::(MESSAGE_ID, cx) +// }) +// .unwrap_or(false) +// { +// cx.update(|cx| { +// cx.update_global::(|tracker, _| { +// let entry = tracker +// .entry(TypeId::of::()) +// .or_default(); +// if !entry.contains(&MESSAGE_ID) { +// entry.push(MESSAGE_ID); +// } +// }); +// }); +// } + +// return; +// } + +// cx.spawn(|_| async move { +// db::kvp::KEY_VALUE_STORE +// .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string()) +// .await +// .ok(); +// }) +// .detach(); // workspace // .update(cx, |workspace, cx| { -// if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) { -// workspace.show_notification_once(0, cx, |cx| { -// cx.add_view(|_| { -// MessageNotification::new("Failed to load the database file.") -// .with_click_message("Click to let us know about this error") -// .on_click(|cx| cx.platform().open_url(REPORT_ISSUE_URL)) -// }) -// }); -// } -// }) -// .log_err(); -// } +// workspace.show_notification_once(2, cx, |cx| { +// cx.build_view(|_| { +// MessageNotification::new_element(|text, _| { +// Text::new( +// "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.", +// text, +// ) +// .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| { +// let code_span_background_color = settings::get::(cx) +// .theme +// .editor +// .document_highlight_read_background; +// cx.scene().push_quad(gpui::Quad { +// bounds, +// background: Some(code_span_background_color), +// border: Default::default(), +// corner_radii: (2.0).into(), +// }) +// }) +// .into_any() +// }) +// .with_click_message("Read more about the new panel system") +// .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST)) +// }) +// }) +// }) +// .ok(); + +fn notify_if_database_failed(_workspace: WindowHandle, _cx: &mut AsyncAppContext) { + const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml"; + + // todo!() + // workspace + // .update(cx, |workspace, cx| { + // if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) { + // workspace.show_notification_once(0, cx, |cx| { + // cx.build_view(|_| { + // MessageNotification::new("Failed to load the database file.") + // .with_click_message("Click to let us know about this error") + // .on_click(|cx| cx.platform().open_url(REPORT_ISSUE_URL)) + // }) + // }); + // } + // }) + // .log_err(); +} + +impl EventEmitter for Workspace { + type Event = Event; +} + +impl Render for Workspace { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div() + .relative() + .size_full() + .flex() + .flex_col() + .font("Zed Sans") + .gap_0() + .justify_start() + .items_start() + .text_color(cx.theme().colors().text) + .bg(cx.theme().colors().background) + .child(self.render_titlebar(cx)) + .child( + div() + .flex_1() + .w_full() + .flex() + .flex_row() + .overflow_hidden() + .border_t() + .border_b() + .border_color(cx.theme().colors().border) + // .children( + // Some( + // Panel::new("project-panel-outer", cx) + // .side(PanelSide::Left) + // .child(ProjectPanel::new("project-panel-inner")), + // ) + // .filter(|_| self.is_project_panel_open()), + // ) + // .children( + // Some( + // Panel::new("collab-panel-outer", cx) + // .child(CollabPanel::new("collab-panel-inner")) + // .side(PanelSide::Left), + // ) + // .filter(|_| self.is_collab_panel_open()), + // ) + // .child(NotificationToast::new( + // "maxbrunsfeld has requested to add you as a contact.".into(), + // )) + .child( + div().flex().flex_col().flex_1().h_full().child( + div().flex().flex_1().child(self.center.render( + &self.project, + &self.follower_states, + self.active_call(), + &self.active_pane, + self.zoomed.as_ref(), + &self.app_state, + cx, + )), + ), // .children( + // Some( + // Panel::new("terminal-panel", cx) + // .child(Terminal::new()) + // .allowed_sides(PanelAllowedSides::BottomOnly) + // .side(PanelSide::Bottom), + // ) + // .filter(|_| self.is_terminal_open()), + // ), + ), // .children( + // Some( + // Panel::new("chat-panel-outer", cx) + // .side(PanelSide::Right) + // .child(ChatPanel::new("chat-panel-inner").messages(vec![ + // ChatMessage::new( + // "osiewicz".to_string(), + // "is this thing on?".to_string(), + // DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") + // .unwrap() + // .naive_local(), + // ), + // ChatMessage::new( + // "maxdeviant".to_string(), + // "Reading you loud and clear!".to_string(), + // DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") + // .unwrap() + // .naive_local(), + // ), + // ])), + // ) + // .filter(|_| self.is_chat_panel_open()), + // ) + // .children( + // Some( + // Panel::new("notifications-panel-outer", cx) + // .side(PanelSide::Right) + // .child(NotificationsPanel::new("notifications-panel-inner")), + // ) + // .filter(|_| self.is_notifications_panel_open()), + // ) + // .children( + // Some( + // Panel::new("assistant-panel-outer", cx) + // .child(AssistantPanel::new("assistant-panel-inner")), + // ) + // .filter(|_| self.is_assistant_panel_open()), + // ), + ) + .child(self.status_bar.clone()) + // .when(self.debug.show_toast, |this| { + // this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast"))) + // }) + // .children( + // Some( + // div() + // .absolute() + // .top(px(50.)) + // .left(px(640.)) + // .z_index(8) + // .child(LanguageSelector::new("language-selector")), + // ) + // .filter(|_| self.is_language_selector_open()), + // ) + .z_index(8) + // Debug + .child( + div() + .flex() + .flex_col() + .z_index(9) + .absolute() + .top_20() + .left_1_4() + .w_40() + .gap_2(), // .when(self.show_debug, |this| { + // this.child(Button::::new("Toggle User Settings").on_click( + // Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)), + // )) + // .child( + // Button::::new("Toggle Toasts").on_click(Arc::new( + // |workspace, cx| workspace.debug_toggle_toast(cx), + // )), + // ) + // .child( + // Button::::new("Toggle Livestream").on_click(Arc::new( + // |workspace, cx| workspace.debug_toggle_livestream(cx), + // )), + // ) + // }) + // .child( + // Button::::new("Toggle Debug") + // .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))), + // ), + ) + } +} + +// todo!() // impl Entity for Workspace { // type Event = Event; @@ -3909,168 +4065,165 @@ impl Workspace { // } // } -// impl WorkspaceStore { -// pub fn new(client: Arc, cx: &mut ModelContext) -> Self { -// Self { -// workspaces: Default::default(), -// followers: Default::default(), -// _subscriptions: vec![ -// client.add_request_handler(cx.handle(), Self::handle_follow), -// client.add_message_handler(cx.handle(), Self::handle_unfollow), -// client.add_message_handler(cx.handle(), Self::handle_update_followers), -// ], -// client, -// } -// } +impl WorkspaceStore { + pub fn new(client: Arc, _cx: &mut ModelContext) -> Self { + Self { + workspaces: Default::default(), + followers: Default::default(), + _subscriptions: vec![], + // client.add_request_handler(cx.weak_model(), Self::handle_follow), + // client.add_message_handler(cx.weak_model(), Self::handle_unfollow), + // client.add_message_handler(cx.weak_model(), Self::handle_update_followers), + // ], + client, + } + } -// pub fn update_followers( -// &self, -// project_id: Option, -// update: proto::update_followers::Variant, -// cx: &AppContext, -// ) -> Option<()> { -// if !cx.has_global::>() { -// return None; -// } + pub fn update_followers( + &self, + project_id: Option, + update: proto::update_followers::Variant, + cx: &AppContext, + ) -> Option<()> { + if !cx.has_global::>() { + return None; + } -// let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id(); -// let follower_ids: Vec<_> = self -// .followers -// .iter() -// .filter_map(|follower| { -// if follower.project_id == project_id || project_id.is_none() { -// Some(follower.peer_id.into()) -// } else { -// None -// } -// }) -// .collect(); -// if follower_ids.is_empty() { -// return None; -// } -// self.client -// .send(proto::UpdateFollowers { -// room_id, -// project_id, -// follower_ids, -// variant: Some(update), -// }) -// .log_err() -// } + let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id(); + let follower_ids: Vec<_> = self + .followers + .iter() + .filter_map(|follower| { + if follower.project_id == project_id || project_id.is_none() { + Some(follower.peer_id.into()) + } else { + None + } + }) + .collect(); + if follower_ids.is_empty() { + return None; + } + self.client + .send(proto::UpdateFollowers { + room_id, + project_id, + follower_ids, + variant: Some(update), + }) + .log_err() + } -// async fn handle_follow( -// this: ModelHandle, -// envelope: TypedEnvelope, -// _: Arc, -// mut cx: AsyncAppContext, -// ) -> Result { -// this.update(&mut cx, |this, cx| { -// let follower = Follower { -// project_id: envelope.payload.project_id, -// peer_id: envelope.original_sender_id()?, -// }; -// let active_project = ActiveCall::global(cx) -// .read(cx) -// .location() -// .map(|project| project.id()); + pub async fn handle_follow( + this: Model, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result { + this.update(&mut cx, |this, cx| { + let follower = Follower { + project_id: envelope.payload.project_id, + peer_id: envelope.original_sender_id()?, + }; + let active_project = ActiveCall::global(cx).read(cx).location().cloned(); -// let mut response = proto::FollowResponse::default(); -// for workspace in &this.workspaces { -// let Some(workspace) = workspace.upgrade(cx) else { -// continue; -// }; + let mut response = proto::FollowResponse::default(); + for workspace in &this.workspaces { + workspace + .update(cx, |workspace, cx| { + let handler_response = workspace.handle_follow(follower.project_id, cx); + if response.views.is_empty() { + response.views = handler_response.views; + } else { + response.views.extend_from_slice(&handler_response.views); + } -// workspace.update(cx.as_mut(), |workspace, cx| { -// let handler_response = workspace.handle_follow(follower.project_id, cx); -// if response.views.is_empty() { -// response.views = handler_response.views; -// } else { -// response.views.extend_from_slice(&handler_response.views); -// } + if let Some(active_view_id) = handler_response.active_view_id.clone() { + if response.active_view_id.is_none() + || Some(workspace.project.downgrade()) == active_project + { + response.active_view_id = Some(active_view_id); + } + } + }) + .ok(); + } -// if let Some(active_view_id) = handler_response.active_view_id.clone() { -// if response.active_view_id.is_none() -// || Some(workspace.project.id()) == active_project -// { -// response.active_view_id = Some(active_view_id); -// } -// } -// }); -// } + if let Err(ix) = this.followers.binary_search(&follower) { + this.followers.insert(ix, follower); + } -// if let Err(ix) = this.followers.binary_search(&follower) { -// this.followers.insert(ix, follower); -// } + Ok(response) + })? + } -// Ok(response) -// }) -// } + async fn handle_unfollow( + model: Model, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + model.update(&mut cx, |this, _| { + let follower = Follower { + project_id: envelope.payload.project_id, + peer_id: envelope.original_sender_id()?, + }; + if let Ok(ix) = this.followers.binary_search(&follower) { + this.followers.remove(ix); + } + Ok(()) + })? + } -// async fn handle_unfollow( -// this: ModelHandle, -// envelope: TypedEnvelope, -// _: Arc, -// mut cx: AsyncAppContext, -// ) -> Result<()> { -// this.update(&mut cx, |this, _| { -// let follower = Follower { -// project_id: envelope.payload.project_id, -// peer_id: envelope.original_sender_id()?, -// }; -// if let Ok(ix) = this.followers.binary_search(&follower) { -// this.followers.remove(ix); -// } -// Ok(()) -// }) -// } + async fn handle_update_followers( + _this: Model, + _envelope: TypedEnvelope, + _: Arc, + mut _cx: AsyncWindowContext, + ) -> Result<()> { + // let leader_id = envelope.original_sender_id()?; + // let update = envelope.payload; -// async fn handle_update_followers( -// this: ModelHandle, -// envelope: TypedEnvelope, -// _: Arc, -// mut cx: AsyncAppContext, -// ) -> Result<()> { -// let leader_id = envelope.original_sender_id()?; -// let update = envelope.payload; -// this.update(&mut cx, |this, cx| { -// for workspace in &this.workspaces { -// let Some(workspace) = workspace.upgrade(cx) else { -// continue; -// }; -// workspace.update(cx.as_mut(), |workspace, cx| { -// let project_id = workspace.project.read(cx).remote_id(); -// if update.project_id != project_id && update.project_id.is_some() { -// return; -// } -// workspace.handle_update_followers(leader_id, update.clone(), cx); -// }); -// } -// Ok(()) -// }) -// } -// } + // this.update(&mut cx, |this, cx| { + // for workspace in &this.workspaces { + // let Some(workspace) = workspace.upgrade() else { + // continue; + // }; + // workspace.update(cx, |workspace, cx| { + // let project_id = workspace.project.read(cx).remote_id(); + // if update.project_id != project_id && update.project_id.is_some() { + // return; + // } + // workspace.handle_update_followers(leader_id, update.clone(), cx); + // }); + // } + // Ok(()) + // })? + todo!() + } +} // impl Entity for WorkspaceStore { // type Event = (); // } -// impl ViewId { -// pub(crate) fn from_proto(message: proto::ViewId) -> Result { -// Ok(Self { -// creator: message -// .creator -// .ok_or_else(|| anyhow!("creator is missing"))?, -// id: message.id, -// }) -// } +impl ViewId { + pub(crate) fn from_proto(message: proto::ViewId) -> Result { + Ok(Self { + creator: message + .creator + .ok_or_else(|| anyhow!("creator is missing"))?, + id: message.id, + }) + } -// pub(crate) fn to_proto(&self) -> proto::ViewId { -// proto::ViewId { -// creator: Some(self.creator), -// id: self.id, -// } -// } -// } + pub(crate) fn to_proto(&self) -> proto::ViewId { + proto::ViewId { + creator: Some(self.creator), + id: self.id, + } + } +} // pub trait WorkspaceHandle { // fn file_project_paths(&self, cx: &AppContext) -> Vec; @@ -4099,45 +4252,41 @@ impl Workspace { // } // } -// pub struct WorkspaceCreated(pub WeakViewHandle); +// pub struct WorkspaceCreated(pub WeakView); -pub async fn activate_workspace_for_project( - cx: &mut AsyncAppContext, +pub fn activate_workspace_for_project( + cx: &mut AppContext, predicate: impl Fn(&Project, &AppContext) -> bool + Send + 'static, ) -> Option> { - cx.run_on_main(move |cx| { - for window in cx.windows() { - let Some(workspace) = window.downcast::() else { - continue; - }; + for window in cx.windows() { + let Some(workspace) = window.downcast::() else { + continue; + }; - let predicate = cx - .update_window_root(&workspace, |workspace, cx| { - let project = workspace.project.read(cx); - if predicate(project, cx) { - cx.activate_window(); - true - } else { - false - } - }) - .log_err() - .unwrap_or(false); + let predicate = workspace + .update(cx, |workspace, cx| { + let project = workspace.project.read(cx); + if predicate(project, cx) { + cx.activate_window(); + true + } else { + false + } + }) + .log_err() + .unwrap_or(false); - if predicate { - return Some(workspace); - } + if predicate { + return Some(workspace); } + } - None - }) - .ok()? - .await + None } -// pub async fn last_opened_workspace_paths() -> Option { -// DB.last_workspace().await.log_err().flatten() -// } +pub async fn last_opened_workspace_paths() -> Option { + DB.last_workspace().await.log_err().flatten() +} // async fn join_channel_internal( // channel_id: u64, @@ -4321,27 +4470,6 @@ pub async fn activate_workspace_for_project( // None // } -use client2::{ - proto::{self, PeerId, ViewId}, - Client, UserStore, -}; -use collections::{HashMap, HashSet}; -use gpui2::{ - AnyHandle, AnyView, AppContext, AsyncAppContext, DisplayId, Handle, MainThread, Task, View, - ViewContext, WeakHandle, WeakView, WindowBounds, WindowHandle, WindowOptions, -}; -use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; -use language2::LanguageRegistry; -use node_runtime::NodeRuntime; -use project2::{Project, ProjectEntryId, ProjectPath, Worktree}; -use std::{ - any::TypeId, - path::{Path, PathBuf}, - sync::Arc, - time::Duration, -}; -use util::ResultExt; - #[allow(clippy::type_complexity)] pub fn open_paths( abs_paths: &[PathBuf], @@ -4356,50 +4484,48 @@ pub fn open_paths( > { let app_state = app_state.clone(); let abs_paths = abs_paths.to_vec(); - cx.spawn(|mut cx| async move { - // Open paths in existing workspace if possible - let existing = activate_workspace_for_project(&mut cx, |project, cx| { - project.contains_paths(&abs_paths, cx) - }) - .await; - + // Open paths in existing workspace if possible + let existing = activate_workspace_for_project(cx, { + let abs_paths = abs_paths.clone(); + move |project, cx| project.contains_paths(&abs_paths, cx) + }); + cx.spawn(move |mut cx| async move { if let Some(existing) = existing { - Ok(( - existing.clone(), - cx.update_window_root(&existing, |workspace, cx| { - workspace.open_paths(abs_paths, true, cx) - })? - .await, - )) - } else { + // // Ok(( + // existing.clone(), + // cx.update_window_root(&existing, |workspace, cx| { + // workspace.open_paths(abs_paths, true, cx) + // })? + // .await, + // )) todo!() - // Ok(cx - // .update(|cx| { - // Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx) - // }) - // .await) + } else { + cx.update(move |cx| { + Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx) + })? + .await } }) } -// pub fn open_new( -// app_state: &Arc, -// cx: &mut AppContext, -// init: impl FnOnce(&mut Workspace, &mut ViewContext) + 'static, -// ) -> Task<()> { -// let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx); -// cx.spawn(|mut cx| async move { -// let (workspace, opened_paths) = task.await; - -// workspace -// .update(&mut cx, |workspace, cx| { -// if opened_paths.is_empty() { -// init(workspace, cx) -// } -// }) -// .log_err(); -// }) -// } +pub fn open_new( + app_state: &Arc, + cx: &mut AppContext, + init: impl FnOnce(&mut Workspace, &mut ViewContext) + 'static + Send, +) -> Task<()> { + let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx); + cx.spawn(|mut cx| async move { + if let Some((workspace, opened_paths)) = task.await.log_err() { + workspace + .update(&mut cx, |workspace, cx| { + if opened_paths.is_empty() { + init(workspace, cx) + } + }) + .log_err(); + } + }) +} // pub fn create_and_open_local_file( // path: &'static Path, @@ -4569,12 +4695,19 @@ pub fn open_paths( // .detach_and_log_err(cx); // } -// fn parse_pixel_position_env_var(value: &str) -> Option { -// let mut parts = value.split(','); -// let width: usize = parts.next()?.parse().ok()?; -// let height: usize = parts.next()?.parse().ok()?; -// Some(vec2f(width as f32, height as f32)) -// } +fn parse_pixel_position_env_var(value: &str) -> Option> { + let mut parts = value.split(','); + let x: usize = parts.next()?.parse().ok()?; + let y: usize = parts.next()?.parse().ok()?; + Some(point((x as f64).into(), (y as f64).into())) +} + +fn parse_pixel_size_env_var(value: &str) -> Option> { + let mut parts = value.split(','); + let width: usize = parts.next()?.parse().ok()?; + let height: usize = parts.next()?.parse().ok()?; + Some(size((width as f64).into(), (height as f64).into())) +} // #[cfg(test)] // mod tests { @@ -4600,7 +4733,7 @@ pub fn open_paths( // let workspace = window.root(cx); // // Adding an item with no ambiguity renders the tab without detail. -// let item1 = window.add_view(cx, |_| { +// let item1 = window.build_view(cx, |_| { // let mut item = TestItem::new(); // item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]); // item @@ -4612,7 +4745,7 @@ pub fn open_paths( // // Adding an item that creates ambiguity increases the level of detail on // // both tabs. -// let item2 = window.add_view(cx, |_| { +// let item2 = window.build_view(cx, |_| { // let mut item = TestItem::new(); // item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); // item @@ -4626,7 +4759,7 @@ pub fn open_paths( // // Adding an item that creates ambiguity increases the level of detail only // // on the ambiguous tabs. In this case, the ambiguity can't be resolved so // // we stop at the highest detail available. -// let item3 = window.add_view(cx, |_| { +// let item3 = window.build_view(cx, |_| { // let mut item = TestItem::new(); // item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); // item @@ -4668,10 +4801,10 @@ pub fn open_paths( // project.worktrees(cx).next().unwrap().read(cx).id() // }); -// let item1 = window.add_view(cx, |cx| { +// let item1 = window.build_view(cx, |cx| { // TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]) // }); -// let item2 = window.add_view(cx, |cx| { +// let item2 = window.build_view(cx, |cx| { // TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)]) // }); @@ -4744,15 +4877,15 @@ pub fn open_paths( // let workspace = window.root(cx); // // When there are no dirty items, there's nothing to do. -// let item1 = window.add_view(cx, |_| TestItem::new()); +// let item1 = window.build_view(cx, |_| TestItem::new()); // workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx)); // let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); // assert!(task.await.unwrap()); // // When there are dirty untitled items, prompt to save each one. If the user // // cancels any prompt, then abort. -// let item2 = window.add_view(cx, |_| TestItem::new().with_dirty(true)); -// let item3 = window.add_view(cx, |cx| { +// let item2 = window.build_view(cx, |_| TestItem::new().with_dirty(true)); +// let item3 = window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) @@ -4781,24 +4914,24 @@ pub fn open_paths( // let window = cx.add_window(|cx| Workspace::test_new(project, cx)); // let workspace = window.root(cx); -// let item1 = window.add_view(cx, |cx| { +// let item1 = window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) // }); -// let item2 = window.add_view(cx, |cx| { +// let item2 = window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_conflict(true) // .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]) // }); -// let item3 = window.add_view(cx, |cx| { +// let item3 = window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_conflict(true) // .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)]) // }); -// let item4 = window.add_view(cx, |cx| { +// let item4 = window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_project_items(&[TestProjectItem::new_untitled(cx)]) @@ -4892,7 +5025,7 @@ pub fn open_paths( // // workspace items with multiple project entries. // let single_entry_items = (0..=4) // .map(|project_entry_id| { -// window.add_view(cx, |cx| { +// window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_project_items(&[TestProjectItem::new( @@ -4903,7 +5036,7 @@ pub fn open_paths( // }) // }) // .collect::>(); -// let item_2_3 = window.add_view(cx, |cx| { +// let item_2_3 = window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_singleton(false) @@ -4912,7 +5045,7 @@ pub fn open_paths( // single_entry_items[3].read(cx).project_items[0].clone(), // ]) // }); -// let item_3_4 = window.add_view(cx, |cx| { +// let item_3_4 = window.build_view(cx, |cx| { // TestItem::new() // .with_dirty(true) // .with_singleton(false) @@ -4999,7 +5132,7 @@ pub fn open_paths( // let workspace = window.root(cx); // let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); -// let item = window.add_view(cx, |cx| { +// let item = window.build_view(cx, |cx| { // TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) // }); // let item_id = item.id(); @@ -5119,7 +5252,7 @@ pub fn open_paths( // let window = cx.add_window(|cx| Workspace::test_new(project, cx)); // let workspace = window.root(cx); -// let item = window.add_view(cx, |cx| { +// let item = window.build_view(cx, |cx| { // TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) // }); // let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); @@ -5174,7 +5307,7 @@ pub fn open_paths( // let workspace = window.root(cx); // let panel = workspace.update(cx, |workspace, cx| { -// let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right)); +// let panel = cx.build_view(|_| TestPanel::new(DockPosition::Right)); // workspace.add_panel(panel.clone(), cx); // workspace @@ -5186,7 +5319,7 @@ pub fn open_paths( // let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // pane.update(cx, |pane, cx| { -// let item = cx.add_view(|_| TestItem::new()); +// let item = cx.build_view(|_| TestItem::new()); // pane.add_item(Box::new(item), true, true, None, cx); // }); @@ -5323,12 +5456,12 @@ pub fn open_paths( // let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| { // // Add panel_1 on the left, panel_2 on the right. -// let panel_1 = cx.add_view(|_| TestPanel::new(DockPosition::Left)); +// let panel_1 = cx.build_view(|_| TestPanel::new(DockPosition::Left)); // workspace.add_panel(panel_1.clone(), cx); // workspace // .left_dock() // .update(cx, |left_dock, cx| left_dock.set_open(true, cx)); -// let panel_2 = cx.add_view(|_| TestPanel::new(DockPosition::Right)); +// let panel_2 = cx.build_view(|_| TestPanel::new(DockPosition::Right)); // workspace.add_panel(panel_2.clone(), cx); // workspace // .right_dock() @@ -5476,7 +5609,7 @@ pub fn open_paths( // // If focus is transferred to another view that's not a panel or another pane, we still show // // the panel as zoomed. -// let focus_receiver = window.add_view(cx, |_| EmptyView); +// let focus_receiver = window.build_view(cx, |_| EmptyView); // focus_receiver.update(cx, |_, cx| cx.focus_self()); // workspace.read_with(cx, |workspace, _| { // assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); diff --git a/crates/workspace2/src/workspace_settings.rs b/crates/workspace2/src/workspace_settings.rs new file mode 100644 index 0000000000..4b93b705a3 --- /dev/null +++ b/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, + pub confirm_quit: Option, + pub show_call_status_icon: Option, + pub autosave: Option, +} + +#[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, + pub gutter_debounce: Option, +} + +#[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::load_via_json_merge(default_value, user_values) + } +} diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index a54e68b51a..71963ff30b 100644 --- a/crates/zed2/Cargo.toml +++ b/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 diff --git a/crates/zed2/src/assets.rs b/crates/zed2/src/assets.rs index c4010edc9f..873138c244 100644 --- a/crates/zed2/src/assets.rs +++ b/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)] diff --git a/crates/zed2/src/languages.rs b/crates/zed2/src/languages.rs index 4f7a97cb97..555f12dd0f 100644 --- a/crates/zed2/src/languages.rs +++ b/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; diff --git a/crates/zed2/src/languages/c.rs b/crates/zed2/src/languages/c.rs index c836fdc740..280d9dd921 100644 --- a/crates/zed2/src/languages/c.rs +++ b/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, ) -> Option { 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, ) -> Option { 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(|store, cx| { store.update_user_settings::(cx, |s| { s.defaults.tab_size = NonZeroU32::new(2); diff --git a/crates/zed2/src/languages/css.rs b/crates/zed2/src/languages/css.rs index fb6fcabe8e..fdbc179209 100644 --- a/crates/zed2/src/languages/css.rs +++ b/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; diff --git a/crates/zed2/src/languages/elixir.rs b/crates/zed2/src/languages/elixir.rs index 09c7305fb0..bd38377c99 100644 --- a/crates/zed2/src/languages/elixir.rs +++ b/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 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, ) -> Option { 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, ) -> Option { 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, ) -> Option { 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, ) -> Option { return Some(CodeLabel { diff --git a/crates/zed2/src/languages/go.rs b/crates/zed2/src/languages/go.rs index 21001015b9..0daf1527c3 100644 --- a/crates/zed2/src/languages/go.rs +++ b/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, ) -> Option { 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, ) -> Option { 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() diff --git a/crates/zed2/src/languages/html.rs b/crates/zed2/src/languages/html.rs index b46675dd79..b8f1c70cce 100644 --- a/crates/zed2/src/languages/html.rs +++ b/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; diff --git a/crates/zed2/src/languages/json.rs b/crates/zed2/src/languages/json.rs index cb912f1042..63f909ae2a 100644 --- a/crates/zed2/src/languages/json.rs +++ b/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, diff --git a/crates/zed2/src/languages/lua.rs b/crates/zed2/src/languages/lua.rs index c92534925c..5fffb37e81 100644 --- a/crates/zed2/src/languages/lua.rs +++ b/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::{ diff --git a/crates/zed2/src/languages/php.rs b/crates/zed2/src/languages/php.rs index d6e462e186..3096fd16e6 100644 --- a/crates/zed2/src/languages/php.rs +++ b/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, - ) -> Option { + _item: &lsp::CompletionItem, + _language: &Arc, + ) -> Option { None } diff --git a/crates/zed2/src/languages/python.rs b/crates/zed2/src/languages/python.rs index 8bbf022a17..3666237e69 100644 --- a/crates/zed2/src/languages/python.rs +++ b/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, - ) -> Option { + item: &lsp::CompletionItem, + language: &Arc, + ) -> Option { 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, - ) -> Option { + kind: lsp::SymbolKind, + language: &Arc, + ) -> Option { 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::(|store, cx| { store.update_user_settings::(cx, |s| { s.defaults.tab_size = NonZeroU32::new(2); diff --git a/crates/zed2/src/languages/ruby.rs b/crates/zed2/src/languages/ruby.rs index 8718f1c757..3890b90dbd 100644 --- a/crates/zed2/src/languages/ruby.rs +++ b/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, - ) -> Option { + item: &lsp::CompletionItem, + language: &Arc, + ) -> Option { 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, - ) -> Option { + kind: lsp::SymbolKind, + language: &Arc, + ) -> Option { 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(), diff --git a/crates/zed2/src/languages/rust.rs b/crates/zed2/src/languages/rust.rs index a0abcedd07..961e6fe7f0 100644 --- a/crates/zed2/src/languages/rust.rs +++ b/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, ) -> Option { 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, ) -> Option { 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) -> Vec".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) -> Vec".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) -> Vec".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::(|store, cx| { store.update_user_settings::(cx, |s| { s.defaults.tab_size = NonZeroU32::new(2); diff --git a/crates/zed2/src/languages/svelte.rs b/crates/zed2/src/languages/svelte.rs index 53f52a6a30..34dab81772 100644 --- a/crates/zed2/src/languages/svelte.rs +++ b/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; diff --git a/crates/zed2/src/languages/tailwind.rs b/crates/zed2/src/languages/tailwind.rs index 0aa2154f1e..6d6006dbd4 100644 --- a/crates/zed2/src/languages/tailwind.rs +++ b/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; diff --git a/crates/zed2/src/languages/typescript.rs b/crates/zed2/src/languages/typescript.rs index 8eecf25540..de0139b3b2 100644 --- a/crates/zed2/src/languages/typescript.rs +++ b/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, - ) -> Option { - use lsp2::CompletionItemKind as Kind; + item: &lsp::CompletionItem, + language: &Arc, + ) -> Option { + 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, - ) -> Option { + _item: &lsp::CompletionItem, + _language: &Arc, + ) -> Option { 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!( diff --git a/crates/zed2/src/languages/vue.rs b/crates/zed2/src/languages/vue.rs index 0c87c4bee8..16afd2e299 100644 --- a/crates/zed2/src/languages/vue.rs +++ b/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, - ) -> Option { - use lsp2::CompletionItemKind as Kind; + item: &lsp::CompletionItem, + language: &Arc, + ) -> Option { + 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, diff --git a/crates/zed2/src/languages/yaml.rs b/crates/zed2/src/languages/yaml.rs index 338a7a7ade..8b438d0949 100644 --- a/crates/zed2/src/languages/yaml.rs +++ b/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; diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 1233bee327..f8b77fe9df 100644 --- a/crates/zed2/src/main.rs +++ b/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 { } } -async fn restore_or_create_workspace(_app_state: &Arc, 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, 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, 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, session_id: Strin } fn upload_previous_panics(http: Arc, 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, 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!() + &[] +} diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index 4389f3012a..4f28536085 100644 --- a/crates/zed2/src/zed2.rs +++ b/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, - pub user_store: Model, -} - pub async fn handle_cli_connection( - (mut requests, _responses): (mpsc::Receiver, IpcSender), - _app_state: Arc, - mut _cx: AsyncAppContext, + (mut requests, responses): (mpsc::Receiver, IpcSender), + app_state: Arc, + mut cx: AsyncAppContext, ) { if let Some(request) = requests.next().await { match request { - CliRequest::Open { paths: _, wait: _ } => { - // let mut caret_positions = HashMap::new(); + 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() - // }; + 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(); + let mut errored = false; - // 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::() { - // 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(); - // } - // } + 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(); - // 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 => {} - // } - // } + 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::() { + // 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(); + // } + } - // 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); + 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 => {} + } + } - // 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(); - // } - // } + 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); - // responses - // .send(CliResponse::Exit { - // status: i32::from(errored), - // }) - // .log_err(); + 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, + display_uuid: Option, + 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, + was_deserialized: bool, + app_state: Arc, + cx: AsyncWindowContext, +) -> Task> { + 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::(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(()) + }) +}