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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ use gpui::{
SemanticVersion, StatefulInteractiveElement, Styled, ViewContext,
};
use menu::Cancel;
use util::channel::ReleaseChannel;
use release_channel::ReleaseChannel;
use workspace::ui::{h_flex, v_flex, Icon, IconName, Label, StyledExt};
pub struct UpdateNotification {
@ -14,7 +14,7 @@ impl EventEmitter<DismissEvent> for UpdateNotification {}
impl Render for UpdateNotification {
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()
.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 futures::{channel::oneshot, future::Shared, Future, FutureExt};
use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task,
WeakModel,
AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Subscription,
Task, WeakModel,
};
use postage::watch;
use project::Project;
@ -21,11 +21,15 @@ use std::sync::Arc;
pub use participant::ParticipantLocation;
pub use room::Room;
struct GlobalActiveCall(Model<ActiveCall>);
impl Global for GlobalActiveCall {}
pub fn init(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut AppContext) {
CallSettings::register(cx);
let active_call = cx.new_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
cx.set_global(GlobalActiveCall(active_call));
}
pub struct OneAtATime {
@ -154,7 +158,12 @@ impl ActiveCall {
}
pub fn global(cx: &AppContext) -> Model<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(

View File

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

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@ use chrono::{DateTime, Utc};
use futures::Future;
use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task};
use parking_lot::Mutex;
use release_channel::ReleaseChannel;
use serde::Serialize;
use settings::{Settings, SettingsStore};
use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration};
@ -15,7 +16,7 @@ use tempfile::NamedTempFile;
use util::http::{HttpClient, ZedHttpClient};
#[cfg(not(debug_assertions))]
use util::ResultExt;
use util::{channel::ReleaseChannel, TryFutureExt};
use util::TryFutureExt;
use self::event_coalescer::EventCoalescer;
@ -143,9 +144,8 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5);
impl Telemetry {
pub fn new(client: Arc<ZedHttpClient>, cx: &mut AppContext) -> Arc<Self> {
let release_channel = cx
.try_global::<ReleaseChannel>()
.map(|release_channel| release_channel.display_name());
let release_channel =
ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name());
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 use rustc_hash::{FxHashMap, FxHashSet};
use std::any::TypeId;
pub use std::collections::*;
// NEW TYPES
#[derive(Default)]
pub struct CommandPaletteFilter {
pub hidden_namespaces: HashSet<&'static str>,
pub hidden_action_types: HashSet<TypeId>,
}

View File

@ -12,11 +12,14 @@ doctest = false
[dependencies]
client = { path = "../client" }
collections = { path = "../collections" }
# HACK: We're only depending on `copilot` here for `CommandPaletteFilter`. See the attached comment on that type.
copilot = { path = "../copilot" }
editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
picker = { path = "../picker" }
project = { path = "../project" }
release_channel = { path = "../release_channel" }
settings = { path = "../settings" }
theme = { path = "../theme" }
ui = { path = "../ui" }

View File

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

View File

@ -5,7 +5,7 @@ use async_tar::Archive;
use collections::{HashMap, HashSet};
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model,
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
ModelContext, Task, WeakModel,
};
use language::{
@ -32,6 +32,17 @@ use util::{
ResultExt,
};
// HACK: This type is only defined in `copilot` since it is the earliest ancestor
// of the crates that use it.
//
// This is not great. Let's find a better place for it to live.
#[derive(Default)]
pub struct CommandPaletteFilter {
pub hidden_namespaces: HashSet<&'static str>,
pub hidden_action_types: HashSet<TypeId>,
}
impl Global for CommandPaletteFilter {}
actions!(
copilot,
[
@ -54,7 +65,7 @@ pub fn init(
let node_runtime = node_runtime.clone();
move |cx| Copilot::start(new_server_id, http, node_runtime, cx)
});
cx.set_global(copilot.clone());
Copilot::set_global(copilot.clone(), cx);
cx.observe(&copilot, |handle, cx| {
let copilot_action_types = [
TypeId::of::<Suggest>(),
@ -65,7 +76,7 @@ pub fn init(
let copilot_auth_action_types = [TypeId::of::<SignOut>()];
let copilot_no_auth_action_types = [TypeId::of::<SignIn>()];
let status = handle.read(cx).status();
let filter = cx.default_global::<collections::CommandPaletteFilter>();
let filter = cx.default_global::<CommandPaletteFilter>();
match status {
Status::Disabled => {
@ -307,9 +318,18 @@ pub enum Event {
impl EventEmitter<Event> for Copilot {}
struct GlobalCopilot(Model<Copilot>);
impl Global for GlobalCopilot {}
impl Copilot {
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(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -258,14 +258,14 @@ pub trait EventEmitter<E: Any>: 'static {}
/// can be used interchangeably.
pub trait BorrowAppContext {
/// 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
where
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)
}
}
@ -287,3 +287,8 @@ impl<T> Flatten<T> for Result<T> {
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::{
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures,
FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size,
SizeRefinement, Styled, TextRun,
FontStyle, FontWeight, Global, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SharedString, Size, SizeRefinement, Styled, TextRun,
};
use collections::HashSet;
use refineable::Refineable;
@ -20,6 +20,8 @@ pub use taffy::style::{
/// GPUI.
pub struct DebugBelow;
impl Global for DebugBelow {}
/// The CSS styling that can be applied to an element via the `Styled` trait
#[derive(Clone, Refineable, Debug)]
#[refineable(Debug)]

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,8 @@ use crate::one_themes::one_dark;
use crate::{SyntaxTheme, Theme, ThemeRegistry, ThemeStyleContent};
use anyhow::Result;
use gpui::{
px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels, Subscription, ViewContext,
px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Global, Pixels, Subscription,
ViewContext,
};
use refineable::Refineable;
use schemars::{
@ -34,6 +35,8 @@ pub struct ThemeSettings {
#[derive(Default)]
pub(crate) struct AdjustedBufferFontSize(Pixels);
impl Global for AdjustedBufferFontSize {}
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct ThemeSettingsContent {
#[serde(default)]

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::All(assets) => (assets, true),
};
registry::init(assets, cx);
ThemeRegistry::set_global(assets, cx);
if load_user_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 {}
pub struct ThemeSelector {

View File

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

View File

@ -28,6 +28,8 @@ regex.workspace = true
collections = { path = "../collections" }
command_palette = { path = "../command_palette" }
# HACK: We're only depending on `copilot` here for `CommandPaletteFilter`. See the attached comment on that type.
copilot = { path = "../copilot" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
language = { path = "../language" }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@ use assets::Assets;
use futures::{channel::mpsc, select_biased, StreamExt};
use project_panel::ProjectPanel;
use quick_action_bar::QuickActionBar;
use release_channel::{AppCommitSha, ReleaseChannel};
use rope::Rope;
use search::project_search::ProjectSearchBar;
use settings::{initial_local_settings_content, KeymapFile, Settings, SettingsStore};
@ -27,7 +28,6 @@ use std::{borrow::Cow, ops::Deref, path::Path, sync::Arc};
use terminal_view::terminal_panel::{self, TerminalPanel};
use util::{
asset_str,
channel::{AppCommitSha, ReleaseChannel},
paths::{self, LOCAL_SETTINGS_RELATIVE_PATH},
ResultExt,
};
@ -202,8 +202,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
cx.toggle_full_screen();
})
.register_action(|_, action: &OpenZedUrl, cx| {
cx.global::<Arc<OpenListener>>()
.open_urls(&[action.url.clone()])
OpenListener::global(cx).open_urls(&[action.url.clone()])
})
.register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
.register_action(move |_, _: &IncreaseBufferFontSize, cx| {
@ -370,12 +369,12 @@ fn initialize_pane(workspace: &mut Workspace, pane: &View<Pane>, cx: &mut ViewCo
}
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 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()
.spawn(async {
prompt.await.ok();