From 301163bab70b289ca9dc5865179bd69bdb1a3373 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 9 Apr 2021 13:03:26 -0600 Subject: [PATCH 1/9] Add lifecycle methods to Platform trait Co-Authored-By: Max Brunsfeld --- gpui/src/app.rs | 12 +- gpui/src/platform/mac/app.rs | 100 -------- gpui/src/platform/mac/mod.rs | 11 +- gpui/src/platform/mac/platform.rs | 387 ++++++++++++++++++++++++++++++ gpui/src/platform/mod.rs | 13 +- gpui/src/platform/test.rs | 31 ++- 6 files changed, 433 insertions(+), 121 deletions(-) delete mode 100644 gpui/src/platform/mac/app.rs create mode 100644 gpui/src/platform/mac/platform.rs diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 63f52f77b1..14380f19e1 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -2,7 +2,7 @@ use crate::{ elements::ElementBox, executor, keymap::{self, Keystroke}, - platform::{self, App as _, WindowOptions}, + platform::{self, Platform as _, WindowOptions}, presenter::Presenter, util::post_inc, AssetCache, AssetSource, FontCache, TextLayoutCache, @@ -88,7 +88,7 @@ impl App { asset_source: A, f: G, ) -> T { - let platform = platform::test::app(); + let platform = platform::test::platform(); let foreground = Rc::new(executor::Foreground::test()); let app = Self(Rc::new(RefCell::new(MutableAppContext::new( foreground.clone(), @@ -269,7 +269,7 @@ impl App { self.0.borrow().font_cache.clone() } - pub fn platform(&self) -> Arc { + pub fn platform(&self) -> Arc { self.0.borrow().platform.clone() } } @@ -309,7 +309,7 @@ type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext); pub struct MutableAppContext { weak_self: Option>>, - platform: Arc, + platform: Arc, font_cache: Arc, assets: Arc, ctx: AppContext, @@ -337,7 +337,7 @@ pub struct MutableAppContext { impl MutableAppContext { pub fn new( foreground: Rc, - platform: Arc, + platform: Arc, asset_source: impl AssetSource, ) -> Self { let fonts = platform.fonts(); @@ -381,7 +381,7 @@ impl MutableAppContext { &self.ctx } - pub fn platform(&self) -> Arc { + pub fn platform(&self) -> Arc { self.platform.clone() } diff --git a/gpui/src/platform/mac/app.rs b/gpui/src/platform/mac/app.rs deleted file mode 100644 index 1965c634a3..0000000000 --- a/gpui/src/platform/mac/app.rs +++ /dev/null @@ -1,100 +0,0 @@ -use super::{BoolExt as _, Dispatcher, FontSystem, Window}; -use crate::{executor, platform}; -use anyhow::Result; -use cocoa::{ - appkit::{NSApplication, NSModalResponse, NSOpenPanel, NSPasteboard, NSPasteboardTypeString}, - base::nil, - foundation::{NSArray, NSData, NSString, NSURL}, -}; -use objc::{msg_send, sel, sel_impl}; -use std::{ffi::c_void, path::PathBuf, rc::Rc, sync::Arc}; - -pub struct App { - dispatcher: Arc, - fonts: Arc, -} - -impl App { - pub fn new() -> Self { - Self { - dispatcher: Arc::new(Dispatcher), - fonts: Arc::new(FontSystem::new()), - } - } -} - -impl platform::App for App { - fn dispatcher(&self) -> Arc { - self.dispatcher.clone() - } - - fn activate(&self, ignoring_other_apps: bool) { - unsafe { - let app = NSApplication::sharedApplication(nil); - app.activateIgnoringOtherApps_(ignoring_other_apps.to_objc()); - } - } - - fn open_window( - &self, - options: platform::WindowOptions, - executor: Rc, - ) -> Result> { - Ok(Box::new(Window::open(options, executor, self.fonts())?)) - } - - fn prompt_for_paths( - &self, - options: platform::PathPromptOptions, - ) -> Option> { - unsafe { - let panel = NSOpenPanel::openPanel(nil); - panel.setCanChooseDirectories_(options.directories.to_objc()); - panel.setCanChooseFiles_(options.files.to_objc()); - panel.setAllowsMultipleSelection_(options.multiple.to_objc()); - panel.setResolvesAliases_(false.to_objc()); - let response = panel.runModal(); - if response == NSModalResponse::NSModalResponseOk { - let mut result = Vec::new(); - let urls = panel.URLs(); - for i in 0..urls.count() { - let url = urls.objectAtIndex(i); - let string = url.absoluteString(); - let string = std::ffi::CStr::from_ptr(string.UTF8String()) - .to_string_lossy() - .to_string(); - if let Some(path) = string.strip_prefix("file://") { - result.push(PathBuf::from(path)); - } - } - Some(result) - } else { - None - } - } - } - - fn fonts(&self) -> Arc { - self.fonts.clone() - } - - fn quit(&self) { - unsafe { - let app = NSApplication::sharedApplication(nil); - let _: () = msg_send![app, terminate: nil]; - } - } - - fn copy(&self, text: &str) { - unsafe { - let data = NSData::dataWithBytes_length_( - nil, - text.as_ptr() as *const c_void, - text.len() as u64, - ); - let pasteboard = NSPasteboard::generalPasteboard(nil); - pasteboard.clearContents(); - pasteboard.setData_forType(data, NSPasteboardTypeString); - } - } -} diff --git a/gpui/src/platform/mac/mod.rs b/gpui/src/platform/mac/mod.rs index b7a19c3647..b54d148682 100644 --- a/gpui/src/platform/mac/mod.rs +++ b/gpui/src/platform/mac/mod.rs @@ -1,27 +1,26 @@ -mod app; mod atlas; mod dispatcher; mod event; mod fonts; mod geometry; +mod platform; mod renderer; mod runner; mod sprite_cache; mod window; -use crate::platform; -pub use app::App; use cocoa::base::{BOOL, NO, YES}; pub use dispatcher::Dispatcher; pub use fonts::FontSystem; +use platform::MacPlatform; pub use runner::Runner; use window::Window; -pub fn app() -> impl platform::App { - App::new() +pub fn app() -> impl super::Platform { + MacPlatform::new() } -pub fn runner() -> impl platform::Runner { +pub fn runner() -> impl super::Runner { Runner::new() } diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs new file mode 100644 index 0000000000..d089ba26d2 --- /dev/null +++ b/gpui/src/platform/mac/platform.rs @@ -0,0 +1,387 @@ +use super::{BoolExt as _, Dispatcher, FontSystem, Window}; +use crate::{executor, keymap::Keystroke, platform, Event, Menu, MenuItem}; +use anyhow::Result; +use cocoa::{ + appkit::{ + NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, + NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard, + NSPasteboardTypeString, NSWindow, + }, + base::{id, nil, selector}, + foundation::{NSArray, NSAutoreleasePool, NSData, NSInteger, NSString, NSURL}, +}; +use ctor::ctor; +use objc::{ + class, + declare::ClassDecl, + msg_send, + runtime::{Class, Object, Sel}, + sel, sel_impl, +}; +use ptr::null_mut; +use std::{ + cell::RefCell, + ffi::{c_void, CStr}, + os::raw::c_char, + path::PathBuf, + ptr, + rc::Rc, + sync::Arc, +}; + +const MAC_PLATFORM_IVAR: &'static str = "runner"; +static mut APP_CLASS: *const Class = ptr::null(); +static mut APP_DELEGATE_CLASS: *const Class = ptr::null(); + +#[ctor] +unsafe fn build_classes() { + APP_CLASS = { + let mut decl = ClassDecl::new("GPUIApplication", class!(NSApplication)).unwrap(); + decl.add_ivar::<*mut c_void>(MAC_PLATFORM_IVAR); + decl.add_method( + sel!(sendEvent:), + send_event as extern "C" fn(&mut Object, Sel, id), + ); + decl.register() + }; + + APP_DELEGATE_CLASS = { + let mut decl = ClassDecl::new("GPUIApplicationDelegate", class!(NSResponder)).unwrap(); + decl.add_ivar::<*mut c_void>(MAC_PLATFORM_IVAR); + decl.add_method( + sel!(applicationDidFinishLaunching:), + did_finish_launching as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(applicationDidBecomeActive:), + did_become_active as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(applicationDidResignActive:), + did_resign_active as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(handleGPUIMenuItem:), + handle_menu_item as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(application:openFiles:), + open_files as extern "C" fn(&mut Object, Sel, id, id), + ); + decl.register() + } +} + +pub struct MacPlatform { + dispatcher: Arc, + fonts: Arc, + callbacks: RefCell, + menu_item_actions: RefCell>, +} + +#[derive(Default)] +struct Callbacks { + become_active: Option>, + resign_active: Option>, + event: Option bool>>, + menu_command: Option>, + open_files: Option)>>, + finish_launching: Option ()>>, +} + +impl MacPlatform { + pub fn new() -> Self { + Self { + dispatcher: Arc::new(Dispatcher), + fonts: Arc::new(FontSystem::new()), + callbacks: Default::default(), + menu_item_actions: Default::default(), + } + } +} + +impl platform::Platform for MacPlatform { + fn on_become_active(&self, callback: Box) { + self.callbacks.borrow_mut().become_active = Some(callback); + } + + fn on_resign_active(&self, callback: Box) { + self.callbacks.borrow_mut().resign_active = Some(callback); + } + + fn on_event(&self, callback: Box bool>) { + self.callbacks.borrow_mut().event = Some(callback); + } + + fn on_menu_command(&self, callback: Box) { + self.callbacks.borrow_mut().menu_command = Some(callback); + } + + fn on_open_files(&self, callback: Box)>) { + self.callbacks.borrow_mut().open_files = Some(callback); + } + + fn run(&self, on_finish_launching: Box ()>) { + self.callbacks.borrow_mut().finish_launching = Some(on_finish_launching); + + unsafe { + let pool = NSAutoreleasePool::new(nil); + let app: id = msg_send![APP_CLASS, sharedApplication]; + let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; + + let self_ptr = self as *const Self as *mut c_void; + (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr); + (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr); + app.setDelegate_(app_delegate); + app.run(); + pool.drain(); + (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); + (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); + } + } + + fn dispatcher(&self) -> Arc { + self.dispatcher.clone() + } + + fn activate(&self, ignoring_other_apps: bool) { + unsafe { + let app = NSApplication::sharedApplication(nil); + app.activateIgnoringOtherApps_(ignoring_other_apps.to_objc()); + } + } + + fn open_window( + &self, + options: platform::WindowOptions, + executor: Rc, + ) -> Result> { + Ok(Box::new(Window::open(options, executor, self.fonts())?)) + } + + fn prompt_for_paths( + &self, + options: platform::PathPromptOptions, + ) -> Option> { + unsafe { + let panel = NSOpenPanel::openPanel(nil); + panel.setCanChooseDirectories_(options.directories.to_objc()); + panel.setCanChooseFiles_(options.files.to_objc()); + panel.setAllowsMultipleSelection_(options.multiple.to_objc()); + panel.setResolvesAliases_(false.to_objc()); + let response = panel.runModal(); + if response == NSModalResponse::NSModalResponseOk { + let mut result = Vec::new(); + let urls = panel.URLs(); + for i in 0..urls.count() { + let url = urls.objectAtIndex(i); + let string = url.absoluteString(); + let string = std::ffi::CStr::from_ptr(string.UTF8String()) + .to_string_lossy() + .to_string(); + if let Some(path) = string.strip_prefix("file://") { + result.push(PathBuf::from(path)); + } + } + Some(result) + } else { + None + } + } + } + + fn fonts(&self) -> Arc { + self.fonts.clone() + } + + fn quit(&self) { + unsafe { + let app = NSApplication::sharedApplication(nil); + let _: () = msg_send![app, terminate: nil]; + } + } + + fn copy(&self, text: &str) { + unsafe { + let data = NSData::dataWithBytes_length_( + nil, + text.as_ptr() as *const c_void, + text.len() as u64, + ); + let pasteboard = NSPasteboard::generalPasteboard(nil); + pasteboard.clearContents(); + pasteboard.setData_forType(data, NSPasteboardTypeString); + } + } + + fn set_menus(&self, menus: &[Menu]) { + unsafe { + let app: id = msg_send![APP_CLASS, sharedApplication]; + app.setMainMenu_(self.create_menu_bar(menus)); + } + } +} + +impl MacPlatform { + unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id { + let menu_bar = NSMenu::new(nil).autorelease(); + let mut menu_item_actions = self.menu_item_actions.borrow_mut(); + menu_item_actions.clear(); + + for menu_config in menus { + let menu_bar_item = NSMenuItem::new(nil).autorelease(); + let menu = NSMenu::new(nil).autorelease(); + + menu.setTitle_(ns_string(menu_config.name)); + + for item_config in menu_config.items { + let item; + + match item_config { + MenuItem::Separator => { + item = NSMenuItem::separatorItem(nil); + } + MenuItem::Action { + name, + keystroke, + action, + } => { + if let Some(keystroke) = keystroke { + let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| { + panic!( + "Invalid keystroke for menu item {}:{} - {:?}", + menu_config.name, name, err + ) + }); + + let mut mask = NSEventModifierFlags::empty(); + for (modifier, flag) in &[ + (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask), + (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask), + (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask), + ] { + if *modifier { + mask |= *flag; + } + } + + item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string(name), + selector("handleGPUIMenuItem:"), + ns_string(&keystroke.key), + ) + .autorelease(); + item.setKeyEquivalentModifierMask_(mask); + } else { + item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string(name), + selector("handleGPUIMenuItem:"), + ns_string(""), + ) + .autorelease(); + } + + let tag = menu_item_actions.len() as NSInteger; + let _: () = msg_send![item, setTag: tag]; + menu_item_actions.push(action.to_string()); + } + } + + menu.addItem_(item); + } + + menu_bar_item.setSubmenu_(menu); + menu_bar.addItem_(menu_bar_item); + } + + menu_bar + } +} + +unsafe fn get_platform(object: &mut Object) -> &MacPlatform { + let platform_ptr: *mut c_void = *object.get_ivar(MAC_PLATFORM_IVAR); + assert!(!platform_ptr.is_null()); + &*(platform_ptr as *const MacPlatform) +} + +extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) { + unsafe { + if let Some(event) = Event::from_native(native_event, None) { + let platform = get_platform(this); + if let Some(callback) = platform.callbacks.borrow_mut().event.as_mut() { + if callback(event) { + return; + } + } + } + + msg_send![super(this, class!(NSApplication)), sendEvent: native_event] + } +} + +extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) { + unsafe { + let app: id = msg_send![APP_CLASS, sharedApplication]; + app.setActivationPolicy_(NSApplicationActivationPolicyRegular); + + let platform = get_platform(this); + if let Some(callback) = platform.callbacks.borrow_mut().finish_launching.take() { + callback(); + } + } +} + +extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) { + let platform = unsafe { get_platform(this) }; + if let Some(callback) = platform.callbacks.borrow_mut().become_active.as_mut() { + callback(); + } +} + +extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) { + let platform = unsafe { get_platform(this) }; + if let Some(callback) = platform.callbacks.borrow_mut().resign_active.as_mut() { + callback(); + } +} + +extern "C" fn open_files(this: &mut Object, _: Sel, _: id, paths: id) { + let paths = unsafe { + (0..paths.count()) + .into_iter() + .filter_map(|i| { + let path = paths.objectAtIndex(i); + match CStr::from_ptr(path.UTF8String() as *mut c_char).to_str() { + Ok(string) => Some(PathBuf::from(string)), + Err(err) => { + log::error!("error converting path to string: {}", err); + None + } + } + }) + .collect::>() + }; + let platform = unsafe { get_platform(this) }; + if let Some(callback) = platform.callbacks.borrow_mut().open_files.as_mut() { + callback(paths); + } +} + +extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) { + unsafe { + let platform = get_platform(this); + if let Some(callback) = platform.callbacks.borrow_mut().menu_command.as_mut() { + let tag: NSInteger = msg_send![item, tag]; + let index = tag as usize; + if let Some(action) = platform.menu_item_actions.borrow().get(index) { + callback(&action); + } + } + } +} + +unsafe fn ns_string(string: &str) -> id { + NSString::alloc(nil).init_str(string).autorelease() +} diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index ae0f3cddb9..b96bffb979 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -33,8 +33,17 @@ pub trait Runner { fn run(self); } -pub trait App { +pub trait Platform { + fn on_menu_command(&self, callback: Box); + fn on_become_active(&self, callback: Box); + fn on_resign_active(&self, callback: Box); + fn on_event(&self, callback: Box bool>); + fn on_open_files(&self, callback: Box)>); + fn run(&self, on_finish_launching: Box ()>); + fn dispatcher(&self) -> Arc; + fn fonts(&self) -> Arc; + fn activate(&self, ignoring_other_apps: bool); fn open_window( &self, @@ -42,9 +51,9 @@ pub trait App { executor: Rc, ) -> Result>; fn prompt_for_paths(&self, options: PathPromptOptions) -> Option>; - fn fonts(&self) -> Arc; fn quit(&self); fn copy(&self, text: &str); + fn set_menus(&self, menus: &[Menu]); } pub trait Dispatcher: Send + Sync { diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 9f719bd2bc..08ec95ca74 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -2,7 +2,7 @@ use pathfinder_geometry::vector::Vector2F; use std::rc::Rc; use std::sync::Arc; -struct App { +struct Platform { dispatcher: Arc, fonts: Arc, } @@ -19,7 +19,7 @@ pub struct Window { pub struct WindowContext {} -impl App { +impl Platform { fn new() -> Self { Self { dispatcher: Arc::new(Dispatcher), @@ -28,11 +28,29 @@ impl App { } } -impl super::App for App { +impl super::Platform for Platform { + fn on_menu_command(&self, _: Box) {} + + fn on_become_active(&self, _: Box) {} + + fn on_resign_active(&self, _: Box) {} + + fn on_event(&self, _: Box bool>) {} + + fn on_open_files(&self, _: Box)>) {} + + fn run(&self, _on_finish_launching: Box ()>) { + unimplemented!() + } + fn dispatcher(&self) -> Arc { self.dispatcher.clone() } + fn fonts(&self) -> std::sync::Arc { + self.fonts.clone() + } + fn activate(&self, _ignoring_other_apps: bool) {} fn open_window( @@ -43,8 +61,7 @@ impl super::App for App { Ok(Box::new(Window::new(options.bounds.size()))) } - fn fonts(&self) -> std::sync::Arc { - self.fonts.clone() + fn set_menus(&self, _menus: &[crate::Menu]) { } fn quit(&self) {} @@ -102,6 +119,6 @@ impl super::Window for Window { } } -pub fn app() -> impl super::App { - App::new() +pub fn platform() -> impl super::Platform { + Platform::new() } From 4ecc17b1bb878cfe6e868a815b04dd4fc18e302d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 9 Apr 2021 13:38:09 -0600 Subject: [PATCH 2/9] WIP: Make App the only entry point from main Co-Authored-By: Max Brunsfeld --- gpui/src/app.rs | 158 +++++++++++++++++++++------- zed/src/editor/buffer_view.rs | 5 +- zed/src/file_finder.rs | 6 +- zed/src/main.rs | 76 ++++++------- zed/src/workspace/mod.rs | 2 +- zed/src/workspace/pane.rs | 4 +- zed/src/workspace/workspace_view.rs | 2 +- 7 files changed, 165 insertions(+), 88 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 14380f19e1..2c7af63af8 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -21,6 +21,7 @@ use std::{ fmt::{self, Debug}, hash::{Hash, Hasher}, marker::PhantomData, + path::PathBuf, rc::{self, Rc}, sync::{Arc, Weak}, }; @@ -83,20 +84,44 @@ pub enum MenuItem<'a> { #[derive(Clone)] pub struct App(Rc>); +pub trait TestClosure<'a, T> { + type Result: 'a + Future; + fn run_test(self, ctx: &'a mut MutableAppContext) -> Self::Result; +} + +impl<'a, F, R, T> TestClosure<'a, T> for F +where + F: FnOnce(&mut MutableAppContext) -> R, + R: 'a + Future, +{ + type Result = R; + + fn run_test(self, ctx: &'a mut MutableAppContext) -> Self::Result { + (self)(ctx) + } +} + impl App { - pub fn test, G: FnOnce(App) -> F>( + pub fn test< + T, + A: AssetSource, + // F: 'static + , + // G: for<'a> FnOnce(&'a mut MutableAppContext) -> impl Future, + G: for<'a> TestClosure<'a, T>, + >( asset_source: A, f: G, ) -> T { let platform = platform::test::platform(); let foreground = Rc::new(executor::Foreground::test()); - let app = Self(Rc::new(RefCell::new(MutableAppContext::new( + let ctx = Rc::new(RefCell::new(MutableAppContext::new( foreground.clone(), Arc::new(platform), asset_source, - )))); - app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0)); - smol::block_on(foreground.run(f(app))) + ))); + ctx.borrow_mut().weak_self = Some(Rc::downgrade(&ctx)); + let mut ctx = ctx.borrow_mut(); + smol::block_on(foreground.run(f.run_test(&mut *ctx))) } pub fn new(asset_source: impl AssetSource) -> Result { @@ -111,6 +136,80 @@ impl App { Ok(app) } + pub fn on_become_active(self, mut callback: F) -> Self + where + F: 'static + FnMut(&mut MutableAppContext), + { + let ctx = self.0.clone(); + self.0 + .borrow() + .platform + .on_become_active(Box::new(move || callback(&mut *ctx.borrow_mut()))); + self + } + + pub fn on_resign_active(self, mut callback: F) -> Self + where + F: 'static + FnMut(&mut MutableAppContext), + { + let ctx = self.0.clone(); + self.0 + .borrow() + .platform + .on_resign_active(Box::new(move || callback(&mut *ctx.borrow_mut()))); + self + } + + pub fn on_event(self, mut callback: F) -> Self + where + F: 'static + FnMut(Event, &mut MutableAppContext) -> bool, + { + let ctx = self.0.clone(); + self.0.borrow().platform.on_event(Box::new(move |event| { + callback(event, &mut *ctx.borrow_mut()) + })); + self + } + + pub fn on_menu_command(self, mut callback: F) -> Self + where + F: 'static + FnMut(&str, &mut MutableAppContext), + { + let ctx = self.0.clone(); + self.0 + .borrow() + .platform + .on_menu_command(Box::new(move |command| { + callback(command, &mut *ctx.borrow_mut()) + })); + self + } + + pub fn on_open_files(self, mut callback: F) -> Self + where + F: 'static + FnMut(Vec, &mut MutableAppContext), + { + let ctx = self.0.clone(); + self.0 + .borrow() + .platform + .on_open_files(Box::new(move |paths| { + callback(paths, &mut *ctx.borrow_mut()) + })); + self + } + + pub fn run(self, callback: F) + where + F: 'static + FnOnce(&mut MutableAppContext), + { + let ctx = self.0.clone(); + self.0 + .borrow() + .platform + .run(Box::new(move || callback(&mut *ctx.borrow_mut()))); + } + pub fn on_window_invalidated( &self, window_id: usize, @@ -155,12 +254,6 @@ impl App { ); } - pub fn dispatch_global_action(&self, name: &str, arg: T) { - self.0 - .borrow_mut() - .dispatch_global_action(name, Box::new(arg).as_ref()); - } - pub fn add_bindings>(&self, bindings: T) { self.0.borrow_mut().add_bindings(bindings); } @@ -556,14 +649,18 @@ impl MutableAppContext { } if !halted_dispatch { - self.dispatch_global_action(name, arg); + self.dispatch_global_action_with_dyn_arg(name, arg); } self.flush_effects(); halted_dispatch } - fn dispatch_global_action(&mut self, name: &str, arg: &dyn Any) { + pub fn dispatch_global_action(&mut self, name: &str, arg: T) { + self.dispatch_global_action_with_dyn_arg(name, Box::new(arg).as_ref()); + } + + fn dispatch_global_action_with_dyn_arg(&mut self, name: &str, arg: &dyn Any) { if let Some((name, mut handlers)) = self.global_actions.remove_entry(name) { self.pending_flushes += 1; for handler in handlers.iter_mut().rev() { @@ -574,7 +671,7 @@ impl MutableAppContext { } } - fn add_bindings>(&mut self, bindings: T) { + pub fn add_bindings>(&mut self, bindings: T) { self.keystroke_matcher.add_bindings(bindings); } @@ -1887,13 +1984,6 @@ impl ModelHandle { app.model(self) } - pub fn read<'a, S, F>(&self, app: &'a App, read: F) -> S - where - F: FnOnce(&T, &AppContext) -> S, - { - app.read_model(self, read) - } - pub fn update(&self, app: &mut A, update: F) -> S where A: UpdateModel, @@ -2362,9 +2452,7 @@ mod tests { } } - App::test((), |mut app| async move { - let app = &mut app; - + App::test((), |app: &mut MutableAppContext| async move { let handle_1 = app.add_model(|ctx| Model::new(None, ctx)); let handle_2 = app.add_model(|ctx| Model::new(Some(handle_1.clone()), ctx)); assert_eq!(app.0.borrow().ctx.models.len(), 2); @@ -2375,19 +2463,15 @@ mod tests { ctx.notify(); ctx.emit(2); }); - handle_1.read(app, |model, _| { - assert_eq!(model.events, vec!["updated".to_string()]); - }); - handle_2.read(app, |model, _| { - assert_eq!( - model.events, - vec![ - "observed event 1".to_string(), - "notified".to_string(), - "observed event 2".to_string(), - ] - ); - }); + assert_eq!(handle_1.as_ref(app).events, vec!["updated".to_string()]); + assert_eq!( + handle_2.as_ref(app).events, + vec![ + "observed event 1".to_string(), + "notified".to_string(), + "observed event 2".to_string(), + ] + ); handle_2.update(app, |model, _| { drop(handle_1); diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 6f3d6f108e..fbd6c8cc83 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -7,7 +7,8 @@ use anyhow::Result; use futures_core::future::LocalBoxFuture; use gpui::{ fonts::Properties as FontProperties, keymap::Binding, text_layout, App, AppContext, Element, - ElementBox, Entity, FontCache, ModelHandle, View, ViewContext, WeakViewHandle, + ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, View, ViewContext, + WeakViewHandle, }; use gpui::{geometry::vector::Vector2F, TextLayoutCache}; use parking_lot::Mutex; @@ -24,7 +25,7 @@ use std::{ const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); -pub fn init(app: &mut App) { +pub fn init(app: &mut MutableAppContext) { app.add_bindings(vec![ Binding::new("backspace", "buffer:backspace", Some("BufferView")), Binding::new("enter", "buffer:newline", Some("BufferView")), diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index c951d12934..28a0ae0360 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -11,8 +11,8 @@ use gpui::{ fonts::{Properties, Weight}, geometry::vector::vec2f, keymap::{self, Binding}, - App, AppContext, Axis, Border, Entity, ModelHandle, View, ViewContext, ViewHandle, - WeakViewHandle, + App, AppContext, Axis, Border, Entity, ModelHandle, MutableAppContext, View, ViewContext, + ViewHandle, WeakViewHandle, }; use std::cmp; @@ -28,7 +28,7 @@ pub struct FileFinder { list_state: UniformListState, } -pub fn init(app: &mut App) { +pub fn init(app: &mut MutableAppContext) { app.add_action("file_finder:toggle", FileFinder::toggle); app.add_action("file_finder:confirm", FileFinder::confirm); app.add_action("file_finder:select", FileFinder::select); diff --git a/zed/src/main.rs b/zed/src/main.rs index 82a8a73106..f7e4f0346b 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -13,55 +13,47 @@ fn main() { let app = gpui::App::new(assets::Assets).unwrap(); let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap(); - - platform::runner() - .set_menus(menus::MENUS) - .on_menu_command({ - let app = app.clone(); - let settings_rx = settings_rx.clone(); - move |command| match command { - "app:open" => { - if let Some(paths) = app.platform().prompt_for_paths(PathPromptOptions { - files: true, - directories: true, - multiple: true, - }) { - app.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths, - settings: settings_rx.clone(), - }, - ); - } - } - _ => app.dispatch_global_action(command, ()), - } - }) - .on_finish_launching({ - let mut app = app.clone(); - move || { - workspace::init(&mut app); - editor::init(&mut app); - file_finder::init(&mut app); - - if stdout_is_a_pty() { - app.platform().activate(true); - } - - let paths = collect_path_args(); - if !paths.is_empty() { - app.dispatch_global_action( + app.on_menu_command({ + let settings_rx = settings_rx.clone(); + move |command, ctx| match command { + "app:open" => { + if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions { + files: true, + directories: true, + multiple: true, + }) { + ctx.dispatch_global_action( "workspace:open_paths", OpenParams { paths, - settings: settings_rx, + settings: settings_rx.clone(), }, ); } } - }) - .run(); + _ => ctx.dispatch_global_action(command, ()), + } + }) + .run(move |ctx| { + workspace::init(ctx); + editor::init(ctx); + file_finder::init(ctx); + + if stdout_is_a_pty() { + ctx.platform().activate(true); + } + + let paths = collect_path_args(); + if !paths.is_empty() { + ctx.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths, + settings: settings_rx, + }, + ); + } + }); } fn init_logger() { diff --git a/zed/src/workspace/mod.rs b/zed/src/workspace/mod.rs index b7a76f9445..e1a087049c 100644 --- a/zed/src/workspace/mod.rs +++ b/zed/src/workspace/mod.rs @@ -12,7 +12,7 @@ use crate::{settings::Settings, watch}; use gpui::{App, MutableAppContext}; use std::path::PathBuf; -pub fn init(app: &mut App) { +pub fn init(app: &mut MutableAppContext) { app.add_global_action("workspace:open_paths", open_paths); app.add_global_action("app:quit", quit); pane::init(app); diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index fad299aa8b..903292a63e 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -5,11 +5,11 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, keymap::Binding, - App, AppContext, Border, Entity, Quad, View, ViewContext, + App, AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext, }; use std::cmp; -pub fn init(app: &mut App) { +pub fn init(app: &mut MutableAppContext) { app.add_action( "pane:activate_item", |pane: &mut Pane, index: &usize, ctx| { diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 5810245be4..32e16fc014 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -8,7 +8,7 @@ use gpui::{ use log::{error, info}; use std::{collections::HashSet, path::PathBuf}; -pub fn init(app: &mut App) { +pub fn init(app: &mut MutableAppContext) { app.add_action("workspace:save", WorkspaceView::save_active_item); app.add_action("workspace:debug_elements", WorkspaceView::debug_elements); app.add_bindings(vec![ From 079050541f1858b6019aecef02ce3d978056b2e2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 9 Apr 2021 15:58:19 -0700 Subject: [PATCH 3/9] Get app running and test passing after gpui App+Platform restructure --- gpui/examples/text.rs | 14 +- gpui/src/app.rs | 325 ++++++++++++------------- gpui/src/platform/mac/mod.rs | 9 +- gpui/src/platform/mac/platform.rs | 200 +++++++-------- gpui/src/platform/mod.rs | 13 +- gpui/src/platform/test.rs | 7 +- zed/src/editor/buffer/mod.rs | 18 +- zed/src/editor/buffer_view.rs | 206 ++++++++-------- zed/src/editor/display_map/fold_map.rs | 279 ++++++++++----------- zed/src/editor/display_map/mod.rs | 91 ++++--- zed/src/file_finder.rs | 47 ++-- zed/src/main.rs | 8 +- zed/src/workspace/mod.rs | 28 ++- zed/src/workspace/pane.rs | 2 +- zed/src/workspace/workspace.rs | 50 ++-- zed/src/workspace/workspace_view.rs | 113 +++++---- zed/src/worktree/worktree.rs | 58 ++--- 17 files changed, 719 insertions(+), 749 deletions(-) diff --git a/gpui/examples/text.rs b/gpui/examples/text.rs index 14bc198270..2dad6e0786 100644 --- a/gpui/examples/text.rs +++ b/gpui/examples/text.rs @@ -1,7 +1,6 @@ use gpui::{ color::ColorU, fonts::{Properties, Weight}, - platform::{current as platform, Runner}, DebugContext, Element as _, Quad, }; use log::LevelFilter; @@ -11,13 +10,12 @@ use simplelog::SimpleLogger; fn main() { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - let mut app = gpui::App::new(()).unwrap(); - platform::runner() - .on_finish_launching(move || { - app.platform().activate(true); - app.add_window(|_| TextView); - }) - .run(); + let app = gpui::App::new(()).unwrap(); + app.on_finish_launching(|app| { + app.platform().activate(true); + app.add_window(|_| TextView); + }) + .run(); } struct TextView; diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 2c7af63af8..ab3f471e73 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -2,7 +2,7 @@ use crate::{ elements::ElementBox, executor, keymap::{self, Keystroke}, - platform::{self, Platform as _, WindowOptions}, + platform::{self, WindowOptions}, presenter::Presenter, util::post_inc, AssetCache, AssetSource, FontCache, TextLayoutCache, @@ -84,33 +84,10 @@ pub enum MenuItem<'a> { #[derive(Clone)] pub struct App(Rc>); -pub trait TestClosure<'a, T> { - type Result: 'a + Future; - fn run_test(self, ctx: &'a mut MutableAppContext) -> Self::Result; -} - -impl<'a, F, R, T> TestClosure<'a, T> for F -where - F: FnOnce(&mut MutableAppContext) -> R, - R: 'a + Future, -{ - type Result = R; - - fn run_test(self, ctx: &'a mut MutableAppContext) -> Self::Result { - (self)(ctx) - } -} - impl App { - pub fn test< - T, - A: AssetSource, - // F: 'static + , - // G: for<'a> FnOnce(&'a mut MutableAppContext) -> impl Future, - G: for<'a> TestClosure<'a, T>, - >( + pub fn test T>( asset_source: A, - f: G, + f: F, ) -> T { let platform = platform::test::platform(); let foreground = Rc::new(executor::Foreground::test()); @@ -121,11 +98,35 @@ impl App { ))); ctx.borrow_mut().weak_self = Some(Rc::downgrade(&ctx)); let mut ctx = ctx.borrow_mut(); - smol::block_on(foreground.run(f.run_test(&mut *ctx))) + f(&mut *ctx) + } + + pub fn test_async<'a, T, F, A: AssetSource, Fn>(asset_source: A, f: Fn) -> T + where + Fn: FnOnce(&'a mut MutableAppContext) -> F, + F: Future + 'a, + { + let platform = platform::test::platform(); + let foreground = Rc::new(executor::Foreground::test()); + let ctx = Rc::new(RefCell::new(MutableAppContext::new( + foreground.clone(), + Arc::new(platform), + asset_source, + ))); + let mut ctx_ref = ctx.borrow_mut(); + ctx_ref.weak_self = Some(Rc::downgrade(&ctx)); + let ctx = &mut *ctx_ref; + + // TODO - is there a better way of getting this to compile? + let ctx = unsafe { std::mem::transmute(ctx) }; + let future = f(ctx); + + drop(ctx_ref); + smol::block_on(foreground.run(future)) } pub fn new(asset_source: impl AssetSource) -> Result { - let platform = Arc::new(platform::current::app()); + let platform = platform::current::platform(); let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?); let app = Self(Rc::new(RefCell::new(MutableAppContext::new( foreground, @@ -199,7 +200,7 @@ impl App { self } - pub fn run(self, callback: F) + pub fn on_finish_launching(self, callback: F) -> Self where F: 'static + FnOnce(&mut MutableAppContext), { @@ -207,7 +208,16 @@ impl App { self.0 .borrow() .platform - .run(Box::new(move || callback(&mut *ctx.borrow_mut()))); + .on_finish_launching(Box::new(move || callback(&mut *ctx.borrow_mut()))); + self + } + + pub fn set_menus(&self, menus: &[Menu]) { + self.0.borrow().platform.set_menus(menus); + } + + pub fn run(self) { + platform::current::run(); } pub fn on_window_invalidated( @@ -246,7 +256,7 @@ impl App { name: &str, arg: T, ) { - self.0.borrow_mut().dispatch_action( + self.0.borrow_mut().dispatch_action_any( window_id, &responder_chain, name, @@ -280,15 +290,6 @@ impl App { handle } - fn read_model(&self, handle: &ModelHandle, read: F) -> S - where - T: Entity, - F: FnOnce(&T, &AppContext) -> S, - { - let state = self.0.borrow(); - read(state.model(handle), &state.ctx) - } - pub fn add_window(&mut self, build_root_view: F) -> (usize, ViewHandle) where T: View, @@ -345,15 +346,6 @@ impl App { result } - fn read_view(&self, handle: &ViewHandle, read: F) -> S - where - T: View, - F: FnOnce(&T, &AppContext) -> S, - { - let state = self.0.borrow(); - read(state.view(handle), state.downgrade()) - } - pub fn finish_pending_tasks(&self) -> impl Future { self.0.borrow().finish_pending_tasks() } @@ -478,6 +470,10 @@ impl MutableAppContext { self.platform.clone() } + pub fn font_cache(&self) -> &Arc { + &self.font_cache + } + pub fn foreground_executor(&self) -> &Rc { &self.foreground } @@ -598,7 +594,24 @@ impl MutableAppContext { self.ctx.render_views(window_id) } - pub fn dispatch_action( + pub fn update T>(&mut self, callback: F) -> T { + self.pending_flushes += 1; + let result = callback(); + self.flush_effects(); + result + } + + pub fn dispatch_action( + &mut self, + window_id: usize, + responder_chain: Vec, + name: &str, + arg: T, + ) { + self.dispatch_action_any(window_id, &responder_chain, name, Box::new(arg).as_ref()); + } + + fn dispatch_action_any( &mut self, window_id: usize, path: &[usize], @@ -709,7 +722,7 @@ impl MutableAppContext { MatchResult::None => {} MatchResult::Pending => pending = true, MatchResult::Action { name, arg } => { - if self.dispatch_action( + if self.dispatch_action_any( window_id, &responder_chain[0..=i], &name, @@ -796,7 +809,7 @@ impl MutableAppContext { .borrow_mut() .dispatch_event(event, ctx.downgrade()); for action in actions { - ctx.dispatch_action( + ctx.dispatch_action_any( window_id, &action.path, action.name, @@ -1328,6 +1341,12 @@ impl UpdateView for MutableAppContext { } } +impl AsRef for MutableAppContext { + fn as_ref(&self) -> &AppContext { + &self.ctx + } +} + pub struct AppContext { models: HashMap>, windows: HashMap, @@ -2112,13 +2131,6 @@ impl ViewHandle { app.view(self) } - pub fn read<'a, F, S>(&self, app: &'a App, read: F) -> S - where - F: FnOnce(&T, &AppContext) -> S, - { - app.read_view(self, read) - } - pub fn update(&self, app: &mut A, update: F) -> S where A: UpdateView, @@ -2452,10 +2464,10 @@ mod tests { } } - App::test((), |app: &mut MutableAppContext| async move { + App::test((), |app| { let handle_1 = app.add_model(|ctx| Model::new(None, ctx)); let handle_2 = app.add_model(|ctx| Model::new(Some(handle_1.clone()), ctx)); - assert_eq!(app.0.borrow().ctx.models.len(), 2); + assert_eq!(app.ctx.models.len(), 2); handle_1.update(app, |model, ctx| { model.events.push("updated".into()); @@ -2478,11 +2490,10 @@ mod tests { model.other.take(); }); - let app_state = app.0.borrow(); - assert_eq!(app_state.ctx.models.len(), 1); - assert!(app_state.subscriptions.is_empty()); - assert!(app_state.observations.is_empty()); - }) + assert_eq!(app.ctx.models.len(), 1); + assert!(app.subscriptions.is_empty()); + assert!(app.observations.is_empty()); + }); } #[test] @@ -2496,8 +2507,7 @@ mod tests { type Event = usize; } - App::test((), |mut app| async move { - let app = &mut app; + App::test((), |app| { let handle_1 = app.add_model(|_| Model::default()); let handle_2 = app.add_model(|_| Model::default()); let handle_2b = handle_2.clone(); @@ -2513,10 +2523,10 @@ mod tests { }); handle_2.update(app, |_, c| c.emit(7)); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); + assert_eq!(handle_1.as_ref(app).events, vec![7]); handle_2.update(app, |_, c| c.emit(5)); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])); + assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]); }) } @@ -2532,8 +2542,7 @@ mod tests { type Event = (); } - App::test((), |mut app| async move { - let app = &mut app; + App::test((), |app| { let handle_1 = app.add_model(|_| Model::default()); let handle_2 = app.add_model(|_| Model::default()); let handle_2b = handle_2.clone(); @@ -2551,13 +2560,13 @@ mod tests { model.count = 7; c.notify() }); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); + assert_eq!(handle_1.as_ref(app).events, vec![7]); handle_2.update(app, |model, c| { model.count = 5; c.notify() }); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])) + assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]) }) } @@ -2572,25 +2581,25 @@ mod tests { type Event = (); } - App::test((), |mut app| async move { + App::test_async((), |app| async move { let handle = app.add_model(|_| Model::default()); handle - .update(&mut app, |_, c| { + .update(app, |_, c| { c.spawn(async { 7 }, |model, output, _| { model.count = output; }) }) .await; - handle.read(&app, |model, _| assert_eq!(model.count, 7)); + assert_eq!(handle.as_ref(app).count, 7); handle - .update(&mut app, |_, c| { + .update(app, |_, c| { c.spawn(async { 14 }, |model, output, _| { model.count = output; }) }) .await; - handle.read(&app, |model, _| assert_eq!(model.count, 14)); + assert_eq!(handle.as_ref(app).count, 14); }); } @@ -2605,10 +2614,10 @@ mod tests { type Event = (); } - App::test((), |mut app| async move { + App::test_async((), |app| async move { let handle = app.add_model(|_| Model::default()); handle - .update(&mut app, |_, c| { + .update(app, |_, c| { c.spawn_stream( smol::stream::iter(vec![1, 2, 3]), |model, output, _| { @@ -2620,10 +2629,7 @@ mod tests { ) }) .await; - - handle.read(&app, |model, _| { - assert_eq!(model.events, [Some(1), Some(2), Some(3), None]) - }); + assert_eq!(handle.as_ref(app).events, [Some(1), Some(2), Some(3), None]) }) } @@ -2662,40 +2668,34 @@ mod tests { } } - App::test((), |mut app| async move { - let app = &mut app; + App::test((), |app| { let (window_id, _) = app.add_window(|ctx| View::new(None, ctx)); let handle_1 = app.add_view(window_id, |ctx| View::new(None, ctx)); let handle_2 = app.add_view(window_id, |ctx| View::new(Some(handle_1.clone()), ctx)); - assert_eq!(app.0.borrow().ctx.windows[&window_id].views.len(), 3); + assert_eq!(app.ctx.windows[&window_id].views.len(), 3); handle_1.update(app, |view, ctx| { view.events.push("updated".into()); ctx.emit(1); ctx.emit(2); }); - handle_1.read(app, |view, _| { - assert_eq!(view.events, vec!["updated".to_string()]); - }); - handle_2.read(app, |view, _| { - assert_eq!( - view.events, - vec![ - "observed event 1".to_string(), - "observed event 2".to_string(), - ] - ); - }); + assert_eq!(handle_1.as_ref(app).events, vec!["updated".to_string()]); + assert_eq!( + handle_2.as_ref(app).events, + vec![ + "observed event 1".to_string(), + "observed event 2".to_string(), + ] + ); handle_2.update(app, |view, _| { drop(handle_1); view.other.take(); }); - let app_state = app.0.borrow(); - assert_eq!(app_state.ctx.windows[&window_id].views.len(), 2); - assert!(app_state.subscriptions.is_empty()); - assert!(app_state.observations.is_empty()); + assert_eq!(app.ctx.windows[&window_id].views.len(), 2); + assert!(app.subscriptions.is_empty()); + assert!(app.observations.is_empty()); }) } @@ -2726,8 +2726,7 @@ mod tests { type Event = usize; } - App::test((), |mut app| async move { - let app = &mut app; + App::test((), |app| { let (window_id, handle_1) = app.add_window(|_| View::default()); let handle_2 = app.add_view(window_id, |_| View::default()); let handle_2b = handle_2.clone(); @@ -2748,13 +2747,13 @@ mod tests { }); handle_2.update(app, |_, c| c.emit(7)); - handle_1.read(app, |view, _| assert_eq!(view.events, vec![7])); + assert_eq!(handle_1.as_ref(app).events, vec![7]); handle_2.update(app, |_, c| c.emit(5)); - handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5])); + assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]); handle_3.update(app, |_, c| c.emit(9)); - handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5, 9])); + assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5, 9]); }) } @@ -2782,9 +2781,7 @@ mod tests { type Event = (); } - App::test((), |mut app| async move { - let app = &mut app; - + App::test((), |app| { let (window_id, _) = app.add_window(|_| View); let observing_view = app.add_view(window_id, |_| View); let emitting_view = app.add_view(window_id, |_| View); @@ -2799,7 +2796,7 @@ mod tests { ctx.subscribe(&observed_model, |_, _, _| {}); }); - app.update(|_| { + app.update(|| { drop(observing_view); drop(observing_model); }); @@ -2839,8 +2836,7 @@ mod tests { type Event = (); } - App::test((), |mut app| async move { - let app = &mut app; + App::test((), |app| { let (_, view) = app.add_window(|_| View::default()); let model = app.add_model(|_| Model::default()); @@ -2854,7 +2850,7 @@ mod tests { model.count = 11; c.notify(); }); - view.read(app, |view, _| assert_eq!(view.events, vec![11])); + assert_eq!(view.as_ref(app).events, vec![11]); }) } @@ -2882,9 +2878,7 @@ mod tests { type Event = (); } - App::test((), |mut app| async move { - let app = &mut app; - + App::test((), |app| { let (window_id, _) = app.add_window(|_| View); let observing_view = app.add_view(window_id, |_| View); let observing_model = app.add_model(|_| Model); @@ -2897,7 +2891,7 @@ mod tests { ctx.observe(&observed_model, |_, _, _| {}); }); - app.update(|_| { + app.update(|| { drop(observing_view); drop(observing_model); }); @@ -2937,8 +2931,7 @@ mod tests { } } - App::test((), |mut app| async move { - let app = &mut app; + App::test((), |app| { let (window_id, view_1) = app.add_window(|_| View::default()); let view_2 = app.add_view(window_id, |_| View::default()); @@ -2953,18 +2946,16 @@ mod tests { ctx.focus(&view_1); }); - view_1.read(app, |view_1, _| { - assert_eq!( - view_1.events, - [ - "self focused".to_string(), - "self blurred".to_string(), - "view 2 focused".to_string(), - "self focused".to_string(), - "view 2 blurred".to_string(), - ], - ); - }); + assert_eq!( + view_1.as_ref(app).events, + [ + "self focused".to_string(), + "self blurred".to_string(), + "view 2 focused".to_string(), + "self focused".to_string(), + "view 2 blurred".to_string(), + ], + ); }) } @@ -2989,24 +2980,24 @@ mod tests { } } - App::test((), |mut app| async move { + App::test_async((), |app| async move { let (_, handle) = app.add_window(|_| View::default()); handle - .update(&mut app, |_, c| { + .update(app, |_, c| { c.spawn(async { 7 }, |me, output, _| { me.count = output; }) }) .await; - handle.read(&app, |view, _| assert_eq!(view.count, 7)); + assert_eq!(handle.as_ref(app).count, 7); handle - .update(&mut app, |_, c| { + .update(app, |_, c| { c.spawn(async { 14 }, |me, output, _| { me.count = output; }) }) .await; - handle.read(&app, |view, _| assert_eq!(view.count, 14)); + assert_eq!(handle.as_ref(app).count, 14); }); } @@ -3031,10 +3022,10 @@ mod tests { } } - App::test((), |mut app| async move { + App::test_async((), |app| async move { let (_, handle) = app.add_window(|_| View::default()); handle - .update(&mut app, |_, c| { + .update(app, |_, c| { c.spawn_stream( smol::stream::iter(vec![1_usize, 2, 3]), |me, output, _| { @@ -3047,9 +3038,7 @@ mod tests { }) .await; - handle.read(&app, |view, _| { - assert_eq!(view.events, [Some(1), Some(2), Some(3), None]) - }); + assert_eq!(handle.as_ref(app).events, [Some(1), Some(2), Some(3), None]) }); } @@ -3095,7 +3084,7 @@ mod tests { foo: String, } - App::test((), |mut app| async move { + App::test((), |app| { let actions = Rc::new(RefCell::new(Vec::new())); let actions_clone = actions.clone(); @@ -3169,7 +3158,7 @@ mod tests { } #[test] - fn test_dispatch_keystroke() -> Result<()> { + fn test_dispatch_keystroke() { use std::cell::Cell; #[derive(Clone)] @@ -3209,7 +3198,7 @@ mod tests { } } - App::test((), |mut app| async move { + App::test((), |app| { let mut view_1 = View::new(1); let mut view_2 = View::new(2); let mut view_3 = View::new(3); @@ -3238,12 +3227,12 @@ mod tests { app.dispatch_keystroke( window_id, vec![view_1.id(), view_2.id(), view_3.id()], - &Keystroke::parse("a")?, - )?; + &Keystroke::parse("a").unwrap(), + ) + .unwrap(); assert!(handled_action.get()); - Ok(()) - }) + }); } // #[test] @@ -3266,7 +3255,7 @@ mod tests { // } // } - // App::test(|mut app| async move { + // App::test(|app| async move { // let (window_id, _) = app.add_window(|_| View { count: 3 }); // let view_1 = app.add_view(window_id, |_| View { count: 1 }); // let view_2 = app.add_view(window_id, |_| View { count: 2 }); @@ -3293,7 +3282,7 @@ mod tests { // }); // let view_2_id = view_2.id(); - // view_1.update(&mut app, |view, ctx| { + // view_1.update(app, |view, ctx| { // view.count = 7; // ctx.notify(); // drop(view_2); @@ -3304,7 +3293,7 @@ mod tests { // assert!(invalidation.updated.contains(&view_1.id())); // assert_eq!(invalidation.removed, vec![view_2_id]); - // let view_3 = view_1.update(&mut app, |_, ctx| ctx.add_view(|_| View { count: 8 })); + // let view_3 = view_1.update(app, |_, ctx| ctx.add_view(|_| View { count: 8 })); // let invalidation = window_invalidations.borrow_mut().drain(..).next().unwrap(); // assert_eq!(invalidation.updated.len(), 1); @@ -3312,7 +3301,7 @@ mod tests { // assert!(invalidation.removed.is_empty()); // view_3 - // .update(&mut app, |_, ctx| { + // .update(app, |_, ctx| { // ctx.spawn_local(async { 9 }, |me, output, ctx| { // me.count = output; // ctx.notify(); @@ -3351,49 +3340,49 @@ mod tests { type Event = (); } - App::test((), |mut app| async move { + App::test_async((), |app| async move { let model = app.add_model(|_| Model); let (_, view) = app.add_window(|_| View); - model.update(&mut app, |_, ctx| { + model.update(app, |_, ctx| { ctx.spawn(async {}, |_, _, _| {}).detach(); // Cancel this task drop(ctx.spawn(async {}, |_, _, _| {})); }); - view.update(&mut app, |_, ctx| { + view.update(app, |_, ctx| { ctx.spawn(async {}, |_, _, _| {}).detach(); // Cancel this task drop(ctx.spawn(async {}, |_, _, _| {})); }); - assert!(!app.0.borrow().future_handlers.borrow().is_empty()); + assert!(!app.future_handlers.borrow().is_empty()); app.finish_pending_tasks().await; - assert!(app.0.borrow().future_handlers.borrow().is_empty()); + assert!(app.future_handlers.borrow().is_empty()); app.finish_pending_tasks().await; // Don't block if there are no tasks - model.update(&mut app, |_, ctx| { + model.update(app, |_, ctx| { ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {}) .detach(); // Cancel this task drop(ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {})); }); - view.update(&mut app, |_, ctx| { + view.update(app, |_, ctx| { ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {}) .detach(); // Cancel this task drop(ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {})); }); - assert!(!app.0.borrow().stream_handlers.borrow().is_empty()); + assert!(!app.stream_handlers.borrow().is_empty()); app.finish_pending_tasks().await; - assert!(app.0.borrow().stream_handlers.borrow().is_empty()); + assert!(app.stream_handlers.borrow().is_empty()); app.finish_pending_tasks().await; // Don't block if there are no tasks // Tasks are considered finished when we drop handles let mut tasks = Vec::new(); - model.update(&mut app, |_, ctx| { + model.update(app, |_, ctx| { tasks.push(Box::new(ctx.spawn(async {}, |_, _, _| {}))); tasks.push(Box::new(ctx.spawn_stream( smol::stream::iter(vec![1, 2, 3]), @@ -3402,7 +3391,7 @@ mod tests { ))); }); - view.update(&mut app, |_, ctx| { + view.update(app, |_, ctx| { tasks.push(Box::new(ctx.spawn(async {}, |_, _, _| {}))); tasks.push(Box::new(ctx.spawn_stream( smol::stream::iter(vec![1, 2, 3]), @@ -3411,12 +3400,12 @@ mod tests { ))); }); - assert!(!app.0.borrow().stream_handlers.borrow().is_empty()); + assert!(!app.stream_handlers.borrow().is_empty()); let finish_pending_tasks = app.finish_pending_tasks(); drop(tasks); finish_pending_tasks.await; - assert!(app.0.borrow().stream_handlers.borrow().is_empty()); + assert!(app.stream_handlers.borrow().is_empty()); app.finish_pending_tasks().await; // Don't block if there are no tasks }); } diff --git a/gpui/src/platform/mac/mod.rs b/gpui/src/platform/mac/mod.rs index b54d148682..07ada3650a 100644 --- a/gpui/src/platform/mac/mod.rs +++ b/gpui/src/platform/mac/mod.rs @@ -5,7 +5,6 @@ mod fonts; mod geometry; mod platform; mod renderer; -mod runner; mod sprite_cache; mod window; @@ -13,15 +12,15 @@ use cocoa::base::{BOOL, NO, YES}; pub use dispatcher::Dispatcher; pub use fonts::FontSystem; use platform::MacPlatform; -pub use runner::Runner; +use std::sync::Arc; use window::Window; -pub fn app() -> impl super::Platform { +pub fn platform() -> Arc { MacPlatform::new() } -pub fn runner() -> impl super::Runner { - Runner::new() +pub fn run() { + MacPlatform::run(); } trait BoolExt { diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index d089ba26d2..40bd3566a5 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -90,13 +90,112 @@ struct Callbacks { } impl MacPlatform { - pub fn new() -> Self { - Self { + pub fn new() -> Arc { + let result = Arc::new(Self { dispatcher: Arc::new(Dispatcher), fonts: Arc::new(FontSystem::new()), callbacks: Default::default(), menu_item_actions: Default::default(), + }); + + unsafe { + let app: id = msg_send![APP_CLASS, sharedApplication]; + let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; + let self_ptr = result.as_ref() as *const Self as *const c_void; + app.setDelegate_(app_delegate); + (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr); + (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr); } + + result + } + + pub fn run() { + unsafe { + let pool = NSAutoreleasePool::new(nil); + let app: id = msg_send![APP_CLASS, sharedApplication]; + + app.run(); + pool.drain(); + (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); + (*app.delegate()).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); + } + } + + unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id { + let menu_bar = NSMenu::new(nil).autorelease(); + let mut menu_item_actions = self.menu_item_actions.borrow_mut(); + menu_item_actions.clear(); + + for menu_config in menus { + let menu_bar_item = NSMenuItem::new(nil).autorelease(); + let menu = NSMenu::new(nil).autorelease(); + + menu.setTitle_(ns_string(menu_config.name)); + + for item_config in menu_config.items { + let item; + + match item_config { + MenuItem::Separator => { + item = NSMenuItem::separatorItem(nil); + } + MenuItem::Action { + name, + keystroke, + action, + } => { + if let Some(keystroke) = keystroke { + let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| { + panic!( + "Invalid keystroke for menu item {}:{} - {:?}", + menu_config.name, name, err + ) + }); + + let mut mask = NSEventModifierFlags::empty(); + for (modifier, flag) in &[ + (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask), + (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask), + (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask), + ] { + if *modifier { + mask |= *flag; + } + } + + item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string(name), + selector("handleGPUIMenuItem:"), + ns_string(&keystroke.key), + ) + .autorelease(); + item.setKeyEquivalentModifierMask_(mask); + } else { + item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string(name), + selector("handleGPUIMenuItem:"), + ns_string(""), + ) + .autorelease(); + } + + let tag = menu_item_actions.len() as NSInteger; + let _: () = msg_send![item, setTag: tag]; + menu_item_actions.push(action.to_string()); + } + } + + menu.addItem_(item); + } + + menu_bar_item.setSubmenu_(menu); + menu_bar.addItem_(menu_bar_item); + } + + menu_bar } } @@ -121,23 +220,8 @@ impl platform::Platform for MacPlatform { self.callbacks.borrow_mut().open_files = Some(callback); } - fn run(&self, on_finish_launching: Box ()>) { - self.callbacks.borrow_mut().finish_launching = Some(on_finish_launching); - - unsafe { - let pool = NSAutoreleasePool::new(nil); - let app: id = msg_send![APP_CLASS, sharedApplication]; - let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; - - let self_ptr = self as *const Self as *mut c_void; - (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr); - (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr); - app.setDelegate_(app_delegate); - app.run(); - pool.drain(); - (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); - (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); - } + fn on_finish_launching(&self, callback: Box ()>) { + self.callbacks.borrow_mut().finish_launching = Some(callback); } fn dispatcher(&self) -> Arc { @@ -222,84 +306,6 @@ impl platform::Platform for MacPlatform { } } -impl MacPlatform { - unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id { - let menu_bar = NSMenu::new(nil).autorelease(); - let mut menu_item_actions = self.menu_item_actions.borrow_mut(); - menu_item_actions.clear(); - - for menu_config in menus { - let menu_bar_item = NSMenuItem::new(nil).autorelease(); - let menu = NSMenu::new(nil).autorelease(); - - menu.setTitle_(ns_string(menu_config.name)); - - for item_config in menu_config.items { - let item; - - match item_config { - MenuItem::Separator => { - item = NSMenuItem::separatorItem(nil); - } - MenuItem::Action { - name, - keystroke, - action, - } => { - if let Some(keystroke) = keystroke { - let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| { - panic!( - "Invalid keystroke for menu item {}:{} - {:?}", - menu_config.name, name, err - ) - }); - - let mut mask = NSEventModifierFlags::empty(); - for (modifier, flag) in &[ - (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask), - (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask), - (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask), - ] { - if *modifier { - mask |= *flag; - } - } - - item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string(name), - selector("handleGPUIMenuItem:"), - ns_string(&keystroke.key), - ) - .autorelease(); - item.setKeyEquivalentModifierMask_(mask); - } else { - item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string(name), - selector("handleGPUIMenuItem:"), - ns_string(""), - ) - .autorelease(); - } - - let tag = menu_item_actions.len() as NSInteger; - let _: () = msg_send![item, setTag: tag]; - menu_item_actions.push(action.to_string()); - } - } - - menu.addItem_(item); - } - - menu_bar_item.setSubmenu_(menu); - menu_bar.addItem_(menu_bar_item); - } - - menu_bar - } -} - unsafe fn get_platform(object: &mut Object) -> &MacPlatform { let platform_ptr: *mut c_void = *object.get_ivar(MAC_PLATFORM_IVAR); assert!(!platform_ptr.is_null()); diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index b96bffb979..6e5d53585f 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -22,24 +22,13 @@ use async_task::Runnable; pub use event::Event; use std::{ops::Range, path::PathBuf, rc::Rc, sync::Arc}; -pub trait Runner { - fn on_finish_launching(self, callback: F) -> Self; - fn on_menu_command(self, callback: F) -> Self; - fn on_become_active(self, callback: F) -> Self; - fn on_resign_active(self, callback: F) -> Self; - fn on_event bool>(self, callback: F) -> Self; - fn on_open_files)>(self, callback: F) -> Self; - fn set_menus(self, menus: &[Menu]) -> Self; - fn run(self); -} - pub trait Platform { fn on_menu_command(&self, callback: Box); fn on_become_active(&self, callback: Box); fn on_resign_active(&self, callback: Box); fn on_event(&self, callback: Box bool>); fn on_open_files(&self, callback: Box)>); - fn run(&self, on_finish_launching: Box ()>); + fn on_finish_launching(&self, callback: Box ()>); fn dispatcher(&self) -> Arc; fn fonts(&self) -> Arc; diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 08ec95ca74..1cd4399c1b 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -39,9 +39,7 @@ impl super::Platform for Platform { fn on_open_files(&self, _: Box)>) {} - fn run(&self, _on_finish_launching: Box ()>) { - unimplemented!() - } + fn on_finish_launching(&self, _: Box ()>) {} fn dispatcher(&self) -> Arc { self.dispatcher.clone() @@ -61,8 +59,7 @@ impl super::Platform for Platform { Ok(Box::new(Window::new(options.bounds.size()))) } - fn set_menus(&self, _menus: &[crate::Menu]) { - } + fn set_menus(&self, _menus: &[crate::Menu]) {} fn quit(&self) {} diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 827da6be9d..9807b8ef97 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -2193,13 +2193,13 @@ mod tests { #[test] fn test_edit_events() { - App::test((), |mut app| async move { + App::test((), |app| { let buffer_1_events = Rc::new(RefCell::new(Vec::new())); let buffer_2_events = Rc::new(RefCell::new(Vec::new())); let buffer1 = app.add_model(|_| Buffer::new(0, "abcdef")); let buffer2 = app.add_model(|_| Buffer::new(1, "abcdef")); - let ops = buffer1.update(&mut app, |buffer, ctx| { + let ops = buffer1.update(app, |buffer, ctx| { let buffer_1_events = buffer_1_events.clone(); ctx.subscribe(&buffer1, move |_, event, _| { buffer_1_events.borrow_mut().push(event.clone()) @@ -2211,7 +2211,7 @@ mod tests { buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap() }); - buffer2.update(&mut app, |buffer, ctx| { + buffer2.update(app, |buffer, ctx| { buffer.apply_ops(ops, Some(ctx)).unwrap(); }); @@ -2715,12 +2715,12 @@ mod tests { #[test] fn test_is_modified() -> Result<()> { - App::test((), |mut app| async move { + App::test((), |app| { let model = app.add_model(|_| Buffer::new(0, "abc")); let events = Rc::new(RefCell::new(Vec::new())); // initially, the buffer isn't dirty. - model.update(&mut app, |buffer, ctx| { + model.update(app, |buffer, ctx| { ctx.subscribe(&model, { let events = events.clone(); move |_, event, _| events.borrow_mut().push(event.clone()) @@ -2733,7 +2733,7 @@ mod tests { }); // after the first edit, the buffer is dirty, and emits a dirtied event. - model.update(&mut app, |buffer, ctx| { + model.update(app, |buffer, ctx| { assert!(buffer.text() == "ac"); assert!(buffer.is_dirty()); assert_eq!( @@ -2752,7 +2752,7 @@ mod tests { }); // after saving, the buffer is not dirty, and emits a saved event. - model.update(&mut app, |buffer, ctx| { + model.update(app, |buffer, ctx| { assert!(!buffer.is_dirty()); assert_eq!(*events.borrow(), &[Event::Saved]); events.borrow_mut().clear(); @@ -2762,7 +2762,7 @@ mod tests { }); // after editing again, the buffer is dirty, and emits another dirty event. - model.update(&mut app, |buffer, ctx| { + model.update(app, |buffer, ctx| { assert!(buffer.text() == "aBDc"); assert!(buffer.is_dirty()); assert_eq!( @@ -2788,7 +2788,7 @@ mod tests { assert!(buffer.is_dirty()); }); - model.update(&mut app, |_, _| { + model.update(app, |_, _| { assert_eq!( *events.borrow(), &[Event::Edited(vec![Edit { diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index fbd6c8cc83..b7974a3a2e 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -6,7 +6,7 @@ use crate::{settings::Settings, watch, workspace}; use anyhow::Result; use futures_core::future::LocalBoxFuture; use gpui::{ - fonts::Properties as FontProperties, keymap::Binding, text_layout, App, AppContext, Element, + fonts::Properties as FontProperties, keymap::Binding, text_layout, AppContext, Element, ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, View, ViewContext, WeakViewHandle, }; @@ -1240,112 +1240,125 @@ mod tests { use super::*; use crate::{editor::Point, settings, test::sample_text}; use anyhow::Error; + use gpui::App; use unindent::Unindent; #[test] fn test_selection_with_mouse() { - App::test((), |mut app| async move { + App::test((), |app| { let buffer = app.add_model(|_| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n")); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, buffer_view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - buffer_view.update(&mut app, |view, ctx| { + buffer_view.update(app, |view, ctx| { view.begin_selection(DisplayPoint::new(2, 2), false, ctx); }); - buffer_view.read(&app, |view, app| { - let selections = view - .selections_in_range(DisplayPoint::zero()..view.max_point(app), app) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] - ); - }); + let view = buffer_view.as_ref(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] + ); - buffer_view.update(&mut app, |view, ctx| { + buffer_view.update(app, |view, ctx| { view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); }); - buffer_view.read(&app, |view, app| { - let selections = view - .selections_in_range(DisplayPoint::zero()..view.max_point(app), app) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] - ); - }); + let view = buffer_view.as_ref(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] + ); - buffer_view.update(&mut app, |view, ctx| { + buffer_view.update(app, |view, ctx| { view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); }); - buffer_view.read(&app, |view, app| { - let selections = view - .selections_in_range(DisplayPoint::zero()..view.max_point(app), app) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] - ); - }); + let view = buffer_view.as_ref(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] + ); - buffer_view.update(&mut app, |view, ctx| { + buffer_view.update(app, |view, ctx| { view.end_selection(ctx); view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); }); - buffer_view.read(&app, |view, app| { - let selections = view - .selections_in_range(DisplayPoint::zero()..view.max_point(app), app) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] - ); - }); + let view = buffer_view.as_ref(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] + ); - buffer_view.update(&mut app, |view, ctx| { + buffer_view.update(app, |view, ctx| { view.begin_selection(DisplayPoint::new(3, 3), true, ctx); view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx); }); - buffer_view.read(&app, |view, app| { - let selections = view - .selections_in_range(DisplayPoint::zero()..view.max_point(app), app) - .collect::>(); - assert_eq!( - selections, - [ - DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) - ] - ); - }); + let view = buffer_view.as_ref(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [ + DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) + ] + ); - buffer_view.update(&mut app, |view, ctx| { + buffer_view.update(app, |view, ctx| { view.end_selection(ctx); }); - buffer_view.read(&app, |view, app| { - let selections = view - .selections_in_range(DisplayPoint::zero()..view.max_point(app), app) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] - ); - }); + let view = buffer_view.as_ref(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] + ); }); } #[test] - fn test_layout_line_numbers() -> Result<()> { - App::test((), |mut app| async move { + fn test_layout_line_numbers() { + App::test((), |app| { let layout_cache = TextLayoutCache::new(app.platform().fonts()); - let font_cache = app.font_cache(); + let font_cache = app.font_cache().clone(); let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6))); @@ -1353,19 +1366,17 @@ mod tests { let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - view.read(&app, |view, app| { - let layouts = view.layout_line_numbers(1000.0, &font_cache, &layout_cache, app)?; - assert_eq!(layouts.len(), 6); - Result::<()>::Ok(()) - })?; - - Ok(()) + let layouts = view + .as_ref(app) + .layout_line_numbers(1000.0, &font_cache, &layout_cache, app.as_ref()) + .unwrap(); + assert_eq!(layouts.len(), 6); }) } #[test] - fn test_fold() -> Result<()> { - App::test((), |mut app| async move { + fn test_fold() { + App::test((), |app| { let buffer = app.add_model(|_| { Buffer::new( 0, @@ -1393,8 +1404,9 @@ mod tests { let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - view.update(&mut app, |view, ctx| { - view.select_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx)?; + view.update(app, |view, ctx| { + view.select_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx) + .unwrap(); view.fold(&(), ctx); assert_eq!( view.text(ctx.app()), @@ -1449,23 +1461,19 @@ mod tests { view.unfold(&(), ctx); assert_eq!(view.text(ctx.app()), buffer.as_ref(ctx).text()); - - Ok::<(), Error>(()) - })?; - - Ok(()) - }) + }); + }); } #[test] fn test_move_cursor() -> Result<()> { - App::test((), |mut app| async move { + App::test((), |app| { let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6))); let settings = settings::channel(&app.font_cache()).unwrap().1; let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - buffer.update(&mut app, |buffer, ctx| { + buffer.update(app, |buffer, ctx| { buffer.edit( vec![ Point::new(1, 0)..Point::new(1, 0), @@ -1476,7 +1484,7 @@ mod tests { ) })?; - view.update(&mut app, |view, ctx| { + view.update(app, |view, ctx| { view.move_down(&(), ctx); assert_eq!( view.selections(ctx.app()), @@ -1495,8 +1503,8 @@ mod tests { } #[test] - fn test_backspace() -> Result<()> { - App::test((), |mut app| async move { + fn test_backspace() { + App::test((), |app| { let buffer = app.add_model(|_| { Buffer::new(0, "one two three\nfour five six\nseven eight nine\nten\n") }); @@ -1504,7 +1512,7 @@ mod tests { let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - view.update(&mut app, |view, ctx| -> Result<()> { + view.update(app, |view, ctx| { view.select_ranges( &[ // an empty selection - the preceding character is deleted @@ -1515,17 +1523,15 @@ mod tests { DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0), ], ctx, - )?; + ) + .unwrap(); view.backspace(&(), ctx); - Ok(()) - })?; + }); - buffer.read(&mut app, |buffer, _| -> Result<()> { - assert_eq!(buffer.text(), "oe two three\nfou five six\nseven ten\n"); - Ok(()) - })?; - - Ok(()) + assert_eq!( + buffer.as_ref(app).text(), + "oe two three\nfou five six\nseven ten\n" + ); }) } diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index f5d76636ff..d67b4b510e 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -469,115 +469,106 @@ mod tests { use gpui::App; #[test] - fn test_basic_folds() -> Result<()> { - App::test((), |mut app| async move { + fn test_basic_folds() { + App::test((), |app| { let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6))); - let mut map = app.read(|app| FoldMap::new(buffer.clone(), app)); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - app.read(|app| { - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(2, 4)..Point::new(4, 1), - ], - app, - )?; - assert_eq!(map.text(app), "aa…cc…eeeee"); - Ok::<(), anyhow::Error>(()) - })?; + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(2, 4)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee"); - let edits = buffer.update(&mut app, |buffer, ctx| { + let edits = buffer.update(app, |buffer, ctx| { let start_version = buffer.version.clone(); - buffer.edit( - vec![ - Point::new(0, 0)..Point::new(0, 1), - Point::new(2, 3)..Point::new(2, 3), - ], - "123", - Some(ctx), - )?; - Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::>()) - })?; + buffer + .edit( + vec![ + Point::new(0, 0)..Point::new(0, 1), + Point::new(2, 3)..Point::new(2, 3), + ], + "123", + Some(ctx), + ) + .unwrap(); + buffer.edits_since(start_version).collect::>() + }); - app.read(|app| { - map.apply_edits(&edits, app)?; - assert_eq!(map.text(app), "123a…c123c…eeeee"); - Ok::<(), anyhow::Error>(()) - })?; + map.apply_edits(&edits, app.as_ref()).unwrap(); + assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee"); - let edits = buffer.update(&mut app, |buffer, ctx| { + let edits = buffer.update(app, |buffer, ctx| { let start_version = buffer.version.clone(); - buffer.edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx))?; - Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::>()) - })?; + buffer + .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx)) + .unwrap(); + buffer.edits_since(start_version).collect::>() + }); - app.read(|app| { - map.apply_edits(&edits, app)?; - assert_eq!(map.text(app), "123a…c123456eee"); + map.apply_edits(&edits, app.as_ref()).unwrap(); + assert_eq!(map.text(app.as_ref()), "123a…c123456eee"); - map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app)?; - assert_eq!(map.text(app), "123aaaaa\nbbbbbb\nccc123456eee"); + map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref()) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee"); + }); + } - Ok(()) - }) + #[test] + fn test_overlapping_folds() { + App::test((), |app| { + let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6))); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(0, 4)..Point::new(1, 0), + Point::new(1, 2)..Point::new(3, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…eeeee"); }) } #[test] - fn test_overlapping_folds() -> Result<()> { - App::test((), |mut app| async move { + fn test_merging_folds_via_edit() { + App::test((), |app| { let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6))); - app.read(|app| { - let mut map = FoldMap::new(buffer.clone(), app); - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(0, 4)..Point::new(1, 0), - Point::new(1, 2)..Point::new(3, 2), - Point::new(3, 1)..Point::new(4, 1), - ], - app, - )?; - assert_eq!(map.text(app), "aa…eeeee"); - Ok(()) - }) - }) - } + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - #[test] - fn test_merging_folds_via_edit() -> Result<()> { - App::test((), |mut app| async move { - let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6))); - let mut map = app.read(|app| FoldMap::new(buffer.clone(), app)); + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee"); - app.read(|app| { - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(3, 1)..Point::new(4, 1), - ], - app, - )?; - assert_eq!(map.text(app), "aa…cccc\nd…eeeee"); - Ok::<(), anyhow::Error>(()) - })?; - - let edits = buffer.update(&mut app, |buffer, ctx| { + let edits = buffer.update(app, |buffer, ctx| { let start_version = buffer.version.clone(); - buffer.edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx))?; - Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::>()) - })?; + buffer + .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx)) + .unwrap(); + buffer.edits_since(start_version).collect::>() + }); - app.read(|app| { - map.apply_edits(&edits, app)?; - assert_eq!(map.text(app), "aa…eeeee"); - Ok(()) - }) - }) + map.apply_edits(&edits, app.as_ref()).unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…eeeee"); + }); } #[test] - fn test_random_folds() -> Result<()> { + fn test_random_folds() { use crate::editor::ToPoint; use crate::util::RandomCharIter; use rand::prelude::*; @@ -597,15 +588,15 @@ mod tests { println!("{:?}", seed); let mut rng = StdRng::seed_from_u64(seed); - App::test((), |mut app| async move { + App::test((), |app| { let buffer = app.add_model(|_| { let len = rng.gen_range(0..10); let text = RandomCharIter::new(&mut rng).take(len).collect::(); Buffer::new(0, text) }); - let mut map = app.read(|app| FoldMap::new(buffer.clone(), app)); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - app.read(|app| { + { let buffer = buffer.as_ref(app); let fold_count = rng.gen_range(0..10); @@ -616,93 +607,83 @@ mod tests { fold_ranges.push(start..end); } - map.fold(fold_ranges, app)?; + map.fold(fold_ranges, app.as_ref()).unwrap(); let mut expected_text = buffer.text(); - for fold_range in map.merged_fold_ranges(app).into_iter().rev() { + for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() { expected_text.replace_range(fold_range.start..fold_range.end, "…"); } - assert_eq!(map.text(app), expected_text); + assert_eq!(map.text(app.as_ref()), expected_text); - for fold_range in map.merged_fold_ranges(app) { + for fold_range in map.merged_fold_ranges(app.as_ref()) { let display_point = map.to_display_point(fold_range.start.to_point(buffer).unwrap()); assert!(map.is_line_folded(display_point.row())); } + } - Ok::<(), anyhow::Error>(()) - })?; - - let edits = buffer.update(&mut app, |buffer, ctx| { + let edits = buffer.update(app, |buffer, ctx| { let start_version = buffer.version.clone(); let edit_count = rng.gen_range(1..10); buffer.randomly_edit(&mut rng, edit_count, Some(ctx)); - Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::>()) - })?; + buffer.edits_since(start_version).collect::>() + }); - app.read(|app| { - map.apply_edits(&edits, app)?; + map.apply_edits(&edits, app.as_ref()).unwrap(); - let buffer = map.buffer.as_ref(app); - let mut expected_text = buffer.text(); - let mut expected_buffer_rows = Vec::new(); - let mut next_row = buffer.max_point().row; - for fold_range in map.merged_fold_ranges(app).into_iter().rev() { - let fold_start = buffer.point_for_offset(fold_range.start).unwrap(); - let fold_end = buffer.point_for_offset(fold_range.end).unwrap(); - expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev()); - next_row = fold_start.row; + let buffer = map.buffer.as_ref(app); + let mut expected_text = buffer.text(); + let mut expected_buffer_rows = Vec::new(); + let mut next_row = buffer.max_point().row; + for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() { + let fold_start = buffer.point_for_offset(fold_range.start).unwrap(); + let fold_end = buffer.point_for_offset(fold_range.end).unwrap(); + expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev()); + next_row = fold_start.row; - expected_text.replace_range(fold_range.start..fold_range.end, "…"); - } - expected_buffer_rows.extend((0..=next_row).rev()); - expected_buffer_rows.reverse(); + expected_text.replace_range(fold_range.start..fold_range.end, "…"); + } + expected_buffer_rows.extend((0..=next_row).rev()); + expected_buffer_rows.reverse(); - assert_eq!(map.text(app), expected_text); + assert_eq!(map.text(app.as_ref()), expected_text); - for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() { - let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row(); - assert_eq!( - map.buffer_rows(display_row).unwrap().collect::>(), - expected_buffer_rows[idx..], - ); - } - - Ok::<(), anyhow::Error>(()) - })?; - - Ok::<(), anyhow::Error>(()) - })?; + for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() { + let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row(); + assert_eq!( + map.buffer_rows(display_row).unwrap().collect::>(), + expected_buffer_rows[idx..], + ); + } + }); } - - Ok(()) } #[test] - fn test_buffer_rows() -> Result<()> { - App::test((), |mut app| async move { + fn test_buffer_rows() { + App::test((), |app| { let text = sample_text(6, 6) + "\n"; let buffer = app.add_model(|_| Buffer::new(0, text)); - app.read(|app| { - let mut map = FoldMap::new(buffer.clone(), app); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(3, 1)..Point::new(4, 1), - ], - app, - )?; + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); - assert_eq!(map.text(app), "aa…cccc\nd…eeeee\nffffff\n"); - assert_eq!(map.buffer_rows(0)?.collect::>(), vec![0, 3, 5, 6]); - assert_eq!(map.buffer_rows(3)?.collect::>(), vec![6]); - - Ok(()) - }) - }) + assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n"); + assert_eq!( + map.buffer_rows(0).unwrap().collect::>(), + vec![0, 3, 5, 6] + ); + assert_eq!(map.buffer_rows(3).unwrap().collect::>(), vec![6]); + }); } impl FoldMap { diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 2679766aa1..7bc17627dd 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -292,52 +292,51 @@ pub fn collapse_tabs( mod tests { use super::*; use crate::test::*; - use anyhow::Error; use gpui::App; #[test] - fn test_chars_at() -> Result<()> { - App::test((), |mut app| async move { + fn test_chars_at() { + App::test((), |app| { let text = sample_text(6, 6); let buffer = app.add_model(|_| Buffer::new(0, text)); let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx)); - buffer.update(&mut app, |buffer, ctx| { - buffer.edit( - vec![ - Point::new(1, 0)..Point::new(1, 0), - Point::new(1, 1)..Point::new(1, 1), - Point::new(2, 1)..Point::new(2, 1), - ], - "\t", - Some(ctx), - ) - })?; + buffer + .update(app, |buffer, ctx| { + buffer.edit( + vec![ + Point::new(1, 0)..Point::new(1, 0), + Point::new(1, 1)..Point::new(1, 1), + Point::new(2, 1)..Point::new(2, 1), + ], + "\t", + Some(ctx), + ) + }) + .unwrap(); - map.read(&app, |map, ctx| { - assert_eq!( - map.chars_at(DisplayPoint::new(1, 0), ctx)? - .take(10) - .collect::(), - " b bb" - ); - assert_eq!( - map.chars_at(DisplayPoint::new(1, 2), ctx)? - .take(10) - .collect::(), - " b bbbb" - ); - assert_eq!( - map.chars_at(DisplayPoint::new(1, 6), ctx)? - .take(13) - .collect::(), - " bbbbb\nc c" - ); - - Ok::<(), Error>(()) - })?; - - Ok(()) - }) + let map = map.as_ref(app); + assert_eq!( + map.chars_at(DisplayPoint::new(1, 0), app.as_ref()) + .unwrap() + .take(10) + .collect::(), + " b bb" + ); + assert_eq!( + map.chars_at(DisplayPoint::new(1, 2), app.as_ref()) + .unwrap() + .take(10) + .collect::(), + " b bbbb" + ); + assert_eq!( + map.chars_at(DisplayPoint::new(1, 6), app.as_ref()) + .unwrap() + .take(13) + .collect::(), + " bbbbb\nc c" + ); + }); } #[test] @@ -364,14 +363,14 @@ mod tests { } #[test] - fn test_max_point() -> Result<()> { - App::test((), |mut app| async move { + fn test_max_point() { + App::test((), |app| { let buffer = app.add_model(|_| Buffer::new(0, "aaa\n\t\tbbb")); let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx)); - map.read(&app, |map, app| { - assert_eq!(map.max_point(app), DisplayPoint::new(1, 11)) - }); - Ok(()) - }) + assert_eq!( + map.as_ref(app).max_point(app.as_ref()), + DisplayPoint::new(1, 11) + ) + }); } } diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 28a0ae0360..d03ce43294 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -11,7 +11,7 @@ use gpui::{ fonts::{Properties, Weight}, geometry::vector::vec2f, keymap::{self, Binding}, - App, AppContext, Axis, Border, Entity, ModelHandle, MutableAppContext, View, ViewContext, + AppContext, Axis, Border, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, WeakViewHandle, }; use std::cmp; @@ -394,20 +394,23 @@ mod tests { editor, settings, workspace::{Workspace, WorkspaceView}, }; - use anyhow::Result; use gpui::App; use smol::fs; use tempdir::TempDir; #[test] - fn test_matching_paths() -> Result<()> { - App::test((), |mut app| async move { - let tmp_dir = TempDir::new("example")?; - fs::create_dir(tmp_dir.path().join("a")).await?; - fs::write(tmp_dir.path().join("a/banana"), "banana").await?; - fs::write(tmp_dir.path().join("a/bandana"), "bandana").await?; - super::init(&mut app); - editor::init(&mut app); + fn test_matching_paths() { + App::test_async((), |app| async move { + let tmp_dir = TempDir::new("example").unwrap(); + fs::create_dir(tmp_dir.path().join("a")).await.unwrap(); + fs::write(tmp_dir.path().join("a/banana"), "banana") + .await + .unwrap(); + fs::write(tmp_dir.path().join("a/bandana"), "bandana") + .await + .unwrap(); + super::init(app); + editor::init(app); let settings = settings::channel(&app.font_cache()).unwrap().1; let workspace = app.add_model(|ctx| Workspace::new(vec![tmp_dir.path().into()], ctx)); @@ -420,16 +423,15 @@ mod tests { "file_finder:toggle".into(), (), ); - let (finder, query_buffer) = workspace_view.read(&app, |view, ctx| { - let finder = view - .modal() - .cloned() - .unwrap() - .downcast::() - .unwrap(); - let query_buffer = finder.as_ref(ctx).query_buffer.clone(); - (finder, query_buffer) - }); + + let finder = workspace_view + .as_ref(app) + .modal() + .cloned() + .unwrap() + .downcast::() + .unwrap(); + let query_buffer = finder.as_ref(app).query_buffer.clone(); let chain = vec![finder.id(), query_buffer.id()]; app.dispatch_action(window_id, chain.clone(), "buffer:insert", "b".to_string()); @@ -452,7 +454,7 @@ mod tests { // (), // ); // app.finish_pending_tasks().await; // Load Buffer and open BufferView. - // let active_pane = workspace_view.read(&app, |view, _| view.active_pane().clone()); + // let active_pane = workspace_view.as_ref(app).active_pane().clone(); // assert_eq!( // active_pane.state(&app), // pane::State { @@ -462,7 +464,6 @@ mod tests { // }] // } // ); - Ok(()) - }) + }); } } diff --git a/zed/src/main.rs b/zed/src/main.rs index f7e4f0346b..6d171a5a29 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -1,5 +1,5 @@ use fs::OpenOptions; -use gpui::platform::{current as platform, PathPromptOptions, Runner as _}; +use gpui::platform::PathPromptOptions; use log::LevelFilter; use simplelog::SimpleLogger; use std::{fs, path::PathBuf}; @@ -13,6 +13,7 @@ fn main() { let app = gpui::App::new(assets::Assets).unwrap(); let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap(); + app.set_menus(menus::MENUS); app.on_menu_command({ let settings_rx = settings_rx.clone(); move |command, ctx| match command { @@ -34,7 +35,7 @@ fn main() { _ => ctx.dispatch_global_action(command, ()), } }) - .run(move |ctx| { + .on_finish_launching(move |ctx| { workspace::init(ctx); editor::init(ctx); file_finder::init(ctx); @@ -53,7 +54,8 @@ fn main() { }, ); } - }); + }) + .run(); } fn init_logger() { diff --git a/zed/src/workspace/mod.rs b/zed/src/workspace/mod.rs index e1a087049c..91dd99419a 100644 --- a/zed/src/workspace/mod.rs +++ b/zed/src/workspace/mod.rs @@ -9,7 +9,7 @@ pub use workspace::*; pub use workspace_view::*; use crate::{settings::Settings, watch}; -use gpui::{App, MutableAppContext}; +use gpui::MutableAppContext; use std::path::PathBuf; pub fn init(app: &mut MutableAppContext) { @@ -64,10 +64,10 @@ mod tests { #[test] fn test_open_paths_action() { - App::test((), |mut app| async move { + App::test((), |app| { let settings = settings::channel(&app.font_cache()).unwrap().1; - init(&mut app); + init(app); let dir = temp_tree(json!({ "a": { @@ -94,7 +94,7 @@ mod tests { settings: settings.clone(), }, ); - assert_eq!(app.window_ids().len(), 1); + assert_eq!(app.window_ids().count(), 1); app.dispatch_global_action( "workspace:open_paths", @@ -103,11 +103,19 @@ mod tests { settings: settings.clone(), }, ); - assert_eq!(app.window_ids().len(), 1); - let workspace_view_1 = app.root_view::(app.window_ids()[0]).unwrap(); - workspace_view_1.read(&app, |view, app| { - assert_eq!(view.workspace.as_ref(app).worktrees().len(), 2); - }); + assert_eq!(app.window_ids().count(), 1); + let workspace_view_1 = app + .root_view::(app.window_ids().next().unwrap()) + .unwrap(); + assert_eq!( + workspace_view_1 + .as_ref(app) + .workspace + .as_ref(app) + .worktrees() + .len(), + 2 + ); app.dispatch_global_action( "workspace:open_paths", @@ -119,7 +127,7 @@ mod tests { settings: settings.clone(), }, ); - assert_eq!(app.window_ids().len(), 2); + assert_eq!(app.window_ids().count(), 2); }); } } diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index 903292a63e..58acb86abb 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -5,7 +5,7 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, keymap::Binding, - App, AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext, + AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext, }; use std::cmp; diff --git a/zed/src/workspace/workspace.rs b/zed/src/workspace/workspace.rs index 092144791c..cc5cfec602 100644 --- a/zed/src/workspace/workspace.rs +++ b/zed/src/workspace/workspace.rs @@ -200,23 +200,22 @@ impl Entity for Workspace { #[cfg(test)] pub trait WorkspaceHandle { - fn file_entries(&self, app: &gpui::App) -> Vec<(usize, usize)>; + fn file_entries(&self, app: &mut MutableAppContext) -> Vec<(usize, usize)>; } #[cfg(test)] impl WorkspaceHandle for ModelHandle { - fn file_entries(&self, app: &gpui::App) -> Vec<(usize, usize)> { - self.read(&app, |w, app| { - w.worktrees() - .iter() - .flat_map(|tree| { - let tree_id = tree.id(); - tree.as_ref(app) - .files() - .map(move |file| (tree_id, file.entry_id)) - }) - .collect::>() - }) + fn file_entries(&self, app: &mut MutableAppContext) -> Vec<(usize, usize)> { + self.as_ref(app) + .worktrees() + .iter() + .flat_map(|tree| { + let tree_id = tree.id(); + tree.as_ref(app) + .files() + .map(move |file| (tree_id, file.entry_id)) + }) + .collect::>() } } @@ -228,8 +227,8 @@ mod tests { use serde_json::json; #[test] - fn test_open_entry() -> Result<(), Arc> { - App::test((), |mut app| async move { + fn test_open_entry() { + App::test_async((), |app| async move { let dir = temp_tree(json!({ "a": { "aa": "aa contents", @@ -241,32 +240,29 @@ mod tests { app.finish_pending_tasks().await; // Open and populate worktree. // Get the first file entry. - let entry = workspace.read(&app, |w, app| { - let tree = w.worktrees.iter().next().unwrap(); - let entry_id = tree.as_ref(app).files().next().unwrap().entry_id; - (tree.id(), entry_id) - }); + let tree = workspace.as_ref(app).worktrees.iter().next().unwrap(); + let entry_id = tree.as_ref(app).files().next().unwrap().entry_id; + let entry = (tree.id(), entry_id); // Open the same entry twice before it finishes loading. - let (future_1, future_2) = workspace.update(&mut app, |w, app| { + let (future_1, future_2) = workspace.update(app, |w, app| { ( w.open_entry(entry, app).unwrap(), w.open_entry(entry, app).unwrap(), ) }); - let handle_1 = future_1.await?; - let handle_2 = future_2.await?; + let handle_1 = future_1.await.unwrap(); + let handle_2 = future_2.await.unwrap(); assert_eq!(handle_1.id(), handle_2.id()); // Open the same entry again now that it has loaded let handle_3 = workspace - .update(&mut app, |w, app| w.open_entry(entry, app).unwrap()) - .await?; + .update(app, |w, app| w.open_entry(entry, app).unwrap()) + .await + .unwrap(); assert_eq!(handle_3.id(), handle_1.id()); - - Ok(()) }) } } diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 32e16fc014..aaf1e9eb9a 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -2,8 +2,8 @@ use super::{pane, Pane, PaneGroup, SplitDirection, Workspace}; use crate::{settings::Settings, watch}; use futures_core::future::LocalBoxFuture; use gpui::{ - color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, App, - AppContext, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, + color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, + Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, }; use log::{error, info}; use std::{collections::HashSet, path::PathBuf}; @@ -389,13 +389,12 @@ impl View for WorkspaceView { mod tests { use super::{pane, Workspace, WorkspaceView}; use crate::{settings, test::temp_tree, workspace::WorkspaceHandle as _}; - use anyhow::Result; use gpui::App; use serde_json::json; #[test] - fn test_open_entry() -> Result<()> { - App::test((), |mut app| async move { + fn test_open_entry() { + App::test_async((), |app| async move { let dir = temp_tree(json!({ "a": { "aa": "aa contents", @@ -407,64 +406,70 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx)); app.finish_pending_tasks().await; // Open and populate worktree. - let entries = workspace.file_entries(&app); + let entries = workspace.file_entries(app); let (_, workspace_view) = app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx)); // Open the first entry - workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx)); + workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx)); app.finish_pending_tasks().await; - workspace_view.read(&app, |w, app| { - assert_eq!(w.active_pane().as_ref(app).items().len(), 1); - }); + assert_eq!( + workspace_view + .as_ref(app) + .active_pane() + .as_ref(app) + .items() + .len(), + 1 + ); // Open the second entry - workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[1], ctx)); + workspace_view.update(app, |w, ctx| w.open_entry(entries[1], ctx)); app.finish_pending_tasks().await; - workspace_view.read(&app, |w, app| { - let active_pane = w.active_pane().as_ref(app); - assert_eq!(active_pane.items().len(), 2); - assert_eq!( - active_pane.active_item().unwrap().entry_id(app), - Some(entries[1]) - ); - }); + let active_pane = workspace_view.as_ref(app).active_pane().as_ref(app); + assert_eq!(active_pane.items().len(), 2); + assert_eq!( + active_pane.active_item().unwrap().entry_id(app.as_ref()), + Some(entries[1]) + ); // Open the first entry again - workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx)); + workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx)); app.finish_pending_tasks().await; - workspace_view.read(&app, |w, app| { - let active_pane = w.active_pane().as_ref(app); - assert_eq!(active_pane.items().len(), 2); - assert_eq!( - active_pane.active_item().unwrap().entry_id(app), - Some(entries[0]) - ); - }); + let active_pane = workspace_view.as_ref(app).active_pane().as_ref(app); + assert_eq!(active_pane.items().len(), 2); + assert_eq!( + active_pane.active_item().unwrap().entry_id(app.as_ref()), + Some(entries[0]) + ); // Open the third entry twice concurrently - workspace_view.update(&mut app, |w, ctx| { + workspace_view.update(app, |w, ctx| { w.open_entry(entries[2], ctx); w.open_entry(entries[2], ctx); }); app.finish_pending_tasks().await; - workspace_view.read(&app, |w, app| { - assert_eq!(w.active_pane().as_ref(app).items().len(), 3); - }); - - Ok(()) - }) + assert_eq!( + workspace_view + .as_ref(app) + .active_pane() + .as_ref(app) + .items() + .len(), + 3 + ); + }); } #[test] - fn test_pane_actions() -> Result<()> { - App::test((), |mut app| async move { - pane::init(&mut app); + fn test_pane_actions() { + App::test_async((), |app| async move { + pane::init(app); let dir = temp_tree(json!({ "a": { @@ -474,35 +479,37 @@ mod tests { }, })); - let settings = settings::channel(&app.font_cache()).unwrap().1; + let settings = settings::channel(app.font_cache()).unwrap().1; let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx)); app.finish_pending_tasks().await; // Open and populate worktree. - let entries = workspace.file_entries(&app); + let entries = workspace.file_entries(app); let (window_id, workspace_view) = app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx)); - workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx)); + workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx)); app.finish_pending_tasks().await; - let pane_1 = workspace_view.read(&app, |w, _| w.active_pane().clone()); + let pane_1 = workspace_view.as_ref(app).active_pane().clone(); app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ()); - let pane_2 = workspace_view.read(&app, |w, _| w.active_pane().clone()); + let pane_2 = workspace_view.as_ref(app).active_pane().clone(); assert_ne!(pane_1, pane_2); - pane_2.read(&app, |p, app| { - assert_eq!(p.active_item().unwrap().entry_id(app), Some(entries[0])); - }); + assert_eq!( + pane_2 + .as_ref(app) + .active_item() + .unwrap() + .entry_id(app.downgrade()), + Some(entries[0]) + ); app.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); - workspace_view.read(&app, |w, _| { - assert_eq!(w.panes.len(), 1); - assert_eq!(w.active_pane(), &pane_1) - }); - - Ok(()) - }) + let w = workspace_view.as_ref(app); + assert_eq!(w.panes.len(), 1); + assert_eq!(w.active_pane(), &pane_1); + }); } } diff --git a/zed/src/worktree/worktree.rs b/zed/src/worktree/worktree.rs index bbb264df62..27f42d904d 100644 --- a/zed/src/worktree/worktree.rs +++ b/zed/src/worktree/worktree.rs @@ -648,8 +648,8 @@ mod test { use std::os::unix; #[test] - fn test_populate_and_search() -> Result<()> { - App::test((), |mut app| async move { + fn test_populate_and_search() { + App::test_async((), |app| async move { let dir = temp_tree(json!({ "root": { "apple": "", @@ -666,33 +666,31 @@ mod test { })); let root_link_path = dir.path().join("root_link"); - unix::fs::symlink(&dir.path().join("root"), &root_link_path)?; + unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap(); let tree = app.add_model(|ctx| Worktree::new(1, root_link_path, Some(ctx))); app.finish_pending_tasks().await; - tree.read(&app, |tree, _| { - assert_eq!(tree.file_count(), 4); - let results = match_paths(&[tree.clone()], "bna", false, false, 10) - .iter() - .map(|result| tree.entry_path(result.entry_id)) - .collect::, _>>() - .unwrap(); - assert_eq!( - results, - vec![ - PathBuf::from("root_link/banana/carrot/date"), - PathBuf::from("root_link/banana/carrot/endive"), - ] - ); - }); - Ok(()) - }) + let tree = tree.as_ref(app); + assert_eq!(tree.file_count(), 4); + let results = match_paths(&[tree.clone()], "bna", false, false, 10) + .iter() + .map(|result| tree.entry_path(result.entry_id)) + .collect::, _>>() + .unwrap(); + assert_eq!( + results, + vec![ + PathBuf::from("root_link/banana/carrot/date"), + PathBuf::from("root_link/banana/carrot/endive"), + ] + ); + }); } #[test] fn test_save_file() { - App::test((), |mut app| async move { + App::test_async((), |app| async move { let dir = temp_tree(json!({ "file1": "the old contents", })); @@ -700,24 +698,18 @@ mod test { let tree = app.add_model(|ctx| Worktree::new(1, dir.path(), Some(ctx))); app.finish_pending_tasks().await; - let file_id = tree.read(&app, |tree, _| { - let entry = tree.files().next().unwrap(); - assert_eq!(entry.path.file_name().unwrap(), "file1"); - entry.entry_id - }); + let entry = tree.as_ref(app).files().next().unwrap(); + assert_eq!(entry.path.file_name().unwrap(), "file1"); + let file_id = entry.entry_id; let buffer = Buffer::new(1, "a line of text.\n".repeat(10 * 1024)); - tree.update(&mut app, |tree, ctx| { + tree.update(app, |tree, ctx| { smol::block_on(tree.save(file_id, buffer.snapshot(), ctx.app())).unwrap() }); - let history = tree - .read(&app, |tree, _| tree.load_history(file_id)) - .await - .unwrap(); - + let history = tree.as_ref(app).load_history(file_id).await.unwrap(); assert_eq!(history.base_text, buffer.text()); - }) + }); } } From 448dace2817a98e730e1fd0b79c5c874f7616e8b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 9 Apr 2021 21:33:17 -0600 Subject: [PATCH 4/9] Pass the on_finish_launching callback to Platform::run --- gpui/examples/text.rs | 10 +++---- gpui/src/app.rs | 37 ++++++++++------------- gpui/src/platform/mac/mod.rs | 10 ++----- gpui/src/platform/mac/platform.rs | 50 ++++++++++++++----------------- gpui/src/platform/mod.rs | 2 +- gpui/src/platform/test.rs | 4 ++- zed/src/main.rs | 5 ++-- 7 files changed, 51 insertions(+), 67 deletions(-) diff --git a/gpui/examples/text.rs b/gpui/examples/text.rs index 2dad6e0786..df1f367cdb 100644 --- a/gpui/examples/text.rs +++ b/gpui/examples/text.rs @@ -10,12 +10,10 @@ use simplelog::SimpleLogger; fn main() { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - let app = gpui::App::new(()).unwrap(); - app.on_finish_launching(|app| { - app.platform().activate(true); - app.add_window(|_| TextView); - }) - .run(); + gpui::App::new(()).unwrap().run(|ctx| { + ctx.platform().activate(true); + ctx.add_window(|_| TextView); + }); } struct TextView; diff --git a/gpui/src/app.rs b/gpui/src/app.rs index ab3f471e73..48720eabcb 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -92,8 +92,8 @@ impl App { let platform = platform::test::platform(); let foreground = Rc::new(executor::Foreground::test()); let ctx = Rc::new(RefCell::new(MutableAppContext::new( - foreground.clone(), - Arc::new(platform), + foreground, + Rc::new(platform), asset_source, ))); ctx.borrow_mut().weak_self = Some(Rc::downgrade(&ctx)); @@ -110,7 +110,7 @@ impl App { let foreground = Rc::new(executor::Foreground::test()); let ctx = Rc::new(RefCell::new(MutableAppContext::new( foreground.clone(), - Arc::new(platform), + Rc::new(platform), asset_source, ))); let mut ctx_ref = ctx.borrow_mut(); @@ -200,24 +200,19 @@ impl App { self } - pub fn on_finish_launching(self, callback: F) -> Self - where - F: 'static + FnOnce(&mut MutableAppContext), - { - let ctx = self.0.clone(); - self.0 - .borrow() - .platform - .on_finish_launching(Box::new(move || callback(&mut *ctx.borrow_mut()))); - self - } - pub fn set_menus(&self, menus: &[Menu]) { self.0.borrow().platform.set_menus(menus); } - pub fn run(self) { - platform::current::run(); + pub fn run(self, on_finish_launching: F) + where + F: 'static + FnOnce(&mut MutableAppContext), + { + let platform = self.platform(); + platform.run(Box::new(move || { + let mut ctx = self.0.borrow_mut(); + on_finish_launching(&mut *ctx); + })) } pub fn on_window_invalidated( @@ -354,7 +349,7 @@ impl App { self.0.borrow().font_cache.clone() } - pub fn platform(&self) -> Arc { + pub fn platform(&self) -> Rc { self.0.borrow().platform.clone() } } @@ -394,7 +389,7 @@ type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext); pub struct MutableAppContext { weak_self: Option>>, - platform: Arc, + platform: Rc, font_cache: Arc, assets: Arc, ctx: AppContext, @@ -422,7 +417,7 @@ pub struct MutableAppContext { impl MutableAppContext { pub fn new( foreground: Rc, - platform: Arc, + platform: Rc, asset_source: impl AssetSource, ) -> Self { let fonts = platform.fonts(); @@ -466,7 +461,7 @@ impl MutableAppContext { &self.ctx } - pub fn platform(&self) -> Arc { + pub fn platform(&self) -> Rc { self.platform.clone() } diff --git a/gpui/src/platform/mac/mod.rs b/gpui/src/platform/mac/mod.rs index 07ada3650a..c2a88c12a4 100644 --- a/gpui/src/platform/mac/mod.rs +++ b/gpui/src/platform/mac/mod.rs @@ -12,15 +12,11 @@ use cocoa::base::{BOOL, NO, YES}; pub use dispatcher::Dispatcher; pub use fonts::FontSystem; use platform::MacPlatform; -use std::sync::Arc; +use std::rc::Rc; use window::Window; -pub fn platform() -> Arc { - MacPlatform::new() -} - -pub fn run() { - MacPlatform::run(); +pub fn platform() -> Rc { + Rc::new(MacPlatform::new()) } trait BoolExt { diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index 40bd3566a5..4201100a6d 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -29,7 +29,7 @@ use std::{ sync::Arc, }; -const MAC_PLATFORM_IVAR: &'static str = "runner"; +const MAC_PLATFORM_IVAR: &'static str = "platform"; static mut APP_CLASS: *const Class = ptr::null(); static mut APP_DELEGATE_CLASS: *const Class = ptr::null(); @@ -90,35 +90,12 @@ struct Callbacks { } impl MacPlatform { - pub fn new() -> Arc { - let result = Arc::new(Self { + pub fn new() -> Self { + Self { dispatcher: Arc::new(Dispatcher), fonts: Arc::new(FontSystem::new()), callbacks: Default::default(), menu_item_actions: Default::default(), - }); - - unsafe { - let app: id = msg_send![APP_CLASS, sharedApplication]; - let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; - let self_ptr = result.as_ref() as *const Self as *const c_void; - app.setDelegate_(app_delegate); - (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr); - (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr); - } - - result - } - - pub fn run() { - unsafe { - let pool = NSAutoreleasePool::new(nil); - let app: id = msg_send![APP_CLASS, sharedApplication]; - - app.run(); - pool.drain(); - (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); - (*app.delegate()).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); } } @@ -220,8 +197,25 @@ impl platform::Platform for MacPlatform { self.callbacks.borrow_mut().open_files = Some(callback); } - fn on_finish_launching(&self, callback: Box ()>) { - self.callbacks.borrow_mut().finish_launching = Some(callback); + fn run(&self, on_finish_launching: Box ()>) { + self.callbacks.borrow_mut().finish_launching = Some(on_finish_launching); + + unsafe { + let app: id = msg_send![APP_CLASS, sharedApplication]; + let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; + app.setDelegate_(app_delegate); + + let self_ptr = self as *const Self as *const c_void; + (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr); + (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr); + + let pool = NSAutoreleasePool::new(nil); + app.run(); + pool.drain(); + + (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); + (*app.delegate()).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); + } } fn dispatcher(&self) -> Arc { diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 6e5d53585f..39825c941e 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -28,7 +28,7 @@ pub trait Platform { fn on_resign_active(&self, callback: Box); fn on_event(&self, callback: Box bool>); fn on_open_files(&self, callback: Box)>); - fn on_finish_launching(&self, callback: Box ()>); + fn run(&self, on_finish_launching: Box ()>); fn dispatcher(&self) -> Arc; fn fonts(&self) -> Arc; diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 1cd4399c1b..b0d19232dc 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -39,7 +39,9 @@ impl super::Platform for Platform { fn on_open_files(&self, _: Box)>) {} - fn on_finish_launching(&self, _: Box ()>) {} + fn run(&self, _on_finish_launching: Box ()>) { + unimplemented!() + } fn dispatcher(&self) -> Arc { self.dispatcher.clone() diff --git a/zed/src/main.rs b/zed/src/main.rs index 6d171a5a29..b214924a80 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -35,7 +35,7 @@ fn main() { _ => ctx.dispatch_global_action(command, ()), } }) - .on_finish_launching(move |ctx| { + .run(move |ctx| { workspace::init(ctx); editor::init(ctx); file_finder::init(ctx); @@ -54,8 +54,7 @@ fn main() { }, ); } - }) - .run(); + }); } fn init_logger() { From 97a8a8ed43f975536d60d617fe7974cb833f62be Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 10 Apr 2021 00:05:09 -0600 Subject: [PATCH 5/9] Remove unsafe code from App::test_async I don't actually think it was correct to allow the future to borrow a mutable app reference. I went back to passing a wrapper around the refcell to async tests. They'll be a bit more annoying to write but also totally safe. --- gpui/src/app.rs | 210 +++++++++++-------------- zed/src/editor/buffer_element.rs | 20 +-- zed/src/editor/buffer_view.rs | 110 ++++++------- zed/src/editor/display_map/fold_map.rs | 18 +-- zed/src/editor/display_map/mod.rs | 10 +- zed/src/file_finder.rs | 40 ++--- zed/src/workspace/mod.rs | 4 +- zed/src/workspace/workspace.rs | 22 +-- zed/src/workspace/workspace_view.rs | 138 ++++++++-------- zed/src/worktree/worktree.rs | 60 ++++--- 10 files changed, 317 insertions(+), 315 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 48720eabcb..702fc952a7 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -45,8 +45,8 @@ pub trait View: Entity { } } -pub trait ModelAsRef { - fn model(&self, handle: &ModelHandle) -> &T; +pub trait ReadModel { + fn read_model(&self, handle: &ModelHandle) -> &T; } pub trait UpdateModel { @@ -56,8 +56,8 @@ pub trait UpdateModel { F: FnOnce(&mut T, &mut ModelContext) -> S; } -pub trait ViewAsRef { - fn view(&self, handle: &ViewHandle) -> &T; +pub trait ReadView { + fn read_view(&self, handle: &ViewHandle) -> &T; } pub trait UpdateView { @@ -84,6 +84,9 @@ pub enum MenuItem<'a> { #[derive(Clone)] pub struct App(Rc>); +#[derive(Clone)] +pub struct TestAppContext(Rc>); + impl App { pub fn test T>( asset_source: A, @@ -101,27 +104,21 @@ impl App { f(&mut *ctx) } - pub fn test_async<'a, T, F, A: AssetSource, Fn>(asset_source: A, f: Fn) -> T + pub fn test_async(asset_source: A, f: Fn) -> T where - Fn: FnOnce(&'a mut MutableAppContext) -> F, - F: Future + 'a, + Fn: FnOnce(TestAppContext) -> F, + F: Future, { let platform = platform::test::platform(); let foreground = Rc::new(executor::Foreground::test()); - let ctx = Rc::new(RefCell::new(MutableAppContext::new( + let ctx = TestAppContext(Rc::new(RefCell::new(MutableAppContext::new( foreground.clone(), Rc::new(platform), asset_source, - ))); - let mut ctx_ref = ctx.borrow_mut(); - ctx_ref.weak_self = Some(Rc::downgrade(&ctx)); - let ctx = &mut *ctx_ref; + )))); + ctx.0.borrow_mut().weak_self = Some(Rc::downgrade(&ctx.0)); - // TODO - is there a better way of getting this to compile? - let ctx = unsafe { std::mem::transmute(ctx) }; let future = f(ctx); - - drop(ctx_ref); smol::block_on(foreground.run(future)) } @@ -208,42 +205,27 @@ impl App { where F: 'static + FnOnce(&mut MutableAppContext), { - let platform = self.platform(); + let platform = self.0.borrow().platform.clone(); platform.run(Box::new(move || { let mut ctx = self.0.borrow_mut(); on_finish_launching(&mut *ctx); })) } - pub fn on_window_invalidated( - &self, - window_id: usize, - callback: F, - ) { - self.0 - .borrow_mut() - .on_window_invalidated(window_id, callback); + pub fn font_cache(&self) -> Arc { + self.0.borrow().font_cache.clone() } - pub fn add_action(&self, name: S, handler: F) - where - S: Into, - V: View, - T: Any, - F: 'static + FnMut(&mut V, &T, &mut ViewContext), - { - self.0.borrow_mut().add_action(name, handler); - } - - pub fn add_global_action(&self, name: S, handler: F) - where - S: Into, - T: 'static + Any, - F: 'static + FnMut(&T, &mut MutableAppContext), - { - self.0.borrow_mut().add_global_action(name, handler); + fn update T>(&mut self, callback: F) -> T { + let mut state = self.0.borrow_mut(); + state.pending_flushes += 1; + let result = callback(&mut *state); + state.flush_effects(); + result } +} +impl TestAppContext { pub fn dispatch_action( &self, window_id: usize, @@ -259,10 +241,6 @@ impl App { ); } - pub fn add_bindings>(&self, bindings: T) { - self.0.borrow_mut().add_bindings(bindings); - } - pub fn dispatch_keystroke( &self, window_id: usize, @@ -329,7 +307,7 @@ impl App { handle } - pub fn read T>(&mut self, callback: F) -> T { + pub fn read T>(&self, callback: F) -> T { callback(self.0.borrow().downgrade()) } @@ -354,7 +332,7 @@ impl App { } } -impl UpdateModel for App { +impl UpdateModel for TestAppContext { fn update_model(&mut self, handle: &ModelHandle, update: F) -> S where T: Entity, @@ -368,7 +346,7 @@ impl UpdateModel for App { } } -impl UpdateView for App { +impl UpdateView for TestAppContext { fn update_view(&mut self, handle: &ViewHandle, update: F) -> S where T: View, @@ -1249,8 +1227,8 @@ impl MutableAppContext { } } -impl ModelAsRef for MutableAppContext { - fn model(&self, handle: &ModelHandle) -> &T { +impl ReadModel for MutableAppContext { + fn read_model(&self, handle: &ModelHandle) -> &T { if let Some(model) = self.ctx.models.get(&handle.model_id) { model .as_any() @@ -1287,8 +1265,8 @@ impl UpdateModel for MutableAppContext { } } -impl ViewAsRef for MutableAppContext { - fn view(&self, handle: &ViewHandle) -> &T { +impl ReadView for MutableAppContext { + fn read_view(&self, handle: &ViewHandle) -> &T { if let Some(window) = self.ctx.windows.get(&handle.window_id) { if let Some(view) = window.views.get(&handle.view_id) { view.as_any().downcast_ref().expect("Downcast is type safe") @@ -1387,8 +1365,8 @@ impl AppContext { } } -impl ModelAsRef for AppContext { - fn model(&self, handle: &ModelHandle) -> &T { +impl ReadModel for AppContext { + fn read_model(&self, handle: &ModelHandle) -> &T { if let Some(model) = self.models.get(&handle.model_id) { model .as_any() @@ -1400,8 +1378,8 @@ impl ModelAsRef for AppContext { } } -impl ViewAsRef for AppContext { - fn view(&self, handle: &ViewHandle) -> &T { +impl ReadView for AppContext { + fn read_view(&self, handle: &ViewHandle) -> &T { if let Some(window) = self.windows.get(&handle.window_id) { if let Some(view) = window.views.get(&handle.view_id) { view.as_any() @@ -1672,9 +1650,9 @@ impl<'a, T: Entity> ModelContext<'a, T> { } } -impl ModelAsRef for ModelContext<'_, M> { - fn model(&self, handle: &ModelHandle) -> &T { - self.app.model(handle) +impl ReadModel for ModelContext<'_, M> { + fn read_model(&self, handle: &ModelHandle) -> &T { + self.app.read_model(handle) } } @@ -1927,9 +1905,9 @@ impl<'a, T: View> ViewContext<'a, T> { } } -impl ModelAsRef for ViewContext<'_, V> { - fn model(&self, handle: &ModelHandle) -> &T { - self.app.model(handle) +impl ReadModel for ViewContext<'_, V> { + fn read_model(&self, handle: &ModelHandle) -> &T { + self.app.read_model(handle) } } @@ -1943,9 +1921,9 @@ impl UpdateModel for ViewContext<'_, V> { } } -impl ViewAsRef for ViewContext<'_, V> { - fn view(&self, handle: &ViewHandle) -> &T { - self.app.view(handle) +impl ReadView for ViewContext<'_, V> { + fn read_view(&self, handle: &ViewHandle) -> &T { + self.app.read_view(handle) } } @@ -1994,8 +1972,8 @@ impl ModelHandle { self.model_id } - pub fn as_ref<'a, A: ModelAsRef>(&self, app: &'a A) -> &'a T { - app.model(self) + pub fn read<'a, A: ReadModel>(&self, app: &'a A) -> &'a T { + app.read_model(self) } pub fn update(&self, app: &mut A, update: F) -> S @@ -2122,8 +2100,8 @@ impl ViewHandle { self.view_id } - pub fn as_ref<'a, A: ViewAsRef>(&self, app: &'a A) -> &'a T { - app.view(self) + pub fn read<'a, A: ReadView>(&self, app: &'a A) -> &'a T { + app.read_view(self) } pub fn update(&self, app: &mut A, update: F) -> S @@ -2470,9 +2448,9 @@ mod tests { ctx.notify(); ctx.emit(2); }); - assert_eq!(handle_1.as_ref(app).events, vec!["updated".to_string()]); + assert_eq!(handle_1.read(app).events, vec!["updated".to_string()]); assert_eq!( - handle_2.as_ref(app).events, + handle_2.read(app).events, vec![ "observed event 1".to_string(), "notified".to_string(), @@ -2518,10 +2496,10 @@ mod tests { }); handle_2.update(app, |_, c| c.emit(7)); - assert_eq!(handle_1.as_ref(app).events, vec![7]); + assert_eq!(handle_1.read(app).events, vec![7]); handle_2.update(app, |_, c| c.emit(5)); - assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]); + assert_eq!(handle_1.read(app).events, vec![7, 10, 5]); }) } @@ -2544,9 +2522,9 @@ mod tests { handle_1.update(app, |_, c| { c.observe(&handle_2, move |model, observed, c| { - model.events.push(observed.as_ref(c).count); + model.events.push(observed.read(c).count); c.observe(&handle_2b, |model, observed, c| { - model.events.push(observed.as_ref(c).count * 2); + model.events.push(observed.read(c).count * 2); }); }); }); @@ -2555,13 +2533,13 @@ mod tests { model.count = 7; c.notify() }); - assert_eq!(handle_1.as_ref(app).events, vec![7]); + assert_eq!(handle_1.read(app).events, vec![7]); handle_2.update(app, |model, c| { model.count = 5; c.notify() }); - assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]) + assert_eq!(handle_1.read(app).events, vec![7, 10, 5]) }) } @@ -2576,25 +2554,25 @@ mod tests { type Event = (); } - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let handle = app.add_model(|_| Model::default()); handle - .update(app, |_, c| { + .update(&mut app, |_, c| { c.spawn(async { 7 }, |model, output, _| { model.count = output; }) }) .await; - assert_eq!(handle.as_ref(app).count, 7); + app.read(|ctx| assert_eq!(handle.read(ctx).count, 7)); handle - .update(app, |_, c| { + .update(&mut app, |_, c| { c.spawn(async { 14 }, |model, output, _| { model.count = output; }) }) .await; - assert_eq!(handle.as_ref(app).count, 14); + app.read(|ctx| assert_eq!(handle.read(ctx).count, 14)); }); } @@ -2609,10 +2587,10 @@ mod tests { type Event = (); } - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let handle = app.add_model(|_| Model::default()); handle - .update(app, |_, c| { + .update(&mut app, |_, c| { c.spawn_stream( smol::stream::iter(vec![1, 2, 3]), |model, output, _| { @@ -2624,7 +2602,7 @@ mod tests { ) }) .await; - assert_eq!(handle.as_ref(app).events, [Some(1), Some(2), Some(3), None]) + app.read(|ctx| assert_eq!(handle.read(ctx).events, [Some(1), Some(2), Some(3), None])); }) } @@ -2674,9 +2652,9 @@ mod tests { ctx.emit(1); ctx.emit(2); }); - assert_eq!(handle_1.as_ref(app).events, vec!["updated".to_string()]); + assert_eq!(handle_1.read(app).events, vec!["updated".to_string()]); assert_eq!( - handle_2.as_ref(app).events, + handle_2.read(app).events, vec![ "observed event 1".to_string(), "observed event 2".to_string(), @@ -2742,13 +2720,13 @@ mod tests { }); handle_2.update(app, |_, c| c.emit(7)); - assert_eq!(handle_1.as_ref(app).events, vec![7]); + assert_eq!(handle_1.read(app).events, vec![7]); handle_2.update(app, |_, c| c.emit(5)); - assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]); + assert_eq!(handle_1.read(app).events, vec![7, 10, 5]); handle_3.update(app, |_, c| c.emit(9)); - assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5, 9]); + assert_eq!(handle_1.read(app).events, vec![7, 10, 5, 9]); }) } @@ -2837,7 +2815,7 @@ mod tests { view.update(app, |_, c| { c.observe(&model, |me, observed, c| { - me.events.push(observed.as_ref(c).count) + me.events.push(observed.read(c).count) }); }); @@ -2845,7 +2823,7 @@ mod tests { model.count = 11; c.notify(); }); - assert_eq!(view.as_ref(app).events, vec![11]); + assert_eq!(view.read(app).events, vec![11]); }) } @@ -2942,7 +2920,7 @@ mod tests { }); assert_eq!( - view_1.as_ref(app).events, + view_1.read(app).events, [ "self focused".to_string(), "self blurred".to_string(), @@ -2975,24 +2953,24 @@ mod tests { } } - App::test_async((), |app| async move { - let (_, handle) = app.add_window(|_| View::default()); + App::test_async((), |mut app| async move { + let handle = app.add_window(|_| View::default()).1; handle - .update(app, |_, c| { + .update(&mut app, |_, c| { c.spawn(async { 7 }, |me, output, _| { me.count = output; }) }) .await; - assert_eq!(handle.as_ref(app).count, 7); + app.read(|ctx| assert_eq!(handle.read(ctx).count, 7)); handle - .update(app, |_, c| { + .update(&mut app, |_, c| { c.spawn(async { 14 }, |me, output, _| { me.count = output; }) }) .await; - assert_eq!(handle.as_ref(app).count, 14); + app.read(|ctx| assert_eq!(handle.read(ctx).count, 14)); }); } @@ -3017,10 +2995,10 @@ mod tests { } } - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let (_, handle) = app.add_window(|_| View::default()); handle - .update(app, |_, c| { + .update(&mut app, |_, c| { c.spawn_stream( smol::stream::iter(vec![1_usize, 2, 3]), |me, output, _| { @@ -3033,7 +3011,7 @@ mod tests { }) .await; - assert_eq!(handle.as_ref(app).events, [Some(1), Some(2), Some(3), None]) + app.read(|ctx| assert_eq!(handle.read(ctx).events, [Some(1), Some(2), Some(3), None])) }); } @@ -3335,49 +3313,49 @@ mod tests { type Event = (); } - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let model = app.add_model(|_| Model); let (_, view) = app.add_window(|_| View); - model.update(app, |_, ctx| { + model.update(&mut app, |_, ctx| { ctx.spawn(async {}, |_, _, _| {}).detach(); // Cancel this task drop(ctx.spawn(async {}, |_, _, _| {})); }); - view.update(app, |_, ctx| { + view.update(&mut app, |_, ctx| { ctx.spawn(async {}, |_, _, _| {}).detach(); // Cancel this task drop(ctx.spawn(async {}, |_, _, _| {})); }); - assert!(!app.future_handlers.borrow().is_empty()); + assert!(!app.0.borrow().future_handlers.borrow().is_empty()); app.finish_pending_tasks().await; - assert!(app.future_handlers.borrow().is_empty()); + assert!(app.0.borrow().future_handlers.borrow().is_empty()); app.finish_pending_tasks().await; // Don't block if there are no tasks - model.update(app, |_, ctx| { + model.update(&mut app, |_, ctx| { ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {}) .detach(); // Cancel this task drop(ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {})); }); - view.update(app, |_, ctx| { + view.update(&mut app, |_, ctx| { ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {}) .detach(); // Cancel this task drop(ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {})); }); - assert!(!app.stream_handlers.borrow().is_empty()); + assert!(!app.0.borrow().stream_handlers.borrow().is_empty()); app.finish_pending_tasks().await; - assert!(app.stream_handlers.borrow().is_empty()); + assert!(app.0.borrow().stream_handlers.borrow().is_empty()); app.finish_pending_tasks().await; // Don't block if there are no tasks // Tasks are considered finished when we drop handles let mut tasks = Vec::new(); - model.update(app, |_, ctx| { + model.update(&mut app, |_, ctx| { tasks.push(Box::new(ctx.spawn(async {}, |_, _, _| {}))); tasks.push(Box::new(ctx.spawn_stream( smol::stream::iter(vec![1, 2, 3]), @@ -3386,7 +3364,7 @@ mod tests { ))); }); - view.update(app, |_, ctx| { + view.update(&mut app, |_, ctx| { tasks.push(Box::new(ctx.spawn(async {}, |_, _, _| {}))); tasks.push(Box::new(ctx.spawn_stream( smol::stream::iter(vec![1, 2, 3]), @@ -3395,12 +3373,12 @@ mod tests { ))); }); - assert!(!app.stream_handlers.borrow().is_empty()); + assert!(!app.0.borrow().stream_handlers.borrow().is_empty()); let finish_pending_tasks = app.finish_pending_tasks(); drop(tasks); finish_pending_tasks.await; - assert!(app.stream_handlers.borrow().is_empty()); + assert!(app.0.borrow().stream_handlers.borrow().is_empty()); app.finish_pending_tasks().await; // Don't block if there are no tasks }); } diff --git a/zed/src/editor/buffer_element.rs b/zed/src/editor/buffer_element.rs index e00c11f1fd..6141c0b7c6 100644 --- a/zed/src/editor/buffer_element.rs +++ b/zed/src/editor/buffer_element.rs @@ -37,7 +37,7 @@ impl BufferElement { ctx: &mut EventContext, ) -> bool { if paint.text_bounds.contains_point(position) { - let view = self.view.as_ref(ctx.app); + let view = self.view.read(ctx.app); let position = paint.point_for_position(view, layout, position, ctx.font_cache, ctx.app); ctx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd }); @@ -48,7 +48,7 @@ impl BufferElement { } fn mouse_up(&self, _position: Vector2F, ctx: &mut EventContext) -> bool { - if self.view.as_ref(ctx.app).is_selecting() { + if self.view.read(ctx.app).is_selecting() { ctx.dispatch_action("buffer:select", SelectAction::End); true } else { @@ -63,7 +63,7 @@ impl BufferElement { paint: &mut PaintState, ctx: &mut EventContext, ) -> bool { - let view = self.view.as_ref(ctx.app); + let view = self.view.read(ctx.app); if view.is_selecting() { let rect = paint.text_bounds; @@ -145,7 +145,7 @@ impl BufferElement { return false; } - let view = self.view.as_ref(ctx.app); + let view = self.view.read(ctx.app); let font_cache = &ctx.font_cache; let layout_cache = &ctx.text_layout_cache; let max_glyph_width = view.em_width(font_cache); @@ -167,7 +167,7 @@ impl BufferElement { } fn paint_gutter(&mut self, rect: RectF, layout: &LayoutState, ctx: &mut PaintContext) { - let view = self.view.as_ref(ctx.app); + let view = self.view.read(ctx.app); let line_height = view.line_height(ctx.font_cache); let scroll_top = view.scroll_position().y() * line_height; @@ -197,7 +197,7 @@ impl BufferElement { } fn paint_text(&mut self, bounds: RectF, layout: &LayoutState, ctx: &mut PaintContext) { - let view = self.view.as_ref(ctx.app); + let view = self.view.read(ctx.app); let line_height = view.line_height(ctx.font_cache); let descent = view.font_descent(ctx.font_cache); let start_row = view.scroll_position().y() as u32; @@ -313,14 +313,14 @@ impl Element for BufferElement { let app = ctx.app; let mut size = constraint.max; if size.y().is_infinite() { - let view = self.view.as_ref(app); + let view = self.view.read(app); size.set_y((view.max_point(app).row() + 1) as f32 * view.line_height(ctx.font_cache)); } if size.x().is_infinite() { unimplemented!("we don't yet handle an infinite width constraint on buffer elements"); } - let view = self.view.as_ref(app); + let view = self.view.read(app); let font_cache = &ctx.font_cache; let layout_cache = &ctx.text_layout_cache; let line_height = view.line_height(font_cache); @@ -404,7 +404,7 @@ impl Element for BufferElement { if let Some(layout) = layout { let app = ctx.app.downgrade(); - let view = self.view.as_ref(app); + let view = self.view.read(app); view.clamp_scroll_left( layout .scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app) @@ -437,7 +437,7 @@ impl Element for BufferElement { layout.text_size, ); - if self.view.as_ref(ctx.app).is_gutter_visible() { + if self.view.read(ctx.app).is_gutter_visible() { self.paint_gutter(gutter_bounds, layout, ctx); } self.paint_text(text_bounds, layout, ctx); diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index b7974a3a2e..fadf782280 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -125,7 +125,7 @@ impl BufferView { }); ctx.observe(&display_map, Self::on_display_map_changed); - let buffer_ref = buffer.as_ref(ctx); + let buffer_ref = buffer.read(ctx); Self { handle: ctx.handle().downgrade(), buffer, @@ -188,7 +188,7 @@ impl BufferView { return false; } - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); let visible_lines = viewport_height / line_height; let first_cursor_top = self .selections @@ -238,7 +238,7 @@ impl BufferView { layouts: &[Arc], app: &AppContext, ) { - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); let mut target_left = std::f32::INFINITY; let mut target_right = 0.0_f32; @@ -287,7 +287,7 @@ impl BufferView { ctx.emit(Event::Activate); } - let display_map = self.display_map.as_ref(ctx); + let display_map = self.display_map.read(ctx); let cursor = display_map .anchor_before(position, Bias::Left, ctx.app()) .unwrap(); @@ -312,8 +312,8 @@ impl BufferView { scroll_position: Vector2F, ctx: &mut ViewContext, ) { - let buffer = self.buffer.as_ref(ctx); - let map = self.display_map.as_ref(ctx); + let buffer = self.buffer.read(ctx); + let map = self.display_map.read(ctx); let cursor = map.anchor_before(position, Bias::Left, ctx.app()).unwrap(); if let Some(selection) = self.pending_selection.as_mut() { selection.set_head(buffer, cursor); @@ -347,8 +347,8 @@ impl BufferView { where T: IntoIterator>, { - let buffer = self.buffer.as_ref(ctx); - let map = self.display_map.as_ref(ctx); + let buffer = self.buffer.read(ctx); + let map = self.display_map.read(ctx); let mut selections = Vec::new(); for range in ranges { selections.push(Selection { @@ -366,7 +366,7 @@ impl BufferView { } fn insert(&mut self, text: &String, ctx: &mut ViewContext) { - let buffer = self.buffer.as_ref(ctx); + let buffer = self.buffer.read(ctx); let mut offset_ranges = SmallVec::<[Range; 32]>::new(); for selection in &self.selections { let start = selection.start.to_offset(buffer).unwrap(); @@ -381,7 +381,7 @@ impl BufferView { }; }); - let buffer = self.buffer.as_ref(ctx); + let buffer = self.buffer.read(ctx); let char_count = text.chars().count() as isize; let mut delta = 0_isize; self.selections = offset_ranges @@ -416,8 +416,8 @@ impl BufferView { } pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext) { - let buffer = self.buffer.as_ref(ctx); - let map = self.display_map.as_ref(ctx); + let buffer = self.buffer.read(ctx); + let map = self.display_map.read(ctx); for selection in &mut self.selections { if selection.range(buffer).is_empty() { let head = selection.head().to_display_point(map, ctx.app()).unwrap(); @@ -439,7 +439,7 @@ impl BufferView { pub fn move_left(&mut self, _: &(), ctx: &mut ViewContext) { { let app = ctx.app(); - let map = self.display_map.as_ref(ctx); + let map = self.display_map.read(ctx); for selection in &mut self.selections { let start = selection.start.to_display_point(map, app).unwrap(); let end = selection.end.to_display_point(map, app).unwrap(); @@ -462,8 +462,8 @@ impl BufferView { pub fn select_left(&mut self, _: &(), ctx: &mut ViewContext) { { - let buffer = self.buffer.as_ref(ctx); - let map = self.display_map.as_ref(ctx); + let buffer = self.buffer.read(ctx); + let map = self.display_map.read(ctx); for selection in &mut self.selections { let head = selection.head().to_display_point(map, ctx.app()).unwrap(); let cursor = map @@ -483,7 +483,7 @@ impl BufferView { pub fn move_right(&mut self, _: &(), ctx: &mut ViewContext) { { let app = ctx.app(); - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); for selection in &mut self.selections { let start = selection.start.to_display_point(map, app).unwrap(); let end = selection.end.to_display_point(map, app).unwrap(); @@ -506,9 +506,9 @@ impl BufferView { pub fn select_right(&mut self, _: &(), ctx: &mut ViewContext) { { - let buffer = self.buffer.as_ref(ctx); + let buffer = self.buffer.read(ctx); let app = ctx.app(); - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); for selection in &mut self.selections { let head = selection.head().to_display_point(map, ctx.app()).unwrap(); let cursor = map @@ -526,7 +526,7 @@ impl BufferView { ctx.propagate_action(); } else { let app = ctx.app(); - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); for selection in &mut self.selections { let start = selection.start.to_display_point(map, app).unwrap(); let end = selection.end.to_display_point(map, app).unwrap(); @@ -551,8 +551,8 @@ impl BufferView { ctx.propagate_action(); } else { let app = ctx.app(); - let buffer = self.buffer.as_ref(app); - let map = self.display_map.as_ref(app); + let buffer = self.buffer.read(app); + let map = self.display_map.read(app); for selection in &mut self.selections { let head = selection.head().to_display_point(map, app).unwrap(); let (head, goal_column) = @@ -569,7 +569,7 @@ impl BufferView { ctx.propagate_action(); } else { let app = ctx.app(); - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); for selection in &mut self.selections { let start = selection.start.to_display_point(map, app).unwrap(); let end = selection.end.to_display_point(map, app).unwrap(); @@ -594,8 +594,8 @@ impl BufferView { ctx.propagate_action(); } else { let app = ctx.app(); - let buffer = self.buffer.as_ref(ctx); - let map = self.display_map.as_ref(ctx); + let buffer = self.buffer.read(ctx); + let map = self.display_map.read(ctx); for selection in &mut self.selections { let head = selection.head().to_display_point(map, app).unwrap(); let (head, goal_column) = @@ -615,7 +615,7 @@ impl BufferView { } fn merge_selections(&mut self, ctx: &AppContext) { - let buffer = self.buffer.as_ref(ctx); + let buffer = self.buffer.read(ctx); let mut i = 1; while i < self.selections.len() { if self.selections[i - 1] @@ -651,14 +651,14 @@ impl BufferView { self.selections .first() .unwrap() - .display_range(self.display_map.as_ref(app), app) + .display_range(self.display_map.read(app), app) } pub fn last_selection(&self, app: &AppContext) -> Range { self.selections .last() .unwrap() - .display_range(self.display_map.as_ref(app), app) + .display_range(self.display_map.read(app), app) } pub fn selections_in_range<'a>( @@ -666,7 +666,7 @@ impl BufferView { range: Range, app: &'a AppContext, ) -> impl 'a + Iterator> { - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); let start = map.anchor_before(range.start, Bias::Left, app).unwrap(); let start_index = self.selection_insertion_index(&start, app); @@ -686,7 +686,7 @@ impl BufferView { } fn selection_insertion_index(&self, start: &Anchor, app: &AppContext) -> usize { - let buffer = self.buffer.as_ref(app); + let buffer = self.buffer.read(app); match self .selections @@ -720,7 +720,7 @@ impl BufferView { let mut fold_ranges = Vec::new(); let app = ctx.app(); - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); for selection in &self.selections { let (start, end) = selection.display_range(map, app).sorted(); let buffer_start_row = start.to_buffer_point(map, Bias::Left, app).unwrap().row; @@ -750,8 +750,8 @@ impl BufferView { use super::RangeExt; let app = ctx.app(); - let map = self.display_map.as_ref(app); - let buffer = self.buffer.as_ref(app); + let map = self.display_map.read(app); + let buffer = self.buffer.read(app); let ranges = self .selections .iter() @@ -796,7 +796,7 @@ impl BufferView { let mut is_blank = true; for c in self .display_map - .as_ref(app) + .read(app) .chars_at(DisplayPoint::new(display_row, 0), app)? { if c == ' ' { @@ -810,7 +810,7 @@ impl BufferView { } fn foldable_range_for_line(&self, start_row: u32, app: &AppContext) -> Result> { - let map = self.display_map.as_ref(app); + let map = self.display_map.read(app); let max_point = self.max_point(app); let (start_indent, _) = self.line_indent(start_row, app)?; @@ -831,7 +831,7 @@ impl BufferView { pub fn fold_selected_ranges(&mut self, _: &(), ctx: &mut ViewContext) { self.display_map.update(ctx, |map, ctx| { - let buffer = self.buffer.as_ref(ctx); + let buffer = self.buffer.read(ctx); let ranges = self .selections .iter() @@ -842,23 +842,23 @@ impl BufferView { } pub fn line(&self, display_row: u32, app: &AppContext) -> Result { - self.display_map.as_ref(app).line(display_row, app) + self.display_map.read(app).line(display_row, app) } pub fn line_len(&self, display_row: u32, app: &AppContext) -> Result { - self.display_map.as_ref(app).line_len(display_row, app) + self.display_map.read(app).line_len(display_row, app) } pub fn rightmost_point(&self, app: &AppContext) -> DisplayPoint { - self.display_map.as_ref(app).rightmost_point() + self.display_map.read(app).rightmost_point() } pub fn max_point(&self, app: &AppContext) -> DisplayPoint { - self.display_map.as_ref(app).max_point(app) + self.display_map.read(app).max_point(app) } pub fn text(&self, app: &AppContext) -> String { - self.display_map.as_ref(app).text(app) + self.display_map.read(app).text(app) } pub fn font_size(&self) -> f32 { @@ -902,7 +902,7 @@ impl BufferView { let font_size = settings.buffer_font_size; let font_id = font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?; - let digit_count = ((self.buffer.as_ref(app).max_point().row + 1) as f32) + let digit_count = ((self.buffer.read(app).max_point().row + 1) as f32) .log10() .floor() as usize + 1; @@ -923,7 +923,7 @@ impl BufferView { layout_cache: &TextLayoutCache, app: &AppContext, ) -> Result>> { - let display_map = self.display_map.as_ref(app); + let display_map = self.display_map.read(app); let settings = smol::block_on(self.settings.read()); let font_size = settings.buffer_font_size; @@ -959,7 +959,7 @@ impl BufferView { layout_cache: &TextLayoutCache, app: &AppContext, ) -> Result>> { - let display_map = self.display_map.as_ref(app); + let display_map = self.display_map.read(app); rows.end = cmp::min(rows.end, display_map.max_point(app).row() + 1); if rows.start >= rows.end { @@ -1149,7 +1149,7 @@ impl workspace::ItemView for BufferView { } fn title(&self, app: &AppContext) -> std::string::String { - if let Some(path) = self.buffer.as_ref(app).path(app) { + if let Some(path) = self.buffer.read(app).path(app) { path.file_name() .expect("buffer's path is always to a file") .to_string_lossy() @@ -1160,7 +1160,7 @@ impl workspace::ItemView for BufferView { } fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)> { - self.buffer.as_ref(app).entry_id() + self.buffer.read(app).entry_id() } fn clone_on_split(&self, ctx: &mut ViewContext) -> Option @@ -1177,7 +1177,7 @@ impl workspace::ItemView for BufferView { } fn is_dirty(&self, ctx: &AppContext) -> bool { - self.buffer.as_ref(ctx).is_dirty() + self.buffer.read(ctx).is_dirty() } } @@ -1255,7 +1255,7 @@ mod tests { view.begin_selection(DisplayPoint::new(2, 2), false, ctx); }); - let view = buffer_view.as_ref(app); + let view = buffer_view.read(app); let selections = view .selections_in_range( DisplayPoint::zero()..view.max_point(app.as_ref()), @@ -1271,7 +1271,7 @@ mod tests { view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); }); - let view = buffer_view.as_ref(app); + let view = buffer_view.read(app); let selections = view .selections_in_range( DisplayPoint::zero()..view.max_point(app.as_ref()), @@ -1287,7 +1287,7 @@ mod tests { view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); }); - let view = buffer_view.as_ref(app); + let view = buffer_view.read(app); let selections = view .selections_in_range( DisplayPoint::zero()..view.max_point(app.as_ref()), @@ -1304,7 +1304,7 @@ mod tests { view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); }); - let view = buffer_view.as_ref(app); + let view = buffer_view.read(app); let selections = view .selections_in_range( DisplayPoint::zero()..view.max_point(app.as_ref()), @@ -1321,7 +1321,7 @@ mod tests { view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx); }); - let view = buffer_view.as_ref(app); + let view = buffer_view.read(app); let selections = view .selections_in_range( DisplayPoint::zero()..view.max_point(app.as_ref()), @@ -1340,7 +1340,7 @@ mod tests { view.end_selection(ctx); }); - let view = buffer_view.as_ref(app); + let view = buffer_view.read(app); let selections = view .selections_in_range( DisplayPoint::zero()..view.max_point(app.as_ref()), @@ -1367,7 +1367,7 @@ mod tests { app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); let layouts = view - .as_ref(app) + .read(app) .layout_line_numbers(1000.0, &font_cache, &layout_cache, app.as_ref()) .unwrap(); assert_eq!(layouts.len(), 6); @@ -1460,7 +1460,7 @@ mod tests { ); view.unfold(&(), ctx); - assert_eq!(view.text(ctx.app()), buffer.as_ref(ctx).text()); + assert_eq!(view.text(ctx.app()), buffer.read(ctx).text()); }); }); } @@ -1529,7 +1529,7 @@ mod tests { }); assert_eq!( - buffer.as_ref(app).text(), + buffer.read(app).text(), "oe two three\nfou five six\nseven ten\n" ); }) diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index d67b4b510e..58f51cee94 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -22,7 +22,7 @@ pub struct FoldMap { impl FoldMap { pub fn new(buffer: ModelHandle, app: &AppContext) -> Self { - let text_summary = buffer.as_ref(app).text_summary(); + let text_summary = buffer.read(app).text_summary(); Self { buffer, folds: Vec::new(), @@ -72,7 +72,7 @@ impl FoldMap { let offset = self.to_display_offset(point, app)?; let mut cursor = self.transforms.cursor(); cursor.seek(&offset, SeekBias::Right); - let buffer = self.buffer.as_ref(app); + let buffer = self.buffer.read(app); Ok(Chars { cursor, offset: offset.0, @@ -95,7 +95,7 @@ impl FoldMap { app: &AppContext, ) -> Result<()> { let mut edits = Vec::new(); - let buffer = self.buffer.as_ref(app); + let buffer = self.buffer.read(app); for range in ranges.into_iter() { let start = range.start.to_offset(buffer)?; let end = range.end.to_offset(buffer)?; @@ -124,7 +124,7 @@ impl FoldMap { ranges: impl IntoIterator>, app: &AppContext, ) -> Result<()> { - let buffer = self.buffer.as_ref(app); + let buffer = self.buffer.read(app); let mut edits = Vec::new(); for range in ranges.into_iter() { @@ -184,7 +184,7 @@ impl FoldMap { .ok_or_else(|| anyhow!("display point {:?} is out of range", point))?; assert!(transform.display_text.is_none()); let end_buffer_offset = - (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.as_ref(app))?; + (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(app))?; offset += end_buffer_offset - cursor.start().buffer.chars; } Ok(DisplayOffset(offset)) @@ -208,7 +208,7 @@ impl FoldMap { } pub fn apply_edits(&mut self, edits: &[Edit], app: &AppContext) -> Result<()> { - let buffer = self.buffer.as_ref(app); + let buffer = self.buffer.read(app); let mut edits = edits.iter().cloned().peekable(); let mut new_transforms = SumTree::new(); @@ -597,7 +597,7 @@ mod tests { let mut map = FoldMap::new(buffer.clone(), app.as_ref()); { - let buffer = buffer.as_ref(app); + let buffer = buffer.read(app); let fold_count = rng.gen_range(0..10); let mut fold_ranges: Vec> = Vec::new(); @@ -632,7 +632,7 @@ mod tests { map.apply_edits(&edits, app.as_ref()).unwrap(); - let buffer = map.buffer.as_ref(app); + let buffer = map.buffer.read(app); let mut expected_text = buffer.text(); let mut expected_buffer_rows = Vec::new(); let mut next_row = buffer.max_point().row; @@ -694,7 +694,7 @@ mod tests { } fn merged_fold_ranges(&self, app: &AppContext) -> Vec> { - let buffer = self.buffer.as_ref(app); + let buffer = self.buffer.read(app); let mut fold_ranges = self .folds .iter() diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 7bc17627dd..e44193104e 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -108,7 +108,7 @@ impl DisplayMap { app: &AppContext, ) -> Result { self.buffer - .as_ref(app) + .read(app) .anchor_before(point.to_buffer_point(self, bias, app)?) } @@ -119,7 +119,7 @@ impl DisplayMap { app: &AppContext, ) -> Result { self.buffer - .as_ref(app) + .read(app) .anchor_after(point.to_buffer_point(self, bias, app)?) } @@ -206,7 +206,7 @@ impl Point { impl Anchor { pub fn to_display_point(&self, map: &DisplayMap, app: &AppContext) -> Result { - self.to_point(map.buffer.as_ref(app))? + self.to_point(map.buffer.read(app))? .to_display_point(map, app) } } @@ -314,7 +314,7 @@ mod tests { }) .unwrap(); - let map = map.as_ref(app); + let map = map.read(app); assert_eq!( map.chars_at(DisplayPoint::new(1, 0), app.as_ref()) .unwrap() @@ -368,7 +368,7 @@ mod tests { let buffer = app.add_model(|_| Buffer::new(0, "aaa\n\t\tbbb")); let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx)); assert_eq!( - map.as_ref(app).max_point(app.as_ref()), + map.read(app).max_point(app.as_ref()), DisplayPoint::new(1, 11) ) }); diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index d03ce43294..169d2243a4 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -114,7 +114,7 @@ impl FileFinder { self.matches.len(), move |mut range, items, app| { let finder = handle.upgrade(app).unwrap(); - let finder = finder.as_ref(app); + let finder = finder.read(app); let start = range.start; range.end = cmp::min(range.end, finder.matches.len()); items.extend(finder.matches[range].iter().enumerate().filter_map( @@ -287,7 +287,7 @@ impl FileFinder { } fn workspace_updated(&mut self, _: ModelHandle, ctx: &mut ViewContext) { - self.spawn_search(self.query_buffer.as_ref(ctx).text(ctx.app()), ctx); + self.spawn_search(self.query_buffer.read(ctx).text(ctx.app()), ctx); } fn on_query_buffer_event( @@ -299,7 +299,7 @@ impl FileFinder { use buffer_view::Event::*; match event { Edited => { - let query = self.query_buffer.as_ref(ctx).text(ctx.app()); + let query = self.query_buffer.read(ctx).text(ctx.app()); if query.is_empty() { self.latest_search_id = util::post_inc(&mut self.search_count); self.matches.clear(); @@ -371,18 +371,18 @@ impl FileFinder { fn worktree<'a>(&'a self, tree_id: usize, app: &'a AppContext) -> Option<&'a Worktree> { self.workspace - .as_ref(app) + .read(app) .worktrees() .get(&tree_id) - .map(|worktree| worktree.as_ref(app)) + .map(|worktree| worktree.read(app)) } fn worktrees(&self, app: &AppContext) -> Vec { self.workspace - .as_ref(app) + .read(app) .worktrees() .iter() - .map(|worktree| worktree.as_ref(app).clone()) + .map(|worktree| worktree.read(app).clone()) .collect() } } @@ -400,7 +400,7 @@ mod tests { #[test] fn test_matching_paths() { - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let tmp_dir = TempDir::new("example").unwrap(); fs::create_dir(tmp_dir.path().join("a")).await.unwrap(); fs::write(tmp_dir.path().join("a/banana"), "banana") @@ -409,8 +409,10 @@ mod tests { fs::write(tmp_dir.path().join("a/bandana"), "bandana") .await .unwrap(); - super::init(app); - editor::init(app); + app.update(|ctx| { + super::init(ctx); + editor::init(ctx); + }); let settings = settings::channel(&app.font_cache()).unwrap().1; let workspace = app.add_model(|ctx| Workspace::new(vec![tmp_dir.path().into()], ctx)); @@ -424,14 +426,16 @@ mod tests { (), ); - let finder = workspace_view - .as_ref(app) - .modal() - .cloned() - .unwrap() - .downcast::() - .unwrap(); - let query_buffer = finder.as_ref(app).query_buffer.clone(); + let finder = app.read(|ctx| { + workspace_view + .read(ctx) + .modal() + .cloned() + .unwrap() + .downcast::() + .unwrap() + }); + let query_buffer = app.read(|ctx| finder.read(ctx).query_buffer.clone()); let chain = vec![finder.id(), query_buffer.id()]; app.dispatch_action(window_id, chain.clone(), "buffer:insert", "b".to_string()); diff --git a/zed/src/workspace/mod.rs b/zed/src/workspace/mod.rs index 91dd99419a..a441eb8b8b 100644 --- a/zed/src/workspace/mod.rs +++ b/zed/src/workspace/mod.rs @@ -109,9 +109,9 @@ mod tests { .unwrap(); assert_eq!( workspace_view_1 - .as_ref(app) + .read(app) .workspace - .as_ref(app) + .read(app) .worktrees() .len(), 2 diff --git a/zed/src/workspace/workspace.rs b/zed/src/workspace/workspace.rs index cc5cfec602..b4e5a85e58 100644 --- a/zed/src/workspace/workspace.rs +++ b/zed/src/workspace/workspace.rs @@ -101,7 +101,7 @@ impl Workspace { pub fn contains_path(&self, path: &Path, app: &AppContext) -> bool { self.worktrees .iter() - .any(|worktree| worktree.as_ref(app).contains_path(path)) + .any(|worktree| worktree.read(app).contains_path(path)) } pub fn open_paths(&mut self, paths: &[PathBuf], ctx: &mut ModelContext) { @@ -112,7 +112,7 @@ impl Workspace { pub fn open_path<'a>(&'a mut self, path: PathBuf, ctx: &mut ModelContext) { for tree in self.worktrees.iter() { - if tree.as_ref(ctx).contains_path(&path) { + if tree.read(ctx).contains_path(&path) { return; } } @@ -200,18 +200,18 @@ impl Entity for Workspace { #[cfg(test)] pub trait WorkspaceHandle { - fn file_entries(&self, app: &mut MutableAppContext) -> Vec<(usize, usize)>; + fn file_entries(&self, app: &AppContext) -> Vec<(usize, usize)>; } #[cfg(test)] impl WorkspaceHandle for ModelHandle { - fn file_entries(&self, app: &mut MutableAppContext) -> Vec<(usize, usize)> { - self.as_ref(app) + fn file_entries(&self, app: &AppContext) -> Vec<(usize, usize)> { + self.read(app) .worktrees() .iter() .flat_map(|tree| { let tree_id = tree.id(); - tree.as_ref(app) + tree.read(app) .files() .map(move |file| (tree_id, file.entry_id)) }) @@ -228,7 +228,7 @@ mod tests { #[test] fn test_open_entry() { - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let dir = temp_tree(json!({ "a": { "aa": "aa contents", @@ -240,12 +240,12 @@ mod tests { app.finish_pending_tasks().await; // Open and populate worktree. // Get the first file entry. - let tree = workspace.as_ref(app).worktrees.iter().next().unwrap(); - let entry_id = tree.as_ref(app).files().next().unwrap().entry_id; + let tree = app.read(|ctx| workspace.read(ctx).worktrees.iter().next().unwrap().clone()); + let entry_id = app.read(|ctx| tree.read(ctx).files().next().unwrap().entry_id); let entry = (tree.id(), entry_id); // Open the same entry twice before it finishes loading. - let (future_1, future_2) = workspace.update(app, |w, app| { + let (future_1, future_2) = workspace.update(&mut app, |w, app| { ( w.open_entry(entry, app).unwrap(), w.open_entry(entry, app).unwrap(), @@ -258,7 +258,7 @@ mod tests { // Open the same entry again now that it has loaded let handle_3 = workspace - .update(app, |w, app| w.open_entry(entry, app).unwrap()) + .update(&mut app, |w, app| w.open_entry(entry, app).unwrap()) .await .unwrap(); diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index aaf1e9eb9a..aece490d37 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -54,11 +54,11 @@ pub trait ItemViewHandle: Send + Sync { impl ItemViewHandle for ViewHandle { fn title(&self, app: &AppContext) -> String { - self.as_ref(app).title(app) + self.read(app).title(app) } fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)> { - self.as_ref(app).entry_id(app) + self.read(app).entry_id(app) } fn boxed_clone(&self) -> Box { @@ -93,7 +93,7 @@ impl ItemViewHandle for ViewHandle { } fn is_dirty(&self, ctx: &AppContext) -> bool { - self.as_ref(ctx).is_dirty(ctx) + self.read(ctx).is_dirty(ctx) } fn id(&self) -> usize { @@ -154,7 +154,7 @@ impl WorkspaceView { } pub fn contains_paths(&self, paths: &[PathBuf], app: &AppContext) -> bool { - self.workspace.as_ref(app).contains_paths(paths, app) + self.workspace.read(app).contains_paths(paths, app) } pub fn open_paths(&self, paths: &[PathBuf], app: &mut MutableAppContext) { @@ -228,8 +228,8 @@ impl WorkspaceView { } pub fn open_example_entry(&mut self, ctx: &mut ViewContext) { - if let Some(tree) = self.workspace.as_ref(ctx).worktrees().iter().next() { - if let Some(file) = tree.as_ref(ctx).files().next() { + if let Some(tree) = self.workspace.read(ctx).worktrees().iter().next() { + if let Some(file) = tree.read(ctx).files().next() { info!("open_entry ({}, {})", tree.id(), file.entry_id); self.open_entry((tree.id(), file.entry_id), ctx); } else { @@ -322,7 +322,7 @@ impl WorkspaceView { ) -> ViewHandle { let new_pane = self.add_pane(ctx); self.activate_pane(new_pane.clone(), ctx); - if let Some(item) = pane.as_ref(ctx).active_item() { + if let Some(item) = pane.read(ctx).active_item() { if let Some(clone) = item.clone_on_split(ctx.app_mut()) { self.add_item(clone, ctx); } @@ -394,7 +394,7 @@ mod tests { #[test] fn test_open_entry() { - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let dir = temp_tree(json!({ "a": { "aa": "aa contents", @@ -406,70 +406,78 @@ mod tests { let settings = settings::channel(&app.font_cache()).unwrap().1; let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx)); app.finish_pending_tasks().await; // Open and populate worktree. - let entries = workspace.file_entries(app); + let entries = app.read(|ctx| workspace.file_entries(ctx)); let (_, workspace_view) = app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx)); // Open the first entry - workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx)); + workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx)); app.finish_pending_tasks().await; - assert_eq!( - workspace_view - .as_ref(app) - .active_pane() - .as_ref(app) - .items() - .len(), - 1 - ); + app.read(|ctx| { + assert_eq!( + workspace_view + .read(ctx) + .active_pane() + .read(ctx) + .items() + .len(), + 1 + ) + }); // Open the second entry - workspace_view.update(app, |w, ctx| w.open_entry(entries[1], ctx)); + workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[1], ctx)); app.finish_pending_tasks().await; - let active_pane = workspace_view.as_ref(app).active_pane().as_ref(app); - assert_eq!(active_pane.items().len(), 2); - assert_eq!( - active_pane.active_item().unwrap().entry_id(app.as_ref()), - Some(entries[1]) - ); + app.read(|ctx| { + let active_pane = workspace_view.read(ctx).active_pane().read(ctx); + assert_eq!(active_pane.items().len(), 2); + assert_eq!( + active_pane.active_item().unwrap().entry_id(ctx), + Some(entries[1]) + ); + }); // Open the first entry again - workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx)); + workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx)); app.finish_pending_tasks().await; - let active_pane = workspace_view.as_ref(app).active_pane().as_ref(app); - assert_eq!(active_pane.items().len(), 2); - assert_eq!( - active_pane.active_item().unwrap().entry_id(app.as_ref()), - Some(entries[0]) - ); + app.read(|ctx| { + let active_pane = workspace_view.read(ctx).active_pane().read(ctx); + assert_eq!(active_pane.items().len(), 2); + assert_eq!( + active_pane.active_item().unwrap().entry_id(ctx), + Some(entries[0]) + ); + }); // Open the third entry twice concurrently - workspace_view.update(app, |w, ctx| { + workspace_view.update(&mut app, |w, ctx| { w.open_entry(entries[2], ctx); w.open_entry(entries[2], ctx); }); app.finish_pending_tasks().await; - assert_eq!( - workspace_view - .as_ref(app) - .active_pane() - .as_ref(app) - .items() - .len(), - 3 - ); + app.read(|ctx| { + assert_eq!( + workspace_view + .read(ctx) + .active_pane() + .read(ctx) + .items() + .len(), + 3 + ); + }); }); } #[test] fn test_pane_actions() { - App::test_async((), |app| async move { - pane::init(app); + App::test_async((), |mut app| async move { + app.update(|ctx| pane::init(ctx)); let dir = temp_tree(json!({ "a": { @@ -479,37 +487,41 @@ mod tests { }, })); - let settings = settings::channel(app.font_cache()).unwrap().1; + let settings = settings::channel(&app.font_cache()).unwrap().1; let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx)); app.finish_pending_tasks().await; // Open and populate worktree. - let entries = workspace.file_entries(app); + let entries = app.read(|ctx| workspace.file_entries(ctx)); let (window_id, workspace_view) = app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx)); - workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx)); + workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx)); app.finish_pending_tasks().await; - let pane_1 = workspace_view.as_ref(app).active_pane().clone(); + let pane_1 = app.read(|ctx| workspace_view.read(ctx).active_pane().clone()); app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ()); - let pane_2 = workspace_view.as_ref(app).active_pane().clone(); - assert_ne!(pane_1, pane_2); + app.update(|ctx| { + let pane_2 = workspace_view.read(ctx).active_pane().clone(); + assert_ne!(pane_1, pane_2); - assert_eq!( - pane_2 - .as_ref(app) - .active_item() - .unwrap() - .entry_id(app.downgrade()), - Some(entries[0]) - ); + assert_eq!( + pane_2 + .read(ctx) + .active_item() + .unwrap() + .entry_id(ctx.as_ref()), + Some(entries[0]) + ); - app.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); + ctx.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); + }); - let w = workspace_view.as_ref(app); - assert_eq!(w.panes.len(), 1); - assert_eq!(w.active_pane(), &pane_1); + app.read(|ctx| { + let w = workspace_view.read(ctx); + assert_eq!(w.panes.len(), 1); + assert_eq!(w.active_pane(), &pane_1); + }) }); } } diff --git a/zed/src/worktree/worktree.rs b/zed/src/worktree/worktree.rs index 27f42d904d..63be9b6eef 100644 --- a/zed/src/worktree/worktree.rs +++ b/zed/src/worktree/worktree.rs @@ -409,7 +409,7 @@ pub trait WorktreeHandle { impl WorktreeHandle for ModelHandle { fn file(&self, entry_id: usize, app: &AppContext) -> Result { - if entry_id >= self.as_ref(app).entry_count() { + if entry_id >= self.read(app).entry_count() { return Err(anyhow!("Entry does not exist in tree")); } @@ -461,15 +461,15 @@ pub struct FileHandle { impl FileHandle { pub fn path(&self, app: &AppContext) -> PathBuf { - self.worktree.as_ref(app).entry_path(self.entry_id).unwrap() + self.worktree.read(app).entry_path(self.entry_id).unwrap() } pub fn load_history(&self, app: &AppContext) -> impl Future> { - self.worktree.as_ref(app).load_history(self.entry_id) + self.worktree.read(app).load_history(self.entry_id) } pub fn save<'a>(&self, content: Snapshot, ctx: &AppContext) -> Task> { - let worktree = self.worktree.as_ref(ctx); + let worktree = self.worktree.read(ctx); worktree.save(self.entry_id, content, ctx) } @@ -649,7 +649,7 @@ mod test { #[test] fn test_populate_and_search() { - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let dir = temp_tree(json!({ "root": { "apple": "", @@ -671,26 +671,28 @@ mod test { let tree = app.add_model(|ctx| Worktree::new(1, root_link_path, Some(ctx))); app.finish_pending_tasks().await; - let tree = tree.as_ref(app); - assert_eq!(tree.file_count(), 4); - let results = match_paths(&[tree.clone()], "bna", false, false, 10) - .iter() - .map(|result| tree.entry_path(result.entry_id)) - .collect::, _>>() - .unwrap(); - assert_eq!( - results, - vec![ - PathBuf::from("root_link/banana/carrot/date"), - PathBuf::from("root_link/banana/carrot/endive"), - ] - ); + app.read(|ctx| { + let tree = tree.read(ctx); + assert_eq!(tree.file_count(), 4); + let results = match_paths(&[tree.clone()], "bna", false, false, 10) + .iter() + .map(|result| tree.entry_path(result.entry_id)) + .collect::, _>>() + .unwrap(); + assert_eq!( + results, + vec![ + PathBuf::from("root_link/banana/carrot/date"), + PathBuf::from("root_link/banana/carrot/endive"), + ] + ); + }) }); } #[test] fn test_save_file() { - App::test_async((), |app| async move { + App::test_async((), |mut app| async move { let dir = temp_tree(json!({ "file1": "the old contents", })); @@ -698,17 +700,23 @@ mod test { let tree = app.add_model(|ctx| Worktree::new(1, dir.path(), Some(ctx))); app.finish_pending_tasks().await; - let entry = tree.as_ref(app).files().next().unwrap(); - assert_eq!(entry.path.file_name().unwrap(), "file1"); - let file_id = entry.entry_id; - let buffer = Buffer::new(1, "a line of text.\n".repeat(10 * 1024)); - tree.update(app, |tree, ctx| { + let entry = app.read(|ctx| { + let entry = tree.read(ctx).files().next().unwrap(); + assert_eq!(entry.path.file_name().unwrap(), "file1"); + entry + }); + let file_id = entry.entry_id; + + tree.update(&mut app, |tree, ctx| { smol::block_on(tree.save(file_id, buffer.snapshot(), ctx.app())).unwrap() }); - let history = tree.as_ref(app).load_history(file_id).await.unwrap(); + let history = app + .read(|ctx| tree.read(ctx).load_history(file_id)) + .await + .unwrap(); assert_eq!(history.base_text, buffer.text()); }); } From 620eedb727cb383e1385c4e8972da6190c4525c8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 10 Apr 2021 00:11:13 -0600 Subject: [PATCH 6/9] Allow effects to be flushed before TestAppContext::update callback completes --- gpui/src/app.rs | 7 +++++-- zed/src/workspace/workspace_view.rs | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 702fc952a7..413c838fda 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -313,8 +313,11 @@ impl TestAppContext { pub fn update T>(&mut self, callback: F) -> T { let mut state = self.0.borrow_mut(); - state.pending_flushes += 1; + // Don't increment pending flushes in order to effects to be flushed before the callback + // completes, which is helpful in tests. let result = callback(&mut *state); + // Flush effects after the callback just in case there are any. This can happen in edge + // cases such as the closure dropping handles. state.flush_effects(); result } @@ -895,7 +898,7 @@ impl MutableAppContext { } fn flush_effects(&mut self) { - self.pending_flushes -= 1; + self.pending_flushes = self.pending_flushes.saturating_sub(1); if !self.flushing_effects && self.pending_flushes == 0 { self.flushing_effects = true; diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index aece490d37..6a9b3ca320 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -515,13 +515,11 @@ mod tests { ); ctx.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); - }); - app.read(|ctx| { let w = workspace_view.read(ctx); assert_eq!(w.panes.len(), 1); assert_eq!(w.active_pane(), &pane_1); - }) + }); }); } } From 4638391412b7ba6ccaf72663f8829f65d7084f51 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 10 Apr 2021 00:14:26 -0600 Subject: [PATCH 7/9] Remove MutableAppContext::downgrade --- gpui/src/app.rs | 21 ++++++++------------- gpui/src/presenter.rs | 4 ++-- zed/src/editor/buffer_element.rs | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 413c838fda..e5f40c6ba5 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -308,7 +308,7 @@ impl TestAppContext { } pub fn read T>(&self, callback: F) -> T { - callback(self.0.borrow().downgrade()) + callback(self.0.borrow().as_ref()) } pub fn update T>(&mut self, callback: F) -> T { @@ -438,10 +438,6 @@ impl MutableAppContext { App(self.weak_self.as_ref().unwrap().upgrade().unwrap()) } - pub fn downgrade(&self) -> &AppContext { - &self.ctx - } - pub fn platform(&self) -> Rc { self.platform.clone() } @@ -679,7 +675,7 @@ impl MutableAppContext { .get(&window_id) .and_then(|w| w.views.get(view_id)) { - context.extend(view.keymap_context(self.downgrade())); + context.extend(view.keymap_context(self.as_ref())); context_chain.push(context.clone()); } else { return Err(anyhow!( @@ -772,7 +768,7 @@ impl MutableAppContext { if ctx .dispatch_keystroke( window_id, - presenter.borrow().dispatch_path(ctx.downgrade()), + presenter.borrow().dispatch_path(ctx.as_ref()), keystroke, ) .unwrap() @@ -781,9 +777,8 @@ impl MutableAppContext { } } - let actions = presenter - .borrow_mut() - .dispatch_event(event, ctx.downgrade()); + let actions = + presenter.borrow_mut().dispatch_event(event, ctx.as_ref()); for action in actions { ctx.dispatch_action_any( window_id, @@ -815,7 +810,7 @@ impl MutableAppContext { let presenter = presenter.clone(); self.on_window_invalidated(window_id, move |invalidation, ctx| { let mut presenter = presenter.borrow_mut(); - presenter.invalidate(invalidation, ctx.downgrade()); + presenter.invalidate(invalidation, ctx.as_ref()); let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx); window.present_scene(scene); @@ -1771,7 +1766,7 @@ impl<'a, T: View> ViewContext<'a, T> { window_id: self.window_id, view_id: self.view_id, callback: Box::new(move |view, payload, app, window_id, view_id| { - if let Some(emitter_handle) = emitter_handle.upgrade(app.downgrade()) { + if let Some(emitter_handle) = emitter_handle.upgrade(app.as_ref()) { let model = view.downcast_mut().expect("downcast is type safe"); let payload = payload.downcast_ref().expect("downcast is type safe"); let mut ctx = ViewContext::new(app, window_id, view_id); @@ -1797,7 +1792,7 @@ impl<'a, T: View> ViewContext<'a, T> { window_id: self.window_id, view_id: self.view_id, callback: Box::new(move |view, payload, app, window_id, view_id| { - if let Some(emitter_handle) = emitter_handle.upgrade(app.downgrade()) { + if let Some(emitter_handle) = emitter_handle.upgrade(app.as_ref()) { let model = view.downcast_mut().expect("downcast is type safe"); let payload = payload.downcast_ref().expect("downcast is type safe"); let mut ctx = ViewContext::new(app, window_id, view_id); diff --git a/gpui/src/presenter.rs b/gpui/src/presenter.rs index 7c3be96a72..6efa214995 100644 --- a/gpui/src/presenter.rs +++ b/gpui/src/presenter.rs @@ -69,14 +69,14 @@ impl Presenter { let mut scene = Scene::new(scale_factor); if let Some(root_view_id) = app.root_view_id(self.window_id) { - self.layout(window_size, app.downgrade()); + self.layout(window_size, app.as_ref()); self.after_layout(app); let mut ctx = PaintContext { scene: &mut scene, font_cache: &self.font_cache, text_layout_cache: &self.text_layout_cache, rendered_views: &mut self.rendered_views, - app: app.downgrade(), + app: app.as_ref(), }; ctx.paint(root_view_id, Vector2F::zero()); self.text_layout_cache.finish_frame(); diff --git a/zed/src/editor/buffer_element.rs b/zed/src/editor/buffer_element.rs index 6141c0b7c6..eabd891791 100644 --- a/zed/src/editor/buffer_element.rs +++ b/zed/src/editor/buffer_element.rs @@ -402,7 +402,7 @@ impl Element for BufferElement { ctx: &mut AfterLayoutContext, ) { if let Some(layout) = layout { - let app = ctx.app.downgrade(); + let app = ctx.app.as_ref(); let view = self.view.read(app); view.clamp_scroll_left( From 619e2b7e018ceab1acee8849d17a727726bf144f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 10 Apr 2021 00:19:25 -0600 Subject: [PATCH 8/9] Remove platform::mac::runner --- gpui/src/platform/mac/runner.rs | 305 -------------------------------- 1 file changed, 305 deletions(-) delete mode 100644 gpui/src/platform/mac/runner.rs diff --git a/gpui/src/platform/mac/runner.rs b/gpui/src/platform/mac/runner.rs deleted file mode 100644 index 2c2c3ecab4..0000000000 --- a/gpui/src/platform/mac/runner.rs +++ /dev/null @@ -1,305 +0,0 @@ -use crate::{keymap::Keystroke, platform::Event, Menu, MenuItem}; -use cocoa::{ - appkit::{ - NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, - NSEventModifierFlags, NSMenu, NSMenuItem, NSWindow, - }, - base::{id, nil, selector}, - foundation::{NSArray, NSAutoreleasePool, NSInteger, NSString}, -}; -use ctor::ctor; -use objc::{ - class, - declare::ClassDecl, - msg_send, - runtime::{Class, Object, Sel}, - sel, sel_impl, -}; -use std::{ - ffi::CStr, - os::raw::{c_char, c_void}, - path::PathBuf, - ptr, -}; - -const RUNNER_IVAR: &'static str = "runner"; -static mut APP_CLASS: *const Class = ptr::null(); -static mut APP_DELEGATE_CLASS: *const Class = ptr::null(); - -#[ctor] -unsafe fn build_classes() { - APP_CLASS = { - let mut decl = ClassDecl::new("GPUIApplication", class!(NSApplication)).unwrap(); - decl.add_ivar::<*mut c_void>(RUNNER_IVAR); - decl.add_method( - sel!(sendEvent:), - send_event as extern "C" fn(&mut Object, Sel, id), - ); - decl.register() - }; - - APP_DELEGATE_CLASS = { - let mut decl = ClassDecl::new("GPUIApplicationDelegate", class!(NSResponder)).unwrap(); - decl.add_ivar::<*mut c_void>(RUNNER_IVAR); - decl.add_method( - sel!(applicationDidFinishLaunching:), - did_finish_launching as extern "C" fn(&mut Object, Sel, id), - ); - decl.add_method( - sel!(applicationDidBecomeActive:), - did_become_active as extern "C" fn(&mut Object, Sel, id), - ); - decl.add_method( - sel!(applicationDidResignActive:), - did_resign_active as extern "C" fn(&mut Object, Sel, id), - ); - decl.add_method( - sel!(handleGPUIMenuItem:), - handle_menu_item as extern "C" fn(&mut Object, Sel, id), - ); - decl.add_method( - sel!(application:openFiles:), - open_files as extern "C" fn(&mut Object, Sel, id, id), - ); - decl.register() - } -} - -#[derive(Default)] -pub struct Runner { - finish_launching_callback: Option>, - become_active_callback: Option>, - resign_active_callback: Option>, - event_callback: Option bool>>, - open_files_callback: Option)>>, - menu_command_callback: Option>, - menu_item_actions: Vec, -} - -impl Runner { - pub fn new() -> Self { - Default::default() - } - - unsafe fn create_menu_bar(&mut self, menus: &[Menu]) -> id { - let menu_bar = NSMenu::new(nil).autorelease(); - self.menu_item_actions.clear(); - - for menu_config in menus { - let menu_bar_item = NSMenuItem::new(nil).autorelease(); - let menu = NSMenu::new(nil).autorelease(); - - menu.setTitle_(ns_string(menu_config.name)); - - for item_config in menu_config.items { - let item; - - match item_config { - MenuItem::Separator => { - item = NSMenuItem::separatorItem(nil); - } - MenuItem::Action { - name, - keystroke, - action, - } => { - if let Some(keystroke) = keystroke { - let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| { - panic!( - "Invalid keystroke for menu item {}:{} - {:?}", - menu_config.name, name, err - ) - }); - - let mut mask = NSEventModifierFlags::empty(); - for (modifier, flag) in &[ - (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask), - (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask), - (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask), - ] { - if *modifier { - mask |= *flag; - } - } - - item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string(name), - selector("handleGPUIMenuItem:"), - ns_string(&keystroke.key), - ) - .autorelease(); - item.setKeyEquivalentModifierMask_(mask); - } else { - item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string(name), - selector("handleGPUIMenuItem:"), - ns_string(""), - ) - .autorelease(); - } - - let tag = self.menu_item_actions.len() as NSInteger; - let _: () = msg_send![item, setTag: tag]; - self.menu_item_actions.push(action.to_string()); - } - } - - menu.addItem_(item); - } - - menu_bar_item.setSubmenu_(menu); - menu_bar.addItem_(menu_bar_item); - } - - menu_bar - } -} - -impl crate::platform::Runner for Runner { - fn on_finish_launching(mut self, callback: F) -> Self { - self.finish_launching_callback = Some(Box::new(callback)); - self - } - - fn on_menu_command(mut self, callback: F) -> Self { - self.menu_command_callback = Some(Box::new(callback)); - self - } - - fn on_become_active(mut self, callback: F) -> Self { - log::info!("become active"); - self.become_active_callback = Some(Box::new(callback)); - self - } - - fn on_resign_active(mut self, callback: F) -> Self { - self.resign_active_callback = Some(Box::new(callback)); - self - } - - fn on_event bool>(mut self, callback: F) -> Self { - self.event_callback = Some(Box::new(callback)); - self - } - - fn on_open_files)>(mut self, callback: F) -> Self { - self.open_files_callback = Some(Box::new(callback)); - self - } - - fn set_menus(mut self, menus: &[Menu]) -> Self { - unsafe { - let app: id = msg_send![APP_CLASS, sharedApplication]; - app.setMainMenu_(self.create_menu_bar(menus)); - } - self - } - - fn run(self) { - unsafe { - let self_ptr = Box::into_raw(Box::new(self)); - - let pool = NSAutoreleasePool::new(nil); - let app: id = msg_send![APP_CLASS, sharedApplication]; - let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; - - (*app).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void); - (*app_delegate).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void); - app.setDelegate_(app_delegate); - app.run(); - pool.drain(); - - // The Runner is done running when we get here, so we can reinstantiate the Box and drop it. - Box::from_raw(self_ptr); - } - } -} - -unsafe fn get_runner(object: &mut Object) -> &mut Runner { - let runner_ptr: *mut c_void = *object.get_ivar(RUNNER_IVAR); - &mut *(runner_ptr as *mut Runner) -} - -extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) { - let event = unsafe { Event::from_native(native_event, None) }; - - if let Some(event) = event { - let runner = unsafe { get_runner(this) }; - if let Some(callback) = runner.event_callback.as_mut() { - if callback(event) { - return; - } - } - } - - unsafe { - let _: () = msg_send![super(this, class!(NSApplication)), sendEvent: native_event]; - } -} - -extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) { - unsafe { - let app: id = msg_send![APP_CLASS, sharedApplication]; - app.setActivationPolicy_(NSApplicationActivationPolicyRegular); - - let runner = get_runner(this); - if let Some(callback) = runner.finish_launching_callback.take() { - callback(); - } - } -} - -extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) { - let runner = unsafe { get_runner(this) }; - if let Some(callback) = runner.become_active_callback.as_mut() { - callback(); - } -} - -extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) { - let runner = unsafe { get_runner(this) }; - if let Some(callback) = runner.resign_active_callback.as_mut() { - callback(); - } -} - -extern "C" fn open_files(this: &mut Object, _: Sel, _: id, paths: id) { - let paths = unsafe { - (0..paths.count()) - .into_iter() - .filter_map(|i| { - let path = paths.objectAtIndex(i); - match CStr::from_ptr(path.UTF8String() as *mut c_char).to_str() { - Ok(string) => Some(PathBuf::from(string)), - Err(err) => { - log::error!("error converting path to string: {}", err); - None - } - } - }) - .collect::>() - }; - let runner = unsafe { get_runner(this) }; - if let Some(callback) = runner.open_files_callback.as_mut() { - callback(paths); - } -} - -extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) { - unsafe { - let runner = get_runner(this); - if let Some(callback) = runner.menu_command_callback.as_mut() { - let tag: NSInteger = msg_send![item, tag]; - let index = tag as usize; - if let Some(action) = runner.menu_item_actions.get(index) { - callback(&action); - } - } - } -} - -unsafe fn ns_string(string: &str) -> id { - NSString::alloc(nil).init_str(string).autorelease() -} From b9b511148bfa6174231decee440d50504831270c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 10 Apr 2021 00:22:45 -0600 Subject: [PATCH 9/9] Make gpui::platform module private --- gpui/src/lib.rs | 4 ++-- gpui/src/platform/test.rs | 2 -- zed/src/main.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/gpui/src/lib.rs b/gpui/src/lib.rs index 7b0ecdddef..b60ce9b92d 100644 --- a/gpui/src/lib.rs +++ b/gpui/src/lib.rs @@ -21,8 +21,8 @@ pub use executor::Task; pub mod color; pub mod json; pub mod keymap; -pub mod platform; -pub use platform::Event; +mod platform; +pub use platform::{Event, PathPromptOptions}; pub use presenter::{ AfterLayoutContext, Axis, DebugContext, EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt, diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index b0d19232dc..f1d6bead66 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -17,8 +17,6 @@ pub struct Window { resize_handlers: Vec>, } -pub struct WindowContext {} - impl Platform { fn new() -> Self { Self { diff --git a/zed/src/main.rs b/zed/src/main.rs index b214924a80..407b4952c1 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -1,5 +1,5 @@ use fs::OpenOptions; -use gpui::platform::PathPromptOptions; +use gpui::PathPromptOptions; use log::LevelFilter; use simplelog::SimpleLogger; use std::{fs, path::PathBuf};