diff --git a/crates/gpui/src/global.rs b/crates/gpui/src/global.rs index 3197967644..05f1598364 100644 --- a/crates/gpui/src/global.rs +++ b/crates/gpui/src/global.rs @@ -49,6 +49,11 @@ pub trait UpdateGlobal { where C: BorrowAppContext, F: FnOnce(&mut Self, &mut C) -> R; + + /// Set the global instance of the implementing type. + fn set_global(cx: &mut C, global: Self) + where + C: BorrowAppContext; } impl UpdateGlobal for T { @@ -59,4 +64,11 @@ impl UpdateGlobal for T { { cx.update_global(update) } + + fn set_global(cx: &mut C, global: Self) + where + C: BorrowAppContext, + { + cx.set_global(global) + } } diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 5bbbc8f18a..78f16c82e1 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -34,6 +34,142 @@ macro_rules! debug_panic { }; } +#[macro_export] +macro_rules! with_clone { + ($i:ident, move ||$l:expr) => {{ + let $i = $i.clone(); + move || { + $l + } + }}; + ($i:ident, move |$($k:pat_param),*|$l:expr) => {{ + let $i = $i.clone(); + move |$( $k ),*| { + $l + } + }}; + + (($($i:ident),+), move ||$l:expr) => {{ + let ($($i),+) = ($($i.clone()),+); + move || { + $l + } + }}; + (($($i:ident),+), move |$($k:pat_param),*|$l:expr) => {{ + let ($($i),+) = ($($i.clone()),+); + move |$( $k ),*| { + $l + } + }}; +} + +mod test_with_clone { + + // If this test compiles, it works + #[test] + fn test() { + let x = "String".to_string(); + let y = std::sync::Arc::new(5); + + fn no_arg(f: impl FnOnce()) { + f() + } + + no_arg(with_clone!(x, move || { + drop(x); + })); + + no_arg(with_clone!((x, y), move || { + drop(x); + drop(y); + })); + + fn one_arg(f: impl FnOnce(usize)) { + f(1) + } + + one_arg(with_clone!(x, move |_| { + drop(x); + })); + one_arg(with_clone!((x, y), move |b| { + drop(x); + drop(y); + println!("{}", b); + })); + + fn two_arg(f: impl FnOnce(usize, bool)) { + f(5, true) + } + + two_arg(with_clone!((x, y), move |a, b| { + drop(x); + drop(y); + println!("{}{}", a, b) + })); + two_arg(with_clone!((x, y), move |a, _| { + drop(x); + drop(y); + println!("{}", a) + })); + two_arg(with_clone!((x, y), move |_, b| { + drop(x); + drop(y); + println!("{}", b) + })); + + struct Example { + z: usize, + } + + fn destructuring_example(f: impl FnOnce(Example)) { + f(Example { z: 10 }) + } + + destructuring_example(with_clone!(x, move |Example { z }| { + drop(x); + println!("{}", z); + })); + + let a_long_variable_1 = "".to_string(); + let a_long_variable_2 = "".to_string(); + let a_long_variable_3 = "".to_string(); + let a_long_variable_4 = "".to_string(); + two_arg(with_clone!( + ( + x, + y, + a_long_variable_1, + a_long_variable_2, + a_long_variable_3, + a_long_variable_4 + ), + move |a, b| { + drop(x); + drop(y); + drop(a_long_variable_1); + drop(a_long_variable_2); + drop(a_long_variable_3); + drop(a_long_variable_4); + println!("{}{}", a, b) + } + )); + + fn single_expression_body(f: impl FnOnce(usize) -> usize) -> usize { + f(20) + } + + let _result = single_expression_body(with_clone!(y, move |z| *y + z)); + + // Explicitly move all variables + drop(x); + drop(y); + drop(a_long_variable_1); + drop(a_long_variable_2); + drop(a_long_variable_3); + drop(a_long_variable_4); + } +} + pub fn truncate(s: &str, max_chars: usize) -> &str { match s.char_indices().nth(max_chars) { None => s, diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 4c649d927e..ae9e0bfc6b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -17,7 +17,9 @@ use env_logger::Builder; use fs::RealFs; use futures::{future, StreamExt}; use git::GitHostingProviderRegistry; -use gpui::{App, AppContext, AsyncAppContext, Context, Global, Task, VisualContext}; +use gpui::{ + App, AppContext, AsyncAppContext, Context, Global, Task, UpdateGlobal as _, VisualContext, +}; use image_viewer; use language::LanguageRegistry; use log::LevelFilter; @@ -38,11 +40,7 @@ use std::{ sync::Arc, }; use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings}; -use util::{ - maybe, parse_env_output, - paths::{self}, - ResultExt, TryFutureExt, -}; +use util::{maybe, parse_env_output, paths, with_clone, ResultExt, TryFutureExt}; use uuid::Uuid; use welcome::{show_welcome_view, BaseKeymap, FIRST_OPEN}; use workspace::{AppState, WorkspaceSettings, WorkspaceStore}; @@ -260,13 +258,11 @@ fn main() { let session_id = Uuid::new_v4().to_string(); reliability::init_panic_hook(&app, installation_id.clone(), session_id.clone()); - let (listener, mut open_rx) = OpenListener::new(); - let listener = Arc::new(listener); - let open_listener = listener.clone(); + let (open_listener, mut open_rx) = OpenListener::new(); #[cfg(target_os = "linux")] { - if crate::zed::listen_for_cli_connections(listener.clone()).is_err() { + if crate::zed::listen_for_cli_connections(open_listener.clone()).is_err() { println!("zed is already running"); return; } @@ -317,7 +313,7 @@ fn main() { }) }; - app.on_open_urls(move |urls| open_listener.open_urls(urls)); + app.on_open_urls(with_clone!(open_listener, move |urls| open_listener.open_urls(urls))); app.on_reopen(move |cx| { if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade()) { @@ -338,7 +334,7 @@ fn main() { GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx); git_hosting_providers::init(cx); - OpenListener::set_global(listener.clone(), cx); + OpenListener::set_global(cx, open_listener.clone()); settings::init(cx); handle_settings_file_changes(user_settings_file_rx, cx); @@ -396,7 +392,7 @@ fn main() { .collect(); if !urls.is_empty() { - listener.open_urls(urls) + open_listener.open_urls(urls) } match open_rx diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1d57263671..1e0e6bb1a3 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -11,7 +11,7 @@ use collections::VecDeque; use editor::{scroll::Autoscroll, Editor, MultiBuffer}; use gpui::{ actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, MenuItem, PromptLevel, - TitlebarOptions, View, ViewContext, VisualContext, WindowKind, WindowOptions, + ReadGlobal, TitlebarOptions, View, ViewContext, VisualContext, WindowKind, WindowOptions, }; pub use open_listener::*; diff --git a/crates/zed/src/zed/open_listener.rs b/crates/zed/src/zed/open_listener.rs index eda7b6f2ec..6cce610d50 100644 --- a/crates/zed/src/zed/open_listener.rs +++ b/crates/zed/src/zed/open_listener.rs @@ -90,30 +90,19 @@ impl OpenRequest { } } -pub struct OpenListener { - tx: UnboundedSender>, -} +#[derive(Clone)] +pub struct OpenListener(UnboundedSender>); -struct GlobalOpenListener(Arc); - -impl Global for GlobalOpenListener {} +impl Global for OpenListener {} impl OpenListener { - pub fn global(cx: &AppContext) -> Arc { - cx.global::().0.clone() - } - - pub fn set_global(listener: Arc, cx: &mut AppContext) { - cx.set_global(GlobalOpenListener(listener)) - } - pub fn new() -> (Self, UnboundedReceiver>) { let (tx, rx) = mpsc::unbounded(); - (OpenListener { tx }, rx) + (OpenListener(tx), rx) } pub fn open_urls(&self, urls: Vec) { - self.tx + self.0 .unbounded_send(urls) .map_err(|_| anyhow!("no listener for open requests")) .log_err(); @@ -121,7 +110,7 @@ impl OpenListener { } #[cfg(target_os = "linux")] -pub fn listen_for_cli_connections(opener: Arc) -> Result<()> { +pub fn listen_for_cli_connections(opener: OpenListener) -> Result<()> { use release_channel::RELEASE_CHANNEL_NAME; use std::os::{linux::net::SocketAddrExt, unix::net::SocketAddr, unix::net::UnixDatagram};