From e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 30 Jan 2024 20:08:20 +0100 Subject: [PATCH] gpui: Add Global marker trait (#7095) This should prevent a class of bugs where one queries the wrong type of global, which results in oddities at runtime. Release Notes: - N/A --------- Co-authored-by: Marshall Co-authored-by: Marshall Bowers --- Cargo.lock | 24 +++++- Cargo.toml | 1 + crates/audio/src/assets.rs | 12 ++- crates/audio/src/audio.rs | 6 +- crates/auto_update/Cargo.toml | 1 + crates/auto_update/src/auto_update.rs | 34 ++++---- crates/auto_update/src/update_notification.rs | 4 +- crates/call/src/call.rs | 17 +++- crates/channel/Cargo.toml | 1 + crates/channel/src/channel_store.rs | 14 ++-- crates/client/Cargo.toml | 1 + crates/client/src/client.rs | 24 ++++-- crates/client/src/telemetry.rs | 8 +- crates/collections/src/collections.rs | 9 --- crates/command_palette/Cargo.toml | 3 + crates/command_palette/src/command_palette.rs | 29 ++++--- crates/copilot/src/copilot.rs | 28 ++++++- crates/db/Cargo.toml | 1 + crates/db/src/db.rs | 15 ++-- crates/editor/src/editor.rs | 5 +- crates/editor/src/editor_tests.rs | 8 +- crates/editor/src/scroll.rs | 4 +- crates/feature_flags/src/feature_flags.rs | 4 +- crates/feedback/Cargo.toml | 1 + crates/feedback/src/feedback_modal.rs | 2 +- crates/feedback/src/system_specs.rs | 4 +- crates/gpui/src/app.rs | 32 ++++---- crates/gpui/src/app/async_context.rs | 14 ++-- crates/gpui/src/app/model_context.rs | 4 +- crates/gpui/src/app/test_context.rs | 14 ++-- crates/gpui/src/elements/div.rs | 12 +-- crates/gpui/src/gpui.rs | 9 ++- crates/gpui/src/style.rs | 6 +- crates/gpui/src/window.rs | 10 +-- .../notifications/src/notification_store.rs | 12 ++- crates/project_panel/src/file_associations.rs | 4 +- crates/release_channel/Cargo.toml | 10 +++ crates/release_channel/LICENSE-GPL | 1 + .../channel.rs => release_channel/src/lib.rs} | 55 ++++++++++--- crates/search/src/project_search.rs | 10 ++- crates/semantic_index/Cargo.toml | 1 + crates/semantic_index/src/semantic_index.rs | 19 +++-- crates/settings/Cargo.toml | 1 + crates/settings/src/settings_store.rs | 18 +++-- crates/theme/src/registry.rs | 12 +-- crates/theme/src/settings.rs | 5 +- crates/theme/src/theme.rs | 2 +- crates/theme_selector/src/theme_selector.rs | 18 ----- crates/util/src/util.rs | 1 - crates/vim/Cargo.toml | 2 + crates/vim/src/vim.rs | 15 ++-- crates/workspace/Cargo.toml | 1 + crates/workspace/src/notifications.rs | 4 +- crates/workspace/src/workspace.rs | 79 +++++++++++++------ crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 19 +++-- crates/zed/src/only_instance.rs | 8 +- crates/zed/src/open_listener.rs | 16 +++- crates/zed/src/zed.rs | 11 ++- 59 files changed, 449 insertions(+), 237 deletions(-) create mode 100644 crates/release_channel/Cargo.toml create mode 120000 crates/release_channel/LICENSE-GPL rename crates/{util/src/channel.rs => release_channel/src/lib.rs} (67%) diff --git a/Cargo.lock b/Cargo.lock index 88cd36edcaf18..c3921a00b1d3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,6 +661,7 @@ dependencies = [ "log", "menu", "project", + "release_channel", "schemars", "serde", "serde_derive", @@ -1188,6 +1189,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "rand 0.8.5", + "release_channel", "rpc", "schemars", "serde", @@ -1361,6 +1363,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "rand 0.8.5", + "release_channel", "rpc", "schemars", "serde", @@ -1596,6 +1599,7 @@ dependencies = [ "anyhow", "client", "collections", + "copilot", "ctor", "editor", "env_logger", @@ -1606,6 +1610,7 @@ dependencies = [ "menu", "picker", "project", + "release_channel", "serde", "serde_json", "settings", @@ -2040,6 +2045,7 @@ dependencies = [ "lazy_static", "log", "parking_lot 0.11.2", + "release_channel", "serde", "serde_derive", "smol", @@ -2512,6 +2518,7 @@ dependencies = [ "postage", "project", "regex", + "release_channel", "serde", "serde_derive", "serde_json", @@ -4907,9 +4914,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -6108,6 +6115,14 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "release_channel" +version = "0.1.0" +dependencies = [ + "gpui", + "once_cell", +] + [[package]] name = "rend" version = "0.4.0" @@ -6849,6 +6864,7 @@ dependencies = [ "pretty_assertions", "project", "rand 0.8.5", + "release_channel", "rpc", "rusqlite", "rust-embed", @@ -6988,6 +7004,7 @@ dependencies = [ "lazy_static", "postage", "pretty_assertions", + "release_channel", "rust-embed", "schemars", "serde", @@ -9139,6 +9156,7 @@ dependencies = [ "async-trait", "collections", "command_palette", + "copilot", "diagnostics", "editor", "futures 0.3.28", @@ -9687,6 +9705,7 @@ dependencies = [ "client", "collections", "db", + "derive_more", "env_logger", "fs", "futures 0.3.28", @@ -9840,6 +9859,7 @@ dependencies = [ "rand 0.8.5", "recent_projects", "regex", + "release_channel", "rope", "rpc", "rsa 0.4.0", diff --git a/Cargo.toml b/Cargo.toml index f3c558356c177..c254233b33ea8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ members = [ "crates/project_symbols", "crates/quick_action_bar", "crates/recent_projects", + "crates/release_channel", "crates/rope", "crates/rpc", "crates/search", diff --git a/crates/audio/src/assets.rs b/crates/audio/src/assets.rs index b58e1f6aee896..387990cb720c7 100644 --- a/crates/audio/src/assets.rs +++ b/crates/audio/src/assets.rs @@ -2,7 +2,7 @@ use std::{io::Cursor, sync::Arc}; use anyhow::Result; use collections::HashMap; -use gpui::{AppContext, AssetSource}; +use gpui::{AppContext, AssetSource, Global}; use rodio::{ source::{Buffered, SamplesConverter}, Decoder, Source, @@ -15,6 +15,10 @@ pub struct SoundRegistry { assets: Box, } +struct GlobalSoundRegistry(Arc); + +impl Global for GlobalSoundRegistry {} + impl SoundRegistry { pub fn new(source: impl AssetSource) -> Arc { Arc::new(Self { @@ -24,7 +28,11 @@ impl SoundRegistry { } pub fn global(cx: &AppContext) -> Arc { - cx.global::>().clone() + cx.global::().0.clone() + } + + pub(crate) fn set_global(source: impl AssetSource, cx: &mut AppContext) { + cx.set_global(GlobalSoundRegistry(SoundRegistry::new(source))); } pub fn get(&self, name: &str) -> Result> { diff --git a/crates/audio/src/audio.rs b/crates/audio/src/audio.rs index 9264ed25d6b46..a1166742885bf 100644 --- a/crates/audio/src/audio.rs +++ b/crates/audio/src/audio.rs @@ -1,12 +1,12 @@ use assets::SoundRegistry; -use gpui::{AppContext, AssetSource}; +use gpui::{AppContext, AssetSource, Global}; use rodio::{OutputStream, OutputStreamHandle}; use util::ResultExt; mod assets; pub fn init(source: impl AssetSource, cx: &mut AppContext) { - cx.set_global(SoundRegistry::new(source)); + SoundRegistry::set_global(source, cx); cx.set_global(Audio::new()); } @@ -37,6 +37,8 @@ pub struct Audio { output_handle: Option, } +impl Global for Audio {} + impl Audio { pub fn new() -> Self { Self { diff --git a/crates/auto_update/Cargo.toml b/crates/auto_update/Cargo.toml index f3315a922e814..dbd06832ba3cb 100644 --- a/crates/auto_update/Cargo.toml +++ b/crates/auto_update/Cargo.toml @@ -15,6 +15,7 @@ client = { path = "../client" } gpui = { path = "../gpui" } menu = { path = "../menu" } project = { path = "../project" } +release_channel = { path = "../release_channel" } settings = { path = "../settings" } theme = { path = "../theme" } workspace = { path = "../workspace" } diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 9673e8bbb24b9..d04acb10f1e54 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -5,8 +5,8 @@ use client::{Client, TelemetrySettings, ZED_APP_PATH, ZED_APP_VERSION}; use db::kvp::KEY_VALUE_STORE; use db::RELEASE_CHANNEL; use gpui::{ - actions, AppContext, AsyncAppContext, Context as _, Model, ModelContext, SemanticVersion, Task, - ViewContext, VisualContext, WindowContext, + actions, AppContext, AsyncAppContext, Context as _, Global, Model, ModelContext, + SemanticVersion, Task, ViewContext, VisualContext, WindowContext, }; use isahc::AsyncBody; @@ -18,6 +18,7 @@ use smol::io::AsyncReadExt; use settings::{Settings, SettingsStore}; use smol::{fs::File, process::Command}; +use release_channel::{AppCommitSha, ReleaseChannel}; use std::{ env::consts::{ARCH, OS}, ffi::OsString, @@ -25,11 +26,7 @@ use std::{ time::Duration, }; use update_notification::UpdateNotification; -use util::http::HttpClient; -use util::{ - channel::{AppCommitSha, ReleaseChannel}, - http::ZedHttpClient, -}; +use util::http::{HttpClient, ZedHttpClient}; use workspace::Workspace; const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification"; @@ -94,6 +91,11 @@ impl Settings for AutoUpdateSetting { } } +#[derive(Default)] +struct GlobalAutoUpdate(Option>); + +impl Global for GlobalAutoUpdate {} + pub fn init(http_client: Arc, cx: &mut AppContext) { AutoUpdateSetting::register(cx); @@ -127,7 +129,7 @@ pub fn init(http_client: Arc, cx: &mut AppContext) { updater }); - cx.set_global(Some(auto_updater)); + cx.set_global(GlobalAutoUpdate(Some(auto_updater))); } } @@ -146,7 +148,7 @@ pub fn check(_: &Check, cx: &mut WindowContext) { pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<()> { let auto_updater = AutoUpdater::get(cx)?; - let release_channel = cx.try_global::()?; + let release_channel = ReleaseChannel::try_global(cx)?; if matches!( release_channel, @@ -191,7 +193,7 @@ pub fn notify_of_any_new_update(cx: &mut ViewContext) -> Option<()> { impl AutoUpdater { pub fn get(cx: &mut AppContext) -> Option> { - cx.default_global::>>().clone() + cx.default_global::().0.clone() } fn new(current_version: SemanticVersion, http_client: Arc) -> Self { @@ -253,8 +255,7 @@ impl AutoUpdater { OS, ARCH )); cx.update(|cx| { - if let Some(param) = cx - .try_global::() + if let Some(param) = ReleaseChannel::try_global(cx) .map(|release_channel| release_channel.release_query_param()) .flatten() { @@ -276,7 +277,9 @@ impl AutoUpdater { let should_download = match *RELEASE_CHANNEL { ReleaseChannel::Nightly => cx - .try_read_global::(|sha, _| release.version != sha.0) + .update(|cx| AppCommitSha::try_global(cx).map(|sha| release.version != sha.0)) + .ok() + .flatten() .unwrap_or(true), _ => release.version.parse::()? > current_version, }; @@ -311,9 +314,8 @@ impl AutoUpdater { let mut dmg_file = File::create(&dmg_path).await?; let (installation_id, release_channel, telemetry) = cx.update(|cx| { - let installation_id = cx.global::>().telemetry().installation_id(); - let release_channel = cx - .try_global::() + let installation_id = Client::global(cx).telemetry().installation_id(); + let release_channel = ReleaseChannel::try_global(cx) .map(|release_channel| release_channel.display_name()); let telemetry = TelemetrySettings::get_global(cx).metrics; diff --git a/crates/auto_update/src/update_notification.rs b/crates/auto_update/src/update_notification.rs index 1562d90057199..eece0c105a1ec 100644 --- a/crates/auto_update/src/update_notification.rs +++ b/crates/auto_update/src/update_notification.rs @@ -3,7 +3,7 @@ use gpui::{ SemanticVersion, StatefulInteractiveElement, Styled, ViewContext, }; use menu::Cancel; -use util::channel::ReleaseChannel; +use release_channel::ReleaseChannel; use workspace::ui::{h_flex, v_flex, Icon, IconName, Label, StyledExt}; pub struct UpdateNotification { @@ -14,7 +14,7 @@ impl EventEmitter for UpdateNotification {} impl Render for UpdateNotification { fn render(&mut self, cx: &mut gpui::ViewContext) -> impl IntoElement { - let app_name = cx.global::().display_name(); + let app_name = ReleaseChannel::global(cx).display_name(); v_flex() .on_action(cx.listener(UpdateNotification::dismiss)) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 6d57a42ff7e68..11fc549084282 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -9,8 +9,8 @@ use client::{proto, Client, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE}; use collections::HashSet; use futures::{channel::oneshot, future::Shared, Future, FutureExt}; use gpui::{ - AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task, - WeakModel, + AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Subscription, + Task, WeakModel, }; use postage::watch; use project::Project; @@ -21,11 +21,15 @@ use std::sync::Arc; pub use participant::ParticipantLocation; pub use room::Room; +struct GlobalActiveCall(Model); + +impl Global for GlobalActiveCall {} + pub fn init(client: Arc, user_store: Model, cx: &mut AppContext) { CallSettings::register(cx); let active_call = cx.new_model(|cx| ActiveCall::new(client, user_store, cx)); - cx.set_global(active_call); + cx.set_global(GlobalActiveCall(active_call)); } pub struct OneAtATime { @@ -154,7 +158,12 @@ impl ActiveCall { } pub fn global(cx: &AppContext) -> Model { - cx.global::>().clone() + cx.global::().0.clone() + } + + pub fn try_global(cx: &AppContext) -> Option> { + cx.try_global::() + .map(|call| call.0.clone()) } pub fn invite( diff --git a/crates/channel/Cargo.toml b/crates/channel/Cargo.toml index f2038d6cdc132..f33f7e7f349b7 100644 --- a/crates/channel/Cargo.toml +++ b/crates/channel/Cargo.toml @@ -21,6 +21,7 @@ util = { path = "../util" } rpc = { path = "../rpc" } text = { path = "../text" } language = { path = "../language" } +release_channel = { path = "../release_channel" } settings = { path = "../settings" } feature_flags = { path = "../feature_flags" } sum_tree = { path = "../sum_tree" } diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index 20a0955678901..0d0752aec8ff8 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -5,13 +5,13 @@ use anyhow::{anyhow, Result}; use channel_index::ChannelIndex; use client::{Client, Subscription, User, UserId, UserStore}; use collections::{hash_map, HashMap, HashSet}; -use db::RELEASE_CHANNEL; use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt}; use gpui::{ - AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task, - WeakModel, + AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, SharedString, + Task, WeakModel, }; use language::Capability; +use release_channel::RELEASE_CHANNEL; use rpc::{ proto::{self, ChannelRole, ChannelVisibility}, TypedEnvelope, @@ -22,7 +22,7 @@ use util::{async_maybe, maybe, ResultExt}; pub fn init(client: &Arc, user_store: Model, cx: &mut AppContext) { let channel_store = cx.new_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx)); - cx.set_global(channel_store); + cx.set_global(GlobalChannelStore(channel_store)); } pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); @@ -143,9 +143,13 @@ enum OpenedModelHandle { Loading(Shared, Arc>>>), } +struct GlobalChannelStore(Model); + +impl Global for GlobalChannelStore {} + impl ChannelStore { pub fn global(cx: &AppContext) -> Model { - cx.global::>().clone() + cx.global::().0.clone() } pub fn new( diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 08627d1641bef..fe012ddcc581f 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -18,6 +18,7 @@ collections = { path = "../collections" } db = { path = "../db" } gpui = { path = "../gpui" } util = { path = "../util" } +release_channel = { path = "../release_channel" } rpc = { path = "../rpc" } text = { path = "../text" } settings = { path = "../settings" } diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index dd9514ac56c9d..3b182e25ca6ed 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -15,13 +15,14 @@ use futures::{ TryFutureExt as _, TryStreamExt, }; use gpui::{ - actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model, SemanticVersion, Task, - WeakModel, + actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Global, Model, SemanticVersion, + Task, WeakModel, }; use lazy_static::lazy_static; use parking_lot::RwLock; use postage::watch; use rand::prelude::*; +use release_channel::ReleaseChannel; use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -41,8 +42,7 @@ use std::{ use telemetry::Telemetry; use thiserror::Error; use url::Url; -use util::http::HttpClient; -use util::{channel::ReleaseChannel, http::ZedHttpClient}; +use util::http::{HttpClient, ZedHttpClient}; use util::{ResultExt, TryFutureExt}; pub use rpc::*; @@ -149,6 +149,10 @@ pub fn init(client: &Arc, cx: &mut AppContext) { }); } +struct GlobalClient(Arc); + +impl Global for GlobalClient {} + pub struct Client { id: AtomicU64, peer: Arc, @@ -483,6 +487,13 @@ impl Client { self } + pub fn global(cx: &AppContext) -> Arc { + cx.global::().0.clone() + } + pub fn set_global(client: Arc, cx: &mut AppContext) { + cx.set_global(GlobalClient(client)) + } + pub fn user_id(&self) -> Option { self.state .read() @@ -996,7 +1007,10 @@ impl Client { credentials: &Credentials, cx: &AsyncAppContext, ) -> Task> { - let release_channel = cx.try_read_global(|channel: &ReleaseChannel, _| *channel); + let release_channel = cx + .update(|cx| ReleaseChannel::try_global(cx)) + .ok() + .flatten(); let request = Request::builder() .header( diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 2132cee38be8e..1a8627f2867c9 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -5,6 +5,7 @@ use chrono::{DateTime, Utc}; use futures::Future; use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task}; use parking_lot::Mutex; +use release_channel::ReleaseChannel; use serde::Serialize; use settings::{Settings, SettingsStore}; use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration}; @@ -15,7 +16,7 @@ use tempfile::NamedTempFile; use util::http::{HttpClient, ZedHttpClient}; #[cfg(not(debug_assertions))] use util::ResultExt; -use util::{channel::ReleaseChannel, TryFutureExt}; +use util::TryFutureExt; use self::event_coalescer::EventCoalescer; @@ -143,9 +144,8 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5); impl Telemetry { pub fn new(client: Arc, cx: &mut AppContext) -> Arc { - let release_channel = cx - .try_global::() - .map(|release_channel| release_channel.display_name()); + let release_channel = + ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name()); TelemetrySettings::register(cx); diff --git a/crates/collections/src/collections.rs b/crates/collections/src/collections.rs index 7628349353a3f..be41aaebd635c 100644 --- a/crates/collections/src/collections.rs +++ b/crates/collections/src/collections.rs @@ -11,13 +11,4 @@ pub type HashMap = std::collections::HashMap; pub type HashSet = std::collections::HashSet; pub use rustc_hash::{FxHashMap, FxHashSet}; -use std::any::TypeId; pub use std::collections::*; - -// NEW TYPES - -#[derive(Default)] -pub struct CommandPaletteFilter { - pub hidden_namespaces: HashSet<&'static str>, - pub hidden_action_types: HashSet, -} diff --git a/crates/command_palette/Cargo.toml b/crates/command_palette/Cargo.toml index b60d46ab9aef3..3591bf016b820 100644 --- a/crates/command_palette/Cargo.toml +++ b/crates/command_palette/Cargo.toml @@ -12,11 +12,14 @@ doctest = false [dependencies] client = { path = "../client" } collections = { path = "../collections" } +# HACK: We're only depending on `copilot` here for `CommandPaletteFilter`. See the attached comment on that type. +copilot = { path = "../copilot" } editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } picker = { path = "../picker" } project = { path = "../project" } +release_channel = { path = "../release_channel" } settings = { path = "../settings" } theme = { path = "../theme" } ui = { path = "../ui" } diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 34e2ca38a5c90..6851f260e70dd 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -4,19 +4,18 @@ use std::{ }; use client::telemetry::Telemetry; -use collections::{CommandPaletteFilter, HashMap}; +use collections::HashMap; +use copilot::CommandPaletteFilter; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, + actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Global, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView, }; use picker::{Picker, PickerDelegate}; +use release_channel::{parse_zed_link, ReleaseChannel}; use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing}; -use util::{ - channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL}, - ResultExt, -}; +use util::ResultExt; use workspace::{ModalView, Workspace}; use zed_actions::OpenZedUrl; @@ -100,8 +99,11 @@ impl Render for CommandPalette { } } -pub type CommandPaletteInterceptor = - Box Option>; +pub struct CommandPaletteInterceptor( + pub Box Option>, +); + +impl Global for CommandPaletteInterceptor {} pub struct CommandInterceptResult { pub action: Box, @@ -139,6 +141,8 @@ impl Clone for Command { #[derive(Default)] struct HitCounts(HashMap); +impl Global for HitCounts {} + impl CommandPaletteDelegate { fn new( command_palette: WeakView, @@ -229,11 +233,14 @@ impl PickerDelegate for CommandPaletteDelegate { let mut intercept_result = cx .try_read_global(|interceptor: &CommandPaletteInterceptor, cx| { - (interceptor)(&query, cx) + (interceptor.0)(&query, cx) }) .flatten(); - - if *RELEASE_CHANNEL == ReleaseChannel::Dev { + let release_channel = cx + .update(|cx| ReleaseChannel::try_global(cx)) + .ok() + .flatten(); + if release_channel == Some(ReleaseChannel::Dev) { if parse_zed_link(&query).is_some() { intercept_result = Some(CommandInterceptResult { action: OpenZedUrl { url: query.clone() }.boxed_clone(), diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 6b2816b6f362a..6f5c523400250 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -5,7 +5,7 @@ use async_tar::Archive; use collections::{HashMap, HashSet}; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt}; use gpui::{ - actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model, + actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model, ModelContext, Task, WeakModel, }; use language::{ @@ -32,6 +32,17 @@ use util::{ ResultExt, }; +// HACK: This type is only defined in `copilot` since it is the earliest ancestor +// of the crates that use it. +// +// This is not great. Let's find a better place for it to live. +#[derive(Default)] +pub struct CommandPaletteFilter { + pub hidden_namespaces: HashSet<&'static str>, + pub hidden_action_types: HashSet, +} + +impl Global for CommandPaletteFilter {} actions!( copilot, [ @@ -54,7 +65,7 @@ pub fn init( let node_runtime = node_runtime.clone(); move |cx| Copilot::start(new_server_id, http, node_runtime, cx) }); - cx.set_global(copilot.clone()); + Copilot::set_global(copilot.clone(), cx); cx.observe(&copilot, |handle, cx| { let copilot_action_types = [ TypeId::of::(), @@ -65,7 +76,7 @@ pub fn init( let copilot_auth_action_types = [TypeId::of::()]; let copilot_no_auth_action_types = [TypeId::of::()]; let status = handle.read(cx).status(); - let filter = cx.default_global::(); + let filter = cx.default_global::(); match status { Status::Disabled => { @@ -307,9 +318,18 @@ pub enum Event { impl EventEmitter for Copilot {} +struct GlobalCopilot(Model); + +impl Global for GlobalCopilot {} + impl Copilot { pub fn global(cx: &AppContext) -> Option> { - cx.try_global::>().map(|model| model.clone()) + cx.try_global::() + .map(|model| model.0.clone()) + } + + pub fn set_global(copilot: Model, cx: &mut AppContext) { + cx.set_global(GlobalCopilot(copilot)); } fn start( diff --git a/crates/db/Cargo.toml b/crates/db/Cargo.toml index e5ca5fcc6b8ba..492c2fb004871 100644 --- a/crates/db/Cargo.toml +++ b/crates/db/Cargo.toml @@ -15,6 +15,7 @@ test-support = [] [dependencies] collections = { path = "../collections" } gpui = { path = "../gpui" } +release_channel = { path = "../release_channel" } sqlez = { path = "../sqlez" } sqlez_macros = { path = "../sqlez_macros" } util = { path = "../util" } diff --git a/crates/db/src/db.rs b/crates/db/src/db.rs index 26c3dee4d07bd..b0ed1606d83d8 100644 --- a/crates/db/src/db.rs +++ b/crates/db/src/db.rs @@ -10,16 +10,16 @@ pub use lazy_static; pub use smol; pub use sqlez; pub use sqlez_macros; -pub use util::channel::{RELEASE_CHANNEL, RELEASE_CHANNEL_NAME}; pub use util::paths::DB_DIR; +use release_channel::ReleaseChannel; +pub use release_channel::RELEASE_CHANNEL; use sqlez::domain::Migrator; use sqlez::thread_safe_connection::ThreadSafeConnection; use sqlez_macros::sql; use std::future::Future; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; -use util::channel::ReleaseChannel; use util::{async_maybe, ResultExt}; const CONNECTION_INITIALIZE_QUERY: &'static str = sql!( @@ -223,7 +223,7 @@ mod tests { .prefix("DbTests") .tempdir() .unwrap(); - let _bad_db = open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + let _bad_db = open_db::(tempdir.path(), &release_channel::ReleaseChannel::Dev).await; } /// Test that DB exists but corrupted (causing recreate) @@ -261,11 +261,12 @@ mod tests { .unwrap(); { let corrupt_db = - open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + open_db::(tempdir.path(), &release_channel::ReleaseChannel::Dev).await; assert!(corrupt_db.persistent()); } - let good_db = open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + let good_db = + open_db::(tempdir.path(), &release_channel::ReleaseChannel::Dev).await; assert!( good_db.select_row::("SELECT * FROM test2").unwrap()() .unwrap() @@ -309,7 +310,7 @@ mod tests { { // Setup the bad database let corrupt_db = - open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + open_db::(tempdir.path(), &release_channel::ReleaseChannel::Dev).await; assert!(corrupt_db.persistent()); } @@ -320,7 +321,7 @@ mod tests { let guard = thread::spawn(move || { let good_db = smol::block_on(open_db::( tmp_path.as_path(), - &util::channel::ReleaseChannel::Dev, + &release_channel::ReleaseChannel::Dev, )); assert!( good_db.select_row::("SELECT * FROM test2").unwrap()() diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6b7eee4c10ea7..c1b2f2087d824 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -104,7 +104,6 @@ use std::{ ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive}, path::Path, sync::Arc, - sync::Weak, time::{Duration, Instant}, }; pub use sum_tree::Bias; @@ -241,7 +240,7 @@ pub fn init(cx: &mut AppContext) { .detach(); cx.on_action(move |_: &workspace::NewFile, cx| { - let app_state = cx.global::>(); + let app_state = workspace::AppState::global(cx); if let Some(app_state) = app_state.upgrade() { workspace::open_new(&app_state, cx, |workspace, cx| { Editor::new_file(workspace, &Default::default(), cx) @@ -250,7 +249,7 @@ pub fn init(cx: &mut AppContext) { } }); cx.on_action(move |_: &workspace::NewWindow, cx| { - let app_state = cx.global::>(); + let app_state = workspace::AppState::global(cx); if let Some(app_state) = app_state.upgrade() { workspace::open_new(&app_state, cx, |workspace, cx| { Editor::new_file(workspace, &Default::default(), cx) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index a039d2709807f..297ee0770bf40 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7226,7 +7226,7 @@ async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContex init_test(cx, |_| {}); let (copilot, copilot_lsp) = Copilot::fake(cx); - _ = cx.update(|cx| cx.set_global(copilot)); + _ = cx.update(|cx| Copilot::set_global(copilot, cx)); let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { completion_provider: Some(lsp::CompletionOptions { @@ -7479,7 +7479,7 @@ async fn test_copilot_completion_invalidation( init_test(cx, |_| {}); let (copilot, copilot_lsp) = Copilot::fake(cx); - _ = cx.update(|cx| cx.set_global(copilot)); + _ = cx.update(|cx| Copilot::set_global(copilot, cx)); let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { completion_provider: Some(lsp::CompletionOptions { @@ -7543,7 +7543,7 @@ async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::T init_test(cx, |_| {}); let (copilot, copilot_lsp) = Copilot::fake(cx); - _ = cx.update(|cx| cx.set_global(copilot)); + _ = cx.update(|cx| Copilot::set_global(copilot, cx)); let buffer_1 = cx.new_model(|cx| { Buffer::new( @@ -7660,7 +7660,7 @@ async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui }); let (copilot, copilot_lsp) = Copilot::fake(cx); - _ = cx.update(|cx| cx.set_global(copilot)); + _ = cx.update(|cx| Copilot::set_global(copilot, cx)); let fs = FakeFs::new(cx.executor()); fs.insert_tree( diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index 993e845e244e4..46af2da37192e 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -10,7 +10,7 @@ use crate::{ MultiBufferSnapshot, ToPoint, }; pub use autoscroll::{Autoscroll, AutoscrollStrategy}; -use gpui::{point, px, AppContext, Entity, Pixels, Task, ViewContext}; +use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext}; use language::{Bias, Point}; pub use scroll_amount::ScrollAmount; use std::{ @@ -27,6 +27,8 @@ const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1); #[derive(Default)] pub struct ScrollbarAutoHide(pub bool); +impl Global for ScrollbarAutoHide {} + #[derive(Clone, Copy, Debug, PartialEq)] pub struct ScrollAnchor { pub offset: gpui::Point, diff --git a/crates/feature_flags/src/feature_flags.rs b/crates/feature_flags/src/feature_flags.rs index 907c37ddcd964..0a3df28c69065 100644 --- a/crates/feature_flags/src/feature_flags.rs +++ b/crates/feature_flags/src/feature_flags.rs @@ -1,4 +1,4 @@ -use gpui::{AppContext, Subscription, ViewContext}; +use gpui::{AppContext, Global, Subscription, ViewContext}; #[derive(Default)] struct FeatureFlags { @@ -12,6 +12,8 @@ impl FeatureFlags { } } +impl Global for FeatureFlags {} + pub trait FeatureFlag { const NAME: &'static str; } diff --git a/crates/feedback/Cargo.toml b/crates/feedback/Cargo.toml index 4ad55d5b0a06e..cbdd903b2dce7 100644 --- a/crates/feedback/Cargo.toml +++ b/crates/feedback/Cargo.toml @@ -19,6 +19,7 @@ gpui = { path = "../gpui" } language = { path = "../language" } menu = { path = "../menu" } project = { path = "../project" } +release_channel = { path = "../release_channel" } settings = { path = "../settings" } theme = { path = "../theme" } ui = { path = "../ui" } diff --git a/crates/feedback/src/feedback_modal.rs b/crates/feedback/src/feedback_modal.rs index 536059989f7ae..31ff5989f3ae2 100644 --- a/crates/feedback/src/feedback_modal.rs +++ b/crates/feedback/src/feedback_modal.rs @@ -225,7 +225,7 @@ impl FeedbackModal { None, &["Yes, Submit!", "No"], ); - let client = cx.global::>().clone(); + let client = Client::global(cx).clone(); let specs = self.system_specs.clone(); cx.spawn(|this, mut cx| async move { let answer = answer.await.ok(); diff --git a/crates/feedback/src/system_specs.rs b/crates/feedback/src/system_specs.rs index 099e7460b4a37..1d1fc93841482 100644 --- a/crates/feedback/src/system_specs.rs +++ b/crates/feedback/src/system_specs.rs @@ -1,10 +1,10 @@ use client::ZED_APP_VERSION; use gpui::AppContext; use human_bytes::human_bytes; +use release_channel::ReleaseChannel; use serde::Serialize; use std::{env, fmt::Display}; use sysinfo::{RefreshKind, System, SystemExt}; -use util::channel::ReleaseChannel; #[derive(Clone, Debug, Serialize)] pub struct SystemSpecs { @@ -21,7 +21,7 @@ impl SystemSpecs { let app_version = ZED_APP_VERSION .or_else(|| cx.app_metadata().app_version) .map(|v| v.to_string()); - let release_channel = cx.global::().display_name(); + let release_channel = ReleaseChannel::global(cx).display_name(); let os_name = cx.app_metadata().os_name; let system = System::new_with_specifics(RefreshKind::new().with_memory()); let memory = system.total_memory(); diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 57f2254ef29b1..9f924450458bb 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -17,7 +17,7 @@ use time::UtcOffset; use crate::{ current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, - DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, KeyBinding, Keymap, + DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId, @@ -823,13 +823,13 @@ impl AppContext { } /// Check whether a global of the given type has been assigned. - pub fn has_global(&self) -> bool { + pub fn has_global(&self) -> bool { self.globals_by_type.contains_key(&TypeId::of::()) } /// Access the global of the given type. Panics if a global for that type has not been assigned. #[track_caller] - pub fn global(&self) -> &G { + pub fn global(&self) -> &G { self.globals_by_type .get(&TypeId::of::()) .map(|any_state| any_state.downcast_ref::().unwrap()) @@ -838,7 +838,7 @@ impl AppContext { } /// Access the global of the given type if a value has been assigned. - pub fn try_global(&self) -> Option<&G> { + pub fn try_global(&self) -> Option<&G> { self.globals_by_type .get(&TypeId::of::()) .map(|any_state| any_state.downcast_ref::().unwrap()) @@ -846,7 +846,7 @@ impl AppContext { /// Access the global of the given type mutably. Panics if a global for that type has not been assigned. #[track_caller] - pub fn global_mut(&mut self) -> &mut G { + pub fn global_mut(&mut self) -> &mut G { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type @@ -858,7 +858,7 @@ impl AppContext { /// Access the global of the given type mutably. A default value is assigned if a global of this type has not /// yet been assigned. - pub fn default_global(&mut self) -> &mut G { + pub fn default_global(&mut self) -> &mut G { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type @@ -869,7 +869,7 @@ impl AppContext { } /// Sets the value of the global of the given type. - pub fn set_global(&mut self, global: G) { + pub fn set_global(&mut self, global: G) { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type.insert(global_type, Box::new(global)); @@ -882,7 +882,7 @@ impl AppContext { } /// Remove the global of the given type from the app context. Does not notify global observers. - pub fn remove_global(&mut self) -> G { + pub fn remove_global(&mut self) -> G { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); *self @@ -895,7 +895,7 @@ impl AppContext { /// Updates the global of the given type with a closure. Unlike `global_mut`, this method provides /// your closure with mutable access to the `AppContext` and the global simultaneously. - pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R { + pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R { self.update(|cx| { let mut global = cx.lease_global::(); let result = f(&mut global, cx); @@ -905,7 +905,7 @@ impl AppContext { } /// Register a callback to be invoked when a global of the given type is updated. - pub fn observe_global( + pub fn observe_global( &mut self, mut f: impl FnMut(&mut Self) + 'static, ) -> Subscription { @@ -921,7 +921,7 @@ impl AppContext { } /// Move the global of the given type to the stack. - pub(crate) fn lease_global(&mut self) -> GlobalLease { + pub(crate) fn lease_global(&mut self) -> GlobalLease { GlobalLease::new( self.globals_by_type .remove(&TypeId::of::()) @@ -931,7 +931,7 @@ impl AppContext { } /// Restore the global of the given type after it is moved to the stack. - pub(crate) fn end_global_lease(&mut self, lease: GlobalLease) { + pub(crate) fn end_global_lease(&mut self, lease: GlobalLease) { let global_type = TypeId::of::(); self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.globals_by_type.insert(global_type, lease.global); @@ -1293,12 +1293,12 @@ pub(crate) enum Effect { } /// Wraps a global variable value during `update_global` while the value has been moved to the stack. -pub(crate) struct GlobalLease { +pub(crate) struct GlobalLease { global: Box, global_type: PhantomData, } -impl GlobalLease { +impl GlobalLease { fn new(global: Box) -> Self { GlobalLease { global, @@ -1307,7 +1307,7 @@ impl GlobalLease { } } -impl Deref for GlobalLease { +impl Deref for GlobalLease { type Target = G; fn deref(&self) -> &Self::Target { @@ -1315,7 +1315,7 @@ impl Deref for GlobalLease { } } -impl DerefMut for GlobalLease { +impl DerefMut for GlobalLease { fn deref_mut(&mut self) -> &mut Self::Target { self.global.downcast_mut().unwrap() } diff --git a/crates/gpui/src/app/async_context.rs b/crates/gpui/src/app/async_context.rs index f1bfe7ef4ec18..6252da6c18be4 100644 --- a/crates/gpui/src/app/async_context.rs +++ b/crates/gpui/src/app/async_context.rs @@ -1,6 +1,6 @@ use crate::{ AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, DismissEvent, - FocusableView, ForegroundExecutor, Model, ModelContext, Render, Result, Task, View, + FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle, }; use anyhow::{anyhow, Context as _}; @@ -144,7 +144,7 @@ impl AsyncAppContext { /// Determine whether global state of the specified type has been assigned. /// Returns an error if the `AppContext` has been dropped. - pub fn has_global(&self) -> Result { + pub fn has_global(&self) -> Result { let app = self .app .upgrade() @@ -157,7 +157,7 @@ impl AsyncAppContext { /// /// Panics if no global state of the specified type has been assigned. /// Returns an error if the `AppContext` has been dropped. - pub fn read_global(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result { + pub fn read_global(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result { let app = self .app .upgrade() @@ -172,7 +172,7 @@ impl AsyncAppContext { /// if no state of the specified type has been assigned. /// /// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped. - pub fn try_read_global( + pub fn try_read_global( &self, read: impl FnOnce(&G, &AppContext) -> R, ) -> Option { @@ -183,7 +183,7 @@ impl AsyncAppContext { /// A convenience method for [AppContext::update_global] /// for updating the global state of the specified type. - pub fn update_global( + pub fn update_global( &mut self, update: impl FnOnce(&mut G, &mut AppContext) -> R, ) -> Result { @@ -235,7 +235,7 @@ impl AsyncWindowContext { } /// A convenience method for [`AppContext::global`]. - pub fn read_global( + pub fn read_global( &mut self, read: impl FnOnce(&G, &WindowContext) -> R, ) -> Result { @@ -249,7 +249,7 @@ impl AsyncWindowContext { update: impl FnOnce(&mut G, &mut WindowContext) -> R, ) -> Result where - G: 'static, + G: Global, { self.window.update(self, |_, cx| cx.update_global(update)) } diff --git a/crates/gpui/src/app/model_context.rs b/crates/gpui/src/app/model_context.rs index 38caa1b260ffa..74569d5e5b2f4 100644 --- a/crates/gpui/src/app/model_context.rs +++ b/crates/gpui/src/app/model_context.rs @@ -1,6 +1,6 @@ use crate::{ AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId, - EventEmitter, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle, + EventEmitter, Global, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle, }; use anyhow::Result; use derive_more::{Deref, DerefMut}; @@ -193,7 +193,7 @@ impl<'a, T: 'static> ModelContext<'a, T> { /// Updates the given global pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R where - G: 'static, + G: Global, { let mut global = self.app.lease_global::(); let result = f(&mut global, self); diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index a33105492bfb2..bf213e2818852 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -1,8 +1,8 @@ use crate::{ Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter, - ForegroundExecutor, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform, Point, - Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View, + ForegroundExecutor, Global, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform, + Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions, }; use anyhow::{anyhow, bail}; @@ -256,20 +256,20 @@ impl TestAppContext { } /// true if the given global is defined - pub fn has_global(&self) -> bool { + pub fn has_global(&self) -> bool { let app = self.app.borrow(); app.has_global::() } /// runs the given closure with a reference to the global /// panics if `has_global` would return false. - pub fn read_global(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R { + pub fn read_global(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R { let app = self.app.borrow(); read(app.global(), &app) } /// runs the given closure with a reference to the global (if set) - pub fn try_read_global( + pub fn try_read_global( &self, read: impl FnOnce(&G, &AppContext) -> R, ) -> Option { @@ -278,13 +278,13 @@ impl TestAppContext { } /// sets the global in this context. - pub fn set_global(&mut self, global: G) { + pub fn set_global(&mut self, global: G) { let mut lock = self.app.borrow_mut(); lock.set_global(global); } /// updates the global in this context. (panics if `has_global` would return false) - pub fn update_global( + pub fn update_global( &mut self, update: impl FnOnce(&mut G, &mut AppContext) -> R, ) -> R { diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 6d529213f0167..e166b99a8e30b 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -17,11 +17,11 @@ use crate::{ point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds, - ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement, - IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, - SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, - WindowContext, + ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, Global, + IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, + ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, + View, Visibility, WindowContext, }; use collections::HashMap; @@ -2070,6 +2070,8 @@ impl ElementClickedState { #[derive(Default)] pub(crate) struct GroupBounds(HashMap; 1]>>); +impl Global for GroupBounds {} + impl GroupBounds { pub fn get(name: &SharedString, cx: &mut AppContext) -> Option> { cx.default_global::() diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 36674b52bf613..4fe5f621118fa 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -258,14 +258,14 @@ pub trait EventEmitter: 'static {} /// can be used interchangeably. pub trait BorrowAppContext { /// Set a global value on the context. - fn set_global(&mut self, global: T); + fn set_global(&mut self, global: T); } impl BorrowAppContext for C where C: BorrowMut, { - fn set_global(&mut self, global: G) { + fn set_global(&mut self, global: G) { self.borrow_mut().set_global(global) } } @@ -287,3 +287,8 @@ impl Flatten for Result { self } } + +/// A marker trait for types that can be stored in GPUI's global state. +/// +/// Implement this on types you want to store in the context as a global. +pub trait Global: 'static {} diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index a12bb6df12836..d5209bd5bd07f 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -3,8 +3,8 @@ use std::{iter, mem, ops::Range}; use crate::{ black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures, - FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size, - SizeRefinement, Styled, TextRun, + FontStyle, FontWeight, Global, Hsla, Length, Pixels, Point, PointRefinement, Rgba, + SharedString, Size, SizeRefinement, Styled, TextRun, }; use collections::HashSet; use refineable::Refineable; @@ -20,6 +20,8 @@ pub use taffy::style::{ /// GPUI. pub struct DebugBelow; +impl Global for DebugBelow {} + /// The CSS styling that can be applied to an element via the `Styled` trait #[derive(Clone, Refineable, Debug)] #[refineable(Debug)] diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 3688b27552c95..d64feb56a446b 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -2,7 +2,7 @@ use crate::{ px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, - GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchMode, + Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchMode, KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, @@ -708,7 +708,7 @@ impl<'a> WindowContext<'a> { /// access both to the global and the context. pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R where - G: 'static, + G: Global, { let mut global = self.app.lease_global::(); let result = f(&mut global, self); @@ -1441,7 +1441,7 @@ impl<'a> WindowContext<'a> { /// Register the given handler to be invoked whenever the global of the given type /// is updated. - pub fn observe_global( + pub fn observe_global( &mut self, f: impl Fn(&mut WindowContext<'_>) + 'static, ) -> Subscription { @@ -2198,7 +2198,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { /// Updates the global state of the given type. pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R where - G: 'static, + G: Global, { let mut global = self.app.lease_global::(); let result = f(&mut global, self); @@ -2207,7 +2207,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { } /// Register a callback to be invoked when the given global state changes. - pub fn observe_global( + pub fn observe_global( &mut self, mut f: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static, ) -> Subscription { diff --git a/crates/notifications/src/notification_store.rs b/crates/notifications/src/notification_store.rs index 77c1d247ca1d0..a01a1e59d87b1 100644 --- a/crates/notifications/src/notification_store.rs +++ b/crates/notifications/src/notification_store.rs @@ -3,7 +3,9 @@ use channel::{ChannelMessage, ChannelMessageId, ChannelStore}; use client::{Client, UserStore}; use collections::HashMap; use db::smol::stream::StreamExt; -use gpui::{AppContext, AsyncAppContext, Context as _, EventEmitter, Model, ModelContext, Task}; +use gpui::{ + AppContext, AsyncAppContext, Context as _, EventEmitter, Global, Model, ModelContext, Task, +}; use rpc::{proto, Notification, TypedEnvelope}; use std::{ops::Range, sync::Arc}; use sum_tree::{Bias, SumTree}; @@ -12,9 +14,13 @@ use util::ResultExt; pub fn init(client: Arc, user_store: Model, cx: &mut AppContext) { let notification_store = cx.new_model(|cx| NotificationStore::new(client, user_store, cx)); - cx.set_global(notification_store); + cx.set_global(GlobalNotificationStore(notification_store)); } +struct GlobalNotificationStore(Model); + +impl Global for GlobalNotificationStore {} + pub struct NotificationStore { client: Arc, user_store: Model, @@ -70,7 +76,7 @@ struct NotificationId(u64); impl NotificationStore { pub fn global(cx: &AppContext) -> Model { - cx.global::>().clone() + cx.global::().0.clone() } pub fn new( diff --git a/crates/project_panel/src/file_associations.rs b/crates/project_panel/src/file_associations.rs index 783fae4c5257a..5f596440ac9f8 100644 --- a/crates/project_panel/src/file_associations.rs +++ b/crates/project_panel/src/file_associations.rs @@ -2,7 +2,7 @@ use std::{path::Path, str, sync::Arc}; use collections::HashMap; -use gpui::{AppContext, AssetSource}; +use gpui::{AppContext, AssetSource, Global}; use serde_derive::Deserialize; use util::{maybe, paths::PathExt}; @@ -17,6 +17,8 @@ pub struct FileAssociations { types: HashMap, } +impl Global for FileAssociations {} + const COLLAPSED_DIRECTORY_TYPE: &'static str = "collapsed_folder"; const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_folder"; const COLLAPSED_CHEVRON_TYPE: &'static str = "collapsed_chevron"; diff --git a/crates/release_channel/Cargo.toml b/crates/release_channel/Cargo.toml new file mode 100644 index 0000000000000..243ea5ace32fe --- /dev/null +++ b/crates/release_channel/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "release_channel" +version = "0.1.0" +edition = "2021" +publish = false +license = "GPL-3.0-or-later" + +[dependencies] +gpui = { path = "../gpui" } +once_cell = "1.19.0" diff --git a/crates/release_channel/LICENSE-GPL b/crates/release_channel/LICENSE-GPL new file mode 120000 index 0000000000000..89e542f750cd3 --- /dev/null +++ b/crates/release_channel/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/crates/util/src/channel.rs b/crates/release_channel/src/lib.rs similarity index 67% rename from crates/util/src/channel.rs rename to crates/release_channel/src/lib.rs index 135e49c87f38b..06860e9af53b8 100644 --- a/crates/util/src/channel.rs +++ b/crates/release_channel/src/lib.rs @@ -1,24 +1,44 @@ -use lazy_static::lazy_static; +use gpui::{AppContext, Global}; +use once_cell::sync::Lazy; use std::env; -lazy_static! { - pub static ref RELEASE_CHANNEL_NAME: String = if cfg!(debug_assertions) { +#[doc(hidden)] +pub static RELEASE_CHANNEL_NAME: Lazy = if cfg!(debug_assertions) { + Lazy::new(|| { env::var("ZED_RELEASE_CHANNEL") .unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").to_string()) - } else { - include_str!("../../zed/RELEASE_CHANNEL").to_string() - }; - pub static ref RELEASE_CHANNEL: ReleaseChannel = match RELEASE_CHANNEL_NAME.as_str().trim() { + }) +} else { + Lazy::new(|| include_str!("../../zed/RELEASE_CHANNEL").to_string()) +}; +#[doc(hidden)] +pub static RELEASE_CHANNEL: Lazy = + Lazy::new(|| match RELEASE_CHANNEL_NAME.as_str().trim() { "dev" => ReleaseChannel::Dev, "nightly" => ReleaseChannel::Nightly, "preview" => ReleaseChannel::Preview, "stable" => ReleaseChannel::Stable, _ => panic!("invalid release channel {}", *RELEASE_CHANNEL_NAME), - }; -} + }); +#[derive(Clone)] pub struct AppCommitSha(pub String); +struct GlobalAppCommitSha(AppCommitSha); + +impl Global for GlobalAppCommitSha {} + +impl AppCommitSha { + pub fn try_global(cx: &AppContext) -> Option { + cx.try_global::() + .map(|sha| sha.0.clone()) + } + + pub fn set_global(sha: AppCommitSha, cx: &mut AppContext) { + cx.set_global(GlobalAppCommitSha(sha)) + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum ReleaseChannel { #[default] @@ -28,7 +48,24 @@ pub enum ReleaseChannel { Stable, } +struct GlobalReleaseChannel(ReleaseChannel); + +impl Global for GlobalReleaseChannel {} + impl ReleaseChannel { + pub fn init(cx: &mut AppContext) { + cx.set_global(GlobalReleaseChannel(*RELEASE_CHANNEL)) + } + + pub fn global(cx: &AppContext) -> Self { + cx.global::().0 + } + + pub fn try_global(cx: &AppContext) -> Option { + cx.try_global::() + .map(|channel| channel.0) + } + pub fn display_name(&self) -> &'static str { match self { ReleaseChannel::Dev => "Zed Dev", diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 0aedb3fd41305..89f94ad6c4315 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -13,10 +13,10 @@ use editor::{ use editor::{EditorElement, EditorStyle}; use gpui::{ actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId, - EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Hsla, InteractiveElement, - IntoElement, KeyContext, Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, - Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakModel, WeakView, - WhiteSpace, WindowContext, + EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Global, Hsla, + InteractiveElement, IntoElement, KeyContext, Model, ModelContext, ParentElement, PromptLevel, + Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, + WeakModel, WeakView, WhiteSpace, WindowContext, }; use menu::Confirm; use project::{ @@ -58,6 +58,8 @@ actions!( #[derive(Default)] struct ActiveSettings(HashMap, ProjectSearchSettings>); +impl Global for ActiveSettings {} + pub fn init(cx: &mut AppContext) { cx.set_global(ActiveSettings::default()); cx.observe_new_views(|workspace: &mut Workspace, _cx| { diff --git a/crates/semantic_index/Cargo.toml b/crates/semantic_index/Cargo.toml index d0c159d994480..274b603313b60 100644 --- a/crates/semantic_index/Cargo.toml +++ b/crates/semantic_index/Cargo.toml @@ -17,6 +17,7 @@ language = { path = "../language" } project = { path = "../project" } workspace = { path = "../workspace" } util = { path = "../util" } +release_channel = { path = "../release_channel" } rpc = { path = "../rpc" } settings = { path = "../settings" } anyhow.workspace = true diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 6725b5a93ea01..324e03381ee9d 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -15,8 +15,8 @@ use db::VectorDatabase; use embedding_queue::{EmbeddingQueue, FileToEmbed}; use futures::{future, FutureExt, StreamExt}; use gpui::{ - AppContext, AsyncAppContext, BorrowWindow, Context, Model, ModelContext, Task, ViewContext, - WeakModel, + AppContext, AsyncAppContext, BorrowWindow, Context, Global, Model, ModelContext, Task, + ViewContext, WeakModel, }; use language::{Anchor, Bias, Buffer, Language, LanguageRegistry}; use lazy_static::lazy_static; @@ -25,6 +25,7 @@ use parking_lot::Mutex; use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES}; use postage::watch; use project::{Fs, PathChange, Project, ProjectEntryId, Worktree, WorktreeId}; +use release_channel::ReleaseChannel; use settings::Settings; use smol::channel; use std::{ @@ -38,7 +39,7 @@ use std::{ time::{Duration, Instant, SystemTime}, }; use util::paths::PathMatcher; -use util::{channel::RELEASE_CHANNEL_NAME, http::HttpClient, paths::EMBEDDINGS_DIR, ResultExt}; +use util::{http::HttpClient, paths::EMBEDDINGS_DIR, ResultExt}; use workspace::Workspace; const SEMANTIC_INDEX_VERSION: usize = 11; @@ -58,7 +59,7 @@ pub fn init( SemanticIndexSettings::register(cx); let db_file_path = EMBEDDINGS_DIR - .join(Path::new(RELEASE_CHANNEL_NAME.as_str())) + .join(Path::new(ReleaseChannel::global(cx).dev_name())) .join("embeddings_db"); cx.observe_new_views( @@ -101,7 +102,7 @@ pub fn init( ) .await?; - cx.update(|cx| cx.set_global(semantic_index.clone()))?; + cx.update(|cx| cx.set_global(GlobalSemanticIndex(semantic_index.clone())))?; anyhow::Ok(()) }) @@ -130,6 +131,10 @@ pub struct SemanticIndex { projects: HashMap, ProjectState>, } +struct GlobalSemanticIndex(Model); + +impl Global for GlobalSemanticIndex {} + struct ProjectState { worktrees: HashMap, pending_file_count_rx: watch::Receiver, @@ -274,8 +279,8 @@ pub struct SearchResult { impl SemanticIndex { pub fn global(cx: &mut AppContext) -> Option> { - cx.try_global::>() - .map(|semantic_index| semantic_index.clone()) + cx.try_global::() + .map(|semantic_index| semantic_index.0.clone()) } pub fn authenticate(&mut self, cx: &mut AppContext) -> Task { diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index f977a872276c2..cd6af095e7a9f 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -17,6 +17,7 @@ collections = { path = "../collections" } gpui = { path = "../gpui" } fs = { path = "../fs" } feature_flags = { path = "../feature_flags" } +release_channel = { path = "../release_channel" } util = { path = "../util" } anyhow.workspace = true diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index e8c0297def7d2..41dfa521edfd0 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, Context, Result}; use collections::{btree_map, hash_map, BTreeMap, HashMap}; -use gpui::{AppContext, AsyncAppContext}; +use gpui::{AppContext, AsyncAppContext, Global}; use lazy_static::lazy_static; use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema}; use serde::{de::DeserializeOwned, Deserialize as _, Serialize}; @@ -13,9 +13,7 @@ use std::{ str, sync::Arc, }; -use util::{ - channel::RELEASE_CHANNEL_NAME, merge_non_null_json_value_into, RangeExt, ResultExt as _, -}; +use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _}; /// A value that can be defined as a user setting. /// @@ -139,6 +137,8 @@ pub struct SettingsStore { )>, } +impl Global for SettingsStore {} + impl Default for SettingsStore { fn default() -> Self { SettingsStore { @@ -207,7 +207,10 @@ impl SettingsStore { user_values_stack = vec![user_settings]; } - if let Some(release_settings) = &self.raw_user_settings.get(&*RELEASE_CHANNEL_NAME) { + if let Some(release_settings) = &self + .raw_user_settings + .get(&*release_channel::RELEASE_CHANNEL_NAME) + { if let Some(release_settings) = setting_value .deserialize_setting(&release_settings) .log_err() @@ -537,7 +540,10 @@ impl SettingsStore { paths_stack.push(None); } - if let Some(release_settings) = &self.raw_user_settings.get(&*RELEASE_CHANNEL_NAME) { + if let Some(release_settings) = &self + .raw_user_settings + .get(&*release_channel::RELEASE_CHANNEL_NAME) + { if let Some(release_settings) = setting_value .deserialize_setting(&release_settings) .log_err() diff --git a/crates/theme/src/registry.rs b/crates/theme/src/registry.rs index afd2983f04503..b5f15004290ae 100644 --- a/crates/theme/src/registry.rs +++ b/crates/theme/src/registry.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Context, Result}; use derive_more::{Deref, DerefMut}; use fs::Fs; use futures::StreamExt; -use gpui::{AppContext, AssetSource, HighlightStyle, SharedString}; +use gpui::{AppContext, AssetSource, Global, HighlightStyle, SharedString}; use parking_lot::RwLock; use refineable::Refineable; use util::ResultExt; @@ -32,10 +32,7 @@ pub struct ThemeMeta { #[derive(Default, Deref, DerefMut)] struct GlobalThemeRegistry(Arc); -/// Initializes the theme registry. -pub fn init(assets: Box, cx: &mut AppContext) { - cx.set_global(GlobalThemeRegistry(Arc::new(ThemeRegistry::new(assets)))); -} +impl Global for GlobalThemeRegistry {} struct ThemeRegistryState { themes: HashMap>, @@ -59,6 +56,11 @@ impl ThemeRegistry { cx.default_global::().0.clone() } + /// Sets the global [`ThemeRegistry`]. + pub(crate) fn set_global(assets: Box, cx: &mut AppContext) { + cx.set_global(GlobalThemeRegistry(Arc::new(ThemeRegistry::new(assets)))); + } + pub fn new(assets: Box) -> Self { let registry = Self { state: RwLock::new(ThemeRegistryState { diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 67c0814dfa83c..f7157fa139dbb 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -2,7 +2,8 @@ use crate::one_themes::one_dark; use crate::{SyntaxTheme, Theme, ThemeRegistry, ThemeStyleContent}; use anyhow::Result; use gpui::{ - px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels, Subscription, ViewContext, + px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Global, Pixels, Subscription, + ViewContext, }; use refineable::Refineable; use schemars::{ @@ -34,6 +35,8 @@ pub struct ThemeSettings { #[derive(Default)] pub(crate) struct AdjustedBufferFontSize(Pixels); +impl Global for AdjustedBufferFontSize {} + #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct ThemeSettingsContent { #[serde(default)] diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index ba0203d999f27..8161217c8f3b6 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -60,7 +60,7 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) { LoadThemes::JustBase => (Box::new(()) as Box, false), LoadThemes::All(assets) => (assets, true), }; - registry::init(assets, cx); + ThemeRegistry::set_global(assets, cx); if load_user_themes { ThemeRegistry::global(cx).load_bundled_themes(); diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 1e87785bc214f..21d90735704eb 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -36,24 +36,6 @@ pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext { - ThemeSelectorDelegate::set_theme(theme, cx); - log::info!("reloaded theme {}", current_theme_name); - } - Err(error) => { - log::error!("failed to load theme {}: {:?}", current_theme_name, error) - } - } -} - impl ModalView for ThemeSelector {} pub struct ThemeSelector { diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index ed03eb25ba534..b1205aa10932e 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -1,5 +1,4 @@ pub mod arc_cow; -pub mod channel; pub mod fs; pub mod github; pub mod http; diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index d76bce811afb8..6207630f8ff25 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -28,6 +28,8 @@ regex.workspace = true collections = { path = "../collections" } command_palette = { path = "../command_palette" } +# HACK: We're only depending on `copilot` here for `CommandPaletteFilter`. See the attached comment on that type. +copilot = { path = "../copilot" } editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 0cb038807bf44..d35a2b9a6faf1 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -15,11 +15,12 @@ mod utils; mod visual; use anyhow::Result; -use collections::{CommandPaletteFilter, HashMap}; +use collections::HashMap; use command_palette::CommandPaletteInterceptor; +use copilot::CommandPaletteFilter; use editor::{movement, Editor, EditorEvent, EditorMode}; use gpui::{ - actions, impl_actions, Action, AppContext, EntityId, KeyContext, Subscription, View, + actions, impl_actions, Action, AppContext, EntityId, Global, KeyContext, Subscription, View, ViewContext, WeakView, WindowContext, }; use language::{CursorShape, Point, Selection, SelectionGoal}; @@ -171,9 +172,9 @@ pub fn observe_keystrokes(cx: &mut WindowContext) { .detach() } -/// The state pertaining to Vim mode. Stored as a global. +/// The state pertaining to Vim mode. #[derive(Default)] -pub struct Vim { +struct Vim { active_editor: Option>, editor_subscription: Option, enabled: bool, @@ -182,6 +183,8 @@ pub struct Vim { default_state: EditorState, } +impl Global for Vim {} + impl Vim { fn read(cx: &mut AppContext) -> &Self { cx.global::() @@ -512,7 +515,9 @@ impl Vim { }); if self.enabled { - cx.set_global::(Box::new(command::command_interceptor)); + cx.set_global::(CommandPaletteInterceptor(Box::new( + command::command_interceptor, + ))); } else if cx.has_global::() { let _ = cx.remove_global::(); } diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index d60b724f35d59..7693ce0509a2b 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -43,6 +43,7 @@ async-recursion = "1.0.0" itertools = "0.10" bincode = "1.2.1" anyhow.workspace = true +derive_more.workspace = true futures.workspace = true lazy_static.workspace = true log.workspace = true diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 30d8ec9e82ada..33d5834ae7979 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -1,7 +1,7 @@ use crate::{Toast, Workspace}; use collections::HashMap; use gpui::{ - AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, + AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, Global, PromptLevel, Render, Task, View, ViewContext, VisualContext, WindowContext, }; use std::{any::TypeId, ops::DerefMut}; @@ -39,6 +39,8 @@ pub(crate) struct NotificationTracker { notifications_sent: HashMap>, } +impl Global for NotificationTracker {} + impl std::ops::Deref for NotificationTracker { type Target = HashMap>; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 35072c4030adb..093393944b067 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -18,6 +18,7 @@ use client::{ Client, ErrorExt, Status, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; +use derive_more::{Deref, DerefMut}; use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle}; use futures::{ channel::{mpsc, oneshot}, @@ -28,7 +29,7 @@ use gpui::{ actions, canvas, div, impl_actions, point, px, size, Action, AnyElement, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, DragMoveEvent, Element, ElementContext, Entity, EntityId, EventEmitter, FocusHandle, - FocusableView, GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, + FocusableView, Global, GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel, Render, SharedString, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions, @@ -59,6 +60,7 @@ use std::{ borrow::Cow, cmp, env, path::{Path, PathBuf}, + sync::Weak, sync::{atomic::AtomicUsize, Arc}, time::Duration, }; @@ -256,8 +258,13 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { }); } -type ProjectItemBuilders = - HashMap, AnyModel, &mut ViewContext) -> Box>; +#[derive(Clone, Default, Deref, DerefMut)] +struct ProjectItemBuilders( + HashMap, AnyModel, &mut ViewContext) -> Box>, +); + +impl Global for ProjectItemBuilders {} + pub fn register_project_item(cx: &mut AppContext) { let builders = cx.default_global::(); builders.insert(TypeId::of::(), |project, model, cx| { @@ -273,13 +280,20 @@ type FollowableItemBuilder = fn( &mut Option, &mut WindowContext, ) -> Option>>>; -type FollowableItemBuilders = HashMap< - TypeId, - ( - FollowableItemBuilder, - fn(&AnyView) -> Box, - ), ->; + +#[derive(Default, Deref, DerefMut)] +struct FollowableItemBuilders( + HashMap< + TypeId, + ( + FollowableItemBuilder, + fn(&AnyView) -> Box, + ), + >, +); + +impl Global for FollowableItemBuilders {} + pub fn register_followable_item(cx: &mut AppContext) { let builders = cx.default_global::(); builders.insert( @@ -296,16 +310,22 @@ pub fn register_followable_item(cx: &mut AppContext) { ); } -type ItemDeserializers = HashMap< - Arc, - fn( - Model, - WeakView, - WorkspaceId, - ItemId, - &mut ViewContext, - ) -> Task>>, ->; +#[derive(Default, Deref, DerefMut)] +struct ItemDeserializers( + HashMap< + Arc, + fn( + Model, + WeakView, + WorkspaceId, + ItemId, + &mut ViewContext, + ) -> Task>>, + >, +); + +impl Global for ItemDeserializers {} + pub fn register_deserializable_item(cx: &mut AppContext) { if let Some(serialized_item_kind) = I::serialized_item_kind() { let deserializers = cx.default_global::(); @@ -331,6 +351,10 @@ pub struct AppState { pub node_runtime: Arc, } +struct GlobalAppState(Weak); + +impl Global for GlobalAppState {} + pub struct WorkspaceStore { workspaces: HashSet>, followers: Vec, @@ -345,6 +369,17 @@ struct Follower { } impl AppState { + pub fn global(cx: &AppContext) -> Weak { + cx.global::().0.clone() + } + pub fn try_global(cx: &AppContext) -> Option> { + cx.try_global::() + .map(|state| state.0.clone()) + } + pub fn set_global(state: Weak, cx: &mut AppContext) { + cx.set_global(GlobalAppState(state)); + } + #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut AppContext) -> Arc { use node_runtime::FakeNodeRuntime; @@ -616,7 +651,7 @@ impl Workspace { let modal_layer = cx.new_view(|_| ModalLayer::new()); let mut active_call = None; - if let Some(call) = cx.try_global::>() { + if let Some(call) = ActiveCall::try_global(cx) { let call = call.clone(); let mut subscriptions = Vec::new(); subscriptions.push(cx.subscribe(&call, Self::on_active_call_event)); @@ -3657,7 +3692,7 @@ impl WorkspaceStore { update: proto::update_followers::Variant, cx: &AppContext, ) -> Option<()> { - let active_call = cx.try_global::>()?; + let active_call = ActiveCall::try_global(cx)?; let room_id = active_call.read(cx).room()?.read(cx).id(); let follower_ids: Vec<_> = self .followers diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d0c7e8d7d7675..7497b26f0d28f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -59,6 +59,7 @@ project_panel = { path = "../project_panel" } project_symbols = { path = "../project_symbols" } quick_action_bar = { path = "../quick_action_bar" } recent_projects = { path = "../recent_projects" } +release_channel = { path = "../release_channel" } rope = { path = "../rope"} rpc = { path = "../rpc" } settings = { path = "../settings" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index d045637f63877..863bdd37120f7 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -19,6 +19,7 @@ use log::LevelFilter; use assets::Assets; use node_runtime::RealNodeRuntime; use parking_lot::Mutex; +use release_channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL}; use serde::{Deserialize, Serialize}; use settings::{ default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore, @@ -34,14 +35,13 @@ use std::{ path::{Path, PathBuf}, sync::{ atomic::{AtomicU32, Ordering}, - Arc, Weak, + Arc, }, thread, }; use theme::{ActiveTheme, ThemeRegistry, ThemeSettings}; use util::{ async_maybe, - channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL}, http::{self, HttpClient, ZedHttpClient}, paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR}, ResultExt, @@ -102,8 +102,7 @@ fn main() { let open_listener = listener.clone(); app.on_open_urls(move |urls, _| open_listener.open_urls(&urls)); app.on_reopen(move |cx| { - if let Some(app_state) = cx - .try_global::>() + if let Some(app_state) = AppState::try_global(cx) .map(|app_state| app_state.upgrade()) .flatten() { @@ -115,12 +114,12 @@ fn main() { }); app.run(move |cx| { - cx.set_global(*RELEASE_CHANNEL); + ReleaseChannel::init(cx); if let Some(build_sha) = option_env!("ZED_COMMIT_SHA") { - cx.set_global(AppCommitSha(build_sha.into())) + AppCommitSha::set_global(AppCommitSha(build_sha.into()), cx); } - cx.set_global(listener.clone()); + OpenListener::set_global(listener.clone(), cx); load_embedded_fonts(cx); @@ -148,7 +147,7 @@ fn main() { let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); - cx.set_global(client.clone()); + Client::set_global(client.clone(), cx); zed::init(cx); theme::init(theme::LoadThemes::All(Box::new(Assets)), cx); @@ -242,7 +241,7 @@ fn main() { workspace_store, node_runtime, }); - cx.set_global(Arc::downgrade(&app_state)); + AppState::set_global(Arc::downgrade(&app_state), cx); audio::init(Assets, cx); auto_update::init(http.clone(), cx); @@ -565,7 +564,7 @@ fn init_panic_hook(app: &App, installation_id: Option, session_id: Strin .or_else(|| info.payload().downcast_ref::().map(|s| s.clone())) .unwrap_or_else(|| "Box".to_string()); - if *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev { + if *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev { let location = info.location().unwrap(); let backtrace = Backtrace::new(); eprintln!( diff --git a/crates/zed/src/only_instance.rs b/crates/zed/src/only_instance.rs index e950392d99554..27d8aa02b578d 100644 --- a/crates/zed/src/only_instance.rs +++ b/crates/zed/src/only_instance.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use util::channel::ReleaseChannel; +use release_channel::ReleaseChannel; const LOCALHOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); const CONNECT_TIMEOUT: Duration = Duration::from_millis(10); @@ -13,7 +13,7 @@ const RECEIVE_TIMEOUT: Duration = Duration::from_millis(35); const SEND_TIMEOUT: Duration = Duration::from_millis(20); fn address() -> SocketAddr { - let port = match *util::channel::RELEASE_CHANNEL { + let port = match *release_channel::RELEASE_CHANNEL { ReleaseChannel::Dev => 43737, ReleaseChannel::Preview => 43738, ReleaseChannel::Stable => 43739, @@ -24,7 +24,7 @@ fn address() -> SocketAddr { } fn instance_handshake() -> &'static str { - match *util::channel::RELEASE_CHANNEL { + match *release_channel::RELEASE_CHANNEL { ReleaseChannel::Dev => "Zed Editor Dev Instance Running", ReleaseChannel::Nightly => "Zed Editor Nightly Instance Running", ReleaseChannel::Preview => "Zed Editor Preview Instance Running", @@ -39,7 +39,7 @@ pub enum IsOnlyInstance { } pub fn ensure_only_instance() -> IsOnlyInstance { - if *db::ZED_STATELESS || *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev { + if *db::ZED_STATELESS || *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev { return IsOnlyInstance::Yes; } diff --git a/crates/zed/src/open_listener.rs b/crates/zed/src/open_listener.rs index f3a10208d0d84..8ef5d61c6b090 100644 --- a/crates/zed/src/open_listener.rs +++ b/crates/zed/src/open_listener.rs @@ -6,8 +6,9 @@ use editor::Editor; use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures::channel::{mpsc, oneshot}; use futures::{FutureExt, SinkExt, StreamExt}; -use gpui::AsyncAppContext; +use gpui::{AppContext, AsyncAppContext, Global}; use language::{Bias, Point}; +use release_channel::parse_zed_link; use std::collections::HashMap; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; @@ -17,7 +18,6 @@ use std::sync::Arc; use std::thread; use std::time::Duration; use std::{path::PathBuf, sync::atomic::AtomicBool}; -use util::channel::parse_zed_link; use util::paths::PathLikeWithPosition; use util::ResultExt; use workspace::AppState; @@ -42,7 +42,19 @@ pub struct OpenListener { pub triggered: AtomicBool, } +struct GlobalOpenListener(Arc); + +impl Global for GlobalOpenListener {} + impl OpenListener { + pub fn global(cx: &AppContext) -> Arc { + cx.global::().0.clone() + } + + pub fn set_global(listener: Arc, cx: &mut AppContext) { + cx.set_global(GlobalOpenListener(listener)) + } + pub fn new() -> (Self, UnboundedReceiver) { let (tx, rx) = mpsc::unbounded(); ( diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 86fe95765f60f..d48b943822907 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -20,6 +20,7 @@ use assets::Assets; use futures::{channel::mpsc, select_biased, StreamExt}; use project_panel::ProjectPanel; use quick_action_bar::QuickActionBar; +use release_channel::{AppCommitSha, ReleaseChannel}; use rope::Rope; use search::project_search::ProjectSearchBar; use settings::{initial_local_settings_content, KeymapFile, Settings, SettingsStore}; @@ -27,7 +28,6 @@ use std::{borrow::Cow, ops::Deref, path::Path, sync::Arc}; use terminal_view::terminal_panel::{self, TerminalPanel}; use util::{ asset_str, - channel::{AppCommitSha, ReleaseChannel}, paths::{self, LOCAL_SETTINGS_RELATIVE_PATH}, ResultExt, }; @@ -202,8 +202,7 @@ pub fn initialize_workspace(app_state: Arc, cx: &mut AppContext) { cx.toggle_full_screen(); }) .register_action(|_, action: &OpenZedUrl, cx| { - cx.global::>() - .open_urls(&[action.url.clone()]) + OpenListener::global(cx).open_urls(&[action.url.clone()]) }) .register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url)) .register_action(move |_, _: &IncreaseBufferFontSize, cx| { @@ -370,12 +369,12 @@ fn initialize_pane(workspace: &mut Workspace, pane: &View, cx: &mut ViewCo } fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext) { - let app_name = cx.global::().display_name(); + let app_name = ReleaseChannel::global(cx).display_name(); let version = env!("CARGO_PKG_VERSION"); let message = format!("{app_name} {version}"); - let detail = cx.try_global::().map(|sha| sha.0.as_ref()); + let detail = AppCommitSha::try_global(cx).map(|sha| sha.0.clone()); - let prompt = cx.prompt(PromptLevel::Info, &message, detail, &["OK"]); + let prompt = cx.prompt(PromptLevel::Info, &message, detail.as_deref(), &["OK"]); cx.foreground_executor() .spawn(async { prompt.await.ok();