Document the gpui platform code (#4180)

Mikayla Maki created

In the process I also:
- Made the AsyncWindowContext slightly more ergonomic.
- Refactored the input handler traits to enable easy, non-view input
handlers
- Locked down the visibility on all mac-specific GPUI code
- Documented all remaining, public types

Release Notes:

- N/A

Change summary

crates/command_palette/src/command_palette.rs     |   2 
crates/copilot_ui/src/copilot_button.rs           |   2 
crates/editor/src/editor.rs                       |  11 
crates/editor/src/element.rs                      |   7 
crates/editor/src/hover_popover.rs                |   2 
crates/gpui/src/app/async_context.rs              |   7 
crates/gpui/src/app/test_context.rs               |   2 
crates/gpui/src/elements/div.rs                   |   2 
crates/gpui/src/input.rs                          |  82 ++--
crates/gpui/src/platform.rs                       | 290 +++++++++++++++-
crates/gpui/src/platform/app_menu.rs              |  38 ++
crates/gpui/src/platform/keystroke.rs             |  34 +
crates/gpui/src/platform/mac.rs                   |  14 
crates/gpui/src/platform/mac/dispatcher.rs        |   2 
crates/gpui/src/platform/mac/display.rs           |   7 
crates/gpui/src/platform/mac/display_linker.rs    |  14 
crates/gpui/src/platform/mac/events.rs            |   5 
crates/gpui/src/platform/mac/metal_atlas.rs       |   4 
crates/gpui/src/platform/mac/platform.rs          |  14 
crates/gpui/src/platform/mac/text_system.rs       |   4 
crates/gpui/src/platform/mac/window.rs            |  22 
crates/gpui/src/platform/mac/window_appearance.rs |   2 
crates/gpui/src/platform/test.rs                  |   8 
crates/gpui/src/platform/test/dispatcher.rs       |   1 
crates/gpui/src/platform/test/display.rs          |   2 
crates/gpui/src/platform/test/platform.rs         |  17 
crates/gpui/src/platform/test/window.rs           |  17 
crates/gpui/src/styled.rs                         |   6 
crates/gpui/src/window.rs                         |  31 
crates/journal/src/journal.rs                     |   2 
crates/terminal_view/src/terminal_element.rs      |  79 ++--
crates/terminal_view/src/terminal_view.rs         |   2 
crates/vim/src/normal/repeat.rs                   |   2 
crates/workspace/src/notifications.rs             |   2 
crates/workspace/src/pane.rs                      |   6 
crates/workspace/src/workspace.rs                 |  14 
crates/zed/src/zed.rs                             |   2 
37 files changed, 510 insertions(+), 248 deletions(-)

Detailed changes

crates/command_palette/src/command_palette.rs ๐Ÿ”—

@@ -311,7 +311,7 @@ impl PickerDelegate for CommandPaletteDelegate {
         let action = command.action;
         cx.focus(&self.previous_focus_handle);
         cx.window_context()
-            .spawn(move |mut cx| async move { cx.update(|_, cx| cx.dispatch_action(action)) })
+            .spawn(move |mut cx| async move { cx.update(|cx| cx.dispatch_action(action)) })
             .detach_and_log_err(cx);
         self.dismissed(cx);
     }

crates/copilot_ui/src/copilot_button.rs ๐Ÿ”—

@@ -355,7 +355,7 @@ fn initiate_sign_in(cx: &mut WindowContext) {
 
             cx.spawn(|mut cx| async move {
                 task.await;
-                if let Some(copilot) = cx.update(|_, cx| Copilot::global(cx)).ok().flatten() {
+                if let Some(copilot) = cx.update(|cx| Copilot::global(cx)).ok().flatten() {
                     workspace
                         .update(&mut cx, |workspace, cx| match copilot.read(cx).status() {
                             Status::Authorized => workspace.show_toast(

crates/editor/src/editor.rs ๐Ÿ”—

@@ -57,9 +57,10 @@ use gpui::{
     div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
     AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
     DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
-    HighlightStyle, Hsla, InputHandler, InteractiveText, KeyContext, Model, MouseButton,
-    ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
-    UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
+    HighlightStyle, Hsla, InteractiveText, KeyContext, Model, MouseButton, ParentElement, Pixels,
+    Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
+    UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakView,
+    WhiteSpace, WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};
@@ -3378,7 +3379,7 @@ impl Editor {
         let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
 
         let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
-        cx.update(|_, cx| {
+        cx.update(|cx| {
             entries.sort_unstable_by_key(|(buffer, _)| {
                 buffer.read(cx).file().map(|f| f.path().clone())
             });
@@ -9166,7 +9167,7 @@ impl Render for Editor {
     }
 }
 
-impl InputHandler for Editor {
+impl ViewInputHandler for Editor {
     fn text_for_range(
         &mut self,
         range_utf16: Range<usize>,

crates/editor/src/element.rs ๐Ÿ”—

@@ -2951,9 +2951,10 @@ impl Element for EditorElement {
                         self.register_key_listeners(cx);
 
                         cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
-                            let input_handler =
-                                ElementInputHandler::new(bounds, self.editor.clone(), cx);
-                            cx.handle_input(&focus_handle, input_handler);
+                            cx.handle_input(
+                                &focus_handle,
+                                ElementInputHandler::new(bounds, self.editor.clone()),
+                            );
 
                             self.paint_background(gutter_bounds, text_bounds, &layout, cx);
                             if layout.gutter_size.width > Pixels::ZERO {

crates/editor/src/hover_popover.rs ๐Ÿ”—

@@ -247,7 +247,7 @@ fn show_hover(
             };
 
             // query the LSP for hover info
-            let hover_request = cx.update(|_, cx| {
+            let hover_request = cx.update(|cx| {
                 project.update(cx, |project, cx| {
                     project.hover(&buffer, buffer_position, cx)
                 })

crates/gpui/src/app/async_context.rs ๐Ÿ”—

@@ -213,7 +213,12 @@ impl AsyncWindowContext {
     }
 
     /// A convenience method for [WindowContext::update()]
-    pub fn update<R>(
+    pub fn update<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
+        self.app.update_window(self.window, |_, cx| update(cx))
+    }
+
+    /// A convenience method for [WindowContext::update()]
+    pub fn update_root<R>(
         &mut self,
         update: impl FnOnce(AnyView, &mut WindowContext) -> R,
     ) -> Result<R> {

crates/gpui/src/app/test_context.rs ๐Ÿ”—

@@ -352,7 +352,7 @@ impl TestAppContext {
     }
 
     /// Returns the `TestWindow` backing the given handle.
-    pub fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
+    pub(crate) fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
         self.app
             .borrow_mut()
             .windows

crates/gpui/src/elements/div.rs ๐Ÿ”—

@@ -1420,7 +1420,7 @@ impl Interactivity {
 
                                         move |mut cx| async move {
                                             cx.background_executor().timer(TOOLTIP_DELAY).await;
-                                            cx.update(|_, cx| {
+                                            cx.update(|cx| {
                                                 active_tooltip.borrow_mut().replace(
                                                     ActiveTooltip {
                                                         tooltip: Some(AnyTooltip {

crates/gpui/src/input.rs ๐Ÿ”—

@@ -1,13 +1,11 @@
-use crate::{
-    AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext, WindowContext,
-};
+use crate::{Bounds, InputHandler, Pixels, View, ViewContext, WindowContext};
 use std::ops::Range;
 
 /// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
 ///
 /// Once your view `V` implements this trait, you can use it to construct an [`ElementInputHandler<V>`].
 /// This input handler can then be assigned during paint by calling [`WindowContext::handle_input`].
-pub trait InputHandler: 'static + Sized {
+pub trait ViewInputHandler: 'static + Sized {
     fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>)
         -> Option<String>;
     fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
@@ -39,7 +37,6 @@ pub trait InputHandler: 'static + Sized {
 pub struct ElementInputHandler<V> {
     view: View<V>,
     element_bounds: Bounds<Pixels>,
-    cx: AsyncWindowContext,
 }
 
 impl<V: 'static> ElementInputHandler<V> {
@@ -47,45 +44,42 @@ impl<V: 'static> ElementInputHandler<V> {
     /// containing view.
     ///
     /// [element_paint]: crate::Element::paint
-    pub fn new(element_bounds: Bounds<Pixels>, view: View<V>, cx: &mut WindowContext) -> Self {
+    pub fn new(element_bounds: Bounds<Pixels>, view: View<V>) -> Self {
         ElementInputHandler {
             view,
             element_bounds,
-            cx: cx.to_async(),
         }
     }
 }
 
-impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
-    fn selected_text_range(&mut self) -> Option<Range<usize>> {
+impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
+    fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
         self.view
-            .update(&mut self.cx, |view, cx| view.selected_text_range(cx))
-            .ok()
-            .flatten()
+            .update(cx, |view, cx| view.selected_text_range(cx))
     }
 
-    fn marked_text_range(&mut self) -> Option<Range<usize>> {
-        self.view
-            .update(&mut self.cx, |view, cx| view.marked_text_range(cx))
-            .ok()
-            .flatten()
+    fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
+        self.view.update(cx, |view, cx| view.marked_text_range(cx))
     }
 
-    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
+    fn text_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<String> {
         self.view
-            .update(&mut self.cx, |view, cx| {
-                view.text_for_range(range_utf16, cx)
-            })
-            .ok()
-            .flatten()
+            .update(cx, |view, cx| view.text_for_range(range_utf16, cx))
     }
 
-    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
-        self.view
-            .update(&mut self.cx, |view, cx| {
-                view.replace_text_in_range(replacement_range, text, cx)
-            })
-            .ok();
+    fn replace_text_in_range(
+        &mut self,
+        replacement_range: Option<Range<usize>>,
+        text: &str,
+        cx: &mut WindowContext,
+    ) {
+        self.view.update(cx, |view, cx| {
+            view.replace_text_in_range(replacement_range, text, cx)
+        });
     }
 
     fn replace_and_mark_text_in_range(
@@ -93,26 +87,24 @@ impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
         range_utf16: Option<Range<usize>>,
         new_text: &str,
         new_selected_range: Option<Range<usize>>,
+        cx: &mut WindowContext,
     ) {
-        self.view
-            .update(&mut self.cx, |view, cx| {
-                view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
-            })
-            .ok();
+        self.view.update(cx, |view, cx| {
+            view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
+        });
     }
 
-    fn unmark_text(&mut self) {
-        self.view
-            .update(&mut self.cx, |view, cx| view.unmark_text(cx))
-            .ok();
+    fn unmark_text(&mut self, cx: &mut WindowContext) {
+        self.view.update(cx, |view, cx| view.unmark_text(cx));
     }
 
-    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
-        self.view
-            .update(&mut self.cx, |view, cx| {
-                view.bounds_for_range(range_utf16, self.element_bounds, cx)
-            })
-            .ok()
-            .flatten()
+    fn bounds_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<Bounds<Pixels>> {
+        self.view.update(cx, |view, cx| {
+            view.bounds_for_range(range_utf16, self.element_bounds, cx)
+        })
     }
 }

crates/gpui/src/platform.rs ๐Ÿ”—

@@ -1,3 +1,5 @@
+#![deny(missing_docs)]
+
 mod app_menu;
 mod keystroke;
 #[cfg(target_os = "macos")]
@@ -6,10 +8,10 @@ mod mac;
 mod test;
 
 use crate::{
-    Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
-    FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, Pixels, PlatformInput,
-    Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
-    Size, TaskLabel,
+    Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font,
+    FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout,
+    Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
+    Scene, SharedString, Size, TaskLabel, WindowContext,
 };
 use anyhow::anyhow;
 use async_task::Runnable;
@@ -34,9 +36,9 @@ use uuid::Uuid;
 pub use app_menu::*;
 pub use keystroke::*;
 #[cfg(target_os = "macos")]
-pub use mac::*;
+pub(crate) use mac::*;
 #[cfg(any(test, feature = "test-support"))]
-pub use test::*;
+pub(crate) use test::*;
 use time::UtcOffset;
 
 #[cfg(target_os = "macos")]
@@ -69,11 +71,10 @@ pub(crate) trait Platform: 'static {
     fn set_display_link_output_callback(
         &self,
         display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+        callback: Box<dyn FnMut() + Send>,
     );
     fn start_display_link(&self, display_id: DisplayId);
     fn stop_display_link(&self, display_id: DisplayId);
-    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn PlatformWindow>;
 
     fn open_url(&self, url: &str);
     fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
@@ -149,8 +150,8 @@ pub(crate) trait PlatformWindow {
     fn mouse_position(&self) -> Point<Pixels>;
     fn modifiers(&self) -> Modifiers;
     fn as_any_mut(&mut self) -> &mut dyn Any;
-    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
-    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>>;
+    fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
+    fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
     fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
     fn activate(&self);
     fn set_title(&mut self, title: &str);
@@ -325,30 +326,168 @@ impl From<TileId> for etagere::AllocId {
     }
 }
 
-pub trait PlatformInputHandler: 'static {
-    fn selected_text_range(&mut self) -> Option<Range<usize>>;
-    fn marked_text_range(&mut self) -> Option<Range<usize>>;
-    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String>;
-    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
+pub(crate) struct PlatformInputHandler {
+    cx: AsyncWindowContext,
+    handler: Box<dyn InputHandler>,
+}
+
+impl PlatformInputHandler {
+    pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
+        Self { cx, handler }
+    }
+
+    fn selected_text_range(&mut self) -> Option<Range<usize>> {
+        self.cx
+            .update(|cx| self.handler.selected_text_range(cx))
+            .ok()
+            .flatten()
+    }
+
+    fn marked_text_range(&mut self) -> Option<Range<usize>> {
+        self.cx
+            .update(|cx| self.handler.marked_text_range(cx))
+            .ok()
+            .flatten()
+    }
+
+    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
+        self.cx
+            .update(|cx| self.handler.text_for_range(range_utf16, cx))
+            .ok()
+            .flatten()
+    }
+
+    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
+        self.cx
+            .update(|cx| {
+                self.handler
+                    .replace_text_in_range(replacement_range, text, cx)
+            })
+            .ok();
+    }
+
+    fn replace_and_mark_text_in_range(
+        &mut self,
+        range_utf16: Option<Range<usize>>,
+        new_text: &str,
+        new_selected_range: Option<Range<usize>>,
+    ) {
+        self.cx
+            .update(|cx| {
+                self.handler.replace_and_mark_text_in_range(
+                    range_utf16,
+                    new_text,
+                    new_selected_range,
+                    cx,
+                )
+            })
+            .ok();
+    }
+
+    fn unmark_text(&mut self) {
+        self.cx.update(|cx| self.handler.unmark_text(cx)).ok();
+    }
+
+    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
+        self.cx
+            .update(|cx| self.handler.bounds_for_range(range_utf16, cx))
+            .ok()
+            .flatten()
+    }
+}
+
+/// Zed's interface for handling text input from the platform's IME system
+/// This is currently a 1:1 exposure of the NSTextInputClient API:
+///
+/// <https://developer.apple.com/documentation/appkit/nstextinputclient>
+pub trait InputHandler: 'static {
+    /// Get the range of the user's currently selected text, if any
+    /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange)
+    ///
+    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
+    fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
+
+    /// Get the range of the currently marked text, if any
+    /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange)
+    ///
+    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
+    fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
+
+    /// Get the text for the given document range in UTF-16 characters
+    /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring)
+    ///
+    /// range_utf16 is in terms of UTF-16 characters
+    fn text_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<String>;
+
+    /// Replace the text in the given document range with the given text
+    /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext)
+    ///
+    /// replacement_range is in terms of UTF-16 characters
+    fn replace_text_in_range(
+        &mut self,
+        replacement_range: Option<Range<usize>>,
+        text: &str,
+        cx: &mut WindowContext,
+    );
+
+    /// Replace the text in the given document range with the given text,
+    /// and mark the given text as part of of an IME 'composing' state
+    /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext)
+    ///
+    /// range_utf16 is in terms of UTF-16 characters
+    /// new_selected_range is in terms of UTF-16 characters
     fn replace_and_mark_text_in_range(
         &mut self,
         range_utf16: Option<Range<usize>>,
         new_text: &str,
         new_selected_range: Option<Range<usize>>,
+        cx: &mut WindowContext,
     );
-    fn unmark_text(&mut self);
-    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
+
+    /// Remove the IME 'composing' state from the document
+    /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext)
+    fn unmark_text(&mut self, cx: &mut WindowContext);
+
+    /// Get the bounds of the given document range in screen coordinates
+    /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect)
+    ///
+    /// This is used for positioning the IME candidate window
+    fn bounds_for_range(
+        &mut self,
+        range_utf16: Range<usize>,
+        cx: &mut WindowContext,
+    ) -> Option<Bounds<Pixels>>;
 }
 
+/// The variables that can be configured when creating a new window
 #[derive(Debug)]
 pub struct WindowOptions {
+    /// The initial bounds of the window
     pub bounds: WindowBounds,
+
+    /// The titlebar configuration of the window
     pub titlebar: Option<TitlebarOptions>,
+
+    /// Whether the window should be centered on the screen
     pub center: bool,
+
+    /// Whether the window should be focused when created
     pub focus: bool,
+
+    /// Whether the window should be shown when created
     pub show: bool,
+
+    /// The kind of window to create
     pub kind: WindowKind,
+
+    /// Whether the window should be movable by the user
     pub is_movable: bool,
+
+    /// The display to create the window on
     pub display_id: Option<DisplayId>,
 }
 
@@ -371,46 +510,67 @@ impl Default for WindowOptions {
     }
 }
 
+/// The options that can be configured for a window's titlebar
 #[derive(Debug, Default)]
 pub struct TitlebarOptions {
+    /// The initial title of the window
     pub title: Option<SharedString>,
-    pub appears_transparent: bool,
-    pub traffic_light_position: Option<Point<Pixels>>,
-}
 
-#[derive(Copy, Clone, Debug)]
-pub enum Appearance {
-    Light,
-    VibrantLight,
-    Dark,
-    VibrantDark,
-}
+    /// Whether the titlebar should appear transparent
+    pub appears_transparent: bool,
 
-impl Default for Appearance {
-    fn default() -> Self {
-        Self::Light
-    }
+    /// The position of the macOS traffic light buttons
+    pub traffic_light_position: Option<Point<Pixels>>,
 }
 
+/// The kind of window to create
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum WindowKind {
+    /// A normal application window
     Normal,
+
+    /// A window that appears above all other windows, usually used for alerts or popups
+    /// use sparingly!
     PopUp,
 }
 
+/// Which bounds algorithm to use for the initial size a window
 #[derive(Copy, Clone, Debug, PartialEq, Default)]
 pub enum WindowBounds {
+    /// The window should be full screen, on macOS this corresponds to the full screen feature
     Fullscreen,
+
+    /// Make the window as large as the current display's size.
     #[default]
     Maximized,
+
+    /// Set the window to the given size in pixels
     Fixed(Bounds<GlobalPixels>),
 }
 
+/// The appearance of the window, as defined by the operating system
+/// On macOS, this corresponds to named [NSAppearance](https://developer.apple.com/documentation/appkit/nsappearance)
+/// values
 #[derive(Copy, Clone, Debug)]
 pub enum WindowAppearance {
+    /// A light appearance
+    ///
+    /// on macOS, this corresponds to the `aqua` appearance
     Light,
+
+    /// A light appearance with vibrant colors
+    ///
+    /// on macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance
     VibrantLight,
+
+    /// A dark appearance
+    ///
+    /// on macOS, this corresponds to the `darkAqua` appearance
     Dark,
+
+    /// A dark appearance with vibrant colors
+    ///
+    /// on macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance
     VibrantDark,
 }
 
@@ -420,40 +580,102 @@ impl Default for WindowAppearance {
     }
 }
 
+/// The options that can be configured for a file dialog prompt
 #[derive(Copy, Clone, Debug)]
 pub struct PathPromptOptions {
+    /// Should the prompt allow files to be selected?
     pub files: bool,
+    /// Should the prompt allow directories to be selected?
     pub directories: bool,
+    /// Should the prompt allow multiple files to be selected?
     pub multiple: bool,
 }
 
+/// What kind of prompt styling to show
 #[derive(Copy, Clone, Debug)]
 pub enum PromptLevel {
+    /// A prompt that is shown when the user should be notified of something
     Info,
+
+    /// A prompt that is shown when the user needs to be warned of a potential problem
     Warning,
+
+    /// A prompt that is shown when a critical problem has occurred
     Critical,
 }
 
 /// The style of the cursor (pointer)
 #[derive(Copy, Clone, Debug)]
 pub enum CursorStyle {
+    /// The default cursor
     Arrow,
+
+    /// A text input cursor
+    /// corresponds to the CSS cursor value `text`
     IBeam,
+
+    /// A crosshair cursor
+    /// corresponds to the CSS cursor value `crosshair`
     Crosshair,
+
+    /// A closed hand cursor
+    /// corresponds to the CSS cursor value `grabbing`
     ClosedHand,
+
+    /// An open hand cursor
+    /// corresponds to the CSS cursor value `grab`
     OpenHand,
+
+    /// A pointing hand cursor
+    /// corresponds to the CSS cursor value `pointer`
     PointingHand,
+
+    /// A resize left cursor
+    /// corresponds to the CSS cursor value `w-resize`
     ResizeLeft,
+
+    /// A resize right cursor
+    /// corresponds to the CSS cursor value `e-resize`
     ResizeRight,
+
+    /// A resize cursor to the left and right
+    /// corresponds to the CSS cursor value `col-resize`
     ResizeLeftRight,
+
+    /// A resize up cursor
+    /// corresponds to the CSS cursor value `n-resize`
     ResizeUp,
+
+    /// A resize down cursor
+    /// corresponds to the CSS cursor value `s-resize`
     ResizeDown,
+
+    /// A resize cursor directing up and down
+    /// corresponds to the CSS cursor value `row-resize`
     ResizeUpDown,
+
+    /// A cursor indicating that something will disappear if moved here
+    /// Does not correspond to a CSS cursor value
     DisappearingItem,
+
+    /// A text input cursor for vertical layout
+    /// corresponds to the CSS cursor value `vertical-text`
     IBeamCursorForVerticalLayout,
+
+    /// A cursor indicating that the operation is not allowed
+    /// corresponds to the CSS cursor value `not-allowed`
     OperationNotAllowed,
+
+    /// A cursor indicating that the operation will result in a link
+    /// corresponds to the CSS cursor value `alias`
     DragLink,
+
+    /// A cursor indicating that the operation will result in a copy
+    /// corresponds to the CSS cursor value `copy`
     DragCopy,
+
+    /// A cursor indicating that the operation will result in a context menu
+    /// corresponds to the CSS cursor value `context-menu`
     ContextualMenu,
 }
 
@@ -463,6 +685,7 @@ impl Default for CursorStyle {
     }
 }
 
+/// A datastructure representing a semantic version number
 #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize)]
 pub struct SemanticVersion {
     major: usize,
@@ -501,6 +724,7 @@ impl Display for SemanticVersion {
     }
 }
 
+/// A clipboard item that should be copied to the clipboard
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ClipboardItem {
     pub(crate) text: String,
@@ -508,6 +732,7 @@ pub struct ClipboardItem {
 }
 
 impl ClipboardItem {
+    /// Create a new clipboard item with the given text
     pub fn new(text: String) -> Self {
         Self {
             text,
@@ -515,15 +740,18 @@ impl ClipboardItem {
         }
     }
 
+    /// Create a new clipboard item with the given text and metadata
     pub fn with_metadata<T: Serialize>(mut self, metadata: T) -> Self {
         self.metadata = Some(serde_json::to_string(&metadata).unwrap());
         self
     }
 
+    /// Get the text of the clipboard item
     pub fn text(&self) -> &String {
         &self.text
     }
 
+    /// Get the metadata of the clipboard item
     pub fn metadata<T>(&self) -> Option<T>
     where
         T: for<'a> Deserialize<'a>,

crates/gpui/src/platform/app_menu.rs ๐Ÿ”—

@@ -1,30 +1,49 @@
 use crate::{Action, AppContext, Platform};
 use util::ResultExt;
 
+/// A menu of the application, either a main menu or a submenu
 pub struct Menu<'a> {
+    /// The name of the menu
     pub name: &'a str,
+
+    /// The items in the menu
     pub items: Vec<MenuItem<'a>>,
 }
 
+/// The different kinds of items that can be in a menu
 pub enum MenuItem<'a> {
+    /// A separator between items
     Separator,
+
+    /// A submenu
     Submenu(Menu<'a>),
+
+    /// An action that can be performed
     Action {
+        /// The name of this menu item
         name: &'a str,
+
+        /// the action to perform when this menu item is selected
         action: Box<dyn Action>,
+
+        /// The OS Action that corresponds to this action, if any
+        /// See [`OsAction`] for more information
         os_action: Option<OsAction>,
     },
 }
 
 impl<'a> MenuItem<'a> {
+    /// Creates a new menu item that is a separator
     pub fn separator() -> Self {
         Self::Separator
     }
 
+    /// Creates a new menu item that is a submenu
     pub fn submenu(menu: Menu<'a>) -> Self {
         Self::Submenu(menu)
     }
 
+    /// Creates a new menu item that invokes an action
     pub fn action(name: &'a str, action: impl Action) -> Self {
         Self::Action {
             name,
@@ -33,6 +52,7 @@ impl<'a> MenuItem<'a> {
         }
     }
 
+    /// Creates a new menu item that invokes an action and has an OS action
     pub fn os_action(name: &'a str, action: impl Action, os_action: OsAction) -> Self {
         Self::Action {
             name,
@@ -42,13 +62,31 @@ impl<'a> MenuItem<'a> {
     }
 }
 
+// TODO: As part of the global selections refactor, these should
+// be moved to GPUI-provided actions that make this association
+// without leaking the platform details to GPUI users
+
+/// OS actions are actions that are recognized by the operating system
+/// This allows the operating system to provide specialized behavior for
+/// these actions
 #[derive(Copy, Clone, Eq, PartialEq)]
 pub enum OsAction {
+    /// The 'cut' action
     Cut,
+
+    /// The 'copy' action
     Copy,
+
+    /// The 'paste' action
     Paste,
+
+    /// The 'select all' action
     SelectAll,
+
+    /// The 'undo' action
     Undo,
+
+    /// The 'redo' action
     Redo,
 }
 

crates/gpui/src/platform/keystroke.rs ๐Ÿ”—

@@ -3,24 +3,31 @@ use serde::Deserialize;
 use smallvec::SmallVec;
 use std::fmt::Write;
 
+/// A keystroke and associated metadata generated by the platform
 #[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
 pub struct Keystroke {
+    /// the state of the modifier keys at the time the keystroke was generated
     pub modifiers: Modifiers,
+
     /// key is the character printed on the key that was pressed
     /// e.g. for option-s, key is "s"
     pub key: String,
+
     /// ime_key is the character inserted by the IME engine when that key was pressed.
     /// e.g. for option-s, ime_key is "รŸ"
     pub ime_key: Option<String>,
 }
 
 impl Keystroke {
-    // When matching a key we cannot know whether the user intended to type
-    // the ime_key or the key. On some non-US keyboards keys we use in our
-    // bindings are behind option (for example `$` is typed `alt-รง` on a Czech keyboard),
-    // and on some keyboards the IME handler converts a sequence of keys into a
-    // specific character (for example `"` is typed as `" space` on a brazilian keyboard).
-    pub fn match_candidates(&self) -> SmallVec<[Keystroke; 2]> {
+    /// When matching a key we cannot know whether the user intended to type
+    /// the ime_key or the key itself. On some non-US keyboards keys we use in our
+    /// bindings are behind option (for example `$` is typed `alt-รง` on a Czech keyboard),
+    /// and on some keyboards the IME handler converts a sequence of keys into a
+    /// specific character (for example `"` is typed as `" space` on a brazilian keyboard).
+    ///
+    /// This method generates a list of potential keystroke candidates that could be matched
+    /// against when resolving a keybinding.
+    pub(crate) fn match_candidates(&self) -> SmallVec<[Keystroke; 2]> {
         let mut possibilities = SmallVec::new();
         match self.ime_key.as_ref() {
             None => possibilities.push(self.clone()),
@@ -47,7 +54,7 @@ impl Keystroke {
 
     /// key syntax is:
     /// [ctrl-][alt-][shift-][cmd-][fn-]key[->ime_key]
-    /// ime_key is only used for generating test events,
+    /// ime_key syntax is only used for generating test events,
     /// when matching a key with an ime_key set will be matched without it.
     pub fn parse(source: &str) -> anyhow::Result<Self> {
         let mut control = false;
@@ -135,16 +142,29 @@ impl std::fmt::Display for Keystroke {
     }
 }
 
+/// The state of the modifier keys at some point in time
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
 pub struct Modifiers {
+    /// The control key
     pub control: bool,
+
+    /// The alt key
+    /// Sometimes also known as the 'meta' key
     pub alt: bool,
+
+    /// The shift key
     pub shift: bool,
+
+    /// The command key, on macos
+    /// the windows key, on windows
     pub command: bool,
+
+    /// The function key
     pub function: bool,
 }
 
 impl Modifiers {
+    /// Returns true if any modifier key is pressed
     pub fn modified(&self) -> bool {
         self.control || self.alt || self.shift || self.command || self.function
     }

crates/gpui/src/platform/mac.rs ๐Ÿ”—

@@ -21,13 +21,13 @@ use metal_renderer::*;
 use objc::runtime::{BOOL, NO, YES};
 use std::ops::Range;
 
-pub use dispatcher::*;
-pub use display::*;
-pub use display_linker::*;
-pub use metal_atlas::*;
-pub use platform::*;
-pub use text_system::*;
-pub use window::*;
+pub(crate) use dispatcher::*;
+pub(crate) use display::*;
+pub(crate) use display_linker::*;
+pub(crate) use metal_atlas::*;
+pub(crate) use platform::*;
+pub(crate) use text_system::*;
+pub(crate) use window::*;
 
 trait BoolExt {
     fn to_objc(self) -> BOOL;

crates/gpui/src/platform/mac/dispatcher.rs ๐Ÿ”—

@@ -24,7 +24,7 @@ pub(crate) fn dispatch_get_main_queue() -> dispatch_queue_t {
     unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
 }
 
-pub struct MacDispatcher {
+pub(crate) struct MacDispatcher {
     parker: Arc<Mutex<Parker>>,
 }
 

crates/gpui/src/platform/mac/display.rs ๐Ÿ”—

@@ -11,7 +11,7 @@ use objc::{msg_send, sel, sel_impl};
 use uuid::Uuid;
 
 #[derive(Debug)]
-pub struct MacDisplay(pub(crate) CGDirectDisplayID);
+pub(crate) struct MacDisplay(pub(crate) CGDirectDisplayID);
 
 unsafe impl Send for MacDisplay {}
 
@@ -21,11 +21,6 @@ impl MacDisplay {
         Self::all().find(|screen| screen.id() == id)
     }
 
-    /// Get the screen with the given persistent [`Uuid`].
-    pub fn find_by_uuid(uuid: Uuid) -> Option<Self> {
-        Self::all().find(|screen| screen.uuid().ok() == Some(uuid))
-    }
-
     /// Get the primary screen - the one with the menu bar, and whose bottom left
     /// corner is at the origin of the AppKit coordinate system.
     pub fn primary() -> Self {

crates/gpui/src/platform/mac/display_linker.rs ๐Ÿ”—

@@ -7,8 +7,6 @@ use std::{
 use crate::DisplayId;
 use collections::HashMap;
 use parking_lot::Mutex;
-pub use sys::CVSMPTETime as SmtpeTime;
-pub use sys::CVTimeStamp as VideoTimestamp;
 
 pub(crate) struct MacDisplayLinker {
     links: HashMap<DisplayId, MacDisplayLink>,
@@ -27,13 +25,13 @@ impl MacDisplayLinker {
     }
 }
 
-type OutputCallback = Mutex<Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>>;
+type OutputCallback = Mutex<Box<dyn FnMut() + Send>>;
 
 impl MacDisplayLinker {
     pub fn set_output_callback(
         &mut self,
         display_id: DisplayId,
-        output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+        output_callback: Box<dyn FnMut() + Send>,
     ) {
         if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } {
             let callback = Arc::new(Mutex::new(output_callback));
@@ -81,11 +79,11 @@ unsafe extern "C" fn trampoline(
     _flags_out: *mut i64,
     user_data: *mut c_void,
 ) -> i32 {
-    if let Some((current_time, output_time)) = current_time.as_ref().zip(output_time.as_ref()) {
+    if let Some((_current_time, _output_time)) = current_time.as_ref().zip(output_time.as_ref()) {
         let output_callback: Weak<OutputCallback> =
             Weak::from_raw(user_data as *mut OutputCallback);
         if let Some(output_callback) = output_callback.upgrade() {
-            (output_callback.lock())(current_time, output_time)
+            (output_callback.lock())()
         }
         mem::forget(output_callback);
     }
@@ -126,7 +124,7 @@ mod sys {
 
     #[repr(C)]
     #[derive(Clone, Copy)]
-    pub struct CVTimeStamp {
+    pub(crate) struct CVTimeStamp {
         pub version: u32,
         pub video_time_scale: i32,
         pub video_time: i64,
@@ -154,7 +152,7 @@ mod sys {
 
     #[repr(C)]
     #[derive(Clone, Copy, Default)]
-    pub struct CVSMPTETime {
+    pub(crate) struct CVSMPTETime {
         pub subframes: i16,
         pub subframe_divisor: i16,
         pub counter: u32,

crates/gpui/src/platform/mac/events.rs ๐Ÿ”—

@@ -83,7 +83,10 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers {
 }
 
 impl PlatformInput {
-    pub unsafe fn from_native(native_event: id, window_height: Option<Pixels>) -> Option<Self> {
+    pub(crate) unsafe fn from_native(
+        native_event: id,
+        window_height: Option<Pixels>,
+    ) -> Option<Self> {
         let event_type = native_event.eventType();
 
         // Filter out event types that aren't in the NSEventType enum.

crates/gpui/src/platform/mac/metal_atlas.rs ๐Ÿ”—

@@ -10,10 +10,10 @@ use metal::Device;
 use parking_lot::Mutex;
 use std::borrow::Cow;
 
-pub struct MetalAtlas(Mutex<MetalAtlasState>);
+pub(crate) struct MetalAtlas(Mutex<MetalAtlasState>);
 
 impl MetalAtlas {
-    pub fn new(device: Device) -> Self {
+    pub(crate) fn new(device: Device) -> Self {
         MetalAtlas(Mutex::new(MetalAtlasState {
             device: AssertSend(device),
             monochrome_textures: Default::default(),

crates/gpui/src/platform/mac/platform.rs ๐Ÿ”—

@@ -3,7 +3,7 @@ use crate::{
     Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
     ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem,
     MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
-    PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions,
+    PlatformTextSystem, PlatformWindow, Result, SemanticVersion, WindowOptions,
 };
 use anyhow::anyhow;
 use block::ConcreteBlock;
@@ -139,9 +139,9 @@ unsafe fn build_classes() {
     }
 }
 
-pub struct MacPlatform(Mutex<MacPlatformState>);
+pub(crate) struct MacPlatform(Mutex<MacPlatformState>);
 
-pub struct MacPlatformState {
+pub(crate) struct MacPlatformState {
     background_executor: BackgroundExecutor,
     foreground_executor: ForegroundExecutor,
     text_system: Arc<MacTextSystem>,
@@ -169,7 +169,7 @@ impl Default for MacPlatform {
 }
 
 impl MacPlatform {
-    pub fn new() -> Self {
+    pub(crate) fn new() -> Self {
         let dispatcher = Arc::new(MacDispatcher::new());
         Self(Mutex::new(MacPlatformState {
             background_executor: BackgroundExecutor::new(dispatcher.clone()),
@@ -475,10 +475,6 @@ impl Platform for MacPlatform {
         }
     }
 
-    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
-    //     Box::new(StatusItem::add(self.fonts()))
-    // }
-
     fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
         MacDisplay::all()
             .map(|screen| Rc::new(screen) as Rc<_>)
@@ -504,7 +500,7 @@ impl Platform for MacPlatform {
     fn set_display_link_output_callback(
         &self,
         display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+        callback: Box<dyn FnMut() + Send>,
     ) {
         self.0
             .lock()

crates/gpui/src/platform/mac/text_system.rs ๐Ÿ”—

@@ -41,7 +41,7 @@ use super::open_type;
 #[allow(non_upper_case_globals)]
 const kCGImageAlphaOnly: u32 = 7;
 
-pub struct MacTextSystem(RwLock<MacTextSystemState>);
+pub(crate) struct MacTextSystem(RwLock<MacTextSystemState>);
 
 struct MacTextSystemState {
     memory_source: MemSource,
@@ -54,7 +54,7 @@ struct MacTextSystemState {
 }
 
 impl MacTextSystem {
-    pub fn new() -> Self {
+    pub(crate) fn new() -> Self {
         Self(RwLock::new(MacTextSystemState {
             memory_source: MemSource::empty(),
             system_source: SystemSource::new(),

crates/gpui/src/platform/mac/window.rs ๐Ÿ”—

@@ -1,9 +1,9 @@
 use super::{global_bounds_from_ns_rect, ns_string, MacDisplay, MetalRenderer, NSRange};
 use crate::{
-    global_bounds_to_ns_rect, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
-    FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent, Keystroke, Modifiers,
-    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
-    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
+    global_bounds_to_ns_rect, platform::PlatformInputHandler, point, px, size, AnyWindowHandle,
+    Bounds, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent,
+    Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
+    MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
     PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
 };
 use block::ConcreteBlock;
@@ -220,7 +220,7 @@ unsafe fn build_classes() {
     };
 }
 
-pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
+pub(crate) fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
     point(
         px(position.x as f32),
         // MacOS screen coordinates are relative to bottom left
@@ -327,7 +327,7 @@ struct MacWindowState {
     should_close_callback: Option<Box<dyn FnMut() -> bool>>,
     close_callback: Option<Box<dyn FnOnce()>>,
     appearance_changed_callback: Option<Box<dyn FnMut()>>,
-    input_handler: Option<Box<dyn PlatformInputHandler>>,
+    input_handler: Option<PlatformInputHandler>,
     pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
     last_key_equivalent: Option<KeyDownEvent>,
     synthetic_drag_counter: usize,
@@ -446,7 +446,7 @@ impl MacWindowState {
 
 unsafe impl Send for MacWindowState {}
 
-pub struct MacWindow(Arc<Mutex<MacWindowState>>);
+pub(crate) struct MacWindow(Arc<Mutex<MacWindowState>>);
 
 impl MacWindow {
     pub fn open(
@@ -764,11 +764,11 @@ impl PlatformWindow for MacWindow {
         self
     }
 
-    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
+    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
         self.0.as_ref().lock().input_handler = Some(input_handler);
     }
 
-    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
         self.0.as_ref().lock().input_handler.take()
     }
 
@@ -1761,13 +1761,13 @@ fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id)
 
 fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
 where
-    F: FnOnce(&mut dyn PlatformInputHandler) -> R,
+    F: FnOnce(&mut PlatformInputHandler) -> R,
 {
     let window_state = unsafe { get_window_state(window) };
     let mut lock = window_state.as_ref().lock();
     if let Some(mut input_handler) = lock.input_handler.take() {
         drop(lock);
-        let result = f(input_handler.as_mut());
+        let result = f(&mut input_handler);
         window_state.lock().input_handler = Some(input_handler);
         Some(result)
     } else {

crates/gpui/src/platform/mac/window_appearance.rs ๐Ÿ”—

@@ -8,7 +8,7 @@ use objc::{msg_send, sel, sel_impl};
 use std::ffi::CStr;
 
 impl WindowAppearance {
-    pub unsafe fn from_native(appearance: id) -> Self {
+    pub(crate) unsafe fn from_native(appearance: id) -> Self {
         let name: id = msg_send![appearance, name];
         if name == NSAppearanceNameVibrantLight {
             Self::VibrantLight

crates/gpui/src/platform/test.rs ๐Ÿ”—

@@ -3,7 +3,7 @@ mod display;
 mod platform;
 mod window;
 
-pub use dispatcher::*;
-pub use display::*;
-pub use platform::*;
-pub use window::*;
+pub(crate) use dispatcher::*;
+pub(crate) use display::*;
+pub(crate) use platform::*;
+pub(crate) use window::*;

crates/gpui/src/platform/test/dispatcher.rs ๐Ÿ”—

@@ -18,6 +18,7 @@ use util::post_inc;
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 struct TestDispatcherId(usize);
 
+#[doc(hidden)]
 pub struct TestDispatcher {
     id: TestDispatcherId,
     state: Arc<Mutex<TestDispatcherState>>,

crates/gpui/src/platform/test/display.rs ๐Ÿ”—

@@ -3,7 +3,7 @@ use anyhow::{Ok, Result};
 use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point};
 
 #[derive(Debug)]
-pub struct TestDisplay {
+pub(crate) struct TestDisplay {
     id: DisplayId,
     uuid: uuid::Uuid,
     bounds: Bounds<GlobalPixels>,

crates/gpui/src/platform/test/platform.rs ๐Ÿ”—

@@ -15,7 +15,7 @@ use std::{
 };
 
 /// TestPlatform implements the Platform trait for use in tests.
-pub struct TestPlatform {
+pub(crate) struct TestPlatform {
     background_executor: BackgroundExecutor,
     foreground_executor: ForegroundExecutor,
 
@@ -178,20 +178,9 @@ impl Platform for TestPlatform {
     fn set_display_link_output_callback(
         &self,
         _display_id: DisplayId,
-        mut callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
+        mut callback: Box<dyn FnMut() + Send>,
     ) {
-        let timestamp = crate::VideoTimestamp {
-            version: 0,
-            video_time_scale: 0,
-            video_time: 0,
-            host_time: 0,
-            rate_scalar: 0.0,
-            video_refresh_period: 0,
-            smpte_time: crate::SmtpeTime::default(),
-            flags: 0,
-            reserved: 0,
-        };
-        callback(&timestamp, &timestamp)
+        callback()
     }
 
     fn start_display_link(&self, _display_id: DisplayId) {}

crates/gpui/src/platform/test/window.rs ๐Ÿ”—

@@ -10,7 +10,7 @@ use std::{
     sync::{self, Arc},
 };
 
-pub struct TestWindowState {
+pub(crate) struct TestWindowState {
     pub(crate) bounds: WindowBounds,
     pub(crate) handle: AnyWindowHandle,
     display: Rc<dyn PlatformDisplay>,
@@ -23,11 +23,11 @@ pub struct TestWindowState {
     active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
     resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
     moved_callback: Option<Box<dyn FnMut()>>,
-    input_handler: Option<Box<dyn PlatformInputHandler>>,
+    input_handler: Option<PlatformInputHandler>,
 }
 
 #[derive(Clone)]
-pub struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
+pub(crate) struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
 
 impl TestWindow {
     pub fn new(
@@ -117,9 +117,6 @@ impl TestWindow {
 
         self.0.lock().input_handler = Some(input_handler);
     }
-    pub fn edited(&self) -> bool {
-        self.0.lock().edited
-    }
 }
 
 impl PlatformWindow for TestWindow {
@@ -163,11 +160,11 @@ impl PlatformWindow for TestWindow {
         self
     }
 
-    fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
+    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
         self.0.lock().input_handler = Some(input_handler);
     }
 
-    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
         self.0.lock().input_handler.take()
     }
 
@@ -269,12 +266,12 @@ impl PlatformWindow for TestWindow {
     }
 }
 
-pub struct TestAtlasState {
+pub(crate) struct TestAtlasState {
     next_id: u32,
     tiles: HashMap<AtlasKey, AtlasTile>,
 }
 
-pub struct TestAtlas(Mutex<TestAtlasState>);
+pub(crate) struct TestAtlas(Mutex<TestAtlasState>);
 
 impl TestAtlas {
     pub fn new() -> Self {

crates/gpui/src/styled.rs ๐Ÿ”—

@@ -1,11 +1,11 @@
 use crate::{
     self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
-    DefiniteLength, Display, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length,
-    Position, SharedString, StyleRefinement, Visibility, WhiteSpace,
+    DefiniteLength, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length, Position,
+    SharedString, StyleRefinement, Visibility, WhiteSpace,
 };
 use crate::{BoxShadow, TextStyleRefinement};
 use smallvec::{smallvec, SmallVec};
-use taffy::style::Overflow;
+use taffy::style::{Display, Overflow};
 
 pub trait Styled: Sized {
     fn style(&mut self) -> &mut StyleRefinement;

crates/gpui/src/window.rs ๐Ÿ”—

@@ -5,13 +5,13 @@ use crate::{
     AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
     DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
     Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
-    ImageData, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, KeystrokeEvent, LayoutId,
-    Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent,
-    MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
-    PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
-    RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene, Shadow,
-    SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task,
-    Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions,
+    ImageData, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent,
+    KeystrokeEvent, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
+    MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
+    PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel,
+    Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
+    Shadow, SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine,
+    Task, Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions,
     SUBPIXEL_VARIANTS,
 };
 use anyhow::{anyhow, Context as _, Result};
@@ -298,7 +298,7 @@ pub(crate) struct ElementStateBox {
 
 struct RequestedInputHandler {
     view_id: EntityId,
-    handler: Option<Box<dyn PlatformInputHandler>>,
+    handler: Option<PlatformInputHandler>,
 }
 
 struct TooltipRequest {
@@ -737,7 +737,7 @@ impl<'a> WindowContext<'a> {
             let (tx, mut rx) = mpsc::unbounded::<()>();
             self.platform.set_display_link_output_callback(
                 display_id,
-                Box::new(move |_current_time, _output_time| _ = tx.unbounded_send(())),
+                Box::new(move || _ = tx.unbounded_send(())),
             );
 
             let consumer_task = self.app.spawn(|cx| async move {
@@ -2188,16 +2188,15 @@ impl<'a> WindowContext<'a> {
     /// rendered.
     ///
     /// [element_input_handler]: crate::ElementInputHandler
-    pub fn handle_input(
-        &mut self,
-        focus_handle: &FocusHandle,
-        input_handler: impl PlatformInputHandler,
-    ) {
+    pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
         if focus_handle.is_focused(self) {
             let view_id = self.parent_view_id();
             self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
                 view_id,
-                handler: Some(Box::new(input_handler)),
+                handler: Some(PlatformInputHandler::new(
+                    self.to_async(),
+                    Box::new(input_handler),
+                )),
             })
         }
     }
@@ -2209,7 +2208,7 @@ impl<'a> WindowContext<'a> {
         self.window
             .platform_window
             .on_should_close(Box::new(move || {
-                this.update(|_, cx| {
+                this.update(|cx| {
                     // Ensure that the window is removed from the app if it's been closed
                     // by always pre-empting the system close event.
                     if f(cx) {

crates/journal/src/journal.rs ๐Ÿ”—

@@ -102,7 +102,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut WindowContext) {
     cx.spawn(|mut cx| async move {
         let (journal_dir, entry_path) = create_entry.await?;
         let (workspace, _) = cx
-            .update(|_, cx| workspace::open_paths(&[journal_dir], &app_state, None, cx))?
+            .update(|cx| workspace::open_paths(&[journal_dir], &app_state, None, cx))?
             .await?;
 
         let opened = workspace

crates/terminal_view/src/terminal_element.rs ๐Ÿ”—

@@ -1,11 +1,11 @@
 use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
 use gpui::{
-    div, fill, point, px, relative, AnyElement, AsyncWindowContext, AvailableSpace, BorrowWindow,
-    Bounds, DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight,
-    HighlightStyle, Hsla, InteractiveBounds, InteractiveElement, InteractiveElementState,
+    div, fill, point, px, relative, AnyElement, AvailableSpace, BorrowWindow, Bounds,
+    DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight, HighlightStyle,
+    Hsla, InputHandler, InteractiveBounds, InteractiveElement, InteractiveElementState,
     Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
-    MouseMoveEvent, Pixels, PlatformInputHandler, Point, ShapedLine, StatefulInteractiveElement,
-    Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
+    MouseMoveEvent, Pixels, Point, ShapedLine, StatefulInteractiveElement, Styled, TextRun,
+    TextStyle, TextSystem, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
 };
 use itertools::Itertools;
 use language::CursorShape;
@@ -749,7 +749,6 @@ impl Element for TerminalElement {
         let origin = bounds.origin + Point::new(layout.gutter, px(0.));
 
         let terminal_input_handler = TerminalInputHandler {
-            cx: cx.to_async(),
             terminal: self.terminal.clone(),
             cursor_bounds: layout
                 .cursor
@@ -838,37 +837,35 @@ impl IntoElement for TerminalElement {
 }
 
 struct TerminalInputHandler {
-    cx: AsyncWindowContext,
     terminal: Model<Terminal>,
     workspace: WeakView<Workspace>,
     cursor_bounds: Option<Bounds<Pixels>>,
 }
 
-impl PlatformInputHandler for TerminalInputHandler {
-    fn selected_text_range(&mut self) -> Option<std::ops::Range<usize>> {
-        self.cx
-            .update(|_, cx| {
-                if self
-                    .terminal
-                    .read(cx)
-                    .last_content
-                    .mode
-                    .contains(TermMode::ALT_SCREEN)
-                {
-                    None
-                } else {
-                    Some(0..0)
-                }
-            })
-            .ok()
-            .flatten()
+impl InputHandler for TerminalInputHandler {
+    fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<std::ops::Range<usize>> {
+        if self
+            .terminal
+            .read(cx)
+            .last_content
+            .mode
+            .contains(TermMode::ALT_SCREEN)
+        {
+            None
+        } else {
+            Some(0..0)
+        }
     }
 
-    fn marked_text_range(&mut self) -> Option<std::ops::Range<usize>> {
+    fn marked_text_range(&mut self, _: &mut WindowContext) -> Option<std::ops::Range<usize>> {
         None
     }
 
-    fn text_for_range(&mut self, _: std::ops::Range<usize>) -> Option<String> {
+    fn text_for_range(
+        &mut self,
+        _: std::ops::Range<usize>,
+        _: &mut WindowContext,
+    ) -> Option<String> {
         None
     }
 
@@ -876,19 +873,16 @@ impl PlatformInputHandler for TerminalInputHandler {
         &mut self,
         _replacement_range: Option<std::ops::Range<usize>>,
         text: &str,
+        cx: &mut WindowContext,
     ) {
-        self.cx
-            .update(|_, cx| {
-                self.terminal.update(cx, |terminal, _| {
-                    terminal.input(text.into());
-                });
+        self.terminal.update(cx, |terminal, _| {
+            terminal.input(text.into());
+        });
 
-                self.workspace
-                    .update(cx, |this, cx| {
-                        let telemetry = this.project().read(cx).client().telemetry().clone();
-                        telemetry.log_edit_event("terminal");
-                    })
-                    .ok();
+        self.workspace
+            .update(cx, |this, cx| {
+                let telemetry = this.project().read(cx).client().telemetry().clone();
+                telemetry.log_edit_event("terminal");
             })
             .ok();
     }
@@ -898,12 +892,17 @@ impl PlatformInputHandler for TerminalInputHandler {
         _range_utf16: Option<std::ops::Range<usize>>,
         _new_text: &str,
         _new_selected_range: Option<std::ops::Range<usize>>,
+        _: &mut WindowContext,
     ) {
     }
 
-    fn unmark_text(&mut self) {}
+    fn unmark_text(&mut self, _: &mut WindowContext) {}
 
-    fn bounds_for_range(&mut self, _range_utf16: std::ops::Range<usize>) -> Option<Bounds<Pixels>> {
+    fn bounds_for_range(
+        &mut self,
+        _range_utf16: std::ops::Range<usize>,
+        _: &mut WindowContext,
+    ) -> Option<Bounds<Pixels>> {
         self.cursor_bounds
     }
 }

crates/terminal_view/src/terminal_view.rs ๐Ÿ”—

@@ -772,7 +772,7 @@ impl Item for TerminalView {
                 .log_err()
                 .flatten()
                 .or_else(|| {
-                    cx.update(|_, cx| {
+                    cx.update(|cx| {
                         let strategy = TerminalSettings::get_global(cx).working_directory.clone();
                         workspace
                             .upgrade()

crates/workspace/src/notifications.rs ๐Ÿ”—

@@ -281,7 +281,7 @@ where
             Ok(value) => Some(value),
             Err(err) => {
                 log::error!("TODO {err:?}");
-                cx.update(|view, cx| {
+                cx.update_root(|view, cx| {
                     if let Ok(workspace) = view.downcast::<Workspace>() {
                         workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
                     }

crates/workspace/src/pane.rs ๐Ÿ”—

@@ -1092,7 +1092,7 @@ impl Pane {
             return Ok(true);
         }
 
-        let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.update(|_, cx| {
+        let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.update(|cx| {
             (
                 item.has_conflict(cx),
                 item.is_dirty(cx),
@@ -1132,7 +1132,7 @@ impl Pane {
             }
         } else if is_dirty && (can_save || can_save_as) {
             if save_intent == SaveIntent::Close {
-                let will_autosave = cx.update(|_, cx| {
+                let will_autosave = cx.update(|cx| {
                     matches!(
                         WorkspaceSettings::get_global(cx).autosave,
                         AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
@@ -1166,7 +1166,7 @@ impl Pane {
                     })?
                     .unwrap_or_else(|| Path::new("").into());
 
-                let abs_path = cx.update(|_, cx| cx.prompt_for_new_path(&start_abs_path))?;
+                let abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path))?;
                 if let Some(abs_path) = abs_path.await.ok().flatten() {
                     pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
                         .await?;

crates/workspace/src/workspace.rs ๐Ÿ”—

@@ -1233,7 +1233,7 @@ impl Workspace {
             }
             for (pane, item) in dirty_items {
                 let (singleton, project_entry_ids) =
-                    cx.update(|_, cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?;
+                    cx.update(|cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?;
                 if singleton || !project_entry_ids.is_empty() {
                     if let Some(ix) =
                         pane.update(&mut cx, |pane, _| pane.index_for_item(item.as_ref()))?
@@ -1307,7 +1307,7 @@ impl Workspace {
             } else {
                 None
             };
-            cx.update(|_, cx| open_paths(&paths, &app_state, window_to_replace, cx))?
+            cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx))?
                 .await?;
             Ok(())
         })
@@ -1912,7 +1912,7 @@ impl Workspace {
         let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
         cx.spawn(|_, mut cx| async move {
             let (project_entry_id, project_item) = project_item.await?;
-            let build_item = cx.update(|_, cx| {
+            let build_item = cx.update(|cx| {
                 cx.default_global::<ProjectItemBuilders>()
                     .get(&project_item.entity_type())
                     .ok_or_else(|| anyhow!("no item builder for project item"))
@@ -2709,7 +2709,7 @@ impl Workspace {
     ) -> Result<()> {
         let this = this.upgrade().context("workspace dropped")?;
 
-        let item_builders = cx.update(|_, cx| {
+        let item_builders = cx.update(|cx| {
             cx.default_global::<FollowableItemBuilders>()
                 .values()
                 .map(|b| b.0)
@@ -2728,7 +2728,7 @@ impl Workspace {
                     Err(anyhow!("missing view variant"))?;
                 }
                 for build_item in &item_builders {
-                    let task = cx.update(|_, cx| {
+                    let task = cx.update(|cx| {
                         build_item(pane.clone(), this.clone(), id, &mut variant, cx)
                     })?;
                     if let Some(task) = task {
@@ -3141,7 +3141,7 @@ impl Workspace {
                 center_group = Some((group, active_pane))
             }
 
-            let mut items_by_project_path = cx.update(|_, cx| {
+            let mut items_by_project_path = cx.update(|cx| {
                 center_items
                     .unwrap_or_default()
                     .into_iter()
@@ -3407,7 +3407,7 @@ fn open_items(
             let restored_project_paths = restored_items
                 .iter()
                 .filter_map(|item| {
-                    cx.update(|_, cx| item.as_ref()?.project_path(cx))
+                    cx.update(|cx| item.as_ref()?.project_path(cx))
                         .ok()
                         .flatten()
                 })

crates/zed/src/zed.rs ๐Ÿ”—

@@ -875,7 +875,7 @@ mod tests {
         let window = cx.update(|cx| cx.windows()[0].downcast::<Workspace>().unwrap());
 
         let window_is_edited = |window: WindowHandle<Workspace>, cx: &mut TestAppContext| {
-            cx.test_window(window.into()).edited()
+            cx.update(|cx| window.read(cx).unwrap().is_edited())
         };
         let pane = window
             .read_with(cx, |workspace, _| workspace.active_pane().clone())