From 3329b2bbd6994a4fcd47b29d950c591b07a48c16 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Tue, 24 Jan 2023 19:46:04 -0500 Subject: [PATCH 01/17] Remove `gpui::` prefix from parameters --- crates/feedback/src/feedback_editor.rs | 32 +++++++++----------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index b3322f50db79ae76ea81db4ab6719fefebe9286a..3a6bc744f2487b7940a973d7797f9e1d563656d5 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -12,7 +12,7 @@ use gpui::{ elements::{ChildView, Flex, Label, MouseEventHandler, ParentElement, Stack, Text}, serde_json, AnyViewHandle, AppContext, CursorStyle, Element, ElementBox, Entity, ModelHandle, MouseButton, MutableAppContext, PromptLevel, RenderContext, Task, View, ViewContext, - ViewHandle, + ViewHandle, WeakViewHandle, }; use isahc::Request; use language::Buffer; @@ -79,12 +79,7 @@ impl View for FeedbackButton { } impl StatusItemView for FeedbackButton { - fn set_active_pane_item( - &mut self, - _: Option<&dyn ItemHandle>, - _: &mut gpui::ViewContext, - ) { - } + fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} } #[derive(Serialize)] @@ -135,7 +130,7 @@ impl FeedbackEditor { fn handle_save( &mut self, - _: gpui::ModelHandle, + _: ModelHandle, cx: &mut ViewContext, ) -> Task> { let feedback_char_count = self.editor.read(cx).buffer().read(cx).len(cx); @@ -269,12 +264,7 @@ impl Entity for FeedbackEditor { } impl Item for FeedbackEditor { - fn tab_content( - &self, - _: Option, - style: &theme::Tab, - _: &gpui::AppContext, - ) -> ElementBox { + fn tab_content(&self, _: Option, style: &theme::Tab, _: &AppContext) -> ElementBox { Flex::row() .with_child( Label::new("Feedback".to_string(), style.label.clone()) @@ -293,19 +283,19 @@ impl Item for FeedbackEditor { Vec::new() } - fn is_singleton(&self, _: &gpui::AppContext) -> bool { + fn is_singleton(&self, _: &AppContext) -> bool { true } fn set_nav_history(&mut self, _: workspace::ItemNavHistory, _: &mut ViewContext) {} - fn can_save(&self, _: &gpui::AppContext) -> bool { + fn can_save(&self, _: &AppContext) -> bool { true } fn save( &mut self, - project: gpui::ModelHandle, + project: ModelHandle, cx: &mut ViewContext, ) -> Task> { self.handle_save(project, cx) @@ -313,7 +303,7 @@ impl Item for FeedbackEditor { fn save_as( &mut self, - project: gpui::ModelHandle, + project: ModelHandle, _: std::path::PathBuf, cx: &mut ViewContext, ) -> Task> { @@ -322,7 +312,7 @@ impl Item for FeedbackEditor { fn reload( &mut self, - _: gpui::ModelHandle, + _: ModelHandle, _: &mut ViewContext, ) -> Task> { unreachable!("reload should not have been called") @@ -356,8 +346,8 @@ impl Item for FeedbackEditor { } fn deserialize( - _: gpui::ModelHandle, - _: gpui::WeakViewHandle, + _: ModelHandle, + _: WeakViewHandle, _: workspace::WorkspaceId, _: workspace::ItemId, _: &mut ViewContext, From 426aeb7c5e919801360eb46ccd6354f34e12da65 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 20 Jan 2023 18:10:24 -0800 Subject: [PATCH 02/17] WIP - adds platform APIs for checking the top most window --- crates/gpui/src/app.rs | 7 +++ crates/gpui/src/platform.rs | 3 +- crates/gpui/src/platform/mac/platform.rs | 15 ++++- crates/gpui/src/platform/mac/status_item.rs | 4 ++ crates/gpui/src/platform/mac/window.rs | 30 ++++++++++ crates/gpui/src/platform/test.rs | 6 +- crates/gpui/src/presenter.rs | 9 ++- crates/gpui/src/presenter/event_dispatcher.rs | 1 + crates/workspace/src/workspace.rs | 56 ++++++++++++++++++- 9 files changed, 126 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index ad1fad85b1938220e4f2259ff623dc274748aac5..0e5697c30bc1d1fbe2b8dcfeb4003254bd460a64 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -21,6 +21,7 @@ use std::{ use anyhow::{anyhow, Context, Result}; use lazy_static::lazy_static; use parking_lot::Mutex; +use pathfinder_geometry::vector::Vector2F; use postage::oneshot; use smallvec::SmallVec; use smol::prelude::*; @@ -865,6 +866,12 @@ impl MutableAppContext { } } + pub fn screen_position(&self, window_id: usize, view_position: &Vector2F) -> Option { + self.presenters_and_platform_windows + .get(&window_id) + .map(|(_, window)| window.screen_position(view_position)) + } + pub fn window_ids(&self) -> impl Iterator + '_ { self.cx.windows.keys().cloned() } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 99d607e4070c5eebb2039dba3754e6100a59b30a..0601fd97640559e16f4b1eb6ba39f61fff5a4af2 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -64,7 +64,7 @@ pub trait Platform: Send + Sync { fn read_credentials(&self, url: &str) -> Result)>>; fn delete_credentials(&self, url: &str) -> Result<()>; - fn set_cursor_style(&self, style: CursorStyle); + fn set_cursor_style(&self, style: CursorStyle, window_id: usize, screen_position: &Vector2F); fn should_auto_hide_scrollbars(&self) -> bool; fn local_timezone(&self) -> UtcOffset; @@ -145,6 +145,7 @@ pub trait Window { fn present_scene(&mut self, scene: Scene); fn appearance(&self) -> Appearance; fn on_appearance_changed(&mut self, callback: Box); + fn screen_position(&self, view_position: &Vector2F) -> Vector2F; } #[derive(Debug)] diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 37406858ecec6ed277ed0f964c5e9d4ef86af2ee..f1b7f75c03f62e4b43fc6022c19ed7c618353dba 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -37,6 +37,7 @@ use objc::{ runtime::{Class, Object, Sel}, sel, sel_impl, }; +use pathfinder_geometry::vector::Vector2F; use postage::oneshot; use ptr::null_mut; use std::{ @@ -695,7 +696,19 @@ impl platform::Platform for MacPlatform { Ok(()) } - fn set_cursor_style(&self, style: CursorStyle) { + fn set_cursor_style(&self, style: CursorStyle, window_id: usize, screen_position: &Vector2F) { + let top_most = Window::window_id_under(screen_position); + if top_most.is_none() { + return; + } + if top_most.unwrap() != window_id { + return; + } + + dbg!(top_most.unwrap(), window_id); + + dbg!(style); + unsafe { let cursor: id = match style { CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], diff --git a/crates/gpui/src/platform/mac/status_item.rs b/crates/gpui/src/platform/mac/status_item.rs index 33feb4808f17c15136df7daeee287a90af921f3a..d05c559772651a3e8aa0ce20b149da31ac9e746e 100644 --- a/crates/gpui/src/platform/mac/status_item.rs +++ b/crates/gpui/src/platform/mac/status_item.rs @@ -258,6 +258,10 @@ impl platform::Window for StatusItem { crate::Appearance::from_native(appearance) } } + + fn screen_position(&self, _view_position: &Vector2F) -> Vector2F { + unimplemented!() + } } impl StatusItemState { diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 6126533644578ca0a023657eef851f26c16432a3..032b88507bca39044ca5bf7f17c9f8e16ec333f8 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -559,6 +559,26 @@ impl Window { } } } + + pub fn window_id_under(screen_position: &Vector2F) -> Option { + unsafe { + let app = NSApplication::sharedApplication(nil); + + let point = NSPoint::new(screen_position.x() as f64, screen_position.y() as f64); + let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:point belowWindowWithWindowNumber:0 as NSInteger]; + // For some reason this API doesn't work when our two windows are on top of each other + let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number]; + dbg!(top_most_window); + let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS]; + let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS]; + if is_panel | is_window { + let id = get_window_state(&*top_most_window).borrow().id; + Some(id) + } else { + None + } + } + } } impl Drop for Window { @@ -755,6 +775,16 @@ impl platform::Window for Window { fn on_appearance_changed(&mut self, callback: Box) { self.0.borrow_mut().appearance_changed_callback = Some(callback); } + + fn screen_position(&self, view_position: &Vector2F) -> Vector2F { + let self_borrow = self.0.borrow_mut(); + unsafe { + let point = NSPoint::new(view_position.x() as f64, view_position.y() as f64); + let screen_point: NSPoint = + msg_send![self_borrow.native_window, convertPointToScreen: point]; + vec2f(screen_point.x as f32, screen_point.y as f32) + } + } } impl WindowState { diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 00cd524c1de6b99e9acfef3a43f1e1f3c3b02630..7295bf525bc1321cd378069ead2f2d1dde15f775 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -178,7 +178,7 @@ impl super::Platform for Platform { Ok(()) } - fn set_cursor_style(&self, style: CursorStyle) { + fn set_cursor_style(&self, style: CursorStyle, _window_id: usize, _position: &Vector2F) { *self.cursor.lock() = style; } @@ -332,6 +332,10 @@ impl super::Window for Window { } fn on_appearance_changed(&mut self, _: Box) {} + + fn screen_position(&self, view_position: &Vector2F) -> Vector2F { + view_position.clone() + } } pub fn platform() -> Platform { diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 0909d95fd0a5ac4f53e5de600ca7bc5c540dfc00..f74eef463a7aab26637f1217d84334424bedabaf 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -316,7 +316,14 @@ impl Presenter { break; } } - cx.platform().set_cursor_style(style_to_assign); + + if let Some(screen_position) = cx.screen_position(self.window_id, position) { + cx.platform().set_cursor_style( + style_to_assign, + self.window_id, + &screen_position, + ); + } if !event_reused { if pressed_button.is_some() { diff --git a/crates/gpui/src/presenter/event_dispatcher.rs b/crates/gpui/src/presenter/event_dispatcher.rs index 4c72334910b514a257be8213410d1aa9dbb05b79..960c565bd49f02a90f41a50f3fb595adc6579b93 100644 --- a/crates/gpui/src/presenter/event_dispatcher.rs +++ b/crates/gpui/src/presenter/event_dispatcher.rs @@ -209,6 +209,7 @@ impl EventDispatcher { break; } } + cx.platform().set_cursor_style(style_to_assign); if !event_reused { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ec7ba8fae07ec149c378d1f4e9dc2dde4e4b6a25..71c5ef97a209e866a9d6ad4ba69d491af59bcb6e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -31,8 +31,9 @@ use futures::{ }; use gpui::{ actions, + color::Color, elements::*, - geometry::vector::Vector2F, + geometry::vector::{vec2f, Vector2F}, impl_actions, impl_internal_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, WindowOptions}, @@ -263,6 +264,59 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { client.add_view_request_handler(Workspace::handle_follow); client.add_view_message_handler(Workspace::handle_unfollow); client.add_view_message_handler(Workspace::handle_update_followers); + + // REMEMBER TO DELETE THE SHOW NOTIF + cx.add_action( + |_workspace: &mut Workspace, _: &ShowNotif, cx: &mut ViewContext| { + struct DummyView; + + impl Entity for DummyView { + type Event = (); + } + + impl View for DummyView { + fn ui_name() -> &'static str { + "DummyView" + } + fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { + MouseEventHandler::::new(0, cx, |state, _cx| { + Empty::new() + .contained() + .with_background_color(if state.hovered() { + Color::red() + } else { + Color::blue() + }) + .constrained() + .with_width(200.) + .with_height(200.) + .boxed() + }) + .on_click(MouseButton::Left, |_, _| { + println!("click"); + }) + .with_cursor_style(CursorStyle::ResizeUpDown) + .boxed() + } + } + + cx.add_window( + WindowOptions { + bounds: gpui::WindowBounds::Fixed(gpui::geometry::rect::RectF::new( + vec2f(400., 200.), + vec2f(300., 200.), + )), + titlebar: None, + center: false, + focus: false, + kind: gpui::WindowKind::PopUp, + is_movable: true, + screen: None, + }, + |_cx| DummyView, + ) + }, + ) } type ProjectItemBuilders = HashMap< From 27a80a1c94c56b2c9f23cd26acf83982e1d40c41 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 23 Jan 2023 10:45:31 -0800 Subject: [PATCH 03/17] WIP --- crates/gpui/src/platform/mac/event.rs | 2 ++ crates/gpui/src/platform/mac/platform.rs | 35 ++++++++++-------------- crates/gpui/src/platform/mac/window.rs | 12 +++++--- crates/gpui/src/presenter.rs | 3 ++ 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs index 2f29898c26d2e9179560e40787d304e6320f33f6..688272137212f823ea4f09debd4e86f030e5ef32 100644 --- a/crates/gpui/src/platform/mac/event.rs +++ b/crates/gpui/src/platform/mac/event.rs @@ -125,6 +125,7 @@ impl Event { button, position: vec2f( native_event.locationInWindow().x as f32, + // MacOS screen coordinates are relative to bottom left window_height - native_event.locationInWindow().y as f32, ), modifiers: read_modifiers(native_event), @@ -150,6 +151,7 @@ impl Event { button, position: vec2f( native_event.locationInWindow().x as f32, + // MacOS view coordinates are relative to bottom left window_height - native_event.locationInWindow().y as f32, ), modifiers: read_modifiers(native_event), diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index f1b7f75c03f62e4b43fc6022c19ed7c618353dba..ae9db6a396951ff786431508f49a862d074048be 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -697,27 +697,20 @@ impl platform::Platform for MacPlatform { } fn set_cursor_style(&self, style: CursorStyle, window_id: usize, screen_position: &Vector2F) { - let top_most = Window::window_id_under(screen_position); - if top_most.is_none() { - return; - } - if top_most.unwrap() != window_id { - return; - } - - dbg!(top_most.unwrap(), window_id); - - dbg!(style); - - unsafe { - let cursor: id = match style { - CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], - CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor], - CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor], - CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor], - CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor], - }; - let _: () = msg_send![cursor, set]; + if Some(window_id) == Window::window_id_under(screen_position) { + dbg!(screen_position, style); + unsafe { + let cursor: id = match style { + CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], + CursorStyle::ResizeLeftRight => { + msg_send![class!(NSCursor), resizeLeftRightCursor] + } + CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor], + CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor], + CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor], + }; + let _: () = msg_send![cursor, set]; + } } } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 032b88507bca39044ca5bf7f17c9f8e16ec333f8..067dd691157124f1bf4c4b6ef737ebf1b8ebeea0 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -52,6 +52,8 @@ use std::{ time::Duration, }; +use super::geometry::Vector2FExt; + const WINDOW_STATE_IVAR: &str = "windowState"; static mut WINDOW_CLASS: *const Class = ptr::null(); @@ -564,11 +566,13 @@ impl Window { unsafe { let app = NSApplication::sharedApplication(nil); - let point = NSPoint::new(screen_position.x() as f64, screen_position.y() as f64); - let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:point belowWindowWithWindowNumber:0 as NSInteger]; + let point = screen_position.to_ns_point(); + let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:point belowWindowWithWindowNumber:0]; + // For some reason this API doesn't work when our two windows are on top of each other let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number]; - dbg!(top_most_window); + + // dbg!(top_most_window); let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS]; let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS]; if is_panel | is_window { @@ -779,7 +783,7 @@ impl platform::Window for Window { fn screen_position(&self, view_position: &Vector2F) -> Vector2F { let self_borrow = self.0.borrow_mut(); unsafe { - let point = NSPoint::new(view_position.x() as f64, view_position.y() as f64); + let point = view_position.to_ns_point(); let screen_point: NSPoint = msg_send![self_borrow.native_window, convertPointToScreen: point]; vec2f(screen_point.x as f32, screen_point.y as f32) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index f74eef463a7aab26637f1217d84334424bedabaf..1b16574cb8784f77a29d1ca45888bc493ace9a28 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -317,6 +317,9 @@ impl Presenter { } } + dbg!("*******"); + dbg!(position); + dbg!(event_reused); if let Some(screen_position) = cx.screen_position(self.window_id, position) { cx.platform().set_cursor_style( style_to_assign, From 45e4e3354e4729578d935bbd65da98b48f9f8e0f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 25 Jan 2023 08:58:41 -0800 Subject: [PATCH 04/17] Changed the presenter to only send 'set_cursor_style' on the topmost window co-authored-by: Antonio --- crates/gpui/src/app.rs | 6 ++- crates/gpui/src/platform.rs | 4 +- crates/gpui/src/platform/mac/platform.rs | 28 +++++----- crates/gpui/src/platform/mac/status_item.rs | 4 +- crates/gpui/src/platform/mac/window.rs | 59 ++++++++++----------- crates/gpui/src/platform/test.rs | 6 +-- crates/gpui/src/presenter.rs | 12 ++--- 7 files changed, 54 insertions(+), 65 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 0e5697c30bc1d1fbe2b8dcfeb4003254bd460a64..2e7378fc163e4703ac9bd9775c0a60644e552dfe 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -866,10 +866,12 @@ impl MutableAppContext { } } - pub fn screen_position(&self, window_id: usize, view_position: &Vector2F) -> Option { + pub fn is_topmost_window_for_position(&self, window_id: usize, position: Vector2F) -> bool { self.presenters_and_platform_windows .get(&window_id) - .map(|(_, window)| window.screen_position(view_position)) + .map_or(false, |(_, window)| { + window.is_topmost_for_position(position) + }) } pub fn window_ids(&self) -> impl Iterator + '_ { diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 0601fd97640559e16f4b1eb6ba39f61fff5a4af2..05ba61a9adc5efa15efe321816cc80dca8e5a65a 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -64,7 +64,7 @@ pub trait Platform: Send + Sync { fn read_credentials(&self, url: &str) -> Result)>>; fn delete_credentials(&self, url: &str) -> Result<()>; - fn set_cursor_style(&self, style: CursorStyle, window_id: usize, screen_position: &Vector2F); + fn set_cursor_style(&self, style: CursorStyle); fn should_auto_hide_scrollbars(&self) -> bool; fn local_timezone(&self) -> UtcOffset; @@ -145,7 +145,7 @@ pub trait Window { fn present_scene(&mut self, scene: Scene); fn appearance(&self) -> Appearance; fn on_appearance_changed(&mut self, callback: Box); - fn screen_position(&self, view_position: &Vector2F) -> Vector2F; + fn is_topmost_for_position(&self, position: Vector2F) -> bool; } #[derive(Debug)] diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index ae9db6a396951ff786431508f49a862d074048be..dbb1a01f31136c20faa429271d840af0e66d6486 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -37,7 +37,6 @@ use objc::{ runtime::{Class, Object, Sel}, sel, sel_impl, }; -use pathfinder_geometry::vector::Vector2F; use postage::oneshot; use ptr::null_mut; use std::{ @@ -696,21 +695,18 @@ impl platform::Platform for MacPlatform { Ok(()) } - fn set_cursor_style(&self, style: CursorStyle, window_id: usize, screen_position: &Vector2F) { - if Some(window_id) == Window::window_id_under(screen_position) { - dbg!(screen_position, style); - unsafe { - let cursor: id = match style { - CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], - CursorStyle::ResizeLeftRight => { - msg_send![class!(NSCursor), resizeLeftRightCursor] - } - CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor], - CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor], - CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor], - }; - let _: () = msg_send![cursor, set]; - } + fn set_cursor_style(&self, style: CursorStyle) { + unsafe { + let cursor: id = match style { + CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor], + CursorStyle::ResizeLeftRight => { + msg_send![class!(NSCursor), resizeLeftRightCursor] + } + CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor], + CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor], + CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor], + }; + let _: () = msg_send![cursor, set]; } } diff --git a/crates/gpui/src/platform/mac/status_item.rs b/crates/gpui/src/platform/mac/status_item.rs index d05c559772651a3e8aa0ce20b149da31ac9e746e..2da7caab7ebc23cbefafc35bf22b130702570171 100644 --- a/crates/gpui/src/platform/mac/status_item.rs +++ b/crates/gpui/src/platform/mac/status_item.rs @@ -259,8 +259,8 @@ impl platform::Window for StatusItem { } } - fn screen_position(&self, _view_position: &Vector2F) -> Vector2F { - unimplemented!() + fn is_topmost_for_position(&self, _: Vector2F) -> bool { + true } } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 067dd691157124f1bf4c4b6ef737ebf1b8ebeea0..191ab7705821367410085df30ff664bb1f87c7ef 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -17,9 +17,9 @@ use crate::{ use block::ConcreteBlock; use cocoa::{ appkit::{ - CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable, - NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior, - NSWindowStyleMask, + CGFloat, CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, + NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton, + NSWindowCollectionBehavior, NSWindowStyleMask, }, base::{id, nil}, foundation::{ @@ -52,8 +52,6 @@ use std::{ time::Duration, }; -use super::geometry::Vector2FExt; - const WINDOW_STATE_IVAR: &str = "windowState"; static mut WINDOW_CLASS: *const Class = ptr::null(); @@ -561,28 +559,6 @@ impl Window { } } } - - pub fn window_id_under(screen_position: &Vector2F) -> Option { - unsafe { - let app = NSApplication::sharedApplication(nil); - - let point = screen_position.to_ns_point(); - let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:point belowWindowWithWindowNumber:0]; - - // For some reason this API doesn't work when our two windows are on top of each other - let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number]; - - // dbg!(top_most_window); - let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS]; - let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS]; - if is_panel | is_window { - let id = get_window_state(&*top_most_window).borrow().id; - Some(id) - } else { - None - } - } - } } impl Drop for Window { @@ -780,13 +756,34 @@ impl platform::Window for Window { self.0.borrow_mut().appearance_changed_callback = Some(callback); } - fn screen_position(&self, view_position: &Vector2F) -> Vector2F { - let self_borrow = self.0.borrow_mut(); + fn is_topmost_for_position(&self, position: Vector2F) -> bool { + let window_bounds = self.bounds(); + let self_borrow = self.0.borrow(); + let self_id = self_borrow.id; + unsafe { - let point = view_position.to_ns_point(); + let app = NSApplication::sharedApplication(nil); + + // Convert back to bottom-left coordinates + let point = NSPoint::new( + position.x() as CGFloat, + (window_bounds.height() - position.y()) as CGFloat, + ); + let screen_point: NSPoint = msg_send![self_borrow.native_window, convertPointToScreen: point]; - vec2f(screen_point.x as f32, screen_point.y as f32) + let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0]; + let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number]; + + let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS]; + let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS]; + if is_panel | is_window { + let topmost_window_id = get_window_state(&*top_most_window).borrow().id; + topmost_window_id == self_id + } else { + // Someone else's window is on top + false + } } } } diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 7295bf525bc1321cd378069ead2f2d1dde15f775..d33e3e2fca5fe2ea6ec65cb85b96790ab0936235 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -178,7 +178,7 @@ impl super::Platform for Platform { Ok(()) } - fn set_cursor_style(&self, style: CursorStyle, _window_id: usize, _position: &Vector2F) { + fn set_cursor_style(&self, style: CursorStyle) { *self.cursor.lock() = style; } @@ -333,8 +333,8 @@ impl super::Window for Window { fn on_appearance_changed(&mut self, _: Box) {} - fn screen_position(&self, view_position: &Vector2F) -> Vector2F { - view_position.clone() + fn is_topmost_for_position(&self, _position: Vector2F) -> bool { + true } } diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 1b16574cb8784f77a29d1ca45888bc493ace9a28..5bff7e7bb1192fd67075819b0c0a612439c9640b 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -31,6 +31,7 @@ use std::{ ops::{Deref, DerefMut, Range}, sync::Arc, }; +use time::Instant; pub struct Presenter { window_id: usize, @@ -317,15 +318,8 @@ impl Presenter { } } - dbg!("*******"); - dbg!(position); - dbg!(event_reused); - if let Some(screen_position) = cx.screen_position(self.window_id, position) { - cx.platform().set_cursor_style( - style_to_assign, - self.window_id, - &screen_position, - ); + if cx.is_topmost_window_for_position(self.window_id, *position) { + cx.platform().set_cursor_style(style_to_assign); } if !event_reused { From 1fc6276eaba51b1544f130cfa4c0f5537e129fea Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 25 Jan 2023 09:09:59 -0800 Subject: [PATCH 05/17] Remove debug wiring --- crates/gpui/src/presenter.rs | 9 +++-- crates/workspace/src/workspace.rs | 57 +------------------------------ 2 files changed, 8 insertions(+), 58 deletions(-) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 5bff7e7bb1192fd67075819b0c0a612439c9640b..1fff5c50b51a3f53aeb55d5052a4412449606301 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -31,7 +31,6 @@ use std::{ ops::{Deref, DerefMut, Range}, sync::Arc, }; -use time::Instant; pub struct Presenter { window_id: usize, @@ -318,8 +317,14 @@ impl Presenter { } } - if cx.is_topmost_window_for_position(self.window_id, *position) { + let t0 = std::time::Instant::now(); + let is_topmost_window = + cx.is_topmost_window_for_position(self.window_id, *position); + println!("is_topmost_window => {:?}", t0.elapsed()); + if is_topmost_window { + let t1 = std::time::Instant::now(); cx.platform().set_cursor_style(style_to_assign); + println!("set_cursor_style => {:?}", t1.elapsed()); } if !event_reused { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 71c5ef97a209e866a9d6ad4ba69d491af59bcb6e..b0abe09070fe25a7ed27266b783410efb48b8bd4 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -31,9 +31,8 @@ use futures::{ }; use gpui::{ actions, - color::Color, elements::*, - geometry::vector::{vec2f, Vector2F}, + geometry::vector::Vector2F, impl_actions, impl_internal_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, WindowOptions}, @@ -101,7 +100,6 @@ actions!( NewTerminal, NewSearch, Feedback, - ShowNotif, ] ); @@ -264,59 +262,6 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { client.add_view_request_handler(Workspace::handle_follow); client.add_view_message_handler(Workspace::handle_unfollow); client.add_view_message_handler(Workspace::handle_update_followers); - - // REMEMBER TO DELETE THE SHOW NOTIF - cx.add_action( - |_workspace: &mut Workspace, _: &ShowNotif, cx: &mut ViewContext| { - struct DummyView; - - impl Entity for DummyView { - type Event = (); - } - - impl View for DummyView { - fn ui_name() -> &'static str { - "DummyView" - } - fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { - MouseEventHandler::::new(0, cx, |state, _cx| { - Empty::new() - .contained() - .with_background_color(if state.hovered() { - Color::red() - } else { - Color::blue() - }) - .constrained() - .with_width(200.) - .with_height(200.) - .boxed() - }) - .on_click(MouseButton::Left, |_, _| { - println!("click"); - }) - .with_cursor_style(CursorStyle::ResizeUpDown) - .boxed() - } - } - - cx.add_window( - WindowOptions { - bounds: gpui::WindowBounds::Fixed(gpui::geometry::rect::RectF::new( - vec2f(400., 200.), - vec2f(300., 200.), - )), - titlebar: None, - center: false, - focus: false, - kind: gpui::WindowKind::PopUp, - is_movable: true, - screen: None, - }, - |_cx| DummyView, - ) - }, - ) } type ProjectItemBuilders = HashMap< From ecb7d1072ffd21a2de5bbd7d2d65170375c87277 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 25 Jan 2023 09:33:07 -0800 Subject: [PATCH 06/17] Fixes a broken conditional that is only caught on darwin systems --- crates/gpui/src/platform/mac/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 191ab7705821367410085df30ff664bb1f87c7ef..bef6f65e428183de8d3314dfb230ca5d21deb6a7 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -777,7 +777,7 @@ impl platform::Window for Window { let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS]; let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS]; - if is_panel | is_window { + if is_panel == YES || is_window == YES { let topmost_window_id = get_window_state(&*top_most_window).borrow().id; topmost_window_id == self_id } else { From 3d8dbee76a3eee2fcf51d164769000afe200285a Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 25 Jan 2023 13:37:04 -0500 Subject: [PATCH 07/17] Clean up some debug printing --- crates/gpui/src/presenter.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 1fff5c50b51a3f53aeb55d5052a4412449606301..499e0df93eea1b61733c956d6f8880d1bbe265d3 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -317,14 +317,8 @@ impl Presenter { } } - let t0 = std::time::Instant::now(); - let is_topmost_window = - cx.is_topmost_window_for_position(self.window_id, *position); - println!("is_topmost_window => {:?}", t0.elapsed()); - if is_topmost_window { - let t1 = std::time::Instant::now(); + if cx.is_topmost_window_for_position(self.window_id, *position) { cx.platform().set_cursor_style(style_to_assign); - println!("set_cursor_style => {:?}", t1.elapsed()); } if !event_reused { From 7003a475a7545c7eee7969be08f6a02925b26dc6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 25 Jan 2023 10:44:15 -0800 Subject: [PATCH 08/17] Assign the language registry to all buffers in the project --- crates/project/src/project.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f324865b5cd291aac25b330940475893266c7b26..34117cf39ecce0ec5a0257e0b23865a87678ffea 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1480,6 +1480,10 @@ impl Project { buffer: &ModelHandle, cx: &mut ModelContext, ) -> Result<()> { + buffer.update(cx, |buffer, _| { + buffer.set_language_registry(self.languages.clone()) + }); + let remote_id = buffer.read(cx).remote_id(); let open_buffer = if self.is_remote() || self.is_shared() { OpenBuffer::Strong(buffer.clone()) @@ -1803,7 +1807,6 @@ impl Project { if buffer.language().map_or(true, |old_language| { !Arc::ptr_eq(old_language, &new_language) }) { - buffer.set_language_registry(self.languages.clone()); buffer.set_language(Some(new_language.clone()), cx); } }); From f68f9f37ab61cdccdc93be044e0c375a63cec50a Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 25 Jan 2023 14:20:23 -0500 Subject: [PATCH 09/17] Add cursor position to feedback editor Co-Authored-By: Mikayla Maki Co-Authored-By: Max Brunsfeld --- crates/editor/src/items.rs | 2 +- crates/feedback/src/feedback_editor.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 7e7f44e5141ae2f87596ed9873ad9f388a93698d..501306aa19e1da3b5437799241a5510a3debf91e 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1094,7 +1094,7 @@ impl StatusItemView for CursorPosition { active_pane_item: Option<&dyn ItemHandle>, cx: &mut ViewContext, ) { - if let Some(editor) = active_pane_item.and_then(|item| item.downcast::()) { + if let Some(editor) = active_pane_item.and_then(|item| item.act_as::(cx)) { self._observe_active_editor = Some(cx.observe(&editor, Self::update_position)); self.update_position(editor, cx); } else { diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index 3a6bc744f2487b7940a973d7797f9e1d563656d5..a3318d3efe5ec7a464adf326703da7927abcb07d 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -1,4 +1,5 @@ use std::{ + any::TypeId, ops::{Range, RangeInclusive}, sync::Arc, }; @@ -358,6 +359,21 @@ impl Item for FeedbackEditor { fn as_searchable(&self, handle: &ViewHandle) -> Option> { Some(Box::new(handle.clone())) } + + fn act_as_type( + &self, + type_id: TypeId, + self_handle: &ViewHandle, + _: &AppContext, + ) -> Option { + if type_id == TypeId::of::() { + Some(self_handle.into()) + } else if type_id == TypeId::of::() { + Some((&self.editor).into()) + } else { + None + } + } } impl SearchableItem for FeedbackEditor { From 7f3d9379380e621e825d35bd9d419d6261386f8c Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 25 Jan 2023 14:20:40 -0500 Subject: [PATCH 10/17] Count chars --- crates/feedback/src/feedback_editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index a3318d3efe5ec7a464adf326703da7927abcb07d..5398a8900ec12984adf90b660631114a761b5cb8 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -134,7 +134,7 @@ impl FeedbackEditor { _: ModelHandle, cx: &mut ViewContext, ) -> Task> { - let feedback_char_count = self.editor.read(cx).buffer().read(cx).len(cx); + let feedback_char_count = self.editor.read(cx).text(cx).chars().count(); let error = if feedback_char_count < *FEEDBACK_CHAR_LIMIT.start() { Some(format!( From 328b779185f796216c95134a57582efcf77ea06e Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 25 Jan 2023 14:20:58 -0500 Subject: [PATCH 11/17] Clean up construction of FeedbackEditor --- crates/feedback/src/feedback_editor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index 5398a8900ec12984adf90b660631114a761b5cb8..e034da7db27454404fbd22c2c48ddd150e7255cc 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -113,8 +113,7 @@ impl FeedbackEditor { cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone())) .detach(); - let this = Self { editor, project }; - this + Self { editor, project } } fn new(project: ModelHandle, cx: &mut ViewContext) -> Self { From a79b4e312baa586b13109ac09831f40f2dc89b04 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 25 Jan 2023 15:09:45 -0500 Subject: [PATCH 12/17] Style Co-Authored-By: Max Brunsfeld --- crates/live_kit_client/src/prod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs index 47fd4f0b69d0f3b40702888c1953e0aa39f54af3..f45667e3c3e4b4d00f264b51ff4782e8471e1ebd 100644 --- a/crates/live_kit_client/src/prod.rs +++ b/crates/live_kit_client/src/prod.rs @@ -128,14 +128,9 @@ impl Room { let url = url.to_string(); let token = token.to_string(); async move { - match rx.await.unwrap().context("error connecting to room") { - Ok(()) => { - *this.connection.lock().0.borrow_mut() = - ConnectionState::Connected { url, token }; - Ok(()) - } - Err(err) => Err(err), - } + rx.await.unwrap().context("error connecting to room")?; + *this.connection.lock().0.borrow_mut() = ConnectionState::Connected { url, token }; + Ok(()) } } From 4c3244b98267a7a90286a3e90c662c4f8251ceb2 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 25 Jan 2023 15:20:41 -0500 Subject: [PATCH 13/17] v0.72.x dev --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f858a61aaa7a7415fa9d33b0315a3bf974a00212..fec8e8dc50a55efbe8b9f78c09b6a8f2e6e1f4cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8216,7 +8216,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zed" -version = "0.71.0" +version = "0.72.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index c2b15c7cb5beb9cb5540ad5b4829bf9aaec9de5d..c82e778d8ddd16d6bde61c0b3424de10cec763f4 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.71.0" +version = "0.72.0" publish = false [lib] From 6e7101ca6bc4eb270c2ef80d54ca897402d8ce11 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 25 Jan 2023 17:47:15 -0500 Subject: [PATCH 14/17] Fix crash when opening feedback while in call --- crates/feedback/src/feedback.rs | 6 ++-- crates/feedback/src/feedback_editor.rs | 46 +++++++++++++++++--------- crates/zed/src/main.rs | 2 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/crates/feedback/src/feedback.rs b/crates/feedback/src/feedback.rs index 4b0dfc4df9f3f89fa0a3ea26efa83d47394cc61e..f47f95d4f382c0662608da7c531ef407ea98ce42 100644 --- a/crates/feedback/src/feedback.rs +++ b/crates/feedback/src/feedback.rs @@ -5,7 +5,7 @@ mod system_specs; use gpui::{actions, impl_actions, ClipboardItem, ViewContext}; use serde::Deserialize; use system_specs::SystemSpecs; -use workspace::Workspace; +use workspace::{AppState, Workspace}; #[derive(Deserialize, Clone, PartialEq)] pub struct OpenBrowser { @@ -19,8 +19,8 @@ actions!( [CopySystemSpecsIntoClipboard, FileBugReport, RequestFeature,] ); -pub fn init(cx: &mut gpui::MutableAppContext) { - feedback_editor::init(cx); +pub fn init(app_state: Arc, cx: &mut gpui::MutableAppContext) { + feedback_editor::init(app_state, cx); cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index e034da7db27454404fbd22c2c48ddd150e7255cc..e57b81a15c41d8aa58516264bdf4bed634cb460f 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -26,7 +26,7 @@ use settings::Settings; use workspace::{ item::{Item, ItemHandle}, searchable::{SearchableItem, SearchableItemHandle}, - StatusItemView, Workspace, + AppState, StatusItemView, Workspace, }; use crate::system_specs::SystemSpecs; @@ -43,8 +43,12 @@ const FEEDBACK_SUBMISSION_ERROR_TEXT: &str = actions!(feedback, [SubmitFeedback, GiveFeedback, DeployFeedback]); -pub fn init(cx: &mut MutableAppContext) { - cx.add_action(FeedbackEditor::deploy); +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { + cx.add_action({ + move |workspace: &mut Workspace, _: &GiveFeedback, cx: &mut ViewContext| { + FeedbackEditor::deploy(workspace, app_state.clone(), cx); + } + }); } pub struct FeedbackButton; @@ -116,15 +120,11 @@ impl FeedbackEditor { Self { editor, project } } - fn new(project: ModelHandle, cx: &mut ViewContext) -> Self { - let markdown_language = project.read(cx).languages().language_for_name("Markdown"); - - let buffer = project - .update(cx, |project, cx| { - project.create_buffer("", markdown_language, cx) - }) - .expect("creating buffers on a local workspace always succeeds"); - + fn new( + project: ModelHandle, + buffer: ModelHandle, + cx: &mut ViewContext, + ) -> Self { Self::new_with_buffer(project, buffer, cx) } @@ -236,10 +236,24 @@ impl FeedbackEditor { } impl FeedbackEditor { - pub fn deploy(workspace: &mut Workspace, _: &GiveFeedback, cx: &mut ViewContext) { - let feedback_editor = - cx.add_view(|cx| FeedbackEditor::new(workspace.project().clone(), cx)); - workspace.add_item(Box::new(feedback_editor), cx); + pub fn deploy( + workspace: &mut Workspace, + app_state: Arc, + cx: &mut ViewContext, + ) { + workspace + .with_local_workspace(&app_state, cx, |workspace, cx| { + let project = workspace.project().clone(); + let markdown_language = project.read(cx).languages().language_for_name("Markdown"); + let buffer = project + .update(cx, |project, cx| { + project.create_buffer("", markdown_language, cx) + }) + .expect("creating buffers on a local workspace always succeeds"); + let feedback_editor = cx.add_view(|cx| FeedbackEditor::new(project, buffer, cx)); + workspace.add_item(Box::new(feedback_editor), cx); + }) + .detach(); } } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 79183f312849804b35d1b3f95a5e1cdd03ff8b23..3cd01a18a1f5eb3f78dcfc8e3677b44edf9d5de9 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -136,7 +136,6 @@ fn main() { client::init(client.clone(), cx); command_palette::init(cx); editor::init(cx); - feedback::init(cx); go_to_line::init(cx); file_finder::init(cx); outline::init(cx); @@ -191,6 +190,7 @@ fn main() { theme_selector::init(app_state.clone(), cx); zed::init(&app_state, cx); collab_ui::init(app_state.clone(), cx); + feedback::init(app_state.clone(), cx); cx.set_menus(menus::menus()); From 3819a67185dc07a86a5e734a88b9236adf003294 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 25 Jan 2023 18:51:25 -0500 Subject: [PATCH 15/17] Add "Close Window" global action which does not need a focused workspace --- crates/gpui/src/app.rs | 2 +- crates/workspace/src/workspace.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 2e7378fc163e4703ac9bd9775c0a60644e552dfe..3fc11a6b582b24489aa4cd143dd5b234ed8c8d57 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -875,7 +875,7 @@ impl MutableAppContext { } pub fn window_ids(&self) -> impl Iterator + '_ { - self.cx.windows.keys().cloned() + self.cx.windows.keys().copied() } pub fn activate_window(&self, window_id: usize) { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b0abe09070fe25a7ed27266b783410efb48b8bd4..b90c62aca158bf18095f8b097b7e762b63d98640 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -198,6 +198,7 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { cx.add_async_action(Workspace::toggle_follow); cx.add_async_action(Workspace::follow_next_collaborator); cx.add_async_action(Workspace::close); + cx.add_global_action(Workspace::close_global); cx.add_async_action(Workspace::save_all); cx.add_action(Workspace::open_shared_screen); cx.add_action(Workspace::add_folder_to_project); @@ -823,6 +824,15 @@ impl Workspace { } } + pub fn close_global(_: &CloseWindow, cx: &mut MutableAppContext) { + let id = cx.window_ids().find(|&id| cx.window_is_active(id)); + if let Some(id) = id { + //This can only get called when the window's project connection has been lost + //so we don't need to prompt the user for anything and instead just close the window + cx.remove_window(id); + } + } + pub fn close( &mut self, _: &CloseWindow, @@ -851,6 +861,7 @@ impl Workspace { .window_ids() .flat_map(|window_id| cx.root_view::(window_id)) .count(); + cx.spawn(|this, mut cx| async move { if let Some(active_call) = active_call { if !quitting @@ -866,6 +877,7 @@ impl Workspace { ) .next() .await; + if answer == Some(1) { return anyhow::Ok(false); } else { From 9ff34bcb6a74e146c2d6d2ab566ee00f28793f04 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 25 Jan 2023 20:03:44 -0500 Subject: [PATCH 16/17] Remove no-longer-needed method --- crates/feedback/src/feedback_editor.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index e57b81a15c41d8aa58516264bdf4bed634cb460f..a4c10d8fc21a4cd471b2b045a43148c5f5310a47 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -102,7 +102,7 @@ struct FeedbackEditor { } impl FeedbackEditor { - fn new_with_buffer( + fn new( project: ModelHandle, buffer: ModelHandle, cx: &mut ViewContext, @@ -120,14 +120,6 @@ impl FeedbackEditor { Self { editor, project } } - fn new( - project: ModelHandle, - buffer: ModelHandle, - cx: &mut ViewContext, - ) -> Self { - Self::new_with_buffer(project, buffer, cx) - } - fn handle_save( &mut self, _: ModelHandle, @@ -348,11 +340,7 @@ impl Item for FeedbackEditor { .as_singleton() .expect("Feedback buffer is only ever singleton"); - Some(Self::new_with_buffer( - self.project.clone(), - buffer.clone(), - cx, - )) + Some(Self::new(self.project.clone(), buffer.clone(), cx)) } fn serialized_item_kind() -> Option<&'static str> { From ddf4e1a3162edf7a03fbdba1cdd41504173756c1 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 25 Jan 2023 17:42:49 -0800 Subject: [PATCH 17/17] Load languages lazily in the background --- crates/language/src/buffer_tests.rs | 20 +- crates/language/src/language.rs | 256 ++++++++++++++++++------- crates/language/src/syntax_map.rs | 26 +-- crates/project/src/project.rs | 8 +- crates/zed/src/languages.rs | 122 +++++------- crates/zed/src/languages/c.rs | 14 +- crates/zed/src/languages/go.rs | 5 +- crates/zed/src/languages/python.rs | 15 +- crates/zed/src/languages/rust.rs | 27 +-- crates/zed/src/languages/typescript.rs | 10 +- crates/zed/src/main.rs | 14 +- crates/zed/src/zed.rs | 4 +- 12 files changed, 311 insertions(+), 210 deletions(-) diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 0b2ef1d7a782713159187b0a5262021afa34b8fe..59fd6a534be599912b788b16401db6ff5fc6b32d 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -51,7 +51,7 @@ fn test_line_endings(cx: &mut gpui::MutableAppContext) { #[gpui::test] fn test_select_language() { - let registry = LanguageRegistry::test(); + let registry = Arc::new(LanguageRegistry::test()); registry.add(Arc::new(Language::new( LanguageConfig { name: "Rust".into(), @@ -71,27 +71,33 @@ fn test_select_language() { // matching file extension assert_eq!( - registry.select_language("zed/lib.rs").map(|l| l.name()), + registry.language_for_path("zed/lib.rs").map(|l| l.name()), Some("Rust".into()) ); assert_eq!( - registry.select_language("zed/lib.mk").map(|l| l.name()), + registry.language_for_path("zed/lib.mk").map(|l| l.name()), Some("Make".into()) ); // matching filename assert_eq!( - registry.select_language("zed/Makefile").map(|l| l.name()), + registry.language_for_path("zed/Makefile").map(|l| l.name()), Some("Make".into()) ); // matching suffix that is not the full file extension or filename - assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None); assert_eq!( - registry.select_language("zed/a.cars").map(|l| l.name()), + registry.language_for_path("zed/cars").map(|l| l.name()), + None + ); + assert_eq!( + registry.language_for_path("zed/a.cars").map(|l| l.name()), + None + ); + assert_eq!( + registry.language_for_path("zed/sumk").map(|l| l.name()), None ); - assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None); } #[gpui::test] diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 6e1a120c819d830a713183d1666c13a34a6dcf2a..4279ce665402be81f9ad26994bcebe8e1ea63a34 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -16,7 +16,7 @@ use futures::{ future::{BoxFuture, Shared}, FutureExt, TryFutureExt, }; -use gpui::{MutableAppContext, Task}; +use gpui::{executor::Background, MutableAppContext, Task}; use highlight_map::HighlightMap; use lazy_static::lazy_static; use parking_lot::{Mutex, RwLock}; @@ -26,6 +26,7 @@ use serde::{de, Deserialize, Deserializer}; use serde_json::Value; use std::{ any::Any, + borrow::Cow, cell::RefCell, fmt::Debug, hash::Hash, @@ -89,8 +90,7 @@ pub struct CachedLspAdapter { } impl CachedLspAdapter { - pub async fn new(adapter: T) -> Arc { - let adapter = Box::new(adapter); + pub async fn new(adapter: Box) -> Arc { let name = adapter.name().await; let server_args = adapter.server_args().await; let initialization_options = adapter.initialization_options().await; @@ -248,6 +248,16 @@ pub struct LanguageConfig { pub overrides: HashMap, } +#[derive(Debug, Default)] +pub struct LanguageQueries { + pub highlights: Option>, + pub brackets: Option>, + pub indents: Option>, + pub outline: Option>, + pub injections: Option>, + pub overrides: Option>, +} + #[derive(Clone)] pub struct LanguageScope { language: Arc, @@ -407,8 +417,17 @@ pub enum LanguageServerBinaryStatus { Failed { error: String }, } +struct AvailableLanguage { + path: &'static str, + config: LanguageConfig, + grammar: tree_sitter::Language, + lsp_adapter: Option>, + get_queries: fn(&str) -> LanguageQueries, +} + pub struct LanguageRegistry { languages: RwLock>>, + available_languages: RwLock>, language_server_download_dir: Option>, lsp_binary_statuses_tx: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc, LanguageServerBinaryStatus)>, @@ -422,6 +441,7 @@ pub struct LanguageRegistry { >, subscription: RwLock<(watch::Sender<()>, watch::Receiver<()>)>, theme: RwLock>>, + executor: Option>, version: AtomicUsize, } @@ -431,6 +451,7 @@ impl LanguageRegistry { Self { language_server_download_dir: None, languages: Default::default(), + available_languages: Default::default(), lsp_binary_statuses_tx, lsp_binary_statuses_rx, login_shell_env_loaded: login_shell_env_loaded.shared(), @@ -438,6 +459,7 @@ impl LanguageRegistry { subscription: RwLock::new(watch::channel()), theme: Default::default(), version: Default::default(), + executor: None, } } @@ -446,6 +468,44 @@ impl LanguageRegistry { Self::new(Task::ready(())) } + pub fn set_executor(&mut self, executor: Arc) { + self.executor = Some(executor); + } + + pub fn register( + &self, + path: &'static str, + config: LanguageConfig, + grammar: tree_sitter::Language, + lsp_adapter: Option>, + get_queries: fn(&str) -> LanguageQueries, + ) { + self.available_languages.write().push(AvailableLanguage { + path, + config, + grammar, + lsp_adapter, + get_queries, + }); + } + + pub fn language_names(&self) -> Vec { + let mut result = self + .available_languages + .read() + .iter() + .map(|l| l.config.name.to_string()) + .chain( + self.languages + .read() + .iter() + .map(|l| l.config.name.to_string()), + ) + .collect::>(); + result.sort_unstable(); + result + } + pub fn add(&self, language: Arc) { if let Some(theme) = self.theme.read().clone() { language.set_theme(&theme.editor.syntax); @@ -474,58 +534,79 @@ impl LanguageRegistry { self.language_server_download_dir = Some(path.into()); } - pub fn language_for_name(&self, name: &str) -> Option> { + pub fn language_for_name(self: &Arc, name: &str) -> Option> { let name = UniCase::new(name); - self.languages - .read() - .iter() - .find(|language| UniCase::new(language.name()) == name) - .cloned() + self.get_or_load_language(|config| UniCase::new(config.name.as_ref()) == name) } - pub fn language_for_extension(&self, extension: &str) -> Option> { - let extension = UniCase::new(extension); - self.languages - .read() - .iter() - .find(|language| { - language - .config + pub fn language_for_name_or_extension(self: &Arc, string: &str) -> Option> { + let string = UniCase::new(string); + self.get_or_load_language(|config| { + UniCase::new(config.name.as_ref()) == string + || config .path_suffixes .iter() - .any(|suffix| UniCase::new(suffix) == extension) - }) - .cloned() - } - - pub fn to_vec(&self) -> Vec> { - self.languages.read().iter().cloned().collect() - } - - pub fn language_names(&self) -> Vec { - self.languages - .read() - .iter() - .map(|language| language.name().to_string()) - .collect() + .any(|suffix| UniCase::new(suffix) == string) + }) } - pub fn select_language(&self, path: impl AsRef) -> Option> { + pub fn language_for_path(self: &Arc, path: impl AsRef) -> Option> { let path = path.as_ref(); let filename = path.file_name().and_then(|name| name.to_str()); let extension = path.extension().and_then(|name| name.to_str()); let path_suffixes = [extension, filename]; - self.languages + self.get_or_load_language(|config| { + config + .path_suffixes + .iter() + .any(|suffix| path_suffixes.contains(&Some(suffix.as_str()))) + }) + } + + fn get_or_load_language( + self: &Arc, + callback: impl Fn(&LanguageConfig) -> bool, + ) -> Option> { + if let Some(language) = self + .languages .read() .iter() - .find(|language| { - language - .config - .path_suffixes - .iter() - .any(|suffix| path_suffixes.contains(&Some(suffix.as_str()))) - }) - .cloned() + .find(|language| callback(&language.config)) + { + return Some(language.clone()); + } + + if let Some(executor) = self.executor.clone() { + let mut available_languages = self.available_languages.write(); + + if let Some(ix) = available_languages.iter().position(|l| callback(&l.config)) { + let language = available_languages.remove(ix); + drop(available_languages); + let name = language.config.name.clone(); + let this = self.clone(); + executor + .spawn(async move { + let queries = (language.get_queries)(&language.path); + let language = Language::new(language.config, Some(language.grammar)) + .with_lsp_adapter(language.lsp_adapter) + .await; + match language.with_queries(queries) { + Ok(language) => this.add(Arc::new(language)), + Err(err) => { + log::error!("failed to load language {}: {}", name, err); + return; + } + }; + }) + .detach(); + } + } + + None + } + + pub fn to_vec(&self) -> Vec> { + self.languages.read().iter().cloned().collect() } pub fn start_language_server( @@ -729,12 +810,70 @@ impl Language { self.grammar.as_ref().map(|g| g.id) } + pub fn with_queries(mut self, queries: LanguageQueries) -> Result { + if let Some(query) = queries.highlights { + self = self + .with_highlights_query(query.as_ref()) + .expect("failed to evaluate highlights query"); + } + if let Some(query) = queries.brackets { + self = self + .with_brackets_query(query.as_ref()) + .expect("failed to load brackets query"); + } + if let Some(query) = queries.indents { + self = self + .with_indents_query(query.as_ref()) + .expect("failed to load indents query"); + } + if let Some(query) = queries.outline { + self = self + .with_outline_query(query.as_ref()) + .expect("failed to load outline query"); + } + if let Some(query) = queries.injections { + self = self + .with_injection_query(query.as_ref()) + .expect("failed to load injection query"); + } + if let Some(query) = queries.overrides { + self = self + .with_override_query(query.as_ref()) + .expect("failed to load override query"); + } + Ok(self) + } pub fn with_highlights_query(mut self, source: &str) -> Result { let grammar = self.grammar_mut(); grammar.highlights_query = Some(Query::new(grammar.ts_language, source)?); Ok(self) } + pub fn with_outline_query(mut self, source: &str) -> Result { + let grammar = self.grammar_mut(); + let query = Query::new(grammar.ts_language, source)?; + let mut item_capture_ix = None; + let mut name_capture_ix = None; + let mut context_capture_ix = None; + get_capture_indices( + &query, + &mut [ + ("item", &mut item_capture_ix), + ("name", &mut name_capture_ix), + ("context", &mut context_capture_ix), + ], + ); + if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) { + grammar.outline_config = Some(OutlineConfig { + query, + item_capture_ix, + name_capture_ix, + context_capture_ix, + }); + } + Ok(self) + } + pub fn with_brackets_query(mut self, source: &str) -> Result { let grammar = self.grammar_mut(); let query = Query::new(grammar.ts_language, source)?; @@ -785,31 +924,6 @@ impl Language { Ok(self) } - pub fn with_outline_query(mut self, source: &str) -> Result { - let grammar = self.grammar_mut(); - let query = Query::new(grammar.ts_language, source)?; - let mut item_capture_ix = None; - let mut name_capture_ix = None; - let mut context_capture_ix = None; - get_capture_indices( - &query, - &mut [ - ("item", &mut item_capture_ix), - ("name", &mut name_capture_ix), - ("context", &mut context_capture_ix), - ], - ); - if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) { - grammar.outline_config = Some(OutlineConfig { - query, - item_capture_ix, - name_capture_ix, - context_capture_ix, - }); - } - Ok(self) - } - pub fn with_injection_query(mut self, source: &str) -> Result { let grammar = self.grammar_mut(); let query = Query::new(grammar.ts_language, source)?; @@ -882,8 +996,10 @@ impl Language { Arc::get_mut(self.grammar.as_mut().unwrap()).unwrap() } - pub fn with_lsp_adapter(mut self, lsp_adapter: Arc) -> Self { - self.adapter = Some(lsp_adapter); + pub async fn with_lsp_adapter(mut self, lsp_adapter: Option>) -> Self { + if let Some(adapter) = lsp_adapter { + self.adapter = Some(CachedLspAdapter::new(adapter).await); + } self } @@ -894,7 +1010,7 @@ impl Language { ) -> mpsc::UnboundedReceiver { let (servers_tx, servers_rx) = mpsc::unbounded(); self.fake_adapter = Some((servers_tx, fake_lsp_adapter.clone())); - let adapter = CachedLspAdapter::new(fake_lsp_adapter).await; + let adapter = CachedLspAdapter::new(Box::new(fake_lsp_adapter)).await; self.adapter = Some(adapter); servers_rx } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index ada981ec264fabe41e7aa123fc160519ed256ac9..41966c75966e91c2840326afcc4b93cffa8e9c5f 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -381,7 +381,12 @@ impl SyntaxSnapshot { cursor.next(text); while let Some(layer) = cursor.item() { let SyntaxLayerContent::Pending { language_name } = &layer.content else { unreachable!() }; - if language_for_injection(language_name, ®istry).is_some() { + if { + let language_registry = ®istry; + language_registry.language_for_name_or_extension(language_name) + } + .is_some() + { resolved_injection_ranges.push(layer.range.to_offset(text)); } @@ -1066,7 +1071,7 @@ fn get_injections( config: &InjectionConfig, text: &BufferSnapshot, node: Node, - language_registry: &LanguageRegistry, + language_registry: &Arc, depth: usize, changed_ranges: &[Range], combined_injection_ranges: &mut HashMap, Vec>, @@ -1078,7 +1083,8 @@ fn get_injections( combined_injection_ranges.clear(); for pattern in &config.patterns { if let (Some(language_name), true) = (pattern.language.as_ref(), pattern.combined) { - if let Some(language) = language_for_injection(language_name, language_registry) { + if let Some(language) = language_registry.language_for_name_or_extension(language_name) + { combined_injection_ranges.insert(language, Vec::new()); } } @@ -1123,7 +1129,10 @@ fn get_injections( }; if let Some(language_name) = language_name { - let language = language_for_injection(&language_name, language_registry); + let language = { + let language_name: &str = &language_name; + language_registry.language_for_name_or_extension(language_name) + }; let range = text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if let Some(language) = language { if combined { @@ -1171,15 +1180,6 @@ fn get_injections( } } -fn language_for_injection( - language_name: &str, - language_registry: &LanguageRegistry, -) -> Option> { - language_registry - .language_for_name(language_name) - .or_else(|| language_registry.language_for_extension(language_name)) -} - fn splice_included_ranges( mut ranges: Vec, changed_ranges: &[Range], diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 34117cf39ecce0ec5a0257e0b23865a87678ffea..54939af8d8dd4a7c684e1747e025bccc49c1e34d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1802,7 +1802,7 @@ impl Project { ) -> Option<()> { // If the buffer has a language, set it and start the language server if we haven't already. let full_path = buffer.read(cx).file()?.full_path(cx); - let new_language = self.languages.select_language(&full_path)?; + let new_language = self.languages.language_for_path(&full_path)?; buffer.update(cx, |buffer, cx| { if buffer.language().map_or(true, |old_language| { !Arc::ptr_eq(old_language, &new_language) @@ -2211,7 +2211,7 @@ impl Project { }) .collect(); for (worktree_id, worktree_abs_path, full_path) in language_server_lookup_info { - let language = self.languages.select_language(&full_path)?; + let language = self.languages.language_for_path(&full_path)?; self.restart_language_server(worktree_id, worktree_abs_path, language, cx); } @@ -3171,7 +3171,7 @@ impl Project { let signature = this.symbol_signature(&project_path); let language = this .languages - .select_language(&project_path.path) + .language_for_path(&project_path.path) .unwrap_or(adapter_language.clone()); let language_server_name = adapter.name.clone(); Some(async move { @@ -5947,7 +5947,7 @@ impl Project { worktree_id, path: PathBuf::from(serialized_symbol.path).into(), }; - let language = languages.select_language(&path.path); + let language = languages.language_for_path(&path.path); Ok(Symbol { language_server_name: LanguageServerName( serialized_symbol.language_server_name.into(), diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 240d1dc49e5ac6603b1c0760a2e11f748274b276..548c07fb82e25d7a1993627d82202dfa4ee40012 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -1,7 +1,5 @@ use anyhow::Context; -use gpui::executor::Background; pub use language::*; -use lazy_static::lazy_static; use rust_embed::RustEmbed; use std::{borrow::Cow, str, sync::Arc}; @@ -32,32 +30,17 @@ mod typescript; #[exclude = "*.rs"] struct LanguageDir; -// TODO - Remove this once the `init` function is synchronous again. -lazy_static! { - pub static ref LANGUAGE_NAMES: Vec = LanguageDir::iter() - .filter_map(|path| { - if path.ends_with("config.toml") { - let config = LanguageDir::get(&path)?; - let config = toml::from_slice::(&config.data).ok()?; - Some(config.name.to_string()) - } else { - None - } - }) - .collect(); -} - -pub async fn init(languages: Arc, _executor: Arc) { +pub fn init(languages: Arc) { for (name, grammar, lsp_adapter) in [ ( "c", tree_sitter_c::language(), - Some(CachedLspAdapter::new(c::CLspAdapter).await), + Some(Box::new(c::CLspAdapter) as Box), ), ( "cpp", tree_sitter_cpp::language(), - Some(CachedLspAdapter::new(c::CLspAdapter).await), + Some(Box::new(c::CLspAdapter)), ), ( "css", @@ -67,17 +50,17 @@ pub async fn init(languages: Arc, _executor: Arc) ( "elixir", tree_sitter_elixir::language(), - Some(CachedLspAdapter::new(elixir::ElixirLspAdapter).await), + Some(Box::new(elixir::ElixirLspAdapter)), ), ( "go", tree_sitter_go::language(), - Some(CachedLspAdapter::new(go::GoLspAdapter).await), + Some(Box::new(go::GoLspAdapter)), ), ( "json", tree_sitter_json::language(), - Some(CachedLspAdapter::new(json::JsonLspAdapter).await), + Some(Box::new(json::JsonLspAdapter)), ), ( "markdown", @@ -87,12 +70,12 @@ pub async fn init(languages: Arc, _executor: Arc) ( "python", tree_sitter_python::language(), - Some(CachedLspAdapter::new(python::PythonLspAdapter).await), + Some(Box::new(python::PythonLspAdapter)), ), ( "rust", tree_sitter_rust::language(), - Some(CachedLspAdapter::new(rust::RustLspAdapter).await), + Some(Box::new(rust::RustLspAdapter)), ), ( "toml", @@ -102,89 +85,82 @@ pub async fn init(languages: Arc, _executor: Arc) ( "tsx", tree_sitter_typescript::language_tsx(), - Some(CachedLspAdapter::new(typescript::TypeScriptLspAdapter).await), + Some(Box::new(typescript::TypeScriptLspAdapter)), ), ( "typescript", tree_sitter_typescript::language_typescript(), - Some(CachedLspAdapter::new(typescript::TypeScriptLspAdapter).await), + Some(Box::new(typescript::TypeScriptLspAdapter)), ), ( "javascript", tree_sitter_typescript::language_tsx(), - Some(CachedLspAdapter::new(typescript::TypeScriptLspAdapter).await), + Some(Box::new(typescript::TypeScriptLspAdapter)), ), ( "html", tree_sitter_html::language(), - Some(CachedLspAdapter::new(html::HtmlLspAdapter).await), + Some(Box::new(html::HtmlLspAdapter)), ), ( "ruby", tree_sitter_ruby::language(), - Some(CachedLspAdapter::new(ruby::RubyLanguageServer).await), + Some(Box::new(ruby::RubyLanguageServer)), ), ( "erb", tree_sitter_embedded_template::language(), - Some(CachedLspAdapter::new(ruby::RubyLanguageServer).await), + Some(Box::new(ruby::RubyLanguageServer)), + ), + ( + "scheme", + tree_sitter_scheme::language(), + None, // + ), + ( + "racket", + tree_sitter_racket::language(), + None, // ), - ("scheme", tree_sitter_scheme::language(), None), - ("racket", tree_sitter_racket::language(), None), ] { - languages.add(language(name, grammar, lsp_adapter)); + languages.register(name, load_config(name), grammar, lsp_adapter, load_queries); } } -pub(crate) fn language( +#[cfg(any(test, feature = "test-support"))] +pub async fn language( name: &str, grammar: tree_sitter::Language, - lsp_adapter: Option>, + lsp_adapter: Option>, ) -> Arc { - let config = toml::from_slice( + Arc::new( + Language::new(load_config(name), Some(grammar)) + .with_lsp_adapter(lsp_adapter) + .await + .with_queries(load_queries(name)) + .unwrap(), + ) +} + +fn load_config(name: &str) -> LanguageConfig { + toml::from_slice( &LanguageDir::get(&format!("{}/config.toml", name)) .unwrap() .data, ) .with_context(|| format!("failed to load config.toml for language {name:?}")) - .unwrap(); - - let mut language = Language::new(config, Some(grammar)); + .unwrap() +} - if let Some(query) = load_query(name, "/highlights") { - language = language - .with_highlights_query(query.as_ref()) - .expect("failed to evaluate highlights query"); - } - if let Some(query) = load_query(name, "/brackets") { - language = language - .with_brackets_query(query.as_ref()) - .expect("failed to load brackets query"); - } - if let Some(query) = load_query(name, "/indents") { - language = language - .with_indents_query(query.as_ref()) - .expect("failed to load indents query"); - } - if let Some(query) = load_query(name, "/outline") { - language = language - .with_outline_query(query.as_ref()) - .expect("failed to load outline query"); - } - if let Some(query) = load_query(name, "/injections") { - language = language - .with_injection_query(query.as_ref()) - .expect("failed to load injection query"); - } - if let Some(query) = load_query(name, "/overrides") { - language = language - .with_override_query(query.as_ref()) - .expect("failed to load override query"); - } - if let Some(lsp_adapter) = lsp_adapter { - language = language.with_lsp_adapter(lsp_adapter) +fn load_queries(name: &str) -> LanguageQueries { + LanguageQueries { + highlights: load_query(name, "/highlights"), + brackets: load_query(name, "/brackets"), + indents: load_query(name, "/indents"), + outline: load_query(name, "/outline"), + injections: load_query(name, "/injections"), + overrides: load_query(name, "/overrides"), } - Arc::new(language) } fn load_query(name: &str, filename_prefix: &str) -> Option> { diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 712e87101b63b74df8fc3aca86d413dde32c0a96..9fbb12857f74c14745d08253d1707327381fbf36 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -248,17 +248,19 @@ impl super::LspAdapter for CLspAdapter { #[cfg(test)] mod tests { - use gpui::MutableAppContext; + use gpui::TestAppContext; use language::{AutoindentMode, Buffer}; use settings::Settings; #[gpui::test] - fn test_c_autoindent(cx: &mut MutableAppContext) { + async fn test_c_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); - let language = crate::languages::language("c", tree_sitter_c::language(), None); + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); + cx.set_global(settings); + }); + let language = crate::languages::language("c", tree_sitter_c::language(), None).await; cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index 19692fdf44f08ed852c03ed9b7cad94f3c62eed2..dc84599e4eff350f7f27e17d15fb4d79692acd88 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -314,8 +314,9 @@ mod tests { let language = language( "go", tree_sitter_go::language(), - Some(CachedLspAdapter::new(GoLspAdapter).await), - ); + Some(Box::new(GoLspAdapter)), + ) + .await; let theme = SyntaxTheme::new(vec![ ("type".into(), Color::green().into()), diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index ba6ccf7bf01b544bde4e91737cf2a89e4e9e4b36..1391494ab1ea1e324caf9da769c34b5962121679 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -165,17 +165,20 @@ impl LspAdapter for PythonLspAdapter { #[cfg(test)] mod tests { - use gpui::{ModelContext, MutableAppContext}; + use gpui::{ModelContext, TestAppContext}; use language::{AutoindentMode, Buffer}; use settings::Settings; #[gpui::test] - fn test_python_autoindent(cx: &mut MutableAppContext) { + async fn test_python_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); - let language = crate::languages::language("python", tree_sitter_python::language(), None); - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + let language = + crate::languages::language("python", tree_sitter_python::language(), None).await; + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); + cx.set_global(settings); + }); cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 30971fef1ae726802fc694c2c2e4c1f1e0d79079..40948d5005d75a8f04441cd5aef62829f0568a10 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -255,8 +255,8 @@ impl LspAdapter for RustLspAdapter { #[cfg(test)] mod tests { use super::*; - use crate::languages::{language, CachedLspAdapter}; - use gpui::{color::Color, MutableAppContext}; + use crate::languages::language; + use gpui::{color::Color, TestAppContext}; use settings::Settings; use theme::SyntaxTheme; @@ -306,8 +306,9 @@ mod tests { let language = language( "rust", tree_sitter_rust::language(), - Some(CachedLspAdapter::new(RustLspAdapter).await), - ); + Some(Box::new(RustLspAdapter)), + ) + .await; let grammar = language.grammar().unwrap(); let theme = SyntaxTheme::new(vec![ ("type".into(), Color::green().into()), @@ -391,8 +392,9 @@ mod tests { let language = language( "rust", tree_sitter_rust::language(), - Some(CachedLspAdapter::new(RustLspAdapter).await), - ); + Some(Box::new(RustLspAdapter)), + ) + .await; let grammar = language.grammar().unwrap(); let theme = SyntaxTheme::new(vec![ ("type".into(), Color::green().into()), @@ -431,12 +433,15 @@ mod tests { } #[gpui::test] - fn test_rust_autoindent(cx: &mut MutableAppContext) { + async fn test_rust_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); - let language = crate::languages::language("rust", tree_sitter_rust::language(), None); - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); + cx.set_global(settings); + }); + + let language = crate::languages::language("rust", tree_sitter_rust::language(), None).await; cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 01b62577ad030deca39225c26c5b054d88a8303d..5290158deaf037b1a9ddb8a52211871c58a2a815 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -154,17 +154,17 @@ impl LspAdapter for TypeScriptLspAdapter { #[cfg(test)] mod tests { - - use gpui::MutableAppContext; + use gpui::TestAppContext; use unindent::Unindent; #[gpui::test] - fn test_outline(cx: &mut MutableAppContext) { + async fn test_outline(cx: &mut TestAppContext) { let language = crate::languages::language( "typescript", tree_sitter_typescript::language_typescript(), None, - ); + ) + .await; let text = r#" function a() { @@ -183,7 +183,7 @@ mod tests { let buffer = cx.add_model(|cx| language::Buffer::new(0, text, cx).with_language(language, cx)); - let outline = buffer.read(cx).snapshot().outline(None).unwrap(); + let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None).unwrap()); assert_eq!( outline .items diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 79183f312849804b35d1b3f95a5e1cdd03ff8b23..e1f151643ccf234322c609fde90be9418704dd71 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -120,11 +120,10 @@ fn main() { let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); + languages.set_executor(cx.background().clone()); languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); - let init_languages = cx - .background() - .spawn(languages::init(languages.clone(), cx.background().clone())); + languages::init(languages.clone()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); watch_keymap_file(keymap_file, cx); @@ -152,14 +151,7 @@ fn main() { cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx)) .detach(); - cx.spawn({ - let languages = languages.clone(); - |cx| async move { - cx.read(|cx| languages.set_theme(cx.global::().theme.clone())); - init_languages.await; - } - }) - .detach(); + languages.set_theme(cx.global::().theme.clone()); cx.observe_global::({ let languages = languages.clone(); move |cx| languages.set_theme(cx.global::().theme.clone()) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c9f8b2d408441aee28a63b1b6242e62aafb61dcc..793172a1114a4d89f262ce8920aa8349f0b75ab1 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -306,7 +306,7 @@ pub fn initialize_workspace( ) .map(|meta| meta.name) .collect(); - let language_names = &languages::LANGUAGE_NAMES; + let language_names = app_state.languages.language_names(); workspace.project().update(cx, |project, cx| { let action_names = cx.all_action_names().collect::>(); @@ -318,7 +318,7 @@ pub fn initialize_workspace( "schemas": [ { "fileMatch": [schema_file_match(&paths::SETTINGS)], - "schema": settings_file_json_schema(theme_names, language_names), + "schema": settings_file_json_schema(theme_names, &language_names), }, { "fileMatch": [schema_file_match(&paths::KEYMAP)],