diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 15b5babf28f6e5b337bbb61bbc602eb19afb78f4..101695508f0ce9f083730babf9ef3842e26542b7 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -3,7 +3,6 @@ use fs::Fs; use futures::{channel::mpsc, StreamExt}; use gpui::{App, BackgroundExecutor, ReadGlobal, UpdateGlobal}; use std::{path::PathBuf, sync::Arc, time::Duration}; -use util::ResultExt; pub const EMPTY_THEME_NAME: &str = "empty-theme"; @@ -73,9 +72,11 @@ pub fn handle_settings_file_changes( .block(user_settings_file_rx.next()) .unwrap(); SettingsStore::update_global(cx, |store, cx| { - store - .set_user_settings(&user_settings_content, cx) - .log_err(); + let result = store.set_user_settings(&user_settings_content, cx); + if let Err(err) = &result { + log::error!("Failed to load user settings: {err}"); + } + settings_changed(result.err(), cx); }); cx.spawn(move |cx| async move { while let Some(user_settings_content) = user_settings_file_rx.next().await { diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 72275f8b54c2b787d1cae1e7aae4e0aa125bd57c..e805527b91f358719395b44674eab6852d717c9d 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -3,17 +3,12 @@ use gpui::{ svg, AnyView, App, AppContext as _, AsyncWindowContext, ClipboardItem, Context, DismissEvent, Entity, EventEmitter, Global, PromptLevel, Render, ScrollHandle, Task, }; -use std::rc::Rc; +use parking_lot::Mutex; +use std::sync::{Arc, LazyLock}; use std::{any::TypeId, time::Duration}; use ui::{prelude::*, Tooltip}; use util::ResultExt; -pub fn init(cx: &mut App) { - cx.set_global(GlobalAppNotifications { - app_notifications: Vec::new(), - }) -} - #[derive(Debug, PartialEq, Clone)] pub enum NotificationId { Unique(TypeId), @@ -162,7 +157,7 @@ impl Workspace { pub fn show_initial_notifications(&mut self, cx: &mut Context) { // Allow absence of the global so that tests don't need to initialize it. let app_notifications = cx - .try_global::() + .try_global::() .iter() .flat_map(|global| global.app_notifications.iter().cloned()) .collect::>(); @@ -500,21 +495,27 @@ pub mod simple_message_notification { } } +static GLOBAL_APP_NOTIFICATIONS: LazyLock> = LazyLock::new(|| { + Mutex::new(AppNotifications { + app_notifications: Vec::new(), + }) +}); + /// Stores app notifications so that they can be shown in new workspaces. -struct GlobalAppNotifications { +struct AppNotifications { app_notifications: Vec<( NotificationId, - Rc) -> AnyView>, + Arc) -> AnyView + Send + Sync>, )>, } -impl Global for GlobalAppNotifications {} +impl Global for AppNotifications {} -impl GlobalAppNotifications { +impl AppNotifications { pub fn insert( &mut self, id: NotificationId, - build_notification: Rc) -> AnyView>, + build_notification: Arc) -> AnyView + Send + Sync>, ) { self.remove(&id); self.app_notifications.push((id, build_notification)) @@ -532,28 +533,30 @@ impl GlobalAppNotifications { pub fn show_app_notification( id: NotificationId, cx: &mut App, - build_notification: impl Fn(&mut Context) -> Entity + 'static, + build_notification: impl Fn(&mut Context) -> Entity + 'static + Send + Sync, ) { // Defer notification creation so that windows on the stack can be returned to GPUI cx.defer(move |cx| { // Handle dismiss events by removing the notification from all workspaces. - let build_notification: Rc) -> AnyView> = Rc::new({ - let id = id.clone(); - move |cx| { - let notification = build_notification(cx); - cx.subscribe(¬ification, { - let id = id.clone(); - move |_, _, _: &DismissEvent, cx| { - dismiss_app_notification(&id, cx); - } - }) - .detach(); - notification.into() - } - }); + let build_notification: Arc) -> AnyView + Send + Sync> = + Arc::new({ + let id = id.clone(); + move |cx| { + let notification = build_notification(cx); + cx.subscribe(¬ification, { + let id = id.clone(); + move |_, _, _: &DismissEvent, cx| { + dismiss_app_notification(&id, cx); + } + }) + .detach(); + notification.into() + } + }); // Store the notification so that new workspaces also receive it. - cx.global_mut::() + GLOBAL_APP_NOTIFICATIONS + .lock() .insert(id.clone(), build_notification.clone()); for window in cx.windows() { @@ -576,7 +579,7 @@ pub fn dismiss_app_notification(id: &NotificationId, cx: &mut App) { let id = id.clone(); // Defer notification dismissal so that windows on the stack can be returned to GPUI cx.defer(move |cx| { - cx.global_mut::().remove(&id); + GLOBAL_APP_NOTIFICATIONS.lock().remove(&id); for window in cx.windows() { if let Some(workspace_window) = window.downcast::() { let id = id.clone(); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b901140d9890127d27e98cddaacdaad7bd507355..bedadc41bdd4cefbb62755680cff736bb7592616 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -365,7 +365,6 @@ fn prompt_and_open_paths(app_state: Arc, options: PathPromptOptions, c pub fn init(app_state: Arc, cx: &mut App) { init_settings(cx); - notifications::init(cx); theme_preview::init(cx); cx.on_action(Workspace::close_global); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 93ad29b236fdb26eb87b6d75e4d66207f22e53fb..8fc4cef8de8d20fc9270c95d3b4d36d4dff455f3 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -18,7 +18,7 @@ use extension::ExtensionHostProxy; use fs::{Fs, RealFs}; use futures::{future, StreamExt}; use git::GitHostingProviderRegistry; -use gpui::{Action, App, AppContext as _, Application, AsyncApp, DismissEvent, UpdateGlobal as _}; +use gpui::{App, AppContext as _, Application, AsyncApp, UpdateGlobal as _}; use http_client::{read_proxy_from_env, Uri}; use language::LanguageRegistry; @@ -33,9 +33,7 @@ use project::project_settings::ProjectSettings; use recent_projects::{open_ssh_project, SshSettings}; use release_channel::{AppCommitSha, AppVersion, ReleaseChannel}; use session::{AppSession, Session}; -use settings::{ - handle_settings_file_changes, watch_config_file, InvalidSettingsError, Settings, SettingsStore, -}; +use settings::{handle_settings_file_changes, watch_config_file, Settings, SettingsStore}; use simplelog::ConfigBuilder; use std::{ env, @@ -50,18 +48,13 @@ use time::UtcOffset; use util::{maybe, ResultExt, TryFutureExt}; use uuid::Uuid; use welcome::{show_welcome_view, BaseKeymap, FIRST_OPEN}; -use workspace::{ - notifications::{simple_message_notification::MessageNotification, NotificationId}, - AppState, SerializedWorkspaceLocation, WorkspaceSettings, WorkspaceStore, -}; +use workspace::{AppState, SerializedWorkspaceLocation, WorkspaceSettings, WorkspaceStore}; use zed::{ app_menus, build_window_options, derive_paths_with_position, handle_cli_connection, - handle_keymap_file_changes, initialize_workspace, open_paths_with_positions, OpenListener, - OpenRequest, + handle_keymap_file_changes, handle_settings_changed, initialize_workspace, + inline_completion_registry, open_paths_with_positions, OpenListener, OpenRequest, }; -use crate::zed::inline_completion_registry; - #[cfg(unix)] use util::{load_login_shell_environment, load_shell_from_passwd}; @@ -614,44 +607,6 @@ fn main() { }); } -fn handle_settings_changed(error: Option, cx: &mut App) { - struct SettingsParseErrorNotification; - let id = NotificationId::unique::(); - - for workspace in workspace::local_workspace_windows(cx) { - workspace - .update(cx, |workspace, _, cx| { - match error.as_ref() { - Some(error) => { - if let Some(InvalidSettingsError::LocalSettings { .. }) = - error.downcast_ref::() - { - // Local settings will be displayed by the projects - } else { - workspace.show_notification(id.clone(), cx, |cx| { - cx.new(|_cx| { - MessageNotification::new(format!( - "Invalid user settings file\n{error}" - )) - .with_click_message("Open settings file") - .on_click(|window, cx| { - window.dispatch_action( - zed_actions::OpenSettings.boxed_clone(), - cx, - ); - cx.emit(DismissEvent); - }) - }) - }); - } - } - None => workspace.dismiss_notification(&id, cx), - } - }) - .log_err(); - } -} - fn handle_open_request(request: OpenRequest, app_state: Arc, cx: &mut App) { if let Some(connection) = request.cli_connection { let app_state = app_state.clone(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 408ca87a22ab450a785d19774084c7f32a26c0f3..422695aa4db6a9918d554b929f28231f001eb59f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -39,12 +39,12 @@ use release_channel::{AppCommitSha, ReleaseChannel}; use rope::Rope; use search::project_search::ProjectSearchBar; use settings::{ - initial_project_settings_content, initial_tasks_content, update_settings_file, KeymapFile, - KeymapFileLoadResult, Settings, SettingsStore, DEFAULT_KEYMAP_PATH, VIM_KEYMAP_PATH, + initial_project_settings_content, initial_tasks_content, update_settings_file, + InvalidSettingsError, KeymapFile, KeymapFileLoadResult, Settings, SettingsStore, + DEFAULT_KEYMAP_PATH, VIM_KEYMAP_PATH, }; use std::any::TypeId; use std::path::PathBuf; -use std::rc::Rc; use std::{borrow::Cow, ops::Deref, path::Path, sync::Arc}; use terminal_view::terminal_panel::{self, TerminalPanel}; use theme::{ActiveTheme, ThemeSettings}; @@ -1220,7 +1220,7 @@ fn show_keymap_file_load_error( }); cx.spawn(move |cx| async move { - let parsed_markdown = Rc::new(parsed_markdown.await); + let parsed_markdown = Arc::new(parsed_markdown.await); cx.update(|cx| { show_app_notification(notification_id, cx, move |cx| { let workspace_handle = cx.entity().downgrade(); @@ -1274,6 +1274,33 @@ pub fn load_default_keymap(cx: &mut App) { } } +pub fn handle_settings_changed(error: Option, cx: &mut App) { + struct SettingsParseErrorNotification; + let id = NotificationId::unique::(); + + match error { + Some(error) => { + if let Some(InvalidSettingsError::LocalSettings { .. }) = + error.downcast_ref::() + { + // Local settings errors are displayed by the projects + return; + } + show_app_notification(id, cx, move |cx| { + cx.new(|_cx| { + MessageNotification::new(format!("Invalid user settings file\n{error}")) + .with_click_message("Open settings file") + .on_click(|window, cx| { + window.dispatch_action(zed_actions::OpenSettings.boxed_clone(), cx); + cx.emit(DismissEvent); + }) + }) + }); + } + None => dismiss_app_notification(&id, cx), + } +} + pub fn open_new_ssh_project_from_project( workspace: &mut Workspace, paths: Vec,