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 <marshall@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
Piotr Osiewicz 2024-01-30 20:08:20 +01:00 committed by GitHub
parent 7bfa584eb6
commit e6ebe7974d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 449 additions and 237 deletions

24
Cargo.lock generated
View File

@ -661,6 +661,7 @@ dependencies = [
"log", "log",
"menu", "menu",
"project", "project",
"release_channel",
"schemars", "schemars",
"serde", "serde",
"serde_derive", "serde_derive",
@ -1188,6 +1189,7 @@ dependencies = [
"parking_lot 0.11.2", "parking_lot 0.11.2",
"postage", "postage",
"rand 0.8.5", "rand 0.8.5",
"release_channel",
"rpc", "rpc",
"schemars", "schemars",
"serde", "serde",
@ -1361,6 +1363,7 @@ dependencies = [
"parking_lot 0.11.2", "parking_lot 0.11.2",
"postage", "postage",
"rand 0.8.5", "rand 0.8.5",
"release_channel",
"rpc", "rpc",
"schemars", "schemars",
"serde", "serde",
@ -1596,6 +1599,7 @@ dependencies = [
"anyhow", "anyhow",
"client", "client",
"collections", "collections",
"copilot",
"ctor", "ctor",
"editor", "editor",
"env_logger", "env_logger",
@ -1606,6 +1610,7 @@ dependencies = [
"menu", "menu",
"picker", "picker",
"project", "project",
"release_channel",
"serde", "serde",
"serde_json", "serde_json",
"settings", "settings",
@ -2040,6 +2045,7 @@ dependencies = [
"lazy_static", "lazy_static",
"log", "log",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"release_channel",
"serde", "serde",
"serde_derive", "serde_derive",
"smol", "smol",
@ -2512,6 +2518,7 @@ dependencies = [
"postage", "postage",
"project", "project",
"regex", "regex",
"release_channel",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
@ -4907,9 +4914,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.18.0" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
@ -6108,6 +6115,14 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "release_channel"
version = "0.1.0"
dependencies = [
"gpui",
"once_cell",
]
[[package]] [[package]]
name = "rend" name = "rend"
version = "0.4.0" version = "0.4.0"
@ -6849,6 +6864,7 @@ dependencies = [
"pretty_assertions", "pretty_assertions",
"project", "project",
"rand 0.8.5", "rand 0.8.5",
"release_channel",
"rpc", "rpc",
"rusqlite", "rusqlite",
"rust-embed", "rust-embed",
@ -6988,6 +7004,7 @@ dependencies = [
"lazy_static", "lazy_static",
"postage", "postage",
"pretty_assertions", "pretty_assertions",
"release_channel",
"rust-embed", "rust-embed",
"schemars", "schemars",
"serde", "serde",
@ -9139,6 +9156,7 @@ dependencies = [
"async-trait", "async-trait",
"collections", "collections",
"command_palette", "command_palette",
"copilot",
"diagnostics", "diagnostics",
"editor", "editor",
"futures 0.3.28", "futures 0.3.28",
@ -9687,6 +9705,7 @@ dependencies = [
"client", "client",
"collections", "collections",
"db", "db",
"derive_more",
"env_logger", "env_logger",
"fs", "fs",
"futures 0.3.28", "futures 0.3.28",
@ -9840,6 +9859,7 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"recent_projects", "recent_projects",
"regex", "regex",
"release_channel",
"rope", "rope",
"rpc", "rpc",
"rsa 0.4.0", "rsa 0.4.0",

View File

@ -59,6 +59,7 @@ members = [
"crates/project_symbols", "crates/project_symbols",
"crates/quick_action_bar", "crates/quick_action_bar",
"crates/recent_projects", "crates/recent_projects",
"crates/release_channel",
"crates/rope", "crates/rope",
"crates/rpc", "crates/rpc",
"crates/search", "crates/search",

View File

@ -2,7 +2,7 @@ use std::{io::Cursor, sync::Arc};
use anyhow::Result; use anyhow::Result;
use collections::HashMap; use collections::HashMap;
use gpui::{AppContext, AssetSource}; use gpui::{AppContext, AssetSource, Global};
use rodio::{ use rodio::{
source::{Buffered, SamplesConverter}, source::{Buffered, SamplesConverter},
Decoder, Source, Decoder, Source,
@ -15,6 +15,10 @@ pub struct SoundRegistry {
assets: Box<dyn AssetSource>, assets: Box<dyn AssetSource>,
} }
struct GlobalSoundRegistry(Arc<SoundRegistry>);
impl Global for GlobalSoundRegistry {}
impl SoundRegistry { impl SoundRegistry {
pub fn new(source: impl AssetSource) -> Arc<Self> { pub fn new(source: impl AssetSource) -> Arc<Self> {
Arc::new(Self { Arc::new(Self {
@ -24,7 +28,11 @@ impl SoundRegistry {
} }
pub fn global(cx: &AppContext) -> Arc<Self> { pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<Arc<Self>>().clone() cx.global::<GlobalSoundRegistry>().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<impl Source<Item = f32>> { pub fn get(&self, name: &str) -> Result<impl Source<Item = f32>> {

View File

@ -1,12 +1,12 @@
use assets::SoundRegistry; use assets::SoundRegistry;
use gpui::{AppContext, AssetSource}; use gpui::{AppContext, AssetSource, Global};
use rodio::{OutputStream, OutputStreamHandle}; use rodio::{OutputStream, OutputStreamHandle};
use util::ResultExt; use util::ResultExt;
mod assets; mod assets;
pub fn init(source: impl AssetSource, cx: &mut AppContext) { 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()); cx.set_global(Audio::new());
} }
@ -37,6 +37,8 @@ pub struct Audio {
output_handle: Option<OutputStreamHandle>, output_handle: Option<OutputStreamHandle>,
} }
impl Global for Audio {}
impl Audio { impl Audio {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {

View File

@ -15,6 +15,7 @@ client = { path = "../client" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
menu = { path = "../menu" } menu = { path = "../menu" }
project = { path = "../project" } project = { path = "../project" }
release_channel = { path = "../release_channel" }
settings = { path = "../settings" } settings = { path = "../settings" }
theme = { path = "../theme" } theme = { path = "../theme" }
workspace = { path = "../workspace" } workspace = { path = "../workspace" }

View File

@ -5,8 +5,8 @@ use client::{Client, TelemetrySettings, ZED_APP_PATH, ZED_APP_VERSION};
use db::kvp::KEY_VALUE_STORE; use db::kvp::KEY_VALUE_STORE;
use db::RELEASE_CHANNEL; use db::RELEASE_CHANNEL;
use gpui::{ use gpui::{
actions, AppContext, AsyncAppContext, Context as _, Model, ModelContext, SemanticVersion, Task, actions, AppContext, AsyncAppContext, Context as _, Global, Model, ModelContext,
ViewContext, VisualContext, WindowContext, SemanticVersion, Task, ViewContext, VisualContext, WindowContext,
}; };
use isahc::AsyncBody; use isahc::AsyncBody;
@ -18,6 +18,7 @@ use smol::io::AsyncReadExt;
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use smol::{fs::File, process::Command}; use smol::{fs::File, process::Command};
use release_channel::{AppCommitSha, ReleaseChannel};
use std::{ use std::{
env::consts::{ARCH, OS}, env::consts::{ARCH, OS},
ffi::OsString, ffi::OsString,
@ -25,11 +26,7 @@ use std::{
time::Duration, time::Duration,
}; };
use update_notification::UpdateNotification; use update_notification::UpdateNotification;
use util::http::HttpClient; use util::http::{HttpClient, ZedHttpClient};
use util::{
channel::{AppCommitSha, ReleaseChannel},
http::ZedHttpClient,
};
use workspace::Workspace; use workspace::Workspace;
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification"; 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<Model<AutoUpdater>>);
impl Global for GlobalAutoUpdate {}
pub fn init(http_client: Arc<ZedHttpClient>, cx: &mut AppContext) { pub fn init(http_client: Arc<ZedHttpClient>, cx: &mut AppContext) {
AutoUpdateSetting::register(cx); AutoUpdateSetting::register(cx);
@ -127,7 +129,7 @@ pub fn init(http_client: Arc<ZedHttpClient>, cx: &mut AppContext) {
updater 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<()> { pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<()> {
let auto_updater = AutoUpdater::get(cx)?; let auto_updater = AutoUpdater::get(cx)?;
let release_channel = cx.try_global::<ReleaseChannel>()?; let release_channel = ReleaseChannel::try_global(cx)?;
if matches!( if matches!(
release_channel, release_channel,
@ -191,7 +193,7 @@ pub fn notify_of_any_new_update(cx: &mut ViewContext<Workspace>) -> Option<()> {
impl AutoUpdater { impl AutoUpdater {
pub fn get(cx: &mut AppContext) -> Option<Model<Self>> { pub fn get(cx: &mut AppContext) -> Option<Model<Self>> {
cx.default_global::<Option<Model<Self>>>().clone() cx.default_global::<GlobalAutoUpdate>().0.clone()
} }
fn new(current_version: SemanticVersion, http_client: Arc<ZedHttpClient>) -> Self { fn new(current_version: SemanticVersion, http_client: Arc<ZedHttpClient>) -> Self {
@ -253,8 +255,7 @@ impl AutoUpdater {
OS, ARCH OS, ARCH
)); ));
cx.update(|cx| { cx.update(|cx| {
if let Some(param) = cx if let Some(param) = ReleaseChannel::try_global(cx)
.try_global::<ReleaseChannel>()
.map(|release_channel| release_channel.release_query_param()) .map(|release_channel| release_channel.release_query_param())
.flatten() .flatten()
{ {
@ -276,7 +277,9 @@ impl AutoUpdater {
let should_download = match *RELEASE_CHANNEL { let should_download = match *RELEASE_CHANNEL {
ReleaseChannel::Nightly => cx ReleaseChannel::Nightly => cx
.try_read_global::<AppCommitSha, _>(|sha, _| release.version != sha.0) .update(|cx| AppCommitSha::try_global(cx).map(|sha| release.version != sha.0))
.ok()
.flatten()
.unwrap_or(true), .unwrap_or(true),
_ => release.version.parse::<SemanticVersion>()? > current_version, _ => release.version.parse::<SemanticVersion>()? > current_version,
}; };
@ -311,9 +314,8 @@ impl AutoUpdater {
let mut dmg_file = File::create(&dmg_path).await?; let mut dmg_file = File::create(&dmg_path).await?;
let (installation_id, release_channel, telemetry) = cx.update(|cx| { let (installation_id, release_channel, telemetry) = cx.update(|cx| {
let installation_id = cx.global::<Arc<Client>>().telemetry().installation_id(); let installation_id = Client::global(cx).telemetry().installation_id();
let release_channel = cx let release_channel = ReleaseChannel::try_global(cx)
.try_global::<ReleaseChannel>()
.map(|release_channel| release_channel.display_name()); .map(|release_channel| release_channel.display_name());
let telemetry = TelemetrySettings::get_global(cx).metrics; let telemetry = TelemetrySettings::get_global(cx).metrics;

View File

@ -3,7 +3,7 @@ use gpui::{
SemanticVersion, StatefulInteractiveElement, Styled, ViewContext, SemanticVersion, StatefulInteractiveElement, Styled, ViewContext,
}; };
use menu::Cancel; use menu::Cancel;
use util::channel::ReleaseChannel; use release_channel::ReleaseChannel;
use workspace::ui::{h_flex, v_flex, Icon, IconName, Label, StyledExt}; use workspace::ui::{h_flex, v_flex, Icon, IconName, Label, StyledExt};
pub struct UpdateNotification { pub struct UpdateNotification {
@ -14,7 +14,7 @@ impl EventEmitter<DismissEvent> for UpdateNotification {}
impl Render for UpdateNotification { impl Render for UpdateNotification {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
let app_name = cx.global::<ReleaseChannel>().display_name(); let app_name = ReleaseChannel::global(cx).display_name();
v_flex() v_flex()
.on_action(cx.listener(UpdateNotification::dismiss)) .on_action(cx.listener(UpdateNotification::dismiss))

View File

@ -9,8 +9,8 @@ use client::{proto, Client, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE};
use collections::HashSet; use collections::HashSet;
use futures::{channel::oneshot, future::Shared, Future, FutureExt}; use futures::{channel::oneshot, future::Shared, Future, FutureExt};
use gpui::{ use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Subscription,
WeakModel, Task, WeakModel,
}; };
use postage::watch; use postage::watch;
use project::Project; use project::Project;
@ -21,11 +21,15 @@ use std::sync::Arc;
pub use participant::ParticipantLocation; pub use participant::ParticipantLocation;
pub use room::Room; pub use room::Room;
struct GlobalActiveCall(Model<ActiveCall>);
impl Global for GlobalActiveCall {}
pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) { pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
CallSettings::register(cx); CallSettings::register(cx);
let active_call = cx.new_model(|cx| ActiveCall::new(client, user_store, 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 { pub struct OneAtATime {
@ -154,7 +158,12 @@ impl ActiveCall {
} }
pub fn global(cx: &AppContext) -> Model<Self> { pub fn global(cx: &AppContext) -> Model<Self> {
cx.global::<Model<Self>>().clone() cx.global::<GlobalActiveCall>().0.clone()
}
pub fn try_global(cx: &AppContext) -> Option<Model<Self>> {
cx.try_global::<GlobalActiveCall>()
.map(|call| call.0.clone())
} }
pub fn invite( pub fn invite(

View File

@ -21,6 +21,7 @@ util = { path = "../util" }
rpc = { path = "../rpc" } rpc = { path = "../rpc" }
text = { path = "../text" } text = { path = "../text" }
language = { path = "../language" } language = { path = "../language" }
release_channel = { path = "../release_channel" }
settings = { path = "../settings" } settings = { path = "../settings" }
feature_flags = { path = "../feature_flags" } feature_flags = { path = "../feature_flags" }
sum_tree = { path = "../sum_tree" } sum_tree = { path = "../sum_tree" }

View File

@ -5,13 +5,13 @@ use anyhow::{anyhow, Result};
use channel_index::ChannelIndex; use channel_index::ChannelIndex;
use client::{Client, Subscription, User, UserId, UserStore}; use client::{Client, Subscription, User, UserId, UserStore};
use collections::{hash_map, HashMap, HashSet}; use collections::{hash_map, HashMap, HashSet};
use db::RELEASE_CHANNEL;
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt}; use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{ use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, SharedString,
WeakModel, Task, WeakModel,
}; };
use language::Capability; use language::Capability;
use release_channel::RELEASE_CHANNEL;
use rpc::{ use rpc::{
proto::{self, ChannelRole, ChannelVisibility}, proto::{self, ChannelRole, ChannelVisibility},
TypedEnvelope, TypedEnvelope,
@ -22,7 +22,7 @@ use util::{async_maybe, maybe, ResultExt};
pub fn init(client: &Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) { pub fn init(client: &Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
let channel_store = let channel_store =
cx.new_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx)); 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); pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
@ -143,9 +143,13 @@ enum OpenedModelHandle<E> {
Loading(Shared<Task<Result<Model<E>, Arc<anyhow::Error>>>>), Loading(Shared<Task<Result<Model<E>, Arc<anyhow::Error>>>>),
} }
struct GlobalChannelStore(Model<ChannelStore>);
impl Global for GlobalChannelStore {}
impl ChannelStore { impl ChannelStore {
pub fn global(cx: &AppContext) -> Model<Self> { pub fn global(cx: &AppContext) -> Model<Self> {
cx.global::<Model<Self>>().clone() cx.global::<GlobalChannelStore>().0.clone()
} }
pub fn new( pub fn new(

View File

@ -18,6 +18,7 @@ collections = { path = "../collections" }
db = { path = "../db" } db = { path = "../db" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
util = { path = "../util" } util = { path = "../util" }
release_channel = { path = "../release_channel" }
rpc = { path = "../rpc" } rpc = { path = "../rpc" }
text = { path = "../text" } text = { path = "../text" }
settings = { path = "../settings" } settings = { path = "../settings" }

View File

@ -15,13 +15,14 @@ use futures::{
TryFutureExt as _, TryStreamExt, TryFutureExt as _, TryStreamExt,
}; };
use gpui::{ use gpui::{
actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model, SemanticVersion, Task, actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Global, Model, SemanticVersion,
WeakModel, Task, WeakModel,
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
use parking_lot::RwLock; use parking_lot::RwLock;
use postage::watch; use postage::watch;
use rand::prelude::*; use rand::prelude::*;
use release_channel::ReleaseChannel;
use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage}; use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -41,8 +42,7 @@ use std::{
use telemetry::Telemetry; use telemetry::Telemetry;
use thiserror::Error; use thiserror::Error;
use url::Url; use url::Url;
use util::http::HttpClient; use util::http::{HttpClient, ZedHttpClient};
use util::{channel::ReleaseChannel, http::ZedHttpClient};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
pub use rpc::*; pub use rpc::*;
@ -149,6 +149,10 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
}); });
} }
struct GlobalClient(Arc<Client>);
impl Global for GlobalClient {}
pub struct Client { pub struct Client {
id: AtomicU64, id: AtomicU64,
peer: Arc<Peer>, peer: Arc<Peer>,
@ -483,6 +487,13 @@ impl Client {
self self
} }
pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<GlobalClient>().0.clone()
}
pub fn set_global(client: Arc<Client>, cx: &mut AppContext) {
cx.set_global(GlobalClient(client))
}
pub fn user_id(&self) -> Option<u64> { pub fn user_id(&self) -> Option<u64> {
self.state self.state
.read() .read()
@ -996,7 +1007,10 @@ impl Client {
credentials: &Credentials, credentials: &Credentials,
cx: &AsyncAppContext, cx: &AsyncAppContext,
) -> Task<Result<Connection, EstablishConnectionError>> { ) -> Task<Result<Connection, EstablishConnectionError>> {
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() let request = Request::builder()
.header( .header(

View File

@ -5,6 +5,7 @@ use chrono::{DateTime, Utc};
use futures::Future; use futures::Future;
use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task}; use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task};
use parking_lot::Mutex; use parking_lot::Mutex;
use release_channel::ReleaseChannel;
use serde::Serialize; use serde::Serialize;
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration}; use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration};
@ -15,7 +16,7 @@ use tempfile::NamedTempFile;
use util::http::{HttpClient, ZedHttpClient}; use util::http::{HttpClient, ZedHttpClient};
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
use util::ResultExt; use util::ResultExt;
use util::{channel::ReleaseChannel, TryFutureExt}; use util::TryFutureExt;
use self::event_coalescer::EventCoalescer; use self::event_coalescer::EventCoalescer;
@ -143,9 +144,8 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5);
impl Telemetry { impl Telemetry {
pub fn new(client: Arc<ZedHttpClient>, cx: &mut AppContext) -> Arc<Self> { pub fn new(client: Arc<ZedHttpClient>, cx: &mut AppContext) -> Arc<Self> {
let release_channel = cx let release_channel =
.try_global::<ReleaseChannel>() ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
.map(|release_channel| release_channel.display_name());
TelemetrySettings::register(cx); TelemetrySettings::register(cx);

View File

@ -11,13 +11,4 @@ pub type HashMap<K, V> = std::collections::HashMap<K, V>;
pub type HashSet<T> = std::collections::HashSet<T>; pub type HashSet<T> = std::collections::HashSet<T>;
pub use rustc_hash::{FxHashMap, FxHashSet}; pub use rustc_hash::{FxHashMap, FxHashSet};
use std::any::TypeId;
pub use std::collections::*; pub use std::collections::*;
// NEW TYPES
#[derive(Default)]
pub struct CommandPaletteFilter {
pub hidden_namespaces: HashSet<&'static str>,
pub hidden_action_types: HashSet<TypeId>,
}

View File

@ -12,11 +12,14 @@ doctest = false
[dependencies] [dependencies]
client = { path = "../client" } client = { path = "../client" }
collections = { path = "../collections" } 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" } editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" } fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
picker = { path = "../picker" } picker = { path = "../picker" }
project = { path = "../project" } project = { path = "../project" }
release_channel = { path = "../release_channel" }
settings = { path = "../settings" } settings = { path = "../settings" }
theme = { path = "../theme" } theme = { path = "../theme" }
ui = { path = "../ui" } ui = { path = "../ui" }

View File

@ -4,19 +4,18 @@ use std::{
}; };
use client::telemetry::Telemetry; use client::telemetry::Telemetry;
use collections::{CommandPaletteFilter, HashMap}; use collections::HashMap;
use copilot::CommandPaletteFilter;
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Global,
ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
}; };
use picker::{Picker, PickerDelegate}; use picker::{Picker, PickerDelegate};
use release_channel::{parse_zed_link, ReleaseChannel};
use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing}; use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing};
use util::{ use util::ResultExt;
channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
ResultExt,
};
use workspace::{ModalView, Workspace}; use workspace::{ModalView, Workspace};
use zed_actions::OpenZedUrl; use zed_actions::OpenZedUrl;
@ -100,8 +99,11 @@ impl Render for CommandPalette {
} }
} }
pub type CommandPaletteInterceptor = pub struct CommandPaletteInterceptor(
Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>; pub Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>,
);
impl Global for CommandPaletteInterceptor {}
pub struct CommandInterceptResult { pub struct CommandInterceptResult {
pub action: Box<dyn Action>, pub action: Box<dyn Action>,
@ -139,6 +141,8 @@ impl Clone for Command {
#[derive(Default)] #[derive(Default)]
struct HitCounts(HashMap<String, usize>); struct HitCounts(HashMap<String, usize>);
impl Global for HitCounts {}
impl CommandPaletteDelegate { impl CommandPaletteDelegate {
fn new( fn new(
command_palette: WeakView<CommandPalette>, command_palette: WeakView<CommandPalette>,
@ -229,11 +233,14 @@ impl PickerDelegate for CommandPaletteDelegate {
let mut intercept_result = cx let mut intercept_result = cx
.try_read_global(|interceptor: &CommandPaletteInterceptor, cx| { .try_read_global(|interceptor: &CommandPaletteInterceptor, cx| {
(interceptor)(&query, cx) (interceptor.0)(&query, cx)
}) })
.flatten(); .flatten();
let release_channel = cx
if *RELEASE_CHANNEL == ReleaseChannel::Dev { .update(|cx| ReleaseChannel::try_global(cx))
.ok()
.flatten();
if release_channel == Some(ReleaseChannel::Dev) {
if parse_zed_link(&query).is_some() { if parse_zed_link(&query).is_some() {
intercept_result = Some(CommandInterceptResult { intercept_result = Some(CommandInterceptResult {
action: OpenZedUrl { url: query.clone() }.boxed_clone(), action: OpenZedUrl { url: query.clone() }.boxed_clone(),

View File

@ -5,7 +5,7 @@ use async_tar::Archive;
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt}; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{ use gpui::{
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model, actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
ModelContext, Task, WeakModel, ModelContext, Task, WeakModel,
}; };
use language::{ use language::{
@ -32,6 +32,17 @@ use util::{
ResultExt, 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<TypeId>,
}
impl Global for CommandPaletteFilter {}
actions!( actions!(
copilot, copilot,
[ [
@ -54,7 +65,7 @@ pub fn init(
let node_runtime = node_runtime.clone(); let node_runtime = node_runtime.clone();
move |cx| Copilot::start(new_server_id, http, node_runtime, cx) 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| { cx.observe(&copilot, |handle, cx| {
let copilot_action_types = [ let copilot_action_types = [
TypeId::of::<Suggest>(), TypeId::of::<Suggest>(),
@ -65,7 +76,7 @@ pub fn init(
let copilot_auth_action_types = [TypeId::of::<SignOut>()]; let copilot_auth_action_types = [TypeId::of::<SignOut>()];
let copilot_no_auth_action_types = [TypeId::of::<SignIn>()]; let copilot_no_auth_action_types = [TypeId::of::<SignIn>()];
let status = handle.read(cx).status(); let status = handle.read(cx).status();
let filter = cx.default_global::<collections::CommandPaletteFilter>(); let filter = cx.default_global::<CommandPaletteFilter>();
match status { match status {
Status::Disabled => { Status::Disabled => {
@ -307,9 +318,18 @@ pub enum Event {
impl EventEmitter<Event> for Copilot {} impl EventEmitter<Event> for Copilot {}
struct GlobalCopilot(Model<Copilot>);
impl Global for GlobalCopilot {}
impl Copilot { impl Copilot {
pub fn global(cx: &AppContext) -> Option<Model<Self>> { pub fn global(cx: &AppContext) -> Option<Model<Self>> {
cx.try_global::<Model<Self>>().map(|model| model.clone()) cx.try_global::<GlobalCopilot>()
.map(|model| model.0.clone())
}
pub fn set_global(copilot: Model<Self>, cx: &mut AppContext) {
cx.set_global(GlobalCopilot(copilot));
} }
fn start( fn start(

View File

@ -15,6 +15,7 @@ test-support = []
[dependencies] [dependencies]
collections = { path = "../collections" } collections = { path = "../collections" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
release_channel = { path = "../release_channel" }
sqlez = { path = "../sqlez" } sqlez = { path = "../sqlez" }
sqlez_macros = { path = "../sqlez_macros" } sqlez_macros = { path = "../sqlez_macros" }
util = { path = "../util" } util = { path = "../util" }

View File

@ -10,16 +10,16 @@ pub use lazy_static;
pub use smol; pub use smol;
pub use sqlez; pub use sqlez;
pub use sqlez_macros; pub use sqlez_macros;
pub use util::channel::{RELEASE_CHANNEL, RELEASE_CHANNEL_NAME};
pub use util::paths::DB_DIR; pub use util::paths::DB_DIR;
use release_channel::ReleaseChannel;
pub use release_channel::RELEASE_CHANNEL;
use sqlez::domain::Migrator; use sqlez::domain::Migrator;
use sqlez::thread_safe_connection::ThreadSafeConnection; use sqlez::thread_safe_connection::ThreadSafeConnection;
use sqlez_macros::sql; use sqlez_macros::sql;
use std::future::Future; use std::future::Future;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use util::channel::ReleaseChannel;
use util::{async_maybe, ResultExt}; use util::{async_maybe, ResultExt};
const CONNECTION_INITIALIZE_QUERY: &'static str = sql!( const CONNECTION_INITIALIZE_QUERY: &'static str = sql!(
@ -223,7 +223,7 @@ mod tests {
.prefix("DbTests") .prefix("DbTests")
.tempdir() .tempdir()
.unwrap(); .unwrap();
let _bad_db = open_db::<BadDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; let _bad_db = open_db::<BadDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
} }
/// Test that DB exists but corrupted (causing recreate) /// Test that DB exists but corrupted (causing recreate)
@ -261,11 +261,12 @@ mod tests {
.unwrap(); .unwrap();
{ {
let corrupt_db = let corrupt_db =
open_db::<CorruptedDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
assert!(corrupt_db.persistent()); assert!(corrupt_db.persistent());
} }
let good_db = open_db::<GoodDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; let good_db =
open_db::<GoodDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
assert!( assert!(
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()() good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
.unwrap() .unwrap()
@ -309,7 +310,7 @@ mod tests {
{ {
// Setup the bad database // Setup the bad database
let corrupt_db = let corrupt_db =
open_db::<CorruptedDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
assert!(corrupt_db.persistent()); assert!(corrupt_db.persistent());
} }
@ -320,7 +321,7 @@ mod tests {
let guard = thread::spawn(move || { let guard = thread::spawn(move || {
let good_db = smol::block_on(open_db::<GoodDB>( let good_db = smol::block_on(open_db::<GoodDB>(
tmp_path.as_path(), tmp_path.as_path(),
&util::channel::ReleaseChannel::Dev, &release_channel::ReleaseChannel::Dev,
)); ));
assert!( assert!(
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()() good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()

View File

@ -104,7 +104,6 @@ use std::{
ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive}, ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
path::Path, path::Path,
sync::Arc, sync::Arc,
sync::Weak,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
pub use sum_tree::Bias; pub use sum_tree::Bias;
@ -241,7 +240,7 @@ pub fn init(cx: &mut AppContext) {
.detach(); .detach();
cx.on_action(move |_: &workspace::NewFile, cx| { cx.on_action(move |_: &workspace::NewFile, cx| {
let app_state = cx.global::<Weak<workspace::AppState>>(); let app_state = workspace::AppState::global(cx);
if let Some(app_state) = app_state.upgrade() { if let Some(app_state) = app_state.upgrade() {
workspace::open_new(&app_state, cx, |workspace, cx| { workspace::open_new(&app_state, cx, |workspace, cx| {
Editor::new_file(workspace, &Default::default(), 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| { cx.on_action(move |_: &workspace::NewWindow, cx| {
let app_state = cx.global::<Weak<workspace::AppState>>(); let app_state = workspace::AppState::global(cx);
if let Some(app_state) = app_state.upgrade() { if let Some(app_state) = app_state.upgrade() {
workspace::open_new(&app_state, cx, |workspace, cx| { workspace::open_new(&app_state, cx, |workspace, cx| {
Editor::new_file(workspace, &Default::default(), cx) Editor::new_file(workspace, &Default::default(), cx)

View File

@ -7226,7 +7226,7 @@ async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContex
init_test(cx, |_| {}); init_test(cx, |_| {});
let (copilot, copilot_lsp) = Copilot::fake(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( let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities { lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions { completion_provider: Some(lsp::CompletionOptions {
@ -7479,7 +7479,7 @@ async fn test_copilot_completion_invalidation(
init_test(cx, |_| {}); init_test(cx, |_| {});
let (copilot, copilot_lsp) = Copilot::fake(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( let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities { lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions { completion_provider: Some(lsp::CompletionOptions {
@ -7543,7 +7543,7 @@ async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::T
init_test(cx, |_| {}); init_test(cx, |_| {});
let (copilot, copilot_lsp) = Copilot::fake(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| { let buffer_1 = cx.new_model(|cx| {
Buffer::new( Buffer::new(
@ -7660,7 +7660,7 @@ async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut gpui
}); });
let (copilot, copilot_lsp) = Copilot::fake(cx); 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()); let fs = FakeFs::new(cx.executor());
fs.insert_tree( fs.insert_tree(

View File

@ -10,7 +10,7 @@ use crate::{
MultiBufferSnapshot, ToPoint, MultiBufferSnapshot, ToPoint,
}; };
pub use autoscroll::{Autoscroll, AutoscrollStrategy}; 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}; use language::{Bias, Point};
pub use scroll_amount::ScrollAmount; pub use scroll_amount::ScrollAmount;
use std::{ use std::{
@ -27,6 +27,8 @@ const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
#[derive(Default)] #[derive(Default)]
pub struct ScrollbarAutoHide(pub bool); pub struct ScrollbarAutoHide(pub bool);
impl Global for ScrollbarAutoHide {}
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct ScrollAnchor { pub struct ScrollAnchor {
pub offset: gpui::Point<f32>, pub offset: gpui::Point<f32>,

View File

@ -1,4 +1,4 @@
use gpui::{AppContext, Subscription, ViewContext}; use gpui::{AppContext, Global, Subscription, ViewContext};
#[derive(Default)] #[derive(Default)]
struct FeatureFlags { struct FeatureFlags {
@ -12,6 +12,8 @@ impl FeatureFlags {
} }
} }
impl Global for FeatureFlags {}
pub trait FeatureFlag { pub trait FeatureFlag {
const NAME: &'static str; const NAME: &'static str;
} }

View File

@ -19,6 +19,7 @@ gpui = { path = "../gpui" }
language = { path = "../language" } language = { path = "../language" }
menu = { path = "../menu" } menu = { path = "../menu" }
project = { path = "../project" } project = { path = "../project" }
release_channel = { path = "../release_channel" }
settings = { path = "../settings" } settings = { path = "../settings" }
theme = { path = "../theme" } theme = { path = "../theme" }
ui = { path = "../ui" } ui = { path = "../ui" }

View File

@ -225,7 +225,7 @@ impl FeedbackModal {
None, None,
&["Yes, Submit!", "No"], &["Yes, Submit!", "No"],
); );
let client = cx.global::<Arc<Client>>().clone(); let client = Client::global(cx).clone();
let specs = self.system_specs.clone(); let specs = self.system_specs.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let answer = answer.await.ok(); let answer = answer.await.ok();

View File

@ -1,10 +1,10 @@
use client::ZED_APP_VERSION; use client::ZED_APP_VERSION;
use gpui::AppContext; use gpui::AppContext;
use human_bytes::human_bytes; use human_bytes::human_bytes;
use release_channel::ReleaseChannel;
use serde::Serialize; use serde::Serialize;
use std::{env, fmt::Display}; use std::{env, fmt::Display};
use sysinfo::{RefreshKind, System, SystemExt}; use sysinfo::{RefreshKind, System, SystemExt};
use util::channel::ReleaseChannel;
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
pub struct SystemSpecs { pub struct SystemSpecs {
@ -21,7 +21,7 @@ impl SystemSpecs {
let app_version = ZED_APP_VERSION let app_version = ZED_APP_VERSION
.or_else(|| cx.app_metadata().app_version) .or_else(|| cx.app_metadata().app_version)
.map(|v| v.to_string()); .map(|v| v.to_string());
let release_channel = cx.global::<ReleaseChannel>().display_name(); let release_channel = ReleaseChannel::global(cx).display_name();
let os_name = cx.app_metadata().os_name; let os_name = cx.app_metadata().os_name;
let system = System::new_with_specifics(RefreshKind::new().with_memory()); let system = System::new_with_specifics(RefreshKind::new().with_memory());
let memory = system.total_memory(); let memory = system.total_memory();

View File

@ -17,7 +17,7 @@ use time::UtcOffset;
use crate::{ use crate::{
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any, current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, 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, Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render,
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId, TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId,
@ -823,13 +823,13 @@ impl AppContext {
} }
/// Check whether a global of the given type has been assigned. /// Check whether a global of the given type has been assigned.
pub fn has_global<G: 'static>(&self) -> bool { pub fn has_global<G: Global>(&self) -> bool {
self.globals_by_type.contains_key(&TypeId::of::<G>()) self.globals_by_type.contains_key(&TypeId::of::<G>())
} }
/// Access the global of the given type. Panics if a global for that type has not been assigned. /// Access the global of the given type. Panics if a global for that type has not been assigned.
#[track_caller] #[track_caller]
pub fn global<G: 'static>(&self) -> &G { pub fn global<G: Global>(&self) -> &G {
self.globals_by_type self.globals_by_type
.get(&TypeId::of::<G>()) .get(&TypeId::of::<G>())
.map(|any_state| any_state.downcast_ref::<G>().unwrap()) .map(|any_state| any_state.downcast_ref::<G>().unwrap())
@ -838,7 +838,7 @@ impl AppContext {
} }
/// Access the global of the given type if a value has been assigned. /// Access the global of the given type if a value has been assigned.
pub fn try_global<G: 'static>(&self) -> Option<&G> { pub fn try_global<G: Global>(&self) -> Option<&G> {
self.globals_by_type self.globals_by_type
.get(&TypeId::of::<G>()) .get(&TypeId::of::<G>())
.map(|any_state| any_state.downcast_ref::<G>().unwrap()) .map(|any_state| any_state.downcast_ref::<G>().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. /// Access the global of the given type mutably. Panics if a global for that type has not been assigned.
#[track_caller] #[track_caller]
pub fn global_mut<G: 'static>(&mut self) -> &mut G { pub fn global_mut<G: Global>(&mut self) -> &mut G {
let global_type = TypeId::of::<G>(); let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_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 /// Access the global of the given type mutably. A default value is assigned if a global of this type has not
/// yet been assigned. /// yet been assigned.
pub fn default_global<G: 'static + Default>(&mut self) -> &mut G { pub fn default_global<G: Global + Default>(&mut self) -> &mut G {
let global_type = TypeId::of::<G>(); let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type self.globals_by_type
@ -869,7 +869,7 @@ impl AppContext {
} }
/// Sets the value of the global of the given type. /// Sets the value of the global of the given type.
pub fn set_global<G: Any>(&mut self, global: G) { pub fn set_global<G: Global>(&mut self, global: G) {
let global_type = TypeId::of::<G>(); let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type.insert(global_type, Box::new(global)); 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. /// Remove the global of the given type from the app context. Does not notify global observers.
pub fn remove_global<G: Any>(&mut self) -> G { pub fn remove_global<G: Global>(&mut self) -> G {
let global_type = TypeId::of::<G>(); let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.push_effect(Effect::NotifyGlobalObservers { global_type });
*self *self
@ -895,7 +895,7 @@ impl AppContext {
/// Updates the global of the given type with a closure. Unlike `global_mut`, this method provides /// 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. /// your closure with mutable access to the `AppContext` and the global simultaneously.
pub fn update_global<G: 'static, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R { pub fn update_global<G: Global, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
self.update(|cx| { self.update(|cx| {
let mut global = cx.lease_global::<G>(); let mut global = cx.lease_global::<G>();
let result = f(&mut global, cx); 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. /// Register a callback to be invoked when a global of the given type is updated.
pub fn observe_global<G: 'static>( pub fn observe_global<G: Global>(
&mut self, &mut self,
mut f: impl FnMut(&mut Self) + 'static, mut f: impl FnMut(&mut Self) + 'static,
) -> Subscription { ) -> Subscription {
@ -921,7 +921,7 @@ impl AppContext {
} }
/// Move the global of the given type to the stack. /// Move the global of the given type to the stack.
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> { pub(crate) fn lease_global<G: Global>(&mut self) -> GlobalLease<G> {
GlobalLease::new( GlobalLease::new(
self.globals_by_type self.globals_by_type
.remove(&TypeId::of::<G>()) .remove(&TypeId::of::<G>())
@ -931,7 +931,7 @@ impl AppContext {
} }
/// Restore the global of the given type after it is moved to the stack. /// Restore the global of the given type after it is moved to the stack.
pub(crate) fn end_global_lease<G: 'static>(&mut self, lease: GlobalLease<G>) { pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
let global_type = TypeId::of::<G>(); let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type }); self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type.insert(global_type, lease.global); 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. /// Wraps a global variable value during `update_global` while the value has been moved to the stack.
pub(crate) struct GlobalLease<G: 'static> { pub(crate) struct GlobalLease<G: Global> {
global: Box<dyn Any>, global: Box<dyn Any>,
global_type: PhantomData<G>, global_type: PhantomData<G>,
} }
impl<G: 'static> GlobalLease<G> { impl<G: Global> GlobalLease<G> {
fn new(global: Box<dyn Any>) -> Self { fn new(global: Box<dyn Any>) -> Self {
GlobalLease { GlobalLease {
global, global,
@ -1307,7 +1307,7 @@ impl<G: 'static> GlobalLease<G> {
} }
} }
impl<G: 'static> Deref for GlobalLease<G> { impl<G: Global> Deref for GlobalLease<G> {
type Target = G; type Target = G;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -1315,7 +1315,7 @@ impl<G: 'static> Deref for GlobalLease<G> {
} }
} }
impl<G: 'static> DerefMut for GlobalLease<G> { impl<G: Global> DerefMut for GlobalLease<G> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.global.downcast_mut().unwrap() self.global.downcast_mut().unwrap()
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, DismissEvent, 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, ViewContext, VisualContext, WindowContext, WindowHandle,
}; };
use anyhow::{anyhow, Context as _}; use anyhow::{anyhow, Context as _};
@ -144,7 +144,7 @@ impl AsyncAppContext {
/// Determine whether global state of the specified type has been assigned. /// Determine whether global state of the specified type has been assigned.
/// Returns an error if the `AppContext` has been dropped. /// Returns an error if the `AppContext` has been dropped.
pub fn has_global<G: 'static>(&self) -> Result<bool> { pub fn has_global<G: Global>(&self) -> Result<bool> {
let app = self let app = self
.app .app
.upgrade() .upgrade()
@ -157,7 +157,7 @@ impl AsyncAppContext {
/// ///
/// Panics if no global state of the specified type has been assigned. /// Panics if no global state of the specified type has been assigned.
/// Returns an error if the `AppContext` has been dropped. /// Returns an error if the `AppContext` has been dropped.
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> { pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
let app = self let app = self
.app .app
.upgrade() .upgrade()
@ -172,7 +172,7 @@ impl AsyncAppContext {
/// if no state of the specified type has been assigned. /// 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. /// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped.
pub fn try_read_global<G: 'static, R>( pub fn try_read_global<G: Global, R>(
&self, &self,
read: impl FnOnce(&G, &AppContext) -> R, read: impl FnOnce(&G, &AppContext) -> R,
) -> Option<R> { ) -> Option<R> {
@ -183,7 +183,7 @@ impl AsyncAppContext {
/// A convenience method for [AppContext::update_global] /// A convenience method for [AppContext::update_global]
/// for updating the global state of the specified type. /// for updating the global state of the specified type.
pub fn update_global<G: 'static, R>( pub fn update_global<G: Global, R>(
&mut self, &mut self,
update: impl FnOnce(&mut G, &mut AppContext) -> R, update: impl FnOnce(&mut G, &mut AppContext) -> R,
) -> Result<R> { ) -> Result<R> {
@ -235,7 +235,7 @@ impl AsyncWindowContext {
} }
/// A convenience method for [`AppContext::global`]. /// A convenience method for [`AppContext::global`].
pub fn read_global<G: 'static, R>( pub fn read_global<G: Global, R>(
&mut self, &mut self,
read: impl FnOnce(&G, &WindowContext) -> R, read: impl FnOnce(&G, &WindowContext) -> R,
) -> Result<R> { ) -> Result<R> {
@ -249,7 +249,7 @@ impl AsyncWindowContext {
update: impl FnOnce(&mut G, &mut WindowContext) -> R, update: impl FnOnce(&mut G, &mut WindowContext) -> R,
) -> Result<R> ) -> Result<R>
where where
G: 'static, G: Global,
{ {
self.window.update(self, |_, cx| cx.update_global(update)) self.window.update(self, |_, cx| cx.update_global(update))
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId, 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 anyhow::Result;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -193,7 +193,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
/// Updates the given global /// Updates the given global
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where where
G: 'static, G: Global,
{ {
let mut global = self.app.lease_global::<G>(); let mut global = self.app.lease_global::<G>();
let result = f(&mut global, self); let result = f(&mut global, self);

View File

@ -1,8 +1,8 @@
use crate::{ use crate::{
Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter,
ForegroundExecutor, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform, Point, ForegroundExecutor, Global, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform,
Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View,
ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
}; };
use anyhow::{anyhow, bail}; use anyhow::{anyhow, bail};
@ -256,20 +256,20 @@ impl TestAppContext {
} }
/// true if the given global is defined /// true if the given global is defined
pub fn has_global<G: 'static>(&self) -> bool { pub fn has_global<G: Global>(&self) -> bool {
let app = self.app.borrow(); let app = self.app.borrow();
app.has_global::<G>() app.has_global::<G>()
} }
/// runs the given closure with a reference to the global /// runs the given closure with a reference to the global
/// panics if `has_global` would return false. /// panics if `has_global` would return false.
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R { pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
let app = self.app.borrow(); let app = self.app.borrow();
read(app.global(), &app) read(app.global(), &app)
} }
/// runs the given closure with a reference to the global (if set) /// runs the given closure with a reference to the global (if set)
pub fn try_read_global<G: 'static, R>( pub fn try_read_global<G: Global, R>(
&self, &self,
read: impl FnOnce(&G, &AppContext) -> R, read: impl FnOnce(&G, &AppContext) -> R,
) -> Option<R> { ) -> Option<R> {
@ -278,13 +278,13 @@ impl TestAppContext {
} }
/// sets the global in this context. /// sets the global in this context.
pub fn set_global<G: 'static>(&mut self, global: G) { pub fn set_global<G: Global>(&mut self, global: G) {
let mut lock = self.app.borrow_mut(); let mut lock = self.app.borrow_mut();
lock.set_global(global); lock.set_global(global);
} }
/// updates the global in this context. (panics if `has_global` would return false) /// updates the global in this context. (panics if `has_global` would return false)
pub fn update_global<G: 'static, R>( pub fn update_global<G: Global, R>(
&mut self, &mut self,
update: impl FnOnce(&mut G, &mut AppContext) -> R, update: impl FnOnce(&mut G, &mut AppContext) -> R,
) -> R { ) -> R {

View File

@ -17,11 +17,11 @@
use crate::{ use crate::{
point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds, point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement, ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, Global,
IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
WindowContext, View, Visibility, WindowContext,
}; };
use collections::HashMap; use collections::HashMap;
@ -2070,6 +2070,8 @@ impl ElementClickedState {
#[derive(Default)] #[derive(Default)]
pub(crate) struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>); pub(crate) struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
impl Global for GroupBounds {}
impl GroupBounds { impl GroupBounds {
pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> { pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
cx.default_global::<Self>() cx.default_global::<Self>()

View File

@ -258,14 +258,14 @@ pub trait EventEmitter<E: Any>: 'static {}
/// can be used interchangeably. /// can be used interchangeably.
pub trait BorrowAppContext { pub trait BorrowAppContext {
/// Set a global value on the context. /// Set a global value on the context.
fn set_global<T: 'static>(&mut self, global: T); fn set_global<T: Global>(&mut self, global: T);
} }
impl<C> BorrowAppContext for C impl<C> BorrowAppContext for C
where where
C: BorrowMut<AppContext>, C: BorrowMut<AppContext>,
{ {
fn set_global<G: 'static>(&mut self, global: G) { fn set_global<G: Global>(&mut self, global: G) {
self.borrow_mut().set_global(global) self.borrow_mut().set_global(global)
} }
} }
@ -287,3 +287,8 @@ impl<T> Flatten<T> for Result<T> {
self 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 {}

View File

@ -3,8 +3,8 @@ use std::{iter, mem, ops::Range};
use crate::{ use crate::{
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement, black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures, CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures,
FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size, FontStyle, FontWeight, Global, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SizeRefinement, Styled, TextRun, SharedString, Size, SizeRefinement, Styled, TextRun,
}; };
use collections::HashSet; use collections::HashSet;
use refineable::Refineable; use refineable::Refineable;
@ -20,6 +20,8 @@ pub use taffy::style::{
/// GPUI. /// GPUI.
pub struct DebugBelow; pub struct DebugBelow;
impl Global for DebugBelow {}
/// The CSS styling that can be applied to an element via the `Styled` trait /// The CSS styling that can be applied to an element via the `Styled` trait
#[derive(Clone, Refineable, Debug)] #[derive(Clone, Refineable, Debug)]
#[refineable(Debug)] #[refineable(Debug)]

View File

@ -2,7 +2,7 @@ use crate::{
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId, AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, 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, KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton,
MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
PlatformWindow, Point, PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, PlatformWindow, Point, PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet,
@ -708,7 +708,7 @@ impl<'a> WindowContext<'a> {
/// access both to the global and the context. /// access both to the global and the context.
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where where
G: 'static, G: Global,
{ {
let mut global = self.app.lease_global::<G>(); let mut global = self.app.lease_global::<G>();
let result = f(&mut global, self); 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 /// Register the given handler to be invoked whenever the global of the given type
/// is updated. /// is updated.
pub fn observe_global<G: 'static>( pub fn observe_global<G: Global>(
&mut self, &mut self,
f: impl Fn(&mut WindowContext<'_>) + 'static, f: impl Fn(&mut WindowContext<'_>) + 'static,
) -> Subscription { ) -> Subscription {
@ -2198,7 +2198,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
/// Updates the global state of the given type. /// Updates the global state of the given type.
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where where
G: 'static, G: Global,
{ {
let mut global = self.app.lease_global::<G>(); let mut global = self.app.lease_global::<G>();
let result = f(&mut global, self); 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. /// Register a callback to be invoked when the given global state changes.
pub fn observe_global<G: 'static>( pub fn observe_global<G: Global>(
&mut self, &mut self,
mut f: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static, mut f: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static,
) -> Subscription { ) -> Subscription {

View File

@ -3,7 +3,9 @@ use channel::{ChannelMessage, ChannelMessageId, ChannelStore};
use client::{Client, UserStore}; use client::{Client, UserStore};
use collections::HashMap; use collections::HashMap;
use db::smol::stream::StreamExt; 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 rpc::{proto, Notification, TypedEnvelope};
use std::{ops::Range, sync::Arc}; use std::{ops::Range, sync::Arc};
use sum_tree::{Bias, SumTree}; use sum_tree::{Bias, SumTree};
@ -12,9 +14,13 @@ use util::ResultExt;
pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) { pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
let notification_store = cx.new_model(|cx| NotificationStore::new(client, user_store, cx)); 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<NotificationStore>);
impl Global for GlobalNotificationStore {}
pub struct NotificationStore { pub struct NotificationStore {
client: Arc<Client>, client: Arc<Client>,
user_store: Model<UserStore>, user_store: Model<UserStore>,
@ -70,7 +76,7 @@ struct NotificationId(u64);
impl NotificationStore { impl NotificationStore {
pub fn global(cx: &AppContext) -> Model<Self> { pub fn global(cx: &AppContext) -> Model<Self> {
cx.global::<Model<Self>>().clone() cx.global::<GlobalNotificationStore>().0.clone()
} }
pub fn new( pub fn new(

View File

@ -2,7 +2,7 @@ use std::{path::Path, str, sync::Arc};
use collections::HashMap; use collections::HashMap;
use gpui::{AppContext, AssetSource}; use gpui::{AppContext, AssetSource, Global};
use serde_derive::Deserialize; use serde_derive::Deserialize;
use util::{maybe, paths::PathExt}; use util::{maybe, paths::PathExt};
@ -17,6 +17,8 @@ pub struct FileAssociations {
types: HashMap<String, TypeConfig>, types: HashMap<String, TypeConfig>,
} }
impl Global for FileAssociations {}
const COLLAPSED_DIRECTORY_TYPE: &'static str = "collapsed_folder"; const COLLAPSED_DIRECTORY_TYPE: &'static str = "collapsed_folder";
const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_folder"; const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_folder";
const COLLAPSED_CHEVRON_TYPE: &'static str = "collapsed_chevron"; const COLLAPSED_CHEVRON_TYPE: &'static str = "collapsed_chevron";

View File

@ -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"

View File

@ -0,0 +1 @@
../../LICENSE-GPL

View File

@ -1,24 +1,44 @@
use lazy_static::lazy_static; use gpui::{AppContext, Global};
use once_cell::sync::Lazy;
use std::env; use std::env;
lazy_static! { #[doc(hidden)]
pub static ref RELEASE_CHANNEL_NAME: String = if cfg!(debug_assertions) { pub static RELEASE_CHANNEL_NAME: Lazy<String> = if cfg!(debug_assertions) {
Lazy::new(|| {
env::var("ZED_RELEASE_CHANNEL") env::var("ZED_RELEASE_CHANNEL")
.unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").to_string()) .unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").to_string())
} else { })
include_str!("../../zed/RELEASE_CHANNEL").to_string() } else {
}; Lazy::new(|| include_str!("../../zed/RELEASE_CHANNEL").to_string())
pub static ref RELEASE_CHANNEL: ReleaseChannel = match RELEASE_CHANNEL_NAME.as_str().trim() { };
#[doc(hidden)]
pub static RELEASE_CHANNEL: Lazy<ReleaseChannel> =
Lazy::new(|| match RELEASE_CHANNEL_NAME.as_str().trim() {
"dev" => ReleaseChannel::Dev, "dev" => ReleaseChannel::Dev,
"nightly" => ReleaseChannel::Nightly, "nightly" => ReleaseChannel::Nightly,
"preview" => ReleaseChannel::Preview, "preview" => ReleaseChannel::Preview,
"stable" => ReleaseChannel::Stable, "stable" => ReleaseChannel::Stable,
_ => panic!("invalid release channel {}", *RELEASE_CHANNEL_NAME), _ => panic!("invalid release channel {}", *RELEASE_CHANNEL_NAME),
}; });
}
#[derive(Clone)]
pub struct AppCommitSha(pub String); pub struct AppCommitSha(pub String);
struct GlobalAppCommitSha(AppCommitSha);
impl Global for GlobalAppCommitSha {}
impl AppCommitSha {
pub fn try_global(cx: &AppContext) -> Option<AppCommitSha> {
cx.try_global::<GlobalAppCommitSha>()
.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)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum ReleaseChannel { pub enum ReleaseChannel {
#[default] #[default]
@ -28,7 +48,24 @@ pub enum ReleaseChannel {
Stable, Stable,
} }
struct GlobalReleaseChannel(ReleaseChannel);
impl Global for GlobalReleaseChannel {}
impl ReleaseChannel { impl ReleaseChannel {
pub fn init(cx: &mut AppContext) {
cx.set_global(GlobalReleaseChannel(*RELEASE_CHANNEL))
}
pub fn global(cx: &AppContext) -> Self {
cx.global::<GlobalReleaseChannel>().0
}
pub fn try_global(cx: &AppContext) -> Option<Self> {
cx.try_global::<GlobalReleaseChannel>()
.map(|channel| channel.0)
}
pub fn display_name(&self) -> &'static str { pub fn display_name(&self) -> &'static str {
match self { match self {
ReleaseChannel::Dev => "Zed Dev", ReleaseChannel::Dev => "Zed Dev",

View File

@ -13,10 +13,10 @@ use editor::{
use editor::{EditorElement, EditorStyle}; use editor::{EditorElement, EditorStyle};
use gpui::{ use gpui::{
actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId, actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId,
EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Hsla, InteractiveElement, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Global, Hsla,
IntoElement, KeyContext, Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, InteractiveElement, IntoElement, KeyContext, Model, ModelContext, ParentElement, PromptLevel,
Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakModel, WeakView, Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext,
WhiteSpace, WindowContext, WeakModel, WeakView, WhiteSpace, WindowContext,
}; };
use menu::Confirm; use menu::Confirm;
use project::{ use project::{
@ -58,6 +58,8 @@ actions!(
#[derive(Default)] #[derive(Default)]
struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>); struct ActiveSettings(HashMap<WeakModel<Project>, ProjectSearchSettings>);
impl Global for ActiveSettings {}
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
cx.set_global(ActiveSettings::default()); cx.set_global(ActiveSettings::default());
cx.observe_new_views(|workspace: &mut Workspace, _cx| { cx.observe_new_views(|workspace: &mut Workspace, _cx| {

View File

@ -17,6 +17,7 @@ language = { path = "../language" }
project = { path = "../project" } project = { path = "../project" }
workspace = { path = "../workspace" } workspace = { path = "../workspace" }
util = { path = "../util" } util = { path = "../util" }
release_channel = { path = "../release_channel" }
rpc = { path = "../rpc" } rpc = { path = "../rpc" }
settings = { path = "../settings" } settings = { path = "../settings" }
anyhow.workspace = true anyhow.workspace = true

View File

@ -15,8 +15,8 @@ use db::VectorDatabase;
use embedding_queue::{EmbeddingQueue, FileToEmbed}; use embedding_queue::{EmbeddingQueue, FileToEmbed};
use futures::{future, FutureExt, StreamExt}; use futures::{future, FutureExt, StreamExt};
use gpui::{ use gpui::{
AppContext, AsyncAppContext, BorrowWindow, Context, Model, ModelContext, Task, ViewContext, AppContext, AsyncAppContext, BorrowWindow, Context, Global, Model, ModelContext, Task,
WeakModel, ViewContext, WeakModel,
}; };
use language::{Anchor, Bias, Buffer, Language, LanguageRegistry}; use language::{Anchor, Bias, Buffer, Language, LanguageRegistry};
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -25,6 +25,7 @@ use parking_lot::Mutex;
use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES}; use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES};
use postage::watch; use postage::watch;
use project::{Fs, PathChange, Project, ProjectEntryId, Worktree, WorktreeId}; use project::{Fs, PathChange, Project, ProjectEntryId, Worktree, WorktreeId};
use release_channel::ReleaseChannel;
use settings::Settings; use settings::Settings;
use smol::channel; use smol::channel;
use std::{ use std::{
@ -38,7 +39,7 @@ use std::{
time::{Duration, Instant, SystemTime}, time::{Duration, Instant, SystemTime},
}; };
use util::paths::PathMatcher; 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; use workspace::Workspace;
const SEMANTIC_INDEX_VERSION: usize = 11; const SEMANTIC_INDEX_VERSION: usize = 11;
@ -58,7 +59,7 @@ pub fn init(
SemanticIndexSettings::register(cx); SemanticIndexSettings::register(cx);
let db_file_path = EMBEDDINGS_DIR 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"); .join("embeddings_db");
cx.observe_new_views( cx.observe_new_views(
@ -101,7 +102,7 @@ pub fn init(
) )
.await?; .await?;
cx.update(|cx| cx.set_global(semantic_index.clone()))?; cx.update(|cx| cx.set_global(GlobalSemanticIndex(semantic_index.clone())))?;
anyhow::Ok(()) anyhow::Ok(())
}) })
@ -130,6 +131,10 @@ pub struct SemanticIndex {
projects: HashMap<WeakModel<Project>, ProjectState>, projects: HashMap<WeakModel<Project>, ProjectState>,
} }
struct GlobalSemanticIndex(Model<SemanticIndex>);
impl Global for GlobalSemanticIndex {}
struct ProjectState { struct ProjectState {
worktrees: HashMap<WorktreeId, WorktreeState>, worktrees: HashMap<WorktreeId, WorktreeState>,
pending_file_count_rx: watch::Receiver<usize>, pending_file_count_rx: watch::Receiver<usize>,
@ -274,8 +279,8 @@ pub struct SearchResult {
impl SemanticIndex { impl SemanticIndex {
pub fn global(cx: &mut AppContext) -> Option<Model<SemanticIndex>> { pub fn global(cx: &mut AppContext) -> Option<Model<SemanticIndex>> {
cx.try_global::<Model<Self>>() cx.try_global::<GlobalSemanticIndex>()
.map(|semantic_index| semantic_index.clone()) .map(|semantic_index| semantic_index.0.clone())
} }
pub fn authenticate(&mut self, cx: &mut AppContext) -> Task<bool> { pub fn authenticate(&mut self, cx: &mut AppContext) -> Task<bool> {

View File

@ -17,6 +17,7 @@ collections = { path = "../collections" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
fs = { path = "../fs" } fs = { path = "../fs" }
feature_flags = { path = "../feature_flags" } feature_flags = { path = "../feature_flags" }
release_channel = { path = "../release_channel" }
util = { path = "../util" } util = { path = "../util" }
anyhow.workspace = true anyhow.workspace = true

View File

@ -1,6 +1,6 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use collections::{btree_map, hash_map, BTreeMap, HashMap}; use collections::{btree_map, hash_map, BTreeMap, HashMap};
use gpui::{AppContext, AsyncAppContext}; use gpui::{AppContext, AsyncAppContext, Global};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema}; use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
use serde::{de::DeserializeOwned, Deserialize as _, Serialize}; use serde::{de::DeserializeOwned, Deserialize as _, Serialize};
@ -13,9 +13,7 @@ use std::{
str, str,
sync::Arc, sync::Arc,
}; };
use util::{ use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _};
channel::RELEASE_CHANNEL_NAME, merge_non_null_json_value_into, RangeExt, ResultExt as _,
};
/// A value that can be defined as a user setting. /// 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 { impl Default for SettingsStore {
fn default() -> Self { fn default() -> Self {
SettingsStore { SettingsStore {
@ -207,7 +207,10 @@ impl SettingsStore {
user_values_stack = vec![user_settings]; 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 if let Some(release_settings) = setting_value
.deserialize_setting(&release_settings) .deserialize_setting(&release_settings)
.log_err() .log_err()
@ -537,7 +540,10 @@ impl SettingsStore {
paths_stack.push(None); 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 if let Some(release_settings) = setting_value
.deserialize_setting(&release_settings) .deserialize_setting(&release_settings)
.log_err() .log_err()

View File

@ -6,7 +6,7 @@ use anyhow::{anyhow, Context, Result};
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use fs::Fs; use fs::Fs;
use futures::StreamExt; use futures::StreamExt;
use gpui::{AppContext, AssetSource, HighlightStyle, SharedString}; use gpui::{AppContext, AssetSource, Global, HighlightStyle, SharedString};
use parking_lot::RwLock; use parking_lot::RwLock;
use refineable::Refineable; use refineable::Refineable;
use util::ResultExt; use util::ResultExt;
@ -32,10 +32,7 @@ pub struct ThemeMeta {
#[derive(Default, Deref, DerefMut)] #[derive(Default, Deref, DerefMut)]
struct GlobalThemeRegistry(Arc<ThemeRegistry>); struct GlobalThemeRegistry(Arc<ThemeRegistry>);
/// Initializes the theme registry. impl Global for GlobalThemeRegistry {}
pub fn init(assets: Box<dyn AssetSource>, cx: &mut AppContext) {
cx.set_global(GlobalThemeRegistry(Arc::new(ThemeRegistry::new(assets))));
}
struct ThemeRegistryState { struct ThemeRegistryState {
themes: HashMap<SharedString, Arc<Theme>>, themes: HashMap<SharedString, Arc<Theme>>,
@ -59,6 +56,11 @@ impl ThemeRegistry {
cx.default_global::<GlobalThemeRegistry>().0.clone() cx.default_global::<GlobalThemeRegistry>().0.clone()
} }
/// Sets the global [`ThemeRegistry`].
pub(crate) fn set_global(assets: Box<dyn AssetSource>, cx: &mut AppContext) {
cx.set_global(GlobalThemeRegistry(Arc::new(ThemeRegistry::new(assets))));
}
pub fn new(assets: Box<dyn AssetSource>) -> Self { pub fn new(assets: Box<dyn AssetSource>) -> Self {
let registry = Self { let registry = Self {
state: RwLock::new(ThemeRegistryState { state: RwLock::new(ThemeRegistryState {

View File

@ -2,7 +2,8 @@ use crate::one_themes::one_dark;
use crate::{SyntaxTheme, Theme, ThemeRegistry, ThemeStyleContent}; use crate::{SyntaxTheme, Theme, ThemeRegistry, ThemeStyleContent};
use anyhow::Result; use anyhow::Result;
use gpui::{ 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 refineable::Refineable;
use schemars::{ use schemars::{
@ -34,6 +35,8 @@ pub struct ThemeSettings {
#[derive(Default)] #[derive(Default)]
pub(crate) struct AdjustedBufferFontSize(Pixels); pub(crate) struct AdjustedBufferFontSize(Pixels);
impl Global for AdjustedBufferFontSize {}
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct ThemeSettingsContent { pub struct ThemeSettingsContent {
#[serde(default)] #[serde(default)]

View File

@ -60,7 +60,7 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) {
LoadThemes::JustBase => (Box::new(()) as Box<dyn AssetSource>, false), LoadThemes::JustBase => (Box::new(()) as Box<dyn AssetSource>, false),
LoadThemes::All(assets) => (assets, true), LoadThemes::All(assets) => (assets, true),
}; };
registry::init(assets, cx); ThemeRegistry::set_global(assets, cx);
if load_user_themes { if load_user_themes {
ThemeRegistry::global(cx).load_bundled_themes(); ThemeRegistry::global(cx).load_bundled_themes();

View File

@ -36,24 +36,6 @@ pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Worksp
}); });
} }
#[cfg(debug_assertions)]
pub fn reload(cx: &mut AppContext) {
let current_theme_name = cx.theme().name.clone();
let current_theme = cx.update_global(|registry: &mut ThemeRegistry, _cx| {
registry.clear();
registry.get(&current_theme_name)
});
match current_theme {
Ok(theme) => {
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 {} impl ModalView for ThemeSelector {}
pub struct ThemeSelector { pub struct ThemeSelector {

View File

@ -1,5 +1,4 @@
pub mod arc_cow; pub mod arc_cow;
pub mod channel;
pub mod fs; pub mod fs;
pub mod github; pub mod github;
pub mod http; pub mod http;

View File

@ -28,6 +28,8 @@ regex.workspace = true
collections = { path = "../collections" } collections = { path = "../collections" }
command_palette = { path = "../command_palette" } 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" } editor = { path = "../editor" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
language = { path = "../language" } language = { path = "../language" }

View File

@ -15,11 +15,12 @@ mod utils;
mod visual; mod visual;
use anyhow::Result; use anyhow::Result;
use collections::{CommandPaletteFilter, HashMap}; use collections::HashMap;
use command_palette::CommandPaletteInterceptor; use command_palette::CommandPaletteInterceptor;
use copilot::CommandPaletteFilter;
use editor::{movement, Editor, EditorEvent, EditorMode}; use editor::{movement, Editor, EditorEvent, EditorMode};
use gpui::{ use gpui::{
actions, impl_actions, Action, AppContext, EntityId, KeyContext, Subscription, View, actions, impl_actions, Action, AppContext, EntityId, Global, KeyContext, Subscription, View,
ViewContext, WeakView, WindowContext, ViewContext, WeakView, WindowContext,
}; };
use language::{CursorShape, Point, Selection, SelectionGoal}; use language::{CursorShape, Point, Selection, SelectionGoal};
@ -171,9 +172,9 @@ pub fn observe_keystrokes(cx: &mut WindowContext) {
.detach() .detach()
} }
/// The state pertaining to Vim mode. Stored as a global. /// The state pertaining to Vim mode.
#[derive(Default)] #[derive(Default)]
pub struct Vim { struct Vim {
active_editor: Option<WeakView<Editor>>, active_editor: Option<WeakView<Editor>>,
editor_subscription: Option<Subscription>, editor_subscription: Option<Subscription>,
enabled: bool, enabled: bool,
@ -182,6 +183,8 @@ pub struct Vim {
default_state: EditorState, default_state: EditorState,
} }
impl Global for Vim {}
impl Vim { impl Vim {
fn read(cx: &mut AppContext) -> &Self { fn read(cx: &mut AppContext) -> &Self {
cx.global::<Self>() cx.global::<Self>()
@ -512,7 +515,9 @@ impl Vim {
}); });
if self.enabled { if self.enabled {
cx.set_global::<CommandPaletteInterceptor>(Box::new(command::command_interceptor)); cx.set_global::<CommandPaletteInterceptor>(CommandPaletteInterceptor(Box::new(
command::command_interceptor,
)));
} else if cx.has_global::<CommandPaletteInterceptor>() { } else if cx.has_global::<CommandPaletteInterceptor>() {
let _ = cx.remove_global::<CommandPaletteInterceptor>(); let _ = cx.remove_global::<CommandPaletteInterceptor>();
} }

View File

@ -43,6 +43,7 @@ async-recursion = "1.0.0"
itertools = "0.10" itertools = "0.10"
bincode = "1.2.1" bincode = "1.2.1"
anyhow.workspace = true anyhow.workspace = true
derive_more.workspace = true
futures.workspace = true futures.workspace = true
lazy_static.workspace = true lazy_static.workspace = true
log.workspace = true log.workspace = true

View File

@ -1,7 +1,7 @@
use crate::{Toast, Workspace}; use crate::{Toast, Workspace};
use collections::HashMap; use collections::HashMap;
use gpui::{ use gpui::{
AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, Global,
PromptLevel, Render, Task, View, ViewContext, VisualContext, WindowContext, PromptLevel, Render, Task, View, ViewContext, VisualContext, WindowContext,
}; };
use std::{any::TypeId, ops::DerefMut}; use std::{any::TypeId, ops::DerefMut};
@ -39,6 +39,8 @@ pub(crate) struct NotificationTracker {
notifications_sent: HashMap<TypeId, Vec<usize>>, notifications_sent: HashMap<TypeId, Vec<usize>>,
} }
impl Global for NotificationTracker {}
impl std::ops::Deref for NotificationTracker { impl std::ops::Deref for NotificationTracker {
type Target = HashMap<TypeId, Vec<usize>>; type Target = HashMap<TypeId, Vec<usize>>;

View File

@ -18,6 +18,7 @@ use client::{
Client, ErrorExt, Status, TypedEnvelope, UserStore, Client, ErrorExt, Status, TypedEnvelope, UserStore,
}; };
use collections::{hash_map, HashMap, HashSet}; use collections::{hash_map, HashMap, HashSet};
use derive_more::{Deref, DerefMut};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle}; use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
use futures::{ use futures::{
channel::{mpsc, oneshot}, channel::{mpsc, oneshot},
@ -28,7 +29,7 @@ use gpui::{
actions, canvas, div, impl_actions, point, px, size, Action, AnyElement, AnyModel, AnyView, actions, canvas, div, impl_actions, point, px, size, Action, AnyElement, AnyModel, AnyView,
AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div,
DragMoveEvent, Element, ElementContext, Entity, EntityId, EventEmitter, FocusHandle, 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, ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel,
Render, SharedString, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, Render, SharedString, Size, Styled, Subscription, Task, View, ViewContext, VisualContext,
WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
@ -59,6 +60,7 @@ use std::{
borrow::Cow, borrow::Cow,
cmp, env, cmp, env,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Weak,
sync::{atomic::AtomicUsize, Arc}, sync::{atomic::AtomicUsize, Arc},
time::Duration, time::Duration,
}; };
@ -256,8 +258,13 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
}); });
} }
type ProjectItemBuilders = #[derive(Clone, Default, Deref, DerefMut)]
HashMap<TypeId, fn(Model<Project>, AnyModel, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>>; struct ProjectItemBuilders(
HashMap<TypeId, fn(Model<Project>, AnyModel, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>>,
);
impl Global for ProjectItemBuilders {}
pub fn register_project_item<I: ProjectItem>(cx: &mut AppContext) { pub fn register_project_item<I: ProjectItem>(cx: &mut AppContext) {
let builders = cx.default_global::<ProjectItemBuilders>(); let builders = cx.default_global::<ProjectItemBuilders>();
builders.insert(TypeId::of::<I::Item>(), |project, model, cx| { builders.insert(TypeId::of::<I::Item>(), |project, model, cx| {
@ -273,13 +280,20 @@ type FollowableItemBuilder = fn(
&mut Option<proto::view::Variant>, &mut Option<proto::view::Variant>,
&mut WindowContext, &mut WindowContext,
) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>>; ) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>>;
type FollowableItemBuilders = HashMap<
TypeId, #[derive(Default, Deref, DerefMut)]
( struct FollowableItemBuilders(
FollowableItemBuilder, HashMap<
fn(&AnyView) -> Box<dyn FollowableItemHandle>, TypeId,
), (
>; FollowableItemBuilder,
fn(&AnyView) -> Box<dyn FollowableItemHandle>,
),
>,
);
impl Global for FollowableItemBuilders {}
pub fn register_followable_item<I: FollowableItem>(cx: &mut AppContext) { pub fn register_followable_item<I: FollowableItem>(cx: &mut AppContext) {
let builders = cx.default_global::<FollowableItemBuilders>(); let builders = cx.default_global::<FollowableItemBuilders>();
builders.insert( builders.insert(
@ -296,16 +310,22 @@ pub fn register_followable_item<I: FollowableItem>(cx: &mut AppContext) {
); );
} }
type ItemDeserializers = HashMap< #[derive(Default, Deref, DerefMut)]
Arc<str>, struct ItemDeserializers(
fn( HashMap<
Model<Project>, Arc<str>,
WeakView<Workspace>, fn(
WorkspaceId, Model<Project>,
ItemId, WeakView<Workspace>,
&mut ViewContext<Pane>, WorkspaceId,
) -> Task<Result<Box<dyn ItemHandle>>>, ItemId,
>; &mut ViewContext<Pane>,
) -> Task<Result<Box<dyn ItemHandle>>>,
>,
);
impl Global for ItemDeserializers {}
pub fn register_deserializable_item<I: Item>(cx: &mut AppContext) { pub fn register_deserializable_item<I: Item>(cx: &mut AppContext) {
if let Some(serialized_item_kind) = I::serialized_item_kind() { if let Some(serialized_item_kind) = I::serialized_item_kind() {
let deserializers = cx.default_global::<ItemDeserializers>(); let deserializers = cx.default_global::<ItemDeserializers>();
@ -331,6 +351,10 @@ pub struct AppState {
pub node_runtime: Arc<dyn NodeRuntime>, pub node_runtime: Arc<dyn NodeRuntime>,
} }
struct GlobalAppState(Weak<AppState>);
impl Global for GlobalAppState {}
pub struct WorkspaceStore { pub struct WorkspaceStore {
workspaces: HashSet<WindowHandle<Workspace>>, workspaces: HashSet<WindowHandle<Workspace>>,
followers: Vec<Follower>, followers: Vec<Follower>,
@ -345,6 +369,17 @@ struct Follower {
} }
impl AppState { impl AppState {
pub fn global(cx: &AppContext) -> Weak<Self> {
cx.global::<GlobalAppState>().0.clone()
}
pub fn try_global(cx: &AppContext) -> Option<Weak<Self>> {
cx.try_global::<GlobalAppState>()
.map(|state| state.0.clone())
}
pub fn set_global(state: Weak<AppState>, cx: &mut AppContext) {
cx.set_global(GlobalAppState(state));
}
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub fn test(cx: &mut AppContext) -> Arc<Self> { pub fn test(cx: &mut AppContext) -> Arc<Self> {
use node_runtime::FakeNodeRuntime; use node_runtime::FakeNodeRuntime;
@ -616,7 +651,7 @@ impl Workspace {
let modal_layer = cx.new_view(|_| ModalLayer::new()); let modal_layer = cx.new_view(|_| ModalLayer::new());
let mut active_call = None; let mut active_call = None;
if let Some(call) = cx.try_global::<Model<ActiveCall>>() { if let Some(call) = ActiveCall::try_global(cx) {
let call = call.clone(); let call = call.clone();
let mut subscriptions = Vec::new(); let mut subscriptions = Vec::new();
subscriptions.push(cx.subscribe(&call, Self::on_active_call_event)); subscriptions.push(cx.subscribe(&call, Self::on_active_call_event));
@ -3657,7 +3692,7 @@ impl WorkspaceStore {
update: proto::update_followers::Variant, update: proto::update_followers::Variant,
cx: &AppContext, cx: &AppContext,
) -> Option<()> { ) -> Option<()> {
let active_call = cx.try_global::<Model<ActiveCall>>()?; let active_call = ActiveCall::try_global(cx)?;
let room_id = active_call.read(cx).room()?.read(cx).id(); let room_id = active_call.read(cx).room()?.read(cx).id();
let follower_ids: Vec<_> = self let follower_ids: Vec<_> = self
.followers .followers

View File

@ -59,6 +59,7 @@ project_panel = { path = "../project_panel" }
project_symbols = { path = "../project_symbols" } project_symbols = { path = "../project_symbols" }
quick_action_bar = { path = "../quick_action_bar" } quick_action_bar = { path = "../quick_action_bar" }
recent_projects = { path = "../recent_projects" } recent_projects = { path = "../recent_projects" }
release_channel = { path = "../release_channel" }
rope = { path = "../rope"} rope = { path = "../rope"}
rpc = { path = "../rpc" } rpc = { path = "../rpc" }
settings = { path = "../settings" } settings = { path = "../settings" }

View File

@ -19,6 +19,7 @@ use log::LevelFilter;
use assets::Assets; use assets::Assets;
use node_runtime::RealNodeRuntime; use node_runtime::RealNodeRuntime;
use parking_lot::Mutex; use parking_lot::Mutex;
use release_channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::{ use settings::{
default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore, default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore,
@ -34,14 +35,13 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{ sync::{
atomic::{AtomicU32, Ordering}, atomic::{AtomicU32, Ordering},
Arc, Weak, Arc,
}, },
thread, thread,
}; };
use theme::{ActiveTheme, ThemeRegistry, ThemeSettings}; use theme::{ActiveTheme, ThemeRegistry, ThemeSettings};
use util::{ use util::{
async_maybe, async_maybe,
channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL},
http::{self, HttpClient, ZedHttpClient}, http::{self, HttpClient, ZedHttpClient},
paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR}, paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR},
ResultExt, ResultExt,
@ -102,8 +102,7 @@ fn main() {
let open_listener = listener.clone(); let open_listener = listener.clone();
app.on_open_urls(move |urls, _| open_listener.open_urls(&urls)); app.on_open_urls(move |urls, _| open_listener.open_urls(&urls));
app.on_reopen(move |cx| { app.on_reopen(move |cx| {
if let Some(app_state) = cx if let Some(app_state) = AppState::try_global(cx)
.try_global::<Weak<AppState>>()
.map(|app_state| app_state.upgrade()) .map(|app_state| app_state.upgrade())
.flatten() .flatten()
{ {
@ -115,12 +114,12 @@ fn main() {
}); });
app.run(move |cx| { app.run(move |cx| {
cx.set_global(*RELEASE_CHANNEL); ReleaseChannel::init(cx);
if let Some(build_sha) = option_env!("ZED_COMMIT_SHA") { 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); load_embedded_fonts(cx);
@ -148,7 +147,7 @@ fn main() {
let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); let 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); zed::init(cx);
theme::init(theme::LoadThemes::All(Box::new(Assets)), cx); theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
@ -242,7 +241,7 @@ fn main() {
workspace_store, workspace_store,
node_runtime, node_runtime,
}); });
cx.set_global(Arc::downgrade(&app_state)); AppState::set_global(Arc::downgrade(&app_state), cx);
audio::init(Assets, cx); audio::init(Assets, cx);
auto_update::init(http.clone(), cx); auto_update::init(http.clone(), cx);
@ -565,7 +564,7 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
.or_else(|| info.payload().downcast_ref::<String>().map(|s| s.clone())) .or_else(|| info.payload().downcast_ref::<String>().map(|s| s.clone()))
.unwrap_or_else(|| "Box<Any>".to_string()); .unwrap_or_else(|| "Box<Any>".to_string());
if *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev { if *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
let location = info.location().unwrap(); let location = info.location().unwrap();
let backtrace = Backtrace::new(); let backtrace = Backtrace::new();
eprintln!( eprintln!(

View File

@ -5,7 +5,7 @@ use std::{
time::Duration, time::Duration,
}; };
use util::channel::ReleaseChannel; use release_channel::ReleaseChannel;
const LOCALHOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); const LOCALHOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
const CONNECT_TIMEOUT: Duration = Duration::from_millis(10); 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); const SEND_TIMEOUT: Duration = Duration::from_millis(20);
fn address() -> SocketAddr { fn address() -> SocketAddr {
let port = match *util::channel::RELEASE_CHANNEL { let port = match *release_channel::RELEASE_CHANNEL {
ReleaseChannel::Dev => 43737, ReleaseChannel::Dev => 43737,
ReleaseChannel::Preview => 43738, ReleaseChannel::Preview => 43738,
ReleaseChannel::Stable => 43739, ReleaseChannel::Stable => 43739,
@ -24,7 +24,7 @@ fn address() -> SocketAddr {
} }
fn instance_handshake() -> &'static str { fn instance_handshake() -> &'static str {
match *util::channel::RELEASE_CHANNEL { match *release_channel::RELEASE_CHANNEL {
ReleaseChannel::Dev => "Zed Editor Dev Instance Running", ReleaseChannel::Dev => "Zed Editor Dev Instance Running",
ReleaseChannel::Nightly => "Zed Editor Nightly Instance Running", ReleaseChannel::Nightly => "Zed Editor Nightly Instance Running",
ReleaseChannel::Preview => "Zed Editor Preview Instance Running", ReleaseChannel::Preview => "Zed Editor Preview Instance Running",
@ -39,7 +39,7 @@ pub enum IsOnlyInstance {
} }
pub fn ensure_only_instance() -> 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; return IsOnlyInstance::Yes;
} }

View File

@ -6,8 +6,9 @@ use editor::Editor;
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
use futures::channel::{mpsc, oneshot}; use futures::channel::{mpsc, oneshot};
use futures::{FutureExt, SinkExt, StreamExt}; use futures::{FutureExt, SinkExt, StreamExt};
use gpui::AsyncAppContext; use gpui::{AppContext, AsyncAppContext, Global};
use language::{Bias, Point}; use language::{Bias, Point};
use release_channel::parse_zed_link;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::os::unix::prelude::OsStrExt; use std::os::unix::prelude::OsStrExt;
@ -17,7 +18,6 @@ use std::sync::Arc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use std::{path::PathBuf, sync::atomic::AtomicBool}; use std::{path::PathBuf, sync::atomic::AtomicBool};
use util::channel::parse_zed_link;
use util::paths::PathLikeWithPosition; use util::paths::PathLikeWithPosition;
use util::ResultExt; use util::ResultExt;
use workspace::AppState; use workspace::AppState;
@ -42,7 +42,19 @@ pub struct OpenListener {
pub triggered: AtomicBool, pub triggered: AtomicBool,
} }
struct GlobalOpenListener(Arc<OpenListener>);
impl Global for GlobalOpenListener {}
impl OpenListener { impl OpenListener {
pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<GlobalOpenListener>().0.clone()
}
pub fn set_global(listener: Arc<OpenListener>, cx: &mut AppContext) {
cx.set_global(GlobalOpenListener(listener))
}
pub fn new() -> (Self, UnboundedReceiver<OpenRequest>) { pub fn new() -> (Self, UnboundedReceiver<OpenRequest>) {
let (tx, rx) = mpsc::unbounded(); let (tx, rx) = mpsc::unbounded();
( (

View File

@ -20,6 +20,7 @@ use assets::Assets;
use futures::{channel::mpsc, select_biased, StreamExt}; use futures::{channel::mpsc, select_biased, StreamExt};
use project_panel::ProjectPanel; use project_panel::ProjectPanel;
use quick_action_bar::QuickActionBar; use quick_action_bar::QuickActionBar;
use release_channel::{AppCommitSha, ReleaseChannel};
use rope::Rope; use rope::Rope;
use search::project_search::ProjectSearchBar; use search::project_search::ProjectSearchBar;
use settings::{initial_local_settings_content, KeymapFile, Settings, SettingsStore}; 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 terminal_view::terminal_panel::{self, TerminalPanel};
use util::{ use util::{
asset_str, asset_str,
channel::{AppCommitSha, ReleaseChannel},
paths::{self, LOCAL_SETTINGS_RELATIVE_PATH}, paths::{self, LOCAL_SETTINGS_RELATIVE_PATH},
ResultExt, ResultExt,
}; };
@ -202,8 +202,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
cx.toggle_full_screen(); cx.toggle_full_screen();
}) })
.register_action(|_, action: &OpenZedUrl, cx| { .register_action(|_, action: &OpenZedUrl, cx| {
cx.global::<Arc<OpenListener>>() OpenListener::global(cx).open_urls(&[action.url.clone()])
.open_urls(&[action.url.clone()])
}) })
.register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url)) .register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
.register_action(move |_, _: &IncreaseBufferFontSize, cx| { .register_action(move |_, _: &IncreaseBufferFontSize, cx| {
@ -370,12 +369,12 @@ fn initialize_pane(workspace: &mut Workspace, pane: &View<Pane>, cx: &mut ViewCo
} }
fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext<Workspace>) { fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext<Workspace>) {
let app_name = cx.global::<ReleaseChannel>().display_name(); let app_name = ReleaseChannel::global(cx).display_name();
let version = env!("CARGO_PKG_VERSION"); let version = env!("CARGO_PKG_VERSION");
let message = format!("{app_name} {version}"); let message = format!("{app_name} {version}");
let detail = cx.try_global::<AppCommitSha>().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() cx.foreground_executor()
.spawn(async { .spawn(async {
prompt.await.ok(); prompt.await.ok();