From 7e5186e4a0d78c4fa07c3b3b8ad8218019b32ac5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Apr 2021 17:48:22 -0700 Subject: [PATCH 01/15] Start work on a native application menu Add an application menu with a quit command, bound to command-q --- gpui/src/platform/mac/app.rs | 7 ++--- gpui/src/platform/mac/runner.rs | 53 ++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/gpui/src/platform/mac/app.rs b/gpui/src/platform/mac/app.rs index daf8335f6081a003a8c6672d3bdee253a754a9d3..b3b2a788cf2a00b95a28d04c997dcd4610c34824 100644 --- a/gpui/src/platform/mac/app.rs +++ b/gpui/src/platform/mac/app.rs @@ -1,8 +1,7 @@ use super::{BoolExt as _, Dispatcher, FontSystem, Window}; use crate::{executor, platform}; use anyhow::Result; -use cocoa::base::id; -use objc::{class, msg_send, sel, sel_impl}; +use cocoa::{appkit::NSApplication, base::nil}; use std::{rc::Rc, sync::Arc}; pub struct App { @@ -26,8 +25,8 @@ impl platform::App for App { fn activate(&self, ignoring_other_apps: bool) { unsafe { - let app: id = msg_send![class!(NSApplication), sharedApplication]; - let _: () = msg_send![app, activateIgnoringOtherApps: ignoring_other_apps.to_objc()]; + let app = NSApplication::sharedApplication(nil); + app.activateIgnoringOtherApps_(ignoring_other_apps.to_objc()); } } diff --git a/gpui/src/platform/mac/runner.rs b/gpui/src/platform/mac/runner.rs index c407bf59d7272d899538c4425ea0ca61a526c091..4b0ebe7dfdb8807c88f4013ee5766fe297c7d909 100644 --- a/gpui/src/platform/mac/runner.rs +++ b/gpui/src/platform/mac/runner.rs @@ -1,7 +1,10 @@ use crate::platform::Event; use cocoa::{ - appkit::NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, - base::{id, nil}, + appkit::{ + NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, NSMenu, + NSMenuItem, NSWindow, + }, + base::{id, nil, selector}, foundation::{NSArray, NSAutoreleasePool, NSString}, }; use ctor::ctor; @@ -106,16 +109,16 @@ impl crate::platform::Runner for Runner { let pool = NSAutoreleasePool::new(nil); let app: id = msg_send![APP_CLASS, sharedApplication]; - let _: () = msg_send![ - app, - setActivationPolicy: NSApplicationActivationPolicyRegular - ]; - (*app).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void); let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; + + app.setActivationPolicy_(NSApplicationActivationPolicyRegular); + (*app).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void); (*app_delegate).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void); - let _: () = msg_send![app, setDelegate: app_delegate]; - let _: () = msg_send![app, run]; - let _: () = msg_send![pool, drain]; + app.setMainMenu_(create_menu_bar()); + 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); } @@ -186,3 +189,33 @@ extern "C" fn open_files(this: &mut Object, _: Sel, _: id, paths: id) { callback(paths); } } + +unsafe fn create_menu_bar() -> id { + let menu_bar = NSMenu::new(nil).autorelease(); + + // App menu + let app_menu_item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string("Application"), + Sel::from_ptr(ptr::null()), + ns_string(""), + ) + .autorelease(); + let quit_item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string("Quit"), + selector("terminate:"), + ns_string("q\0"), + ) + .autorelease(); + let app_menu = NSMenu::new(nil).autorelease(); + app_menu.addItem_(quit_item); + app_menu_item.setSubmenu_(app_menu); + menu_bar.addItem_(app_menu_item); + + menu_bar +} + +unsafe fn ns_string(string: &str) -> id { + NSString::alloc(nil).init_str(string).autorelease() +} From 0a1277468007aca3a0e362ad8500491919bd9745 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Apr 2021 17:49:44 -0700 Subject: [PATCH 02/15] Add a stub of a native 'File' menu --- gpui/src/platform/mac/runner.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/gpui/src/platform/mac/runner.rs b/gpui/src/platform/mac/runner.rs index 4b0ebe7dfdb8807c88f4013ee5766fe297c7d909..a4b7281753ff5b9997d034de91df09ca8f8a3578 100644 --- a/gpui/src/platform/mac/runner.rs +++ b/gpui/src/platform/mac/runner.rs @@ -213,6 +213,27 @@ unsafe fn create_menu_bar() -> id { app_menu_item.setSubmenu_(app_menu); menu_bar.addItem_(app_menu_item); + // File menu + let file_menu_item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string("File"), + Sel::from_ptr(ptr::null()), + ns_string(""), + ) + .autorelease(); + let open_item = NSMenuItem::alloc(nil) + .initWithTitle_action_keyEquivalent_( + ns_string("Open"), + selector("openDocument:"), + ns_string("o\0"), + ) + .autorelease(); + let file_menu = NSMenu::new(nil).autorelease(); + file_menu.setTitle_(ns_string("File")); + file_menu.addItem_(open_item); + file_menu_item.setSubmenu_(file_menu); + menu_bar.addItem_(file_menu_item); + menu_bar } From 334de06322f4a4c79bc1d9bbf7908eb9a080766d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 8 Apr 2021 15:56:21 -0700 Subject: [PATCH 03/15] Create an API for assigning the menubar contents --- gpui/src/app.rs | 18 ++++ gpui/src/platform/mac/app.rs | 8 ++ gpui/src/platform/mac/runner.rs | 159 ++++++++++++++++++++++---------- gpui/src/platform/mod.rs | 7 +- gpui/src/platform/test.rs | 2 + zed/src/lib.rs | 1 + zed/src/main.rs | 24 +++-- zed/src/menus.rs | 52 +++++++++++ zed/src/workspace/mod.rs | 5 + 9 files changed, 216 insertions(+), 60 deletions(-) create mode 100644 zed/src/menus.rs diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 8dbc34b893c76a28a95204e92e707df7e31acaf7..8863bb05eb8c4117b18eb2180b4affc0d87dc650 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -66,6 +66,20 @@ pub trait UpdateView { F: FnOnce(&mut T, &mut ViewContext) -> S; } +pub struct Menu<'a> { + pub name: &'a str, + pub items: &'a [MenuItem<'a>], +} + +pub enum MenuItem<'a> { + Action { + name: &'a str, + keystroke: Option<&'a str>, + action: &'a str, + }, + Separator, +} + #[derive(Clone)] pub struct App(Rc>); @@ -365,6 +379,10 @@ impl MutableAppContext { &self.ctx } + pub fn platform(&self) -> Arc { + self.platform.clone() + } + pub fn foreground_executor(&self) -> &Rc { &self.foreground } diff --git a/gpui/src/platform/mac/app.rs b/gpui/src/platform/mac/app.rs index b3b2a788cf2a00b95a28d04c997dcd4610c34824..b77cb2ee51ade7fc733e431b842a8d8aa5c29e77 100644 --- a/gpui/src/platform/mac/app.rs +++ b/gpui/src/platform/mac/app.rs @@ -2,6 +2,7 @@ use super::{BoolExt as _, Dispatcher, FontSystem, Window}; use crate::{executor, platform}; use anyhow::Result; use cocoa::{appkit::NSApplication, base::nil}; +use objc::{msg_send, sel, sel_impl}; use std::{rc::Rc, sync::Arc}; pub struct App { @@ -41,4 +42,11 @@ impl platform::App for App { fn fonts(&self) -> Arc { self.fonts.clone() } + + fn quit(&self) { + unsafe { + let app = NSApplication::sharedApplication(nil); + let _: () = msg_send![app, terminate: nil]; + } + } } diff --git a/gpui/src/platform/mac/runner.rs b/gpui/src/platform/mac/runner.rs index a4b7281753ff5b9997d034de91df09ca8f8a3578..1783e03ea050feaebd2deb935fd0385f44fa5745 100644 --- a/gpui/src/platform/mac/runner.rs +++ b/gpui/src/platform/mac/runner.rs @@ -1,11 +1,11 @@ -use crate::platform::Event; +use crate::{keymap::Keystroke, platform::Event, Menu, MenuItem}; use cocoa::{ appkit::{ - NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, NSMenu, - NSMenuItem, NSWindow, + NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, + NSEventModifierFlags, NSMenu, NSMenuItem, NSWindow, }, base::{id, nil, selector}, - foundation::{NSArray, NSAutoreleasePool, NSString}, + foundation::{NSArray, NSAutoreleasePool, NSInteger, NSString}, }; use ctor::ctor; use objc::{ @@ -53,6 +53,10 @@ unsafe fn build_classes() { 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), @@ -68,12 +72,89 @@ pub struct Runner { 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 { @@ -82,6 +163,11 @@ impl crate::platform::Runner for Runner { 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)); @@ -103,6 +189,14 @@ impl crate::platform::Runner for Runner { 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)); @@ -114,7 +208,6 @@ impl crate::platform::Runner for Runner { app.setActivationPolicy_(NSApplicationActivationPolicyRegular); (*app).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void); (*app_delegate).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void); - app.setMainMenu_(create_menu_bar()); app.setDelegate_(app_delegate); app.run(); pool.drain(); @@ -190,51 +283,17 @@ extern "C" fn open_files(this: &mut Object, _: Sel, _: id, paths: id) { } } -unsafe fn create_menu_bar() -> id { - let menu_bar = NSMenu::new(nil).autorelease(); - - // App menu - let app_menu_item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string("Application"), - Sel::from_ptr(ptr::null()), - ns_string(""), - ) - .autorelease(); - let quit_item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string("Quit"), - selector("terminate:"), - ns_string("q\0"), - ) - .autorelease(); - let app_menu = NSMenu::new(nil).autorelease(); - app_menu.addItem_(quit_item); - app_menu_item.setSubmenu_(app_menu); - menu_bar.addItem_(app_menu_item); - - // File menu - let file_menu_item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string("File"), - Sel::from_ptr(ptr::null()), - ns_string(""), - ) - .autorelease(); - let open_item = NSMenuItem::alloc(nil) - .initWithTitle_action_keyEquivalent_( - ns_string("Open"), - selector("openDocument:"), - ns_string("o\0"), - ) - .autorelease(); - let file_menu = NSMenu::new(nil).autorelease(); - file_menu.setTitle_(ns_string("File")); - file_menu.addItem_(open_item); - file_menu_item.setSubmenu_(file_menu); - menu_bar.addItem_(file_menu_item); - - menu_bar +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 { diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 5bd7e4659e40011457c6ba50a346bf9d6be628fd..bbd2dc9838c705ceaebcac0f4a5a1122ce4276bc 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -15,7 +15,7 @@ use crate::{ vector::Vector2F, }, text_layout::Line, - Scene, + Menu, Scene, }; use anyhow::Result; use async_task::Runnable; @@ -23,11 +23,13 @@ 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 where; + 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); } @@ -40,6 +42,7 @@ pub trait App { executor: Rc, ) -> Result>; fn fonts(&self) -> Arc; + fn quit(&self); } pub trait Dispatcher: Send + Sync { diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 545ab10e34149b4fd65bea4e40933c508fadad44..067184adea27b52f3733fb4a27cb77e97913634b 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -46,6 +46,8 @@ impl super::App for App { fn fonts(&self) -> std::sync::Arc { self.fonts.clone() } + + fn quit(&self) {} } impl Window { diff --git a/zed/src/lib.rs b/zed/src/lib.rs index a66a892ebfd5d3c033315f7ee49d48637de6dbd5..752df470c5c8078ea04263223b1bf3539f8e37bd 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -1,6 +1,7 @@ pub mod assets; pub mod editor; pub mod file_finder; +pub mod menus; mod operation_queue; pub mod settings; mod sum_tree; diff --git a/zed/src/main.rs b/zed/src/main.rs index ed52fb6163758aee7ec13a99f5e85d5c6e8f82d1..08fb09878437a1564fb8caccd4f511e3fe27336a 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -4,7 +4,7 @@ use log::LevelFilter; use simplelog::SimpleLogger; use std::{fs, path::PathBuf}; use zed::{ - assets, editor, file_finder, settings, + assets, editor, file_finder, menus, settings, workspace::{self, OpenParams}, }; @@ -14,10 +14,18 @@ fn main() { let app = gpui::App::new(assets::Assets).unwrap(); let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap(); - { - let mut app = app.clone(); - platform::runner() - .on_finish_launching(move || { + platform::runner() + .set_menus(menus::MENUS) + .on_menu_command({ + let app = app.clone(); + move |command| { + log::info!("menu command: {}", command); + 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); @@ -36,9 +44,9 @@ fn main() { }, ); } - }) - .run(); - } + } + }) + .run(); } fn init_logger() { diff --git a/zed/src/menus.rs b/zed/src/menus.rs new file mode 100644 index 0000000000000000000000000000000000000000..c6ef18b74616b4828e2fe5bc4641ceb2aeea368d --- /dev/null +++ b/zed/src/menus.rs @@ -0,0 +1,52 @@ +use gpui::{Menu, MenuItem}; + +#[cfg(target_os = "macos")] +pub const MENUS: &'static [Menu] = &[ + Menu { + name: "Zed", + items: &[ + MenuItem::Action { + name: "About Zed...", + keystroke: None, + action: "app:about-zed", + }, + MenuItem::Separator, + MenuItem::Action { + name: "Quit", + keystroke: Some("cmd-q"), + action: "app:quit", + }, + ], + }, + Menu { + name: "File", + items: &[ + MenuItem::Action { + name: "Undo", + keystroke: Some("cmd-z"), + action: "editor:undo", + }, + MenuItem::Action { + name: "Redo", + keystroke: Some("cmd-Z"), + action: "editor:redo", + }, + MenuItem::Separator, + MenuItem::Action { + name: "Cut", + keystroke: Some("cmd-x"), + action: "editor:cut", + }, + MenuItem::Action { + name: "Copy", + keystroke: Some("cmd-c"), + action: "editor:copy", + }, + MenuItem::Action { + name: "Paste", + keystroke: Some("cmd-v"), + action: "editor:paste", + }, + ], + }, +]; diff --git a/zed/src/workspace/mod.rs b/zed/src/workspace/mod.rs index c58fa864d2c00ce7c11d2adc6f90888db6942c5d..b7a76f94458fa15436d8bf0a4c2d2685954630dc 100644 --- a/zed/src/workspace/mod.rs +++ b/zed/src/workspace/mod.rs @@ -14,6 +14,7 @@ use std::path::PathBuf; pub fn init(app: &mut App) { app.add_global_action("workspace:open_paths", open_paths); + app.add_global_action("app:quit", quit); pane::init(app); workspace_view::init(app); } @@ -50,6 +51,10 @@ fn open_paths(params: &OpenParams, app: &mut MutableAppContext) { app.add_window(|ctx| WorkspaceView::new(workspace, params.settings.clone(), ctx)); } +fn quit(_: &(), app: &mut MutableAppContext) { + app.platform().quit(); +} + #[cfg(test)] mod tests { use super::*; From f656b387b3fa6e63628ff8c2cd5d88a63efda899 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 8 Apr 2021 16:11:45 -0700 Subject: [PATCH 04/15] Call SetActivationPolicy at the proper time If this method is called too early, the menu bar won't be clickable on startup until the window loses focus. Calling it once the application finishes launching seems to fix the issue. See https://github.com/glfw/glfw/issues/1648 --- gpui/src/platform/mac/runner.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gpui/src/platform/mac/runner.rs b/gpui/src/platform/mac/runner.rs index 1783e03ea050feaebd2deb935fd0385f44fa5745..2c2c3ecab4df1f7a473821b34ec9b47024d49a13 100644 --- a/gpui/src/platform/mac/runner.rs +++ b/gpui/src/platform/mac/runner.rs @@ -205,7 +205,6 @@ impl crate::platform::Runner for Runner { let app: id = msg_send![APP_CLASS, sharedApplication]; let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; - app.setActivationPolicy_(NSApplicationActivationPolicyRegular); (*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); @@ -241,9 +240,14 @@ extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) { } extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) { - let runner = unsafe { get_runner(this) }; - if let Some(callback) = runner.finish_launching_callback.take() { - callback(); + 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(); + } } } From 7ebcbdc0cb5b7f8014f8d68ec9aed2d184b2dcf2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 8 Apr 2021 22:25:54 -0700 Subject: [PATCH 05/15] Implement File > Open menu item --- Cargo.lock | 18 ++++++----------- Cargo.toml | 6 ++++++ gpui/src/platform/mac/app.rs | 39 ++++++++++++++++++++++++++++++++++-- gpui/src/platform/mod.rs | 7 +++++++ gpui/src/platform/test.rs | 4 ++++ zed/src/main.rs | 23 +++++++++++++++++---- zed/src/menus.rs | 10 ++++++++- 7 files changed, 88 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38d75ed246ed6c1ed7525fa8da20c5aed33e0f85..ce7dedfdf6f4626dc77d290eb1cf8c4502dbae52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,8 +399,7 @@ dependencies = [ [[package]] name = "cocoa" version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" dependencies = [ "bitflags", "block", @@ -415,8 +414,7 @@ dependencies = [ [[package]] name = "cocoa-foundation" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" dependencies = [ "bitflags", "block", @@ -445,8 +443,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" dependencies = [ "core-foundation-sys", "libc", @@ -455,14 +452,12 @@ dependencies = [ [[package]] name = "core-foundation-sys" version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" [[package]] name = "core-graphics" version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" dependencies = [ "bitflags", "core-foundation", @@ -474,8 +469,7 @@ dependencies = [ [[package]] name = "core-graphics-types" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" dependencies = [ "bitflags", "core-foundation", diff --git a/Cargo.toml b/Cargo.toml index 83f3ad985a58a5d311c4978c0a53ab6e1e52c7af..e062eb99eb97a890ac07a3c28982555dbb1ad24d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,3 +3,9 @@ members = ["zed", "gpui"] [patch.crates-io] async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"} + +# TODO - Remove when this is merged: https://github.com/servo/core-foundation-rs/pull/454 +cocoa = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} +cocoa-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} +core-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} +core-graphics = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} diff --git a/gpui/src/platform/mac/app.rs b/gpui/src/platform/mac/app.rs index b77cb2ee51ade7fc733e431b842a8d8aa5c29e77..849f112d4503e0127a3e7ce3a2bc487810be0a8f 100644 --- a/gpui/src/platform/mac/app.rs +++ b/gpui/src/platform/mac/app.rs @@ -1,9 +1,13 @@ use super::{BoolExt as _, Dispatcher, FontSystem, Window}; use crate::{executor, platform}; use anyhow::Result; -use cocoa::{appkit::NSApplication, base::nil}; +use cocoa::{ + appkit::{NSApplication, NSOpenPanel, NSModalResponse}, + base::nil, + foundation::{NSArray, NSString, NSURL}, +}; use objc::{msg_send, sel, sel_impl}; -use std::{rc::Rc, sync::Arc}; +use std::{path::PathBuf, rc::Rc, sync::Arc}; pub struct App { dispatcher: Arc, @@ -39,6 +43,37 @@ impl platform::App for App { 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() } diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index bbd2dc9838c705ceaebcac0f4a5a1122ce4276bc..9ece82fb787c52f9a5503babbf130406ad816833 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -41,6 +41,7 @@ pub trait App { options: WindowOptions, executor: Rc, ) -> Result>; + fn prompt_for_paths(&self, options: PathPromptOptions) -> Option>; fn fonts(&self) -> Arc; fn quit(&self); } @@ -66,6 +67,12 @@ pub struct WindowOptions<'a> { pub title: Option<&'a str>, } +pub struct PathPromptOptions { + pub files: bool, + pub directories: bool, + pub multiple: bool, +} + pub trait FontSystem: Send + Sync { fn load_family(&self, name: &str) -> anyhow::Result>; fn select_font( diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 067184adea27b52f3733fb4a27cb77e97913634b..fdb497ae9765eb56df0bd8f9116d7bc3b18597cb 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -48,6 +48,10 @@ impl super::App for App { } fn quit(&self) {} + + fn prompt_for_paths(&self, _: super::PathPromptOptions) -> Option> { + None + } } impl Window { diff --git a/zed/src/main.rs b/zed/src/main.rs index 08fb09878437a1564fb8caccd4f511e3fe27336a..82a8a731064322c4851bf74224de0b33bc9672d7 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -1,5 +1,5 @@ use fs::OpenOptions; -use gpui::platform::{current as platform, Runner as _}; +use gpui::platform::{current as platform, PathPromptOptions, Runner as _}; use log::LevelFilter; use simplelog::SimpleLogger; use std::{fs, path::PathBuf}; @@ -18,9 +18,24 @@ fn main() { .set_menus(menus::MENUS) .on_menu_command({ let app = app.clone(); - move |command| { - log::info!("menu command: {}", command); - app.dispatch_global_action(command, ()) + 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({ diff --git a/zed/src/menus.rs b/zed/src/menus.rs index c6ef18b74616b4828e2fe5bc4641ceb2aeea368d..cda749c30e5ee3a4678f678e5d879381756003f8 100644 --- a/zed/src/menus.rs +++ b/zed/src/menus.rs @@ -6,7 +6,7 @@ pub const MENUS: &'static [Menu] = &[ name: "Zed", items: &[ MenuItem::Action { - name: "About Zed...", + name: "About Zed…", keystroke: None, action: "app:about-zed", }, @@ -20,6 +20,14 @@ pub const MENUS: &'static [Menu] = &[ }, Menu { name: "File", + items: &[MenuItem::Action { + name: "Open…", + keystroke: Some("cmd-o"), + action: "app:open", + }], + }, + Menu { + name: "Edit", items: &[ MenuItem::Action { name: "Undo", From 6873662c472561e228dfb90409f656be2705be89 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 9 Apr 2021 08:45:23 -0700 Subject: [PATCH 06/15] Use upstream git revision of core-foundation-rs --- Cargo.lock | 12 ++++++------ Cargo.toml | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce7dedfdf6f4626dc77d290eb1cf8c4502dbae52..b497ce19815c045b4503fa6b9c5513f79812a8c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ dependencies = [ [[package]] name = "cocoa" version = "0.24.0" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" +source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" dependencies = [ "bitflags", "block", @@ -414,7 +414,7 @@ dependencies = [ [[package]] name = "cocoa-foundation" version = "0.1.0" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" +source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" dependencies = [ "bitflags", "block", @@ -443,7 +443,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" version = "0.9.1" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" +source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" dependencies = [ "core-foundation-sys", "libc", @@ -452,12 +452,12 @@ dependencies = [ [[package]] name = "core-foundation-sys" version = "0.8.2" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" +source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" [[package]] name = "core-graphics" version = "0.22.2" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" +source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" dependencies = [ "bitflags", "core-foundation", @@ -469,7 +469,7 @@ dependencies = [ [[package]] name = "core-graphics-types" version = "0.1.1" -source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b" +source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60" dependencies = [ "bitflags", "core-foundation", diff --git a/Cargo.toml b/Cargo.toml index e062eb99eb97a890ac07a3c28982555dbb1ad24d..b60e33d04262cdcd0a57edbbf83ce3634f97ca20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ members = ["zed", "gpui"] [patch.crates-io] async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"} -# TODO - Remove when this is merged: https://github.com/servo/core-foundation-rs/pull/454 -cocoa = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} -cocoa-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} -core-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} -core-graphics = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"} +# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/454 +cocoa = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} +cocoa-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} +core-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} +core-graphics = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"} From 301163bab70b289ca9dc5865179bd69bdb1a3373 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 9 Apr 2021 13:03:26 -0600 Subject: [PATCH 07/15] 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 63f52f77b15c76366fc517e139255d46bd7bef10..14380f19e1331f1195bab5fad5ce054212629a8e 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 1965c634a388967a7fb50cfaeff8626766540bf6..0000000000000000000000000000000000000000 --- 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 b7a19c3647a98f97918e85b92ce43ad9001c2844..b54d148682e9699c454c6606390dbdd0dbb6afc8 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 0000000000000000000000000000000000000000..d089ba26d2d23bebdf14b71d1f7628b91650ca13 --- /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 ae0f3cddb9ffd157eef1292d349ca2ff4d39492f..b96bffb979add78ad50e2422b3c0afd8505d1ccd 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 9f719bd2bca670225ef6049ea3b5f6b1a85b5d3f..08ec95ca74fdb79a5c25d8a9d51410068297ff04 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 08/15] 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 14380f19e1331f1195bab5fad5ce054212629a8e..2c7af63af812cc36817cabbcc5c287d821471e59 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 6f3d6f108eefbcd08ce4b9b6b87156d372d25df3..fbd6c8cc833665058d5b5334c478085e3345e2f5 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 c951d1293421ffd7ed124c7caa3153aa440c3367..28a0ae036046ba77be1d92c25cef1cadb03e62fb 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 82a8a731064322c4851bf74224de0b33bc9672d7..f7e4f0346bdb841d8583e51794f9bd8bb3f63119 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 b7a76f94458fa15436d8bf0a4c2d2685954630dc..e1a087049c68f9082e7895a91c55a4485b829ef4 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 fad299aa8bd004fa37d2768787785e53dda57c07..903292a63e23520e8229f16fe9a2a8f7a974638e 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 5810245be44a84b9968764e1f8e9939c7058ec7d..32e16fc0145b7eea522450c03622dee8027ce71a 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 09/15] 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 | 291 +++++++++++----------- zed/src/editor/display_map/mod.rs | 93 ++++--- 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, 726 insertions(+), 756 deletions(-) diff --git a/gpui/examples/text.rs b/gpui/examples/text.rs index 14bc198270e0085f53dfe8b537c53a89fcb72efa..2dad6e07869a0d291ea2f764cbf7da515304ee8d 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 2c7af63af812cc36817cabbcc5c287d821471e59..ab3f471e737a8e4e273ce1339f04e59ccf5cecf8 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 b54d148682e9699c454c6606390dbdd0dbb6afc8..07ada3650a561d04cd0daf48a068ff86f92ba7de 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 d089ba26d2d23bebdf14b71d1f7628b91650ca13..40bd3566a584c12938213b820aa405b0fc1d13d8 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -90,14 +90,113 @@ 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 + } } impl platform::Platform for MacPlatform { @@ -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 b96bffb979add78ad50e2422b3c0afd8505d1ccd..6e5d53585f6eedde0ab4b890e08cdb006e61fbb2 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 08ec95ca74fdb79a5c25d8a9d51410068297ff04..1cd4399c1bbc4238c18ac2dc00e81b19f9cf28f7 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 827da6be9d1a8ab3a005c7503620a93b64dc7174..9807b8ef97bdf93ce0f2b8ef9fd245eeb3289a70 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 fbd6c8cc833665058d5b5334c478085e3345e2f5..b7974a3a2e2d4a6ca2553a1a1da0340f6835e435 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 f5d76636ffc2a06dac0bc03c14b120d8cf630ede..d67b4b510ece1482b8159ca7bbbb2fe08134bc8f 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)); - - 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>(()) - })?; - - let edits = buffer.update(&mut app, |buffer, ctx| { + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + + 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(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::>()) - })?; - - app.read(|app| { - map.apply_edits(&edits, app)?; - assert_eq!(map.text(app), "123a…c123c…eeeee"); - Ok::<(), anyhow::Error>(()) - })?; - - let edits = buffer.update(&mut 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( + 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…c123456eee"); + map.apply_edits(&edits, app.as_ref()).unwrap(); + assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee"); + + 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)) + .unwrap(); + buffer.edits_since(start_version).collect::>() + }); - map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app)?; - assert_eq!(map.text(app), "123aaaaa\nbbbbbb\nccc123456eee"); + map.apply_edits(&edits, app.as_ref()).unwrap(); + assert_eq!(map.text(app.as_ref()), "123a…c123456eee"); - Ok(()) - }) - }) + 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"); + }); } #[test] - fn test_overlapping_folds() -> Result<()> { - App::test((), |mut app| async move { + fn test_overlapping_folds() { + 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()); + 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_merging_folds_via_edit() -> 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))); - let mut map = app.read(|app| FoldMap::new(buffer.clone(), app)); - - 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 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.as_ref(), + ) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee"); + + 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::>()) - })?; - - app.read(|app| { - map.apply_edits(&edits, app)?; - assert_eq!(map.text(app), "aa…eeeee"); - Ok(()) - }) - }) + buffer + .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx)) + .unwrap(); + buffer.edits_since(start_version).collect::>() + }); + + 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::>()) - })?; - - app.read(|app| { - map.apply_edits(&edits, app)?; - - 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; + buffer.edits_since(start_version).collect::>() + }); - expected_text.replace_range(fold_range.start..fold_range.end, "…"); - } - expected_buffer_rows.extend((0..=next_row).rev()); - expected_buffer_rows.reverse(); + map.apply_edits(&edits, app.as_ref()).unwrap(); - assert_eq!(map.text(app), expected_text); + 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; - 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..], - ); - } + expected_text.replace_range(fold_range.start..fold_range.end, "…"); + } + expected_buffer_rows.extend((0..=next_row).rev()); + expected_buffer_rows.reverse(); - Ok::<(), anyhow::Error>(()) - })?; + assert_eq!(map.text(app.as_ref()), expected_text); - 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); - - 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\nffffff\n"); - assert_eq!(map.buffer_rows(0)?.collect::>(), vec![0, 3, 5, 6]); - assert_eq!(map.buffer_rows(3)?.collect::>(), vec![6]); - - Ok(()) - }) - }) + 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.as_ref(), + ) + .unwrap(); + + 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 2679766aa16224859c2a4b62b87fafeb2534d5d2..7bc17627dddc3bcd418b4efa66e02f4f1d960c52 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), - ) - })?; - - 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(()) - }) + 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(); + + 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 28a0ae036046ba77be1d92c25cef1cadb03e62fb..d03ce43294b5528cdf98942cc178bc1e062983ec 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 f7e4f0346bdb841d8583e51794f9bd8bb3f63119..6d171a5a29694ff9f2ec510958a3fefce6d43ec8 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 e1a087049c68f9082e7895a91c55a4485b829ef4..91dd99419a0e2792db5ce9b6406a883677a7e39e 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 903292a63e23520e8229f16fe9a2a8f7a974638e..58acb86abb12c0c42516d29b70aba9b95d0bd14d 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 092144791c0da52cee98fda778fd781d590ad534..cc5cfec60295e00550063a003fb279942a22d897 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 32e16fc0145b7eea522450c03622dee8027ce71a..aaf1e9eb9a93e1abf3bb03fef71d9ad3f5e16e53 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 bbb264df625806bda0e08be83f5d6cffa9746bcf..27f42d904d3b695d46a1d057d4484176e6b3f541 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 10/15] 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 2dad6e07869a0d291ea2f764cbf7da515304ee8d..df1f367cdbd9bcef0128be2941bb4a4724f8d7bd 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 ab3f471e737a8e4e273ce1339f04e59ccf5cecf8..48720eabcbfd9abbb7632e7550e7c4a53e898d6f 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 07ada3650a561d04cd0daf48a068ff86f92ba7de..c2a88c12a44c390895c39970061643c19fab6217 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 40bd3566a584c12938213b820aa405b0fc1d13d8..4201100a6d6ce5556dddca064b056404c5ddc6a4 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 6e5d53585f6eedde0ab4b890e08cdb006e61fbb2..39825c941ecfc1efbc7d311b2226314302422de2 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 1cd4399c1bbc4238c18ac2dc00e81b19f9cf28f7..b0d19232dc8435aaa22fcfc31de360a7b691a674 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 6d171a5a29694ff9f2ec510958a3fefce6d43ec8..b214924a80aadd7ee67559c07cecda0142a88164 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 11/15] 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 | 144 +++++++++-------- zed/src/worktree/worktree.rs | 60 ++++--- 10 files changed, 320 insertions(+), 318 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 48720eabcbfd9abbb7632e7550e7c4a53e898d6f..702fc952a721a0fc4078d20183927d8163bf383e 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 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 font_cache(&self) -> Arc { + self.0.borrow().font_cache.clone() } - 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 e00c11f1fded06bc1a0fce3b5ec5fb2bbc195e43..6141c0b7c6e0a1edd6f279d335e4de5fa5dec1b0 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 b7974a3a2e2d4a6ca2553a1a1da0340f6835e435..fadf7822803280e8bd326f82dd93d6a47c623669 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 d67b4b510ece1482b8159ca7bbbb2fe08134bc8f..58f51cee94096d5c256ddce7804fef9e5be8d781 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 7bc17627dddc3bcd418b4efa66e02f4f1d960c52..e44193104e311d037c7e567f5e754769e8f9f77f 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 d03ce43294b5528cdf98942cc178bc1e062983ec..169d2243a4d69b65870a25e0215f2133ef6066a5 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 91dd99419a0e2792db5ce9b6406a883677a7e39e..a441eb8b8b1c06baf299c05e6f65088c933430bb 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 cc5cfec60295e00550063a003fb279942a22d897..b4e5a85e585db47210dbb20559071b589677027f 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 aaf1e9eb9a93e1abf3bb03fef71d9ad3f5e16e53..aece490d376eabe1a79c2ff65089a32269220c35 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); - - 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", ()); - - let w = workspace_view.as_ref(app); - assert_eq!(w.panes.len(), 1); - assert_eq!(w.active_pane(), &pane_1); + app.update(|ctx| { + let pane_2 = workspace_view.read(ctx).active_pane().clone(); + assert_ne!(pane_1, pane_2); + + assert_eq!( + pane_2 + .read(ctx) + .active_item() + .unwrap() + .entry_id(ctx.as_ref()), + Some(entries[0]) + ); + + 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); + }) }); } } diff --git a/zed/src/worktree/worktree.rs b/zed/src/worktree/worktree.rs index 27f42d904d3b695d46a1d057d4484176e6b3f541..63be9b6eefdb3a1878c63d8906a6f3b502352bc6 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 12/15] 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 702fc952a721a0fc4078d20183927d8163bf383e..413c838fdad66d9aa7aec7728e3d2d238cac950c 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 aece490d376eabe1a79c2ff65089a32269220c35..6a9b3ca320618839f6cdc67899cf05ac8bd3d707 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 13/15] 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 413c838fdad66d9aa7aec7728e3d2d238cac950c..e5f40c6ba59ec9dac6e3c6e2c1ffe8f3b5065869 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 7c3be96a72c0d94b15f06db76b12adcad6da86ef..6efa21499565d40fc96f7e203b614c04bcd0ef71 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 6141c0b7c6e0a1edd6f279d335e4de5fa5dec1b0..eabd89179144f84902443db42846b63ed9102fb3 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 14/15] 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 2c2c3ecab4df1f7a473821b34ec9b47024d49a13..0000000000000000000000000000000000000000 --- 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 15/15] 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 7b0ecdddef2e56ef14ca02c8f04ad3f094e6c377..b60ce9b92d1ba0719517cae8e5892ba79ff872bc 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 b0d19232dc8435aaa22fcfc31de360a7b691a674..f1d6bead6692b5773c2ae168b97a0603f9104664 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 b214924a80aadd7ee67559c07cecda0142a88164..407b4952c1b4a0fca1e314cacec48274c82f36b4 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};