From fbf1552be910a61e9a5a237d836847736fc197ae Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Mon, 10 Jul 2023 20:41:39 +0100 Subject: [PATCH 01/53] Add color_family to theme --- styles/src/theme/create_theme.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/styles/src/theme/create_theme.ts b/styles/src/theme/create_theme.ts index d2701f8341af973a3a4511c149c983cb221fa14d..3f4a0766547534d39e43546c722cd2179ce628b1 100644 --- a/styles/src/theme/create_theme.ts +++ b/styles/src/theme/create_theme.ts @@ -1,4 +1,4 @@ -import { Scale, Color } from "chroma-js" +import chroma, { Scale, Color } from "chroma-js" import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax" export { Syntax, ThemeSyntax, SyntaxHighlightStyle } import { @@ -32,6 +32,7 @@ export interface Theme { players: Players syntax?: Partial + color_family: ColorFamily } export interface Meta { @@ -69,6 +70,12 @@ export interface Players { "7": Player } +export interface ColorFamily { + range: ColorFamilyRange +} + +export type ColorFamilyRange = Partial<{ [K in keyof RampSet]: number }> + export interface Shadow { blur: number color: string @@ -162,6 +169,10 @@ export function create_theme(theme: ThemeConfig): Theme { "7": player(ramps.yellow), } + const color_family = { + range: build_color_family(ramps) + } + return { name, is_light, @@ -177,6 +188,7 @@ export function create_theme(theme: ThemeConfig): Theme { players, syntax, + color_family, } } @@ -187,6 +199,16 @@ function player(ramp: Scale): Player { } } +function build_color_family(ramps: RampSet): ColorFamilyRange { + const color_family: ColorFamilyRange = {} + + for (const ramp in ramps) { + color_family[ramp as keyof RampSet] = chroma(ramps[ramp as keyof RampSet](0.5)).luminance() + } + + return color_family +} + function lowest_layer(ramps: RampSet): Layer { return { base: build_style_set(ramps.neutral, 0.2, 1), From 036d3e811aa48bf62368dfcd6c9e5812bc16398f Mon Sep 17 00:00:00 2001 From: Sergey Onufrienko Date: Thu, 13 Jul 2023 22:09:31 +0100 Subject: [PATCH 02/53] feat: add low, high, range and scaling --- styles/src/theme/create_theme.ts | 33 ++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/styles/src/theme/create_theme.ts b/styles/src/theme/create_theme.ts index 3f4a0766547534d39e43546c722cd2179ce628b1..e0da345bc52a81a8519f5f36d1d7ab76d9a6ff4c 100644 --- a/styles/src/theme/create_theme.ts +++ b/styles/src/theme/create_theme.ts @@ -70,11 +70,14 @@ export interface Players { "7": Player } -export interface ColorFamily { - range: ColorFamilyRange -} +export type ColorFamily = Partial<{ [K in keyof RampSet]: ColorFamilyRange }> -export type ColorFamilyRange = Partial<{ [K in keyof RampSet]: number }> +export interface ColorFamilyRange { + low: number + high: number + range: number + scaling_value: number +} export interface Shadow { blur: number @@ -169,9 +172,7 @@ export function create_theme(theme: ThemeConfig): Theme { "7": player(ramps.yellow), } - const color_family = { - range: build_color_family(ramps) - } + const color_family = build_color_family(ramps) return { name, @@ -199,11 +200,23 @@ function player(ramp: Scale): Player { } } -function build_color_family(ramps: RampSet): ColorFamilyRange { - const color_family: ColorFamilyRange = {} +function build_color_family(ramps: RampSet): ColorFamily { + const color_family: ColorFamily = {} for (const ramp in ramps) { - color_family[ramp as keyof RampSet] = chroma(ramps[ramp as keyof RampSet](0.5)).luminance() + const ramp_value = ramps[ramp as keyof RampSet] + + const lightnessValues = [ramp_value(0).get('hsl.l') * 100, ramp_value(1).get('hsl.l') * 100] + const low = Math.min(...lightnessValues) + const high = Math.max(...lightnessValues) + const range = high - low + + color_family[ramp as keyof RampSet] = { + low, + high, + range, + scaling_value: 100 / range, + } } return color_family From 1b03c5d69c20ebc4dca4790ccf5be43a6afe5011 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 25 Jul 2023 17:32:31 -0600 Subject: [PATCH 03/53] Pass PaintContext to Element::paint I want to use this on another branch, but it's a sweeping change, so this prepares the ground for it. This can always be reverted if it doesn't work out. --- crates/collab_ui/src/collab_titlebar_item.rs | 6 +- crates/collab_ui/src/face_pile.rs | 4 +- crates/editor/src/element.rs | 13 ++- crates/gpui/src/app.rs | 87 +++++++++++++++++++ crates/gpui/src/app/window.rs | 6 +- crates/gpui/src/elements.rs | 26 ++++-- crates/gpui/src/elements/align.rs | 5 +- crates/gpui/src/elements/canvas.rs | 4 +- crates/gpui/src/elements/clipped.rs | 5 +- crates/gpui/src/elements/constrained_box.rs | 5 +- crates/gpui/src/elements/container.rs | 5 +- crates/gpui/src/elements/empty.rs | 4 +- crates/gpui/src/elements/expanded.rs | 5 +- crates/gpui/src/elements/flex.rs | 8 +- crates/gpui/src/elements/hook.rs | 5 +- crates/gpui/src/elements/image.rs | 6 +- crates/gpui/src/elements/keystroke_label.rs | 2 +- crates/gpui/src/elements/label.rs | 4 +- crates/gpui/src/elements/list.rs | 10 +-- .../gpui/src/elements/mouse_event_handler.rs | 6 +- crates/gpui/src/elements/overlay.rs | 6 +- crates/gpui/src/elements/resizable.rs | 6 +- crates/gpui/src/elements/stack.rs | 5 +- crates/gpui/src/elements/svg.rs | 3 +- crates/gpui/src/elements/text.rs | 6 +- crates/gpui/src/elements/tooltip.rs | 6 +- crates/gpui/src/elements/uniform_list.rs | 4 +- crates/gpui/src/fonts.rs | 26 ++++++ crates/terminal_view/src/terminal_element.rs | 7 +- crates/workspace/src/pane.rs | 6 +- crates/workspace/src/pane_group.rs | 6 +- crates/workspace/src/status_bar.rs | 6 +- 32 files changed, 223 insertions(+), 80 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index ce8d10d655d76b018aef855a934c95e1d7b1640c..04abdf8c1cc850d4913ea2f67fee9e5cfa4b1825 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -15,8 +15,8 @@ use gpui::{ geometry::{rect::RectF, vector::vec2f, PathBuilder}, json::{self, ToJson}, platform::{CursorStyle, MouseButton}, - AppContext, Entity, ImageData, LayoutContext, ModelHandle, SceneBuilder, Subscription, View, - ViewContext, ViewHandle, WeakViewHandle, + AppContext, Entity, ImageData, LayoutContext, ModelHandle, PaintContext, SceneBuilder, + Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use picker::PickerEvent; use project::{Project, RepositoryEntry}; @@ -1312,7 +1312,7 @@ impl Element for AvatarRibbon { _: RectF, _: &mut Self::LayoutState, _: &mut CollabTitlebarItem, - _: &mut ViewContext, + _: &mut PaintContext, ) -> Self::PaintState { let mut path = PathBuilder::new(); path.reset(bounds.lower_left()); diff --git a/crates/collab_ui/src/face_pile.rs b/crates/collab_ui/src/face_pile.rs index 1bbceee9af1bec406bf9b1398fde94dd230ac73d..9685d86b402db8dad693ac60af01a64bd21f141d 100644 --- a/crates/collab_ui/src/face_pile.rs +++ b/crates/collab_ui/src/face_pile.rs @@ -7,7 +7,7 @@ use gpui::{ }, json::ToJson, serde_json::{self, json}, - AnyElement, Axis, Element, LayoutContext, SceneBuilder, ViewContext, + AnyElement, Axis, Element, LayoutContext, PaintContext, SceneBuilder, ViewContext, }; use crate::CollabTitlebarItem; @@ -54,7 +54,7 @@ impl Element for FacePile { visible_bounds: RectF, _layout: &mut Self::LayoutState, view: &mut CollabTitlebarItem, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b48fa5b56dd831f82852f224370500027302f3af..94a849b0d0519af7f0fe5831ccbd993498f53b2d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -32,7 +32,7 @@ use gpui::{ platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent}, text_layout::{self, Line, RunStyle, TextLayoutCache}, AnyElement, Axis, Border, CursorRegion, Element, EventContext, FontCache, LayoutContext, - MouseRegion, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext, + MouseRegion, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext, }; use itertools::Itertools; use json::json; @@ -2455,7 +2455,7 @@ impl Element for EditorElement { visible_bounds: RectF, layout: &mut Self::LayoutState, editor: &mut Editor, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); scene.push_layer(Some(visible_bounds)); @@ -3051,7 +3051,14 @@ mod tests { let mut scene = SceneBuilder::new(1.0); let bounds = RectF::new(Default::default(), size); editor.update(cx, |editor, cx| { - element.paint(&mut scene, bounds, bounds, &mut state, editor, cx); + element.paint( + &mut scene, + bounds, + bounds, + &mut state, + editor, + &mut PaintContext::new(cx), + ); }); } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 7af363d596b63abe06f78717d8945dcba820d7fd..fd22dc466e24be2bab1eb9d22c720218e4734380 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -44,6 +44,7 @@ use window_input_handler::WindowInputHandler; use crate::{ elements::{AnyElement, AnyRootElement, RootElement}, executor::{self, Task}, + fonts::TextStyle, json, keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult}, platform::{ @@ -3363,6 +3364,7 @@ pub struct LayoutContext<'a, 'b, 'c, V: View> { view_context: &'c mut ViewContext<'a, 'b, V>, new_parents: &'c mut HashMap, views_to_notify_if_ancestors_change: &'c mut HashMap>, + text_style_stack: Vec>, pub refreshing: bool, } @@ -3377,6 +3379,7 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> { view_context, new_parents, views_to_notify_if_ancestors_change, + text_style_stack: Vec::new(), refreshing, } } @@ -3428,6 +3431,24 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> { .or_default() .push(self_view_id); } + + pub fn text_style(&self) -> Arc { + self.text_style_stack + .last() + .cloned() + .unwrap_or(Default::default()) + } + + pub fn with_text_style(&mut self, style: S, f: F) -> T + where + S: Into>, + F: FnOnce(&mut Self) -> T, + { + self.text_style_stack.push(style.into()); + let result = f(self); + self.text_style_stack.pop(); + result + } } impl<'a, 'b, 'c, V: View> Deref for LayoutContext<'a, 'b, 'c, V> { @@ -3464,6 +3485,72 @@ impl BorrowWindowContext for LayoutContext<'_, '_, '_, V> { } } +pub struct PaintContext<'a, 'b, 'c, V: View> { + view_context: &'c mut ViewContext<'a, 'b, V>, + text_style_stack: Vec>, +} + +impl<'a, 'b, 'c, V: View> PaintContext<'a, 'b, 'c, V> { + pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self { + Self { + view_context, + text_style_stack: Vec::new(), + } + } + + pub fn text_style(&self) -> Arc { + self.text_style_stack + .last() + .cloned() + .unwrap_or(Default::default()) + } + + pub fn with_text_style(&mut self, style: S, f: F) -> T + where + S: Into>, + F: FnOnce(&mut Self) -> T, + { + self.text_style_stack.push(style.into()); + let result = f(self); + self.text_style_stack.pop(); + result + } +} + +impl<'a, 'b, 'c, V: View> Deref for PaintContext<'a, 'b, 'c, V> { + type Target = ViewContext<'a, 'b, V>; + + fn deref(&self) -> &Self::Target { + &self.view_context + } +} + +impl DerefMut for PaintContext<'_, '_, '_, V> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.view_context + } +} + +impl BorrowAppContext for PaintContext<'_, '_, '_, V> { + fn read_with T>(&self, f: F) -> T { + BorrowAppContext::read_with(&*self.view_context, f) + } + + fn update T>(&mut self, f: F) -> T { + BorrowAppContext::update(&mut *self.view_context, f) + } +} + +impl BorrowWindowContext for PaintContext<'_, '_, '_, V> { + fn read_with T>(&self, window_id: usize, f: F) -> T { + BorrowWindowContext::read_with(&*self.view_context, window_id, f) + } + + fn update T>(&mut self, window_id: usize, f: F) -> T { + BorrowWindowContext::update(&mut *self.view_context, window_id, f) + } +} + pub struct EventContext<'a, 'b, 'c, V: View> { view_context: &'c mut ViewContext<'a, 'b, V>, pub(crate) handled: bool, diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 1dc88d2e717a985be4aed9984597f4abf7b92a1a..c6ab7c6ebb21d322a514ab580b58a786858a6bbe 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -14,8 +14,8 @@ use crate::{ text_layout::TextLayoutCache, util::post_inc, Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect, - Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription, - View, ViewContext, ViewHandle, WindowInvalidation, + Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, PaintContext, SceneBuilder, + Subscription, View, ViewContext, ViewHandle, WindowInvalidation, }; use anyhow::{anyhow, bail, Result}; use collections::{HashMap, HashSet}; @@ -1400,7 +1400,7 @@ impl Element for ChildView { visible_bounds: RectF, _: &mut Self::LayoutState, _: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) { if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) { rendered_view diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index 78403444fff1807ee7b01ef8fe6a8f8493edfab2..5bed935319f6cb89dbdcb0955a66d404ee629e69 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -33,8 +33,8 @@ use crate::{ rect::RectF, vector::{vec2f, Vector2F}, }, - json, Action, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle, - WindowContext, + json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext, + WeakViewHandle, WindowContext, }; use anyhow::{anyhow, Result}; use collections::HashMap; @@ -61,7 +61,7 @@ pub trait Element: 'static { visible_bounds: RectF, layout: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState; fn rect_for_text_range( @@ -298,7 +298,14 @@ impl> AnyElementState for ElementState { mut layout, } => { let bounds = RectF::new(origin, size); - let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx); + let paint = element.paint( + scene, + bounds, + visible_bounds, + &mut layout, + view, + &mut PaintContext::new(cx), + ); ElementState::PostPaint { element, constraint, @@ -316,7 +323,14 @@ impl> AnyElementState for ElementState { .. } => { let bounds = RectF::new(origin, bounds.size()); - let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx); + let paint = element.paint( + scene, + bounds, + visible_bounds, + &mut layout, + view, + &mut PaintContext::new(cx), + ); ElementState::PostPaint { element, constraint, @@ -513,7 +527,7 @@ impl Element for AnyElement { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { self.paint(scene, bounds.origin(), visible_bounds, view, cx); } diff --git a/crates/gpui/src/elements/align.rs b/crates/gpui/src/elements/align.rs index 165cfcf190c7d69304c424f0bbed34b048905c44..e60c1ff907474b05a2d179b5f3a6fab0e8028e16 100644 --- a/crates/gpui/src/elements/align.rs +++ b/crates/gpui/src/elements/align.rs @@ -1,6 +1,7 @@ use crate::{ geometry::{rect::RectF, vector::Vector2F}, - json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, }; use json::ToJson; @@ -69,7 +70,7 @@ impl Element for Align { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let my_center = bounds.size() / 2.; let my_target = my_center + my_center * self.alignment; diff --git a/crates/gpui/src/elements/canvas.rs b/crates/gpui/src/elements/canvas.rs index bbd8d0393cd038b3e1bde28aa4811280c0dcd768..2d33ba45e54f6a586ab91a41535c919f3d903355 100644 --- a/crates/gpui/src/elements/canvas.rs +++ b/crates/gpui/src/elements/canvas.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use super::Element; use crate::{ json::{self, json}, - SceneBuilder, View, ViewContext, + PaintContext, SceneBuilder, View, ViewContext, }; use json::ToJson; use pathfinder_geometry::{ @@ -56,7 +56,7 @@ where visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { self.0(scene, bounds, visible_bounds, view, cx) } diff --git a/crates/gpui/src/elements/clipped.rs b/crates/gpui/src/elements/clipped.rs index a87dc3e7735c181a8716a61acf8fc7720c62532c..4e8cd4bc15e92ad870f38104bf51c4a6fe11ab31 100644 --- a/crates/gpui/src/elements/clipped.rs +++ b/crates/gpui/src/elements/clipped.rs @@ -4,7 +4,8 @@ use pathfinder_geometry::{rect::RectF, vector::Vector2F}; use serde_json::json; use crate::{ - json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, }; pub struct Clipped { @@ -37,7 +38,7 @@ impl Element for Clipped { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { scene.paint_layer(Some(bounds), |scene| { self.child diff --git a/crates/gpui/src/elements/constrained_box.rs b/crates/gpui/src/elements/constrained_box.rs index 46916c74f1054d442e190a809d69a5fa945ad631..0d540a47b44025f4a2628df40a044325c4bdaf84 100644 --- a/crates/gpui/src/elements/constrained_box.rs +++ b/crates/gpui/src/elements/constrained_box.rs @@ -5,7 +5,8 @@ use serde_json::json; use crate::{ geometry::{rect::RectF, vector::Vector2F}, - json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, }; pub struct ConstrainedBox { @@ -156,7 +157,7 @@ impl Element for ConstrainedBox { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { scene.paint_layer(Some(visible_bounds), |scene| { self.child diff --git a/crates/gpui/src/elements/container.rs b/crates/gpui/src/elements/container.rs index 3b95feb9ef86b7c49f6535fff38bfcc4183888cb..656847980c9095c2dcfe0914e1d3b34c3a1556be 100644 --- a/crates/gpui/src/elements/container.rs +++ b/crates/gpui/src/elements/container.rs @@ -10,7 +10,8 @@ use crate::{ json::ToJson, platform::CursorStyle, scene::{self, Border, CursorRegion, Quad}, - AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, }; use schemars::JsonSchema; use serde::Deserialize; @@ -214,7 +215,7 @@ impl Element for Container { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let quad_bounds = RectF::from_points( bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top), diff --git a/crates/gpui/src/elements/empty.rs b/crates/gpui/src/elements/empty.rs index 42a3824bfcb7f610b715424af6297363a469751b..70580684538b710b96df043ca9edd9d1a78be5ec 100644 --- a/crates/gpui/src/elements/empty.rs +++ b/crates/gpui/src/elements/empty.rs @@ -6,7 +6,7 @@ use crate::{ vector::{vec2f, Vector2F}, }, json::{json, ToJson}, - LayoutContext, SceneBuilder, View, ViewContext, + LayoutContext, PaintContext, SceneBuilder, View, ViewContext, }; use crate::{Element, SizeConstraint}; @@ -57,7 +57,7 @@ impl Element for Empty { _: RectF, _: &mut Self::LayoutState, _: &mut V, - _: &mut ViewContext, + _: &mut PaintContext, ) -> Self::PaintState { } diff --git a/crates/gpui/src/elements/expanded.rs b/crates/gpui/src/elements/expanded.rs index 1fb935b2b8fa272413698fb14d7cfa8ab9cdf51b..1f4f2f40a1e031a4cc3436975cad3a520f4dadae 100644 --- a/crates/gpui/src/elements/expanded.rs +++ b/crates/gpui/src/elements/expanded.rs @@ -2,7 +2,8 @@ use std::ops::Range; use crate::{ geometry::{rect::RectF, vector::Vector2F}, - json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, }; use serde_json::json; @@ -61,7 +62,7 @@ impl Element for Expanded { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { self.child .paint(scene, bounds.origin(), visible_bounds, view, cx); diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index 857f3f56fc08b0b24f39011d7f4323838b97dde2..3000b9575d5824ea42fd09cb5d10de4353434b25 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -2,8 +2,8 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc}; use crate::{ json::{self, ToJson, Value}, - AnyElement, Axis, Element, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, - Vector2FExt, View, ViewContext, + AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, + SizeConstraint, Vector2FExt, View, ViewContext, }; use pathfinder_geometry::{ rect::RectF, @@ -258,7 +258,7 @@ impl Element for Flex { visible_bounds: RectF, remaining_space: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); @@ -449,7 +449,7 @@ impl Element for FlexItem { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { self.child .paint(scene, bounds.origin(), visible_bounds, view, cx) diff --git a/crates/gpui/src/elements/hook.rs b/crates/gpui/src/elements/hook.rs index 310b3c25ebeda1778daf3fd060bf24af6be10822..7ac6c95f3e5cf4c42b2728e1643c1cce38efd513 100644 --- a/crates/gpui/src/elements/hook.rs +++ b/crates/gpui/src/elements/hook.rs @@ -3,7 +3,8 @@ use std::ops::Range; use crate::{ geometry::{rect::RectF, vector::Vector2F}, json::json, - AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, }; pub struct Hook { @@ -52,7 +53,7 @@ impl Element for Hook { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) { self.child .paint(scene, bounds.origin(), visible_bounds, view, cx); diff --git a/crates/gpui/src/elements/image.rs b/crates/gpui/src/elements/image.rs index df200eae7ff256424ffc9b402cc87fb6ba72b3a4..e87c06591756ccdbc463dbc03af88dd1adc3aa5f 100644 --- a/crates/gpui/src/elements/image.rs +++ b/crates/gpui/src/elements/image.rs @@ -5,8 +5,8 @@ use crate::{ vector::{vec2f, Vector2F}, }, json::{json, ToJson}, - scene, Border, Element, ImageData, LayoutContext, SceneBuilder, SizeConstraint, View, - ViewContext, + scene, Border, Element, ImageData, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, + View, ViewContext, }; use schemars::JsonSchema; use serde::Deserialize; @@ -97,7 +97,7 @@ impl Element for Image { _: RectF, layout: &mut Self::LayoutState, _: &mut V, - _: &mut ViewContext, + _: &mut PaintContext, ) -> Self::PaintState { if let Some(data) = layout { scene.push_image(scene::Image { diff --git a/crates/gpui/src/elements/keystroke_label.rs b/crates/gpui/src/elements/keystroke_label.rs index c011649b2e946f980cd5b3eb2e7ab448bde8e2e1..268f3ccb1c856b3f0ee5f14360a1eb625550fc69 100644 --- a/crates/gpui/src/elements/keystroke_label.rs +++ b/crates/gpui/src/elements/keystroke_label.rs @@ -66,7 +66,7 @@ impl Element for KeystrokeLabel { visible_bounds: RectF, element: &mut AnyElement, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) { element.paint(scene, bounds.origin(), visible_bounds, view, cx); } diff --git a/crates/gpui/src/elements/label.rs b/crates/gpui/src/elements/label.rs index d9cf537333c4e60b81f1cd218f678eae4f0a19ad..80555c7442cdbefb8bb9e83d30f2cfb69da8efe3 100644 --- a/crates/gpui/src/elements/label.rs +++ b/crates/gpui/src/elements/label.rs @@ -8,7 +8,7 @@ use crate::{ }, json::{ToJson, Value}, text_layout::{Line, RunStyle}, - Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext, }; use schemars::JsonSchema; use serde::Deserialize; @@ -163,7 +163,7 @@ impl Element for Label { visible_bounds: RectF, line: &mut Self::LayoutState, _: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); line.paint( diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 4c6298d8f55a28abeb6cec468af63a94eec21759..54545199056d2f7e61f6ce352a8639fc91add6ee 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -4,8 +4,8 @@ use crate::{ vector::{vec2f, Vector2F}, }, json::json, - AnyElement, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View, - ViewContext, + AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, SizeConstraint, + View, ViewContext, }; use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc}; use sum_tree::{Bias, SumTree}; @@ -255,7 +255,7 @@ impl Element for List { visible_bounds: RectF, scroll_top: &mut ListOffset, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) { let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); scene.push_layer(Some(visible_bounds)); @@ -647,7 +647,7 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height { #[cfg(test)] mod tests { use super::*; - use crate::{elements::Empty, geometry::vector::vec2f, Entity}; + use crate::{elements::Empty, geometry::vector::vec2f, Entity, PaintContext}; use rand::prelude::*; use std::env; @@ -988,7 +988,7 @@ mod tests { _: RectF, _: &mut (), _: &mut V, - _: &mut ViewContext, + _: &mut PaintContext, ) { unimplemented!() } diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 1b8142d96464ff7c9f30230510a13c58c6f08cb6..6005277f73567381fd8e3c019b9a57789781bae1 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -10,8 +10,8 @@ use crate::{ CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, }, - AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, SceneBuilder, - SizeConstraint, View, ViewContext, + AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext, + SceneBuilder, SizeConstraint, View, ViewContext, }; use serde_json::json; use std::{marker::PhantomData, ops::Range}; @@ -256,7 +256,7 @@ impl Element for MouseEventHandler { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { if self.above { self.child diff --git a/crates/gpui/src/elements/overlay.rs b/crates/gpui/src/elements/overlay.rs index 0f7e4a35c673ec78b5001420957670f5943ffe0f..56e3d10de3cf21fa3d217976c38a9de1613e2f1b 100644 --- a/crates/gpui/src/elements/overlay.rs +++ b/crates/gpui/src/elements/overlay.rs @@ -3,8 +3,8 @@ use std::ops::Range; use crate::{ geometry::{rect::RectF, vector::Vector2F}, json::ToJson, - AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View, - ViewContext, + AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, + SizeConstraint, View, ViewContext, }; use serde_json::json; @@ -143,7 +143,7 @@ impl Element for Overlay { _: RectF, size: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) { let (anchor_position, mut bounds) = match self.position_mode { OverlayPositionMode::Window => { diff --git a/crates/gpui/src/elements/resizable.rs b/crates/gpui/src/elements/resizable.rs index da4b3473b3069ea343d7acdd0cc85c262e7a76cf..2e252cfaabbbaffa323a5cdb26660b5f7bad8f48 100644 --- a/crates/gpui/src/elements/resizable.rs +++ b/crates/gpui/src/elements/resizable.rs @@ -7,8 +7,8 @@ use crate::{ geometry::rect::RectF, platform::{CursorStyle, MouseButton}, scene::MouseDrag, - AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View, - ViewContext, + AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, + SizeConstraint, View, ViewContext, }; #[derive(Copy, Clone, Debug)] @@ -125,7 +125,7 @@ impl Element for Resizable { visible_bounds: pathfinder_geometry::rect::RectF, constraint: &mut SizeConstraint, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { scene.push_stacking_context(None, None); diff --git a/crates/gpui/src/elements/stack.rs b/crates/gpui/src/elements/stack.rs index 196c04d2034a0efa796d9d0ac47dcda6c6f6ab51..8f9d1e4d0536536af0788c1551419c199f63d293 100644 --- a/crates/gpui/src/elements/stack.rs +++ b/crates/gpui/src/elements/stack.rs @@ -3,7 +3,8 @@ use std::ops::Range; use crate::{ geometry::{rect::RectF, vector::Vector2F}, json::{self, json, ToJson}, - AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, + AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, + ViewContext, }; /// Element which renders it's children in a stack on top of each other. @@ -57,7 +58,7 @@ impl Element for Stack { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { for child in &mut self.children { scene.paint_layer(None, |scene| { diff --git a/crates/gpui/src/elements/svg.rs b/crates/gpui/src/elements/svg.rs index 9792f16cbe19b65c7ff8d7d620f71d87d3c51f57..c4d58cd7a77df7e7d97cd8bdc16fad337d82e484 100644 --- a/crates/gpui/src/elements/svg.rs +++ b/crates/gpui/src/elements/svg.rs @@ -1,5 +1,6 @@ use super::constrain_size_preserving_aspect_ratio; use crate::json::ToJson; +use crate::PaintContext; use crate::{ color::Color, geometry::{ @@ -73,7 +74,7 @@ impl Element for Svg { _visible_bounds: RectF, svg: &mut Self::LayoutState, _: &mut V, - _: &mut ViewContext, + _: &mut PaintContext, ) { if let Some(svg) = svg.clone() { scene.push_icon(scene::Icon { diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index 66654fbe93f00065c2c9963743a1fdce9453ff41..9357c31f193f3c6bbaf0a0ca41fdda01535cf3e7 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -7,8 +7,8 @@ use crate::{ }, json::{ToJson, Value}, text_layout::{Line, RunStyle, ShapedBoundary}, - AppContext, Element, FontCache, LayoutContext, SceneBuilder, SizeConstraint, TextLayoutCache, - View, ViewContext, + AppContext, Element, FontCache, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, + TextLayoutCache, View, ViewContext, }; use log::warn; use serde_json::json; @@ -171,7 +171,7 @@ impl Element for Text { visible_bounds: RectF, layout: &mut Self::LayoutState, _: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let mut origin = bounds.origin(); let empty = Vec::new(); diff --git a/crates/gpui/src/elements/tooltip.rs b/crates/gpui/src/elements/tooltip.rs index f21b1c363c0d55403654b92394d24649079cbb34..0510baa9e45e2da3764240d6406950f423e5ce0d 100644 --- a/crates/gpui/src/elements/tooltip.rs +++ b/crates/gpui/src/elements/tooltip.rs @@ -6,8 +6,8 @@ use crate::{ fonts::TextStyle, geometry::{rect::RectF, vector::Vector2F}, json::json, - Action, Axis, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, Task, View, - ViewContext, + Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, + Task, View, ViewContext, }; use schemars::JsonSchema; use serde::Deserialize; @@ -194,7 +194,7 @@ impl Element for Tooltip { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) { self.child .paint(scene, bounds.origin(), visible_bounds, view, cx); diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index 8344914da0d8f0124fd6ddc5cc1cbc29ab2e4d1d..b9bfadb17f17e0a938431b89f4e45b182331d34c 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -6,7 +6,7 @@ use crate::{ }, json::{self, json}, platform::ScrollWheelEvent, - AnyElement, LayoutContext, MouseRegion, SceneBuilder, View, ViewContext, + AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, View, ViewContext, }; use json::ToJson; use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; @@ -278,7 +278,7 @@ impl Element for UniformList { visible_bounds: RectF, layout: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); diff --git a/crates/gpui/src/fonts.rs b/crates/gpui/src/fonts.rs index 3b4a94dd0e69137ab95457d3991f88e245758243..b003042866efdf21bcdb54695029765871afa31d 100644 --- a/crates/gpui/src/fonts.rs +++ b/crates/gpui/src/fonts.rs @@ -71,6 +71,32 @@ pub struct TextStyle { pub underline: Underline, } +impl TextStyle { + pub fn refine(self, refinement: TextStyleRefinement) -> TextStyle { + TextStyle { + color: refinement.color.unwrap_or(self.color), + font_family_name: refinement + .font_family_name + .unwrap_or_else(|| self.font_family_name.clone()), + font_family_id: refinement.font_family_id.unwrap_or(self.font_family_id), + font_id: refinement.font_id.unwrap_or(self.font_id), + font_size: refinement.font_size.unwrap_or(self.font_size), + font_properties: refinement.font_properties.unwrap_or(self.font_properties), + underline: refinement.underline.unwrap_or(self.underline), + } + } +} + +pub struct TextStyleRefinement { + pub color: Option, + pub font_family_name: Option>, + pub font_family_id: Option, + pub font_id: Option, + pub font_size: Option, + pub font_properties: Option, + pub underline: Option, +} + #[derive(JsonSchema)] #[serde(remote = "Properties")] pub struct PropertiesDef { diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index e29beb3ad52592bee273a34c24aa25b60c172754..9c402d139a0d35f906f175da27334db4a60d28c1 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -10,8 +10,9 @@ use gpui::{ platform::{CursorStyle, MouseButton}, serde_json::json, text_layout::{Line, RunStyle}, - AnyElement, Element, EventContext, FontCache, LayoutContext, ModelContext, MouseRegion, Quad, - SceneBuilder, SizeConstraint, TextLayoutCache, ViewContext, WeakModelHandle, + AnyElement, Element, EventContext, FontCache, LayoutContext, ModelContext, MouseRegion, + PaintContext, Quad, SceneBuilder, SizeConstraint, TextLayoutCache, ViewContext, + WeakModelHandle, }; use itertools::Itertools; use language::CursorShape; @@ -730,7 +731,7 @@ impl Element for TerminalElement { visible_bounds: RectF, layout: &mut Self::LayoutState, view: &mut TerminalView, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 2972c307f2624a6bb23bef0f1919aab5a43eb66d..928c9b23f82ba9198dc1d7237cefe5e7a43e1895 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -25,8 +25,8 @@ use gpui::{ keymap_matcher::KeymapContext, platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel}, Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext, - LayoutContext, ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, - WeakViewHandle, WindowContext, + LayoutContext, ModelHandle, MouseRegion, PaintContext, Quad, Task, View, ViewContext, + ViewHandle, WeakViewHandle, WindowContext, }; use project::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; @@ -1896,7 +1896,7 @@ impl Element for PaneBackdrop { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let background = theme::current(cx).editor.background; diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index e60f6deb2ff2663918c7fa3dd8c2722a69dbb2e9..4a90d92b35065c1f51841f90fa02e3881363a8ad 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -593,8 +593,8 @@ mod element { }, json::{self, ToJson}, platform::{CursorStyle, MouseButton}, - AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, RectFExt, - SceneBuilder, SizeConstraint, Vector2FExt, ViewContext, + AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, PaintContext, + RectFExt, SceneBuilder, SizeConstraint, Vector2FExt, ViewContext, }; use crate::{ @@ -765,7 +765,7 @@ mod element { visible_bounds: RectF, remaining_space: &mut Self::LayoutState, view: &mut Workspace, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let can_resize = settings::get::(cx).active_pane_magnification == 1.; let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index 6fc1467566bec3a3c93ff6807a15bf1fec988e67..3def545d710d9e7cbfbb13ef419a33ed02f9c651 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -8,8 +8,8 @@ use gpui::{ vector::{vec2f, Vector2F}, }, json::{json, ToJson}, - AnyElement, AnyViewHandle, Entity, LayoutContext, SceneBuilder, SizeConstraint, Subscription, - View, ViewContext, ViewHandle, WindowContext, + AnyElement, AnyViewHandle, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, + Subscription, View, ViewContext, ViewHandle, WindowContext, }; pub trait StatusItemView: View { @@ -177,7 +177,7 @@ impl Element for StatusBarElement { visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut StatusBar, - cx: &mut ViewContext, + cx: &mut PaintContext, ) -> Self::PaintState { let origin_y = bounds.upper_right().y(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); From b695c42e1152ad9019b2c6bfcaf21834fe00b386 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 1 Aug 2023 22:28:04 -0600 Subject: [PATCH 04/53] WIP: Return WindowHandle from AppContext::add_window --- crates/gpui/src/app.rs | 352 ++++++++++++++++-------- crates/gpui/src/app/ref_counts.rs | 23 ++ crates/gpui/src/app/test_app_context.rs | 11 +- 3 files changed, 268 insertions(+), 118 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index da601ba3510e3624b7acd0954f922082b2197755..b2d732d1700d3da202afc965b484463efda5958e 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -130,8 +130,12 @@ pub trait BorrowAppContext { } pub trait BorrowWindowContext { - fn read_with T>(&self, window_id: usize, f: F) -> T; - fn update T>(&mut self, window_id: usize, f: F) -> T; + fn read_with(&self, window_id: usize, f: F) -> T + where + F: FnOnce(&WindowContext) -> T; + fn update(&mut self, window_id: usize, f: F) -> T + where + F: FnOnce(&mut WindowContext) -> T; } #[derive(Clone)] @@ -402,7 +406,7 @@ impl AsyncAppContext { &mut self, window_options: WindowOptions, build_root_view: F, - ) -> (usize, ViewHandle) + ) -> WindowHandle where T: View, F: FnOnce(&mut ViewContext) -> T, @@ -1300,7 +1304,7 @@ impl AppContext { &mut self, window_options: WindowOptions, build_root_view: F, - ) -> (usize, ViewHandle) + ) -> WindowHandle where V: View, F: FnOnce(&mut ViewContext) -> V, @@ -1311,9 +1315,8 @@ impl AppContext { this.platform .open_window(window_id, window_options, this.foreground.clone()); let window = this.build_window(window_id, platform_window, build_root_view); - let root_view = window.root_view().clone().downcast::().unwrap(); this.windows.insert(window_id, window); - (window_id, root_view) + WindowHandle::new(window_id, this.ref_counts.clone()) }) } @@ -3802,6 +3805,131 @@ impl Clone for WeakModelHandle { impl Copy for WeakModelHandle {} +pub struct WindowHandle { + any_handle: AnyWindowHandle, + view_type: PhantomData, +} + +impl WindowHandle { + fn id(&self) -> usize { + self.any_handle.id() + } + + fn new(window_id: usize, ref_counts: Arc>) -> Self { + WindowHandle { + any_handle: AnyWindowHandle::new::(window_id, ref_counts), + view_type: PhantomData, + } + } + + fn root(&self, cx: &impl BorrowAppContext) -> ViewHandle { + self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap()) + } + + pub fn read_with(&self, cx: &C, read: F) -> R + where + C: BorrowAppContext, + F: FnOnce(&WindowContext) -> R, + { + cx.read_with(|cx| cx.read_window(self.id(), read).unwrap()) + } + + pub fn update(&self, cx: &mut C, update: F) -> R + where + C: BorrowAppContext, + F: FnOnce(&mut WindowContext) -> R, + { + cx.update(|cx| cx.update_window(self.id(), update).unwrap()) + } + + pub fn update_root(&self, cx: &mut C, update: F) -> R + where + C: BorrowAppContext, + F: FnOnce(&mut V, &mut ViewContext) -> R, + { + let window_id = self.id(); + cx.update(|cx| { + cx.update_window(window_id, |cx| { + cx.root_view() + .clone() + .downcast::() + .unwrap() + .update(cx, update) + }) + .unwrap() + }) + } + + pub fn read_root<'a>(&self, cx: &'a AppContext) -> &'a V { + let root_view = cx + .read_window(self.id(), |cx| cx.root_view().clone().downcast().unwrap()) + .unwrap(); + root_view.read(cx) + } + + pub fn read_root_with(&self, cx: &C, read: F) -> R + where + C: BorrowAppContext, + F: FnOnce(&V, &ViewContext) -> R, + { + self.read_with(cx, |cx| { + cx.root_view() + .downcast_ref::() + .unwrap() + .read_with(cx, read) + }) + } + + pub fn add_view(&self, cx: &mut C, build_view: F) -> ViewHandle + where + C: BorrowAppContext, + U: View, + F: FnOnce(&mut ViewContext) -> U, + { + self.update(cx, |cx| cx.add_view(build_view)) + } +} + +pub struct AnyWindowHandle { + window_id: usize, + root_view_type: TypeId, + ref_counts: Arc>, + + #[cfg(any(test, feature = "test-support"))] + handle_id: usize, +} + +impl AnyWindowHandle { + fn new(window_id: usize, ref_counts: Arc>) -> Self { + ref_counts.lock().inc_window(window_id); + + #[cfg(any(test, feature = "test-support"))] + let handle_id = ref_counts + .lock() + .leak_detector + .lock() + .handle_created(None, window_id); + + Self { + window_id, + root_view_type: TypeId::of::(), + ref_counts, + #[cfg(any(test, feature = "test-support"))] + handle_id, + } + } + + pub fn id(&self) -> usize { + self.window_id + } +} + +impl Drop for AnyWindowHandle { + fn drop(&mut self) { + self.ref_counts.lock().dec_window(self.window_id) + } +} + #[repr(transparent)] pub struct ViewHandle { any_handle: AnyViewHandle, @@ -4684,11 +4812,11 @@ mod tests { } } - let (_, view) = cx.add_window(|_| View { render_count: 0 }); + let window = cx.add_window(|_| View { render_count: 0 }); let called_defer = Rc::new(AtomicBool::new(false)); let called_after_window_update = Rc::new(AtomicBool::new(false)); - view.update(cx, |this, cx| { + window.update_root(cx, |this, cx| { assert_eq!(this.render_count, 1); cx.defer({ let called_defer = called_defer.clone(); @@ -4712,7 +4840,7 @@ mod tests { assert!(called_defer.load(SeqCst)); assert!(called_after_window_update.load(SeqCst)); - assert_eq!(view.read_with(cx, |view, _| view.render_count), 3); + assert_eq!(window.read_root_with(cx, |view, _| view.render_count), 3); } #[crate::test(self)] @@ -4751,9 +4879,9 @@ mod tests { } } - let (window_id, _root_view) = cx.add_window(|cx| View::new(None, cx)); - let handle_1 = cx.add_view(window_id, |cx| View::new(None, cx)); - let handle_2 = cx.add_view(window_id, |cx| View::new(Some(handle_1.clone()), cx)); + let window = cx.add_window(|cx| View::new(None, cx)); + let handle_1 = window.add_view(cx, |cx| View::new(None, cx)); + let handle_2 = window.add_view(cx, |cx| View::new(Some(handle_1.clone()), cx)); assert_eq!(cx.read(|cx| cx.views.len()), 3); handle_1.update(cx, |view, cx| { @@ -4813,11 +4941,11 @@ mod tests { } let mouse_down_count = Arc::new(AtomicUsize::new(0)); - let (window_id, _) = cx.add_window(Default::default(), |_| View { + let window = cx.add_window(Default::default(), |_| View { mouse_down_count: mouse_down_count.clone(), }); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { // Ensure window's root element is in a valid lifecycle state. cx.dispatch_event( Event::MouseDown(MouseButtonEvent { @@ -4876,9 +5004,11 @@ mod tests { let model = cx.add_model(|_| Model { released: model_released.clone(), }); - let (window_id, view) = cx.add_window(Default::default(), |_| View { + let window = cx.add_window(Default::default(), |_| View { released: view_released.clone(), }); + let view = window.root(cx); + assert!(!model_released.get()); assert!(!view_released.get()); @@ -4900,7 +5030,7 @@ mod tests { assert!(model_release_observed.get()); drop(view); - cx.update_window(window_id, |cx| cx.remove_window()); + window.update(cx, |cx| cx.remove_window()); assert!(view_released.get()); assert!(view_release_observed.get()); } @@ -4913,8 +5043,9 @@ mod tests { type Event = String; } - let (window_id, handle_1) = cx.add_window(|_| TestView::default()); - let handle_2 = cx.add_view(window_id, |_| TestView::default()); + let window = cx.add_window(|_| TestView::default()); + let handle_1 = window.root(cx); + let handle_2 = window.add_view(cx, |_| TestView::default()); let handle_3 = cx.add_model(|_| Model); handle_1.update(cx, |_, cx| { @@ -5140,9 +5271,9 @@ mod tests { type Event = (); } - let (window_id, _root_view) = cx.add_window(|_| TestView::default()); - let observing_view = cx.add_view(window_id, |_| TestView::default()); - let emitting_view = cx.add_view(window_id, |_| TestView::default()); + let window = cx.add_window(|_| TestView::default()); + let observing_view = window.add_view(cx, |_| TestView::default()); + let emitting_view = window.add_view(cx, |_| TestView::default()); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); @@ -5165,7 +5296,7 @@ mod tests { #[crate::test(self)] fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut AppContext) { - let (_, view) = cx.add_window::(Default::default(), |cx| { + let window = cx.add_window::(Default::default(), |cx| { drop(cx.subscribe(&cx.handle(), { move |this, _, _, _| this.events.push("dropped before flush".into()) })); @@ -5181,7 +5312,7 @@ mod tests { TestView { events: Vec::new() } }); - assert_eq!(view.read(cx).events, ["before emit"]); + assert_eq!(window.read_root(cx).events, ["before emit"]); } #[crate::test(self)] @@ -5195,7 +5326,8 @@ mod tests { type Event = (); } - let (_, view) = cx.add_window(|_| TestView::default()); + let window = cx.add_window(|_| TestView::default()); + let view = window.root(cx); let model = cx.add_model(|_| Model { state: "old-state".into(), }); @@ -5216,7 +5348,7 @@ mod tests { #[crate::test(self)] fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut AppContext) { - let (_, view) = cx.add_window::(Default::default(), |cx| { + let window = cx.add_window::(Default::default(), |cx| { drop(cx.observe(&cx.handle(), { move |this, _, _| this.events.push("dropped before flush".into()) })); @@ -5232,7 +5364,7 @@ mod tests { TestView { events: Vec::new() } }); - assert_eq!(view.read(cx).events, ["before notify"]); + assert_eq!(window.read_root(cx).events, ["before notify"]); } #[crate::test(self)] @@ -5243,7 +5375,8 @@ mod tests { } let model = cx.add_model(|_| Model); - let (_, view) = cx.add_window(|_| TestView::default()); + let window = cx.add_window(|_| TestView::default()); + let view = window.root(cx); view.update(cx, |_, cx| { model.update(cx, |_, cx| cx.notify()); @@ -5267,8 +5400,8 @@ mod tests { type Event = (); } - let (window_id, _root_view) = cx.add_window(|_| TestView::default()); - let observing_view = cx.add_view(window_id, |_| TestView::default()); + let window = cx.add_window(|_| TestView::default()); + let observing_view = window.add_view(cx, |_| TestView::default()); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); @@ -5390,9 +5523,9 @@ mod tests { } } - let (window_id, _root_view) = cx.add_window(|_| View); - let observing_view = cx.add_view(window_id, |_| View); - let observed_view = cx.add_view(window_id, |_| View); + let window = cx.add_window(|_| View); + let observing_view = window.add_view(cx, |_| View); + let observed_view = window.add_view(cx, |_| View); let observation_count = Rc::new(RefCell::new(0)); observing_view.update(cx, |_, cx| { @@ -5474,13 +5607,14 @@ mod tests { } let view_events: Arc>> = Default::default(); - let (window_id, view_1) = cx.add_window(|_| View { + let window = cx.add_window(|_| View { events: view_events.clone(), name: "view 1".to_string(), child: None, }); - let view_2 = cx - .update_window(window_id, |cx| { + let view_1 = window.root(cx); + let view_2 = window + .update(cx, |cx| { let view_2 = cx.add_view(|_| View { events: view_events.clone(), name: "view 2".to_string(), @@ -5731,40 +5865,34 @@ mod tests { }) .detach(); - let (window_id, view_1) = - cx.add_window(Default::default(), |_| ViewA { id: 1, child: None }); - let view_2 = cx - .update_window(window_id, |cx| { - let child = cx.add_view(|_| ViewB { id: 2, child: None }); - view_1.update(cx, |view, cx| { - view.child = Some(child.clone().into_any()); - cx.notify(); - }); - child - }) - .unwrap(); - let view_3 = cx - .update_window(window_id, |cx| { - let child = cx.add_view(|_| ViewA { id: 3, child: None }); - view_2.update(cx, |view, cx| { - view.child = Some(child.clone().into_any()); - cx.notify(); - }); - child - }) - .unwrap(); - let view_4 = cx - .update_window(window_id, |cx| { - let child = cx.add_view(|_| ViewB { id: 4, child: None }); - view_3.update(cx, |view, cx| { - view.child = Some(child.clone().into_any()); - cx.notify(); - }); - child - }) - .unwrap(); + let window = cx.add_window(Default::default(), |_| ViewA { id: 1, child: None }); + let view_1 = window.root(cx); + let view_2 = window.update(cx, |cx| { + let child = cx.add_view(|_| ViewB { id: 2, child: None }); + view_1.update(cx, |view, cx| { + view.child = Some(child.clone().into_any()); + cx.notify(); + }); + child + }); + let view_3 = window.update(cx, |cx| { + let child = cx.add_view(|_| ViewA { id: 3, child: None }); + view_2.update(cx, |view, cx| { + view.child = Some(child.clone().into_any()); + cx.notify(); + }); + child + }); + let view_4 = window.update(cx, |cx| { + let child = cx.add_view(|_| ViewB { id: 4, child: None }); + view_3.update(cx, |view, cx| { + view.child = Some(child.clone().into_any()); + cx.notify(); + }); + child + }); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string())) }); @@ -5786,31 +5914,27 @@ mod tests { // Remove view_1, which doesn't propagate the action - let (window_id, view_2) = - cx.add_window(Default::default(), |_| ViewB { id: 2, child: None }); - let view_3 = cx - .update_window(window_id, |cx| { - let child = cx.add_view(|_| ViewA { id: 3, child: None }); - view_2.update(cx, |view, cx| { - view.child = Some(child.clone().into_any()); - cx.notify(); - }); - child - }) - .unwrap(); - let view_4 = cx - .update_window(window_id, |cx| { - let child = cx.add_view(|_| ViewB { id: 4, child: None }); - view_3.update(cx, |view, cx| { - view.child = Some(child.clone().into_any()); - cx.notify(); - }); - child - }) - .unwrap(); + let window = cx.add_window(Default::default(), |_| ViewB { id: 2, child: None }); + let view_2 = window.root(cx); + let view_3 = window.update(cx, |cx| { + let child = cx.add_view(|_| ViewA { id: 3, child: None }); + view_2.update(cx, |view, cx| { + view.child = Some(child.clone().into_any()); + cx.notify(); + }); + child + }); + let view_4 = window.update(cx, |cx| { + let child = cx.add_view(|_| ViewB { id: 4, child: None }); + view_3.update(cx, |view, cx| { + view.child = Some(child.clone().into_any()); + cx.notify(); + }); + child + }); actions.borrow_mut().clear(); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string())) }); @@ -5887,7 +6011,7 @@ mod tests { view_3.keymap_context.add_identifier("b"); view_3.keymap_context.add_identifier("c"); - let (window_id, _view_1) = cx.add_window(Default::default(), |cx| { + let window = cx.add_window(Default::default(), |cx| { let view_2 = cx.add_view(|cx| { let view_3 = cx.add_view(|cx| { cx.focus_self(); @@ -6006,13 +6130,14 @@ mod tests { } } - let (window_id, view_1) = cx.add_window(|cx| { + let window = cx.add_window(|cx| { let view_2 = cx.add_view(|cx| { cx.focus_self(); View2 {} }); View1 { child: view_2 } }); + let view_1 = window.root(cx); let view_2 = view_1.read_with(cx, |view, _| view.child.clone()); cx.update(|cx| { @@ -6138,7 +6263,8 @@ mod tests { impl_actions!(test, [ActionWithArg]); - let (window_id, view) = cx.add_window(|_| View); + let window = cx.add_window(|_| View); + let view = window.root(cx); cx.update(|cx| { cx.add_global_action(|_: &ActionWithArg, _| {}); cx.add_bindings(vec![ @@ -6250,7 +6376,8 @@ mod tests { } } - let (_, view) = cx.add_window(|_| Counter(0)); + let window = cx.add_window(|_| Counter(0)); + let view = window.root(cx); let condition1 = view.condition(cx, |view, _| view.0 == 2); let condition2 = view.condition(cx, |view, _| view.0 == 3); @@ -6272,15 +6399,15 @@ mod tests { #[crate::test(self)] #[should_panic] async fn test_view_condition_timeout(cx: &mut TestAppContext) { - let (_, view) = cx.add_window(|_| TestView::default()); - view.condition(cx, |_, _| false).await; + let window = cx.add_window(|_| TestView::default()); + window.root(cx).condition(cx, |_, _| false).await; } #[crate::test(self)] #[should_panic(expected = "view dropped with pending condition")] async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) { - let (window_id, _root_view) = cx.add_window(|_| TestView::default()); - let view = cx.add_view(window_id, |_| TestView::default()); + let window = cx.add_window(|_| TestView::default()); + let view = window.add_view(cx, |_| TestView::default()); let condition = view.condition(cx, |_, _| false); cx.update(|_| drop(view)); @@ -6305,22 +6432,21 @@ mod tests { } } - let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0)); - cx.update_window(window_id, |cx| { + let window = cx.add_window(Default::default(), |_| View(0)); + let root_view = window.root(cx); + window.update(cx, |cx| { assert_eq!( cx.window.rendered_views[&root_view.id()].name(), Some("render count: 0") ); }); - let view = cx - .update_window(window_id, |cx| { - cx.refresh_windows(); - cx.add_view(|_| View(0)) - }) - .unwrap(); + let view = window.update(cx, |cx| { + cx.refresh_windows(); + cx.add_view(|_| View(0)) + }); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { assert_eq!( cx.window.rendered_views[&root_view.id()].name(), Some("render count: 1") @@ -6333,7 +6459,7 @@ mod tests { cx.update(|cx| cx.refresh_windows()); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { assert_eq!( cx.window.rendered_views[&root_view.id()].name(), Some("render count: 2") @@ -6349,7 +6475,7 @@ mod tests { drop(view); }); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { assert_eq!( cx.window.rendered_views[&root_view.id()].name(), Some("render count: 3") @@ -6397,7 +6523,7 @@ mod tests { } let events = Rc::new(RefCell::new(Vec::new())); - let (window_1, _) = cx.add_window(|cx: &mut ViewContext| { + let window_1 = cx.add_window(|cx: &mut ViewContext| { cx.observe_window_activation({ let events = events.clone(); move |this, active, _| events.borrow_mut().push((this.0, active)) @@ -6407,7 +6533,7 @@ mod tests { }); assert_eq!(mem::take(&mut *events.borrow_mut()), [("window 1", true)]); - let (window_2, _) = cx.add_window(|cx: &mut ViewContext| { + let window_2 = cx.add_window(|cx: &mut ViewContext| { cx.observe_window_activation({ let events = events.clone(); move |this, active, _| events.borrow_mut().push((this.0, active)) @@ -6420,7 +6546,7 @@ mod tests { [("window 1", false), ("window 2", true)] ); - let (window_3, _) = cx.add_window(|cx: &mut ViewContext| { + let window_3 = cx.add_window(|cx: &mut ViewContext| { cx.observe_window_activation({ let events = events.clone(); move |this, active, _| events.borrow_mut().push((this.0, active)) diff --git a/crates/gpui/src/app/ref_counts.rs b/crates/gpui/src/app/ref_counts.rs index f0c1699f165ea8100ccdfe1facbfb8a3ac1a2d8e..c076a8a476fe2f91484f7f3e407707455be6c442 100644 --- a/crates/gpui/src/app/ref_counts.rs +++ b/crates/gpui/src/app/ref_counts.rs @@ -23,8 +23,10 @@ struct ElementStateRefCount { #[derive(Default)] pub struct RefCounts { + window_counts: HashMap, entity_counts: HashMap, element_state_counts: HashMap, + dropped_windows: HashSet, dropped_models: HashSet, dropped_views: HashSet<(usize, usize)>, dropped_element_states: HashSet, @@ -43,6 +45,18 @@ impl RefCounts { } } + pub fn inc_window(&mut self, window_id: usize) { + match self.window_counts.entry(window_id) { + Entry::Occupied(mut entry) => { + *entry.get_mut() += 1; + } + Entry::Vacant(entry) => { + entry.insert(1); + self.dropped_windows.remove(&window_id); + } + } + } + pub fn inc_model(&mut self, model_id: usize) { match self.entity_counts.entry(model_id) { Entry::Occupied(mut entry) => { @@ -85,6 +99,15 @@ impl RefCounts { } } + pub fn dec_window(&mut self, window_id: usize) { + let count = self.window_counts.get_mut(&window_id).unwrap(); + *count -= 1; + if *count == 0 { + self.entity_counts.remove(&window_id); + self.dropped_windows.insert(window_id); + } + } + pub fn dec_model(&mut self, model_id: usize) { let count = self.entity_counts.get_mut(&model_id).unwrap(); *count -= 1; diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 2fa86998837984d97139f4248493a29f70fa4f75..0fa64f531e1217fe9b9a66e4feee39f20a5f1623 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -6,7 +6,7 @@ use crate::{ platform::{Event, InputHandler, KeyDownEvent, Platform}, Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle, - WindowContext, + WindowContext, WindowHandle, }; use collections::BTreeMap; use futures::Future; @@ -148,17 +148,18 @@ impl TestAppContext { self.cx.borrow_mut().add_model(build_model) } - pub fn add_window(&mut self, build_root_view: F) -> (usize, ViewHandle) + pub fn add_window(&mut self, build_root_view: F) -> WindowHandle where T: View, F: FnOnce(&mut ViewContext) -> T, { - let (window_id, view) = self + let window = self .cx .borrow_mut() .add_window(Default::default(), build_root_view); - self.simulate_window_activation(Some(window_id)); - (window_id, view) + self.simulate_window_activation(Some(window.id())); + + WindowHandle::new(window.id(), self.cx.borrow_mut().ref_counts.clone()) } pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle From 300ce61bd0d6bf77153669eb3be044a870e76676 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Aug 2023 08:25:40 -0600 Subject: [PATCH 05/53] WIP --- crates/gpui/src/app.rs | 76 ++++++++++++++----------- crates/gpui/src/app/ref_counts.rs | 5 +- crates/gpui/src/app/test_app_context.rs | 2 +- crates/gpui/src/app/window.rs | 2 +- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index b2d732d1700d3da202afc965b484463efda5958e..adabcf0a8b9585ee1c3f86621477d12d6ff28c40 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -498,8 +498,8 @@ pub struct AppContext { // Action Types -> Action Handlers global_actions: HashMap>, keystroke_matcher: KeymapMatcher, - next_entity_id: usize, - next_window_id: usize, + next_id: usize, + // next_window_id: usize, next_subscription_id: usize, frame_count: usize, @@ -558,8 +558,7 @@ impl AppContext { actions: Default::default(), global_actions: Default::default(), keystroke_matcher: KeymapMatcher::default(), - next_entity_id: 0, - next_window_id: 0, + next_id: 0, next_subscription_id: 0, frame_count: 0, subscriptions: Default::default(), @@ -1230,7 +1229,7 @@ impl AppContext { F: FnOnce(&mut ModelContext) -> T, { self.update(|this| { - let model_id = post_inc(&mut this.next_entity_id); + let model_id = post_inc(&mut this.next_id); let handle = ModelHandle::new(model_id, &this.ref_counts); let mut cx = ModelContext::new(this, model_id); let model = build_model(&mut cx); @@ -1310,7 +1309,7 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let window_id = post_inc(&mut this.next_window_id); + let window_id = post_inc(&mut this.next_id); let platform_window = this.platform .open_window(window_id, window_options, this.foreground.clone()); @@ -1326,7 +1325,7 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let window_id = post_inc(&mut this.next_window_id); + let window_id = post_inc(&mut this.next_id); let platform_window = this.platform.add_status_item(window_id); let window = this.build_window(window_id, platform_window, build_root_view); let root_view = window.root_view().clone().downcast::().unwrap(); @@ -3810,6 +3809,7 @@ pub struct WindowHandle { view_type: PhantomData, } +#[allow(dead_code)] impl WindowHandle { fn id(&self) -> usize { self.any_handle.id() @@ -3922,6 +3922,17 @@ impl AnyWindowHandle { pub fn id(&self) -> usize { self.window_id } + + pub fn downcast(self) -> Option> { + if TypeId::of::() == self.root_view_type { + Some(WindowHandle { + any_handle: self, + view_type: PhantomData, + }) + } else { + None + } + } } impl Drop for AnyWindowHandle { @@ -5613,20 +5624,18 @@ mod tests { child: None, }); let view_1 = window.root(cx); - let view_2 = window - .update(cx, |cx| { - let view_2 = cx.add_view(|_| View { - events: view_events.clone(), - name: "view 2".to_string(), - child: None, - }); - view_1.update(cx, |view_1, cx| { - view_1.child = Some(view_2.clone().into_any()); - cx.notify(); - }); - view_2 - }) - .unwrap(); + let view_2 = window.update(cx, |cx| { + let view_2 = cx.add_view(|_| View { + events: view_events.clone(), + name: "view 2".to_string(), + child: None, + }); + view_1.update(cx, |view_1, cx| { + view_1.child = Some(view_2.clone().into_any()); + cx.notify(); + }); + view_2 + }); let observed_events: Arc>> = Default::default(); view_1.update(cx, |_, cx| { @@ -6071,26 +6080,26 @@ mod tests { } }); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { cx.dispatch_keystroke(&Keystroke::parse("a").unwrap()) }); assert_eq!(&*actions.borrow(), &["2 a"]); actions.borrow_mut().clear(); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { cx.dispatch_keystroke(&Keystroke::parse("b").unwrap()); }); assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]); actions.borrow_mut().clear(); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { cx.dispatch_keystroke(&Keystroke::parse("c").unwrap()); }); assert_eq!(&*actions.borrow(), &["3 c"]); actions.borrow_mut().clear(); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { cx.dispatch_keystroke(&Keystroke::parse("d").unwrap()); }); assert_eq!(&*actions.borrow(), &["2 d"]); @@ -6201,7 +6210,7 @@ mod tests { // Check that global actions do not have a binding, even if a binding does exist in another view assert_eq!( - &available_actions(window_id, view_1.id(), cx), + &available_actions(window.id(), view_1.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::GlobalAction", vec![]) @@ -6210,7 +6219,7 @@ mod tests { // Check that view 1 actions and bindings are available even when called from view 2 assert_eq!( - &available_actions(window_id, view_2.id(), cx), + &available_actions(window.id(), view_2.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action2", vec![Keystroke::parse("b").unwrap()]), @@ -6273,7 +6282,7 @@ mod tests { ]); }); - let actions = cx.available_actions(window_id, view.id()); + let actions = cx.available_actions(window.id(), view.id()); assert_eq!( actions[0].1.as_any().downcast_ref::(), Some(&ActionWithArg { arg: false }) @@ -6559,25 +6568,25 @@ mod tests { [("window 2", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_2)); + cx.simulate_window_activation(Some(window_2.id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 3", false), ("window 2", true)] ); - cx.simulate_window_activation(Some(window_1)); + cx.simulate_window_activation(Some(window_1.id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 2", false), ("window 1", true)] ); - cx.simulate_window_activation(Some(window_3)); + cx.simulate_window_activation(Some(window_3.id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 1", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_3)); + cx.simulate_window_activation(Some(window_3.id())); assert_eq!(mem::take(&mut *events.borrow_mut()), []); } @@ -6633,12 +6642,13 @@ mod tests { let child_rendered = Rc::new(Cell::new(false)); let child_dropped = Rc::new(Cell::new(false)); - let (_, root_view) = cx.add_window(|cx| Parent { + let window = cx.add_window(|cx| Parent { child: Some(cx.add_view(|_| Child { rendered: child_rendered.clone(), dropped: child_dropped.clone(), })), }); + let root_view = window.root(cx); assert!(child_rendered.take()); assert!(!child_dropped.take()); diff --git a/crates/gpui/src/app/ref_counts.rs b/crates/gpui/src/app/ref_counts.rs index c076a8a476fe2f91484f7f3e407707455be6c442..74563d05bc00d403dd768fe08fd269da9a8bfb5a 100644 --- a/crates/gpui/src/app/ref_counts.rs +++ b/crates/gpui/src/app/ref_counts.rs @@ -23,7 +23,6 @@ struct ElementStateRefCount { #[derive(Default)] pub struct RefCounts { - window_counts: HashMap, entity_counts: HashMap, element_state_counts: HashMap, dropped_windows: HashSet, @@ -46,7 +45,7 @@ impl RefCounts { } pub fn inc_window(&mut self, window_id: usize) { - match self.window_counts.entry(window_id) { + match self.entity_counts.entry(window_id) { Entry::Occupied(mut entry) => { *entry.get_mut() += 1; } @@ -100,7 +99,7 @@ impl RefCounts { } pub fn dec_window(&mut self, window_id: usize) { - let count = self.window_counts.get_mut(&window_id).unwrap(); + let count = self.entity_counts.get_mut(&window_id).unwrap(); *count -= 1; if *count == 0 { self.entity_counts.remove(&window_id); diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 0fa64f531e1217fe9b9a66e4feee39f20a5f1623..80f10374665f9c2215ca4b2582088dd43df3bb04 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -60,7 +60,7 @@ impl TestAppContext { RefCounts::new(leak_detector), (), ); - cx.next_entity_id = first_entity_id; + cx.next_id = first_entity_id; let cx = TestAppContext { cx: Rc::new(RefCell::new(cx)), foreground_platform, diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index e4beb58873cfb3599be6fc81a2ad0bd87ce811c3..9dc5d99bc5e8ac0b0994aae8b66169151d9b0b93 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -1176,7 +1176,7 @@ impl<'a> WindowContext<'a> { F: FnOnce(&mut ViewContext) -> Option, { let window_id = self.window_id; - let view_id = post_inc(&mut self.next_entity_id); + let view_id = post_inc(&mut self.next_id); let mut cx = ViewContext::mutable(self, view_id); let handle = if let Some(view) = build_view(&mut cx) { let mut keymap_context = KeymapContext::default(); From 60e190e5001553e4c3c0cd472e6feb45ea07aca2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Aug 2023 12:08:56 -0600 Subject: [PATCH 06/53] WIP --- crates/copilot/src/sign_in.rs | 13 ++++++------- crates/gpui/src/app.rs | 10 +++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index 803cb5cc85636d45946446b4e059a5e7d9aeae30..fec8f27c9730d776f4311498635772fb8a2791a5 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -4,7 +4,7 @@ use gpui::{ geometry::rect::RectF, platform::{WindowBounds, WindowKind, WindowOptions}, AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext, - ViewHandle, + WindowHandle, }; use theme::ui::modal; @@ -18,14 +18,14 @@ const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot"; pub fn init(cx: &mut AppContext) { if let Some(copilot) = Copilot::global(cx) { - let mut code_verification: Option> = None; + let mut code_verification: Option> = None; cx.observe(&copilot, move |copilot, cx| { let status = copilot.read(cx).status(); match &status { crate::Status::SigningIn { prompt } => { if let Some(code_verification_handle) = code_verification.as_mut() { - let window_id = code_verification_handle.window_id(); + let window_id = code_verification_handle.id(); let updated = cx.update_window(window_id, |cx| { code_verification_handle.update(cx, |code_verification, cx| { code_verification.set_status(status.clone(), cx) @@ -66,7 +66,7 @@ pub fn init(cx: &mut AppContext) { fn create_copilot_auth_window( cx: &mut AppContext, status: &Status, -) -> ViewHandle { +) -> WindowHandle { let window_size = theme::current(cx).copilot.modal.dimensions(); let window_options = WindowOptions { bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)), @@ -78,10 +78,9 @@ fn create_copilot_auth_window( is_movable: true, screen: None, }; - let (_, view) = cx.add_window(window_options, |_cx| { + cx.add_window(window_options, |_cx| { CopilotCodeVerification::new(status.clone()) - }); - view + }) } pub struct CopilotCodeVerification { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index adabcf0a8b9585ee1c3f86621477d12d6ff28c40..9c0e50647c6138fc94ee388220cb65d0112e3dd6 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3811,10 +3811,6 @@ pub struct WindowHandle { #[allow(dead_code)] impl WindowHandle { - fn id(&self) -> usize { - self.any_handle.id() - } - fn new(window_id: usize, ref_counts: Arc>) -> Self { WindowHandle { any_handle: AnyWindowHandle::new::(window_id, ref_counts), @@ -3822,7 +3818,11 @@ impl WindowHandle { } } - fn root(&self, cx: &impl BorrowAppContext) -> ViewHandle { + pub fn id(&self) -> usize { + self.any_handle.id() + } + + pub fn root(&self, cx: &impl BorrowAppContext) -> ViewHandle { self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap()) } From 884cee6dfda9f4b887976cb14f87e82ac6b87fc0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Aug 2023 14:05:03 -0600 Subject: [PATCH 07/53] Get tests compiling returning WindowHandle from add_window --- crates/collab/src/tests.rs | 5 +- crates/collab/src/tests/integration_tests.rs | 52 +- .../src/incoming_call_notification.rs | 4 +- .../src/project_shared_notification.rs | 4 +- crates/command_palette/src/command_palette.rs | 4 +- crates/copilot/src/sign_in.rs | 8 +- crates/diagnostics/src/diagnostics.rs | 8 +- crates/editor/src/editor_tests.rs | 627 ++++++++++-------- crates/editor/src/element.rs | 30 +- crates/editor/src/inlay_hint_cache.rs | 30 +- .../src/test/editor_lsp_test_context.rs | 5 +- crates/editor/src/test/editor_test_context.rs | 12 +- crates/file_finder/src/file_finder.rs | 230 +++---- crates/gpui/src/app.rs | 26 +- crates/gpui/src/app/window.rs | 8 +- crates/language_tools/src/lsp_log_tests.rs | 4 +- crates/project_panel/src/project_panel.rs | 32 +- crates/project_symbols/src/project_symbols.rs | 4 +- crates/search/src/buffer_search.rs | 19 +- crates/search/src/project_search.rs | 16 +- crates/terminal_view/src/terminal_view.rs | 4 +- crates/workspace/src/pane.rs | 30 +- crates/workspace/src/workspace.rs | 108 +-- crates/zed/src/zed.rs | 34 +- 24 files changed, 726 insertions(+), 578 deletions(-) diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index b1d0bedb2cf23c856f320aacabb8d2fb489f2e2a..4804f5b0f1f24033fc79c12a6af5d87096a49a84 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -495,8 +495,9 @@ impl TestClient { // We use a workspace container so that we don't need to remove the window in order to // drop the workspace and we can use a ViewHandle instead. - let (window_id, container) = cx.add_window(|_| WorkspaceContainer { workspace: None }); - let workspace = cx.add_view(window_id, |cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|_| WorkspaceContainer { workspace: None }); + let container = window.root(cx); + let workspace = window.add_view(cx, |cx| Workspace::test_new(project.clone(), cx)); container.update(cx, |container, cx| { container.workspace = Some(workspace.downgrade()); cx.notify(); diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index ab94f16a07f011b8166f7b7ff779539b89401034..1a8e6d938d6d6caac2b8caee501e1088ddeea5b3 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -7,8 +7,7 @@ use client::{User, RECEIVE_TIMEOUT}; use collections::HashSet; use editor::{ test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion, - ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions, - Undo, + ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToggleCodeActions, Undo, }; use fs::{repository::GitFileStatus, FakeFs, Fs as _, LineEnding, RemoveOptions}; use futures::StreamExt as _; @@ -1208,7 +1207,7 @@ async fn test_share_project( cx_c: &mut TestAppContext, ) { deterministic.forbid_parking(); - let (window_b, _) = cx_b.add_window(|_| EmptyView); + let window_b = cx_b.add_window(|_| EmptyView); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -1316,7 +1315,7 @@ async fn test_share_project( .await .unwrap(); - let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx)); + let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx)); // Client A sees client B's selection deterministic.run_until_parked(); @@ -1499,8 +1498,8 @@ async fn test_host_disconnect( deterministic.run_until_parked(); assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); - let (window_id_b, workspace_b) = - cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); + let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); + let workspace_b = window_b.root(cx_b); let editor_b = workspace_b .update(cx_b, |workspace, cx| { workspace.open_path((worktree_id, "b.txt"), None, true, cx) @@ -1509,9 +1508,7 @@ async fn test_host_disconnect( .unwrap() .downcast::() .unwrap(); - assert!(cx_b - .read_window(window_id_b, |cx| editor_b.is_focused(cx)) - .unwrap()); + assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx))); editor_b.update(cx_b, |editor, cx| editor.insert("X", cx)); assert!(cx_b.is_window_edited(workspace_b.window_id())); @@ -1525,7 +1522,7 @@ async fn test_host_disconnect( assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared())); // Ensure client B's edited state is reset and that the whole window is blurred. - cx_b.read_window(window_id_b, |cx| { + window_b.read_with(cx_b, |cx| { assert_eq!(cx.focused_view_id(), None); }); assert!(!cx_b.is_window_edited(workspace_b.window_id())); @@ -3445,13 +3442,11 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) .await .unwrap(); - let (window_a, _) = cx_a.add_window(|_| EmptyView); - let editor_a = cx_a.add_view(window_a, |cx| { - Editor::for_buffer(buffer_a, Some(project_a), cx) - }); + let window_a = cx_a.add_window(|_| EmptyView); + let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx)); let mut editor_cx_a = EditorTestContext { cx: cx_a, - window_id: window_a, + window_id: window_a.id(), editor: editor_a, }; @@ -3460,13 +3455,11 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) .await .unwrap(); - let (window_b, _) = cx_b.add_window(|_| EmptyView); - let editor_b = cx_b.add_view(window_b, |cx| { - Editor::for_buffer(buffer_b, Some(project_b), cx) - }); + let window_b = cx_b.add_window(|_| EmptyView); + let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx)); let mut editor_cx_b = EditorTestContext { cx: cx_b, - window_id: window_b, + window_id: window_b.id(), editor: editor_b, }; @@ -4205,8 +4198,8 @@ async fn test_collaborating_with_completion( .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)) .await .unwrap(); - let (window_b, _) = cx_b.add_window(|_| EmptyView); - let editor_b = cx_b.add_view(window_b, |cx| { + let window_b = cx_b.add_window(|_| EmptyView); + let editor_b = window_b.add_view(cx_b, |cx| { Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx) }); @@ -5316,7 +5309,8 @@ async fn test_collaborating_with_code_actions( // Join the project as client B. let project_b = client_b.build_remote_project(project_id, cx_b).await; - let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); + let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); + let workspace_b = window_b.root(cx_b); let editor_b = workspace_b .update(cx_b, |workspace, cx| { workspace.open_path((worktree_id, "main.rs"), None, true, cx) @@ -5540,7 +5534,8 @@ async fn test_collaborating_with_renames( .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; - let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); + let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); + let workspace_b = window_b.root(cx_b); let editor_b = workspace_b .update(cx_b, |workspace, cx| { workspace.open_path((worktree_id, "one.rs"), None, true, cx) @@ -5571,6 +5566,7 @@ async fn test_collaborating_with_renames( .unwrap(); prepare_rename.await.unwrap(); editor_b.update(cx_b, |editor, cx| { + use editor::ToOffset; let rename = editor.pending_rename().unwrap(); let buffer = editor.buffer().read(cx).snapshot(cx); assert_eq!( @@ -7601,8 +7597,8 @@ async fn test_on_input_format_from_host_to_guest( .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)) .await .unwrap(); - let (window_a, _) = cx_a.add_window(|_| EmptyView); - let editor_a = cx_a.add_view(window_a, |cx| { + let window_a = cx_a.add_window(|_| EmptyView); + let editor_a = window_a.add_view(cx_a, |cx| { Editor::for_buffer(buffer_a, Some(project_a.clone()), cx) }); @@ -7730,8 +7726,8 @@ async fn test_on_input_format_from_guest_to_host( .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)) .await .unwrap(); - let (window_b, _) = cx_b.add_window(|_| EmptyView); - let editor_b = cx_b.add_view(window_b, |cx| { + let window_b = cx_b.add_window(|_| EmptyView); + let editor_b = window_b.add_view(cx_b, |cx| { Editor::for_buffer(buffer_b, Some(project_b.clone()), cx) }); diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 4066b5b229a536da0b49e0ad8c5b7ab492968e18..a9c5e697a5f60d0cb6f9c0e6baf7cac50aba192a 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -31,7 +31,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { for screen in cx.platform().screens() { let screen_bounds = screen.bounds(); - let (window_id, _) = cx.add_window( + let window = cx.add_window( WindowOptions { bounds: WindowBounds::Fixed(RectF::new( screen_bounds.upper_right() @@ -49,7 +49,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { |_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()), ); - notification_windows.push(window_id); + notification_windows.push(window.id()); } } } diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index fea6118bdf4055d2b4bb862d1cde876b9dd8c60f..03ab91623b43270bf2592ced0f460b474fd0c435 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -26,7 +26,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { for screen in cx.platform().screens() { let screen_bounds = screen.bounds(); - let (window_id, _) = cx.add_window( + let window = cx.add_window( WindowOptions { bounds: WindowBounds::Fixed(RectF::new( screen_bounds.upper_right() - vec2f(PADDING + window_size.x(), PADDING), @@ -52,7 +52,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { notification_windows .entry(*project_id) .or_insert(Vec::new()) - .push(window_id); + .push(window.id()); } } room::Event::RemoteProjectUnshared { project_id } => { diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 7461fb28c7382bfaaf9f97385579e161251276fc..7d4b4126b702774bf8c295d3b61f904ff7ebf80c 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -295,7 +295,9 @@ mod tests { let app_state = init_test(cx); let project = Project::test(app_state.fs.clone(), [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); let editor = cx.add_view(window_id, |cx| { let mut editor = Editor::single_line(None, cx); editor.set_text("abc", cx); diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index fec8f27c9730d776f4311498635772fb8a2791a5..659bee7445f0696d32bf7532c902907d7ddcf11c 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -27,7 +27,7 @@ pub fn init(cx: &mut AppContext) { if let Some(code_verification_handle) = code_verification.as_mut() { let window_id = code_verification_handle.id(); let updated = cx.update_window(window_id, |cx| { - code_verification_handle.update(cx, |code_verification, cx| { + code_verification_handle.update_root(cx, |code_verification, cx| { code_verification.set_status(status.clone(), cx) }); cx.activate_window(); @@ -41,9 +41,9 @@ pub fn init(cx: &mut AppContext) { } Status::Authorized | Status::Unauthorized => { if let Some(code_verification) = code_verification.as_ref() { - let window_id = code_verification.window_id(); + let window_id = code_verification.id(); cx.update_window(window_id, |cx| { - code_verification.update(cx, |code_verification, cx| { + code_verification.update_root(cx, |code_verification, cx| { code_verification.set_status(status, cx) }); @@ -54,7 +54,7 @@ pub fn init(cx: &mut AppContext) { } _ => { if let Some(code_verification) = code_verification.take() { - cx.update_window(code_verification.window_id(), |cx| cx.remove_window()); + code_verification.update(cx, |cx| cx.remove_window()); } } } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index d0cd437946a54cf6cb5446fecd0d310b1c82a269..2444465be666124adb706bcf1833c50c77cee081 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -855,7 +855,9 @@ mod tests { let language_server_id = LanguageServerId(0); let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); // Create some diagnostics project.update(cx, |project, cx| { @@ -1248,7 +1250,9 @@ mod tests { let server_id_1 = LanguageServerId(100); let server_id_2 = LanguageServerId(101); let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); let view = cx.add_view(window_id, |cx| { ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index eb03d2bdc0f103c2b94352312d3e86df860c821c..96921643d45a1b7c1083dab5df471b3bd95adb2d 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -48,36 +48,40 @@ fn test_edit_events(cx: &mut TestAppContext) { }); let events = Rc::new(RefCell::new(Vec::new())); - let (_, editor1) = cx.add_window({ - let events = events.clone(); - |cx| { - cx.subscribe(&cx.handle(), move |_, _, event, _| { - if matches!( - event, - Event::Edited | Event::BufferEdited | Event::DirtyChanged - ) { - events.borrow_mut().push(("editor1", event.clone())); - } - }) - .detach(); - Editor::for_buffer(buffer.clone(), None, cx) - } - }); - let (_, editor2) = cx.add_window({ - let events = events.clone(); - |cx| { - cx.subscribe(&cx.handle(), move |_, _, event, _| { - if matches!( - event, - Event::Edited | Event::BufferEdited | Event::DirtyChanged - ) { - events.borrow_mut().push(("editor2", event.clone())); - } - }) - .detach(); - Editor::for_buffer(buffer.clone(), None, cx) - } - }); + let editor1 = cx + .add_window({ + let events = events.clone(); + |cx| { + cx.subscribe(&cx.handle(), move |_, _, event, _| { + if matches!( + event, + Event::Edited | Event::BufferEdited | Event::DirtyChanged + ) { + events.borrow_mut().push(("editor1", event.clone())); + } + }) + .detach(); + Editor::for_buffer(buffer.clone(), None, cx) + } + }) + .detach(cx); + let editor2 = cx + .add_window({ + let events = events.clone(); + |cx| { + cx.subscribe(&cx.handle(), move |_, _, event, _| { + if matches!( + event, + Event::Edited | Event::BufferEdited | Event::DirtyChanged + ) { + events.borrow_mut().push(("editor2", event.clone())); + } + }) + .detach(); + Editor::for_buffer(buffer.clone(), None, cx) + } + }) + .detach(cx); assert_eq!(mem::take(&mut *events.borrow_mut()), []); // Mutating editor 1 will emit an `Edited` event only for that editor. @@ -173,7 +177,9 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval()); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer.clone(), cx)); + let editor = cx + .add_window(|cx| build_editor(buffer.clone(), cx)) + .detach(cx); editor.update(cx, |editor, cx| { editor.start_transaction_at(now, cx); @@ -343,10 +349,12 @@ fn test_ime_composition(cx: &mut TestAppContext) { fn test_selection_with_mouse(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); - build_editor(buffer, cx) - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); editor.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); }); @@ -410,10 +418,12 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) { fn test_canceling_pending_selection(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); @@ -456,10 +466,12 @@ fn test_clone(cx: &mut TestAppContext) { true, ); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&text, cx); - build_editor(buffer, cx) - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&text, cx); + build_editor(buffer, cx) + }) + .detach(cx); editor.update(cx, |editor, cx| { editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone())); @@ -473,9 +485,11 @@ fn test_clone(cx: &mut TestAppContext) { ); }); - let (_, cloned_editor) = editor.update(cx, |editor, cx| { - cx.add_window(Default::default(), |cx| editor.clone(cx)) - }); + let cloned_editor = editor + .update(cx, |editor, cx| { + cx.add_window(Default::default(), |cx| editor.clone(cx)) + }) + .detach(cx); let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)); let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)); @@ -509,7 +523,9 @@ async fn test_navigation_history(cx: &mut TestAppContext) { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); cx.add_view(window_id, |cx| { let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); @@ -618,10 +634,12 @@ async fn test_navigation_history(cx: &mut TestAppContext) { fn test_cancel(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx); @@ -661,9 +679,10 @@ fn test_cancel(cx: &mut TestAppContext) { fn test_fold_action(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple( - &" + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple( + &" impl Foo { // Hello! @@ -680,11 +699,12 @@ fn test_fold_action(cx: &mut TestAppContext) { } } " - .unindent(), - cx, - ); - build_editor(buffer.clone(), cx) - }); + .unindent(), + cx, + ); + build_editor(buffer.clone(), cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -752,7 +772,9 @@ fn test_move_cursor(cx: &mut TestAppContext) { init_test(cx, |_| {}); let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer.clone(), cx)); + let view = cx + .add_window(|cx| build_editor(buffer.clone(), cx)) + .detach(cx); buffer.update(cx, |buffer, cx| { buffer.edit( @@ -827,10 +849,12 @@ fn test_move_cursor(cx: &mut TestAppContext) { fn test_move_cursor_multibyte(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); - build_editor(buffer.clone(), cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); + build_editor(buffer.clone(), cx) + }) + .detach(cx); assert_eq!('ⓐ'.len_utf8(), 3); assert_eq!('α'.len_utf8(), 2); @@ -932,10 +956,12 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) { fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); - build_editor(buffer.clone(), cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); + build_editor(buffer.clone(), cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]); @@ -982,10 +1008,12 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) { fn test_beginning_end_of_line(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("abc\n def", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("abc\n def", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -1145,10 +1173,12 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) { fn test_prev_next_word_boundary(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -1197,10 +1227,13 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) { fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = + MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.set_wrap_width(Some(140.), cx); @@ -1530,10 +1563,12 @@ async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) { fn test_delete_to_word_boundary(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("one two three four", cx); - build_editor(buffer.clone(), cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("one two three four", cx); + build_editor(buffer.clone(), cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -1566,10 +1601,12 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) { fn test_newline(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); - build_editor(buffer.clone(), cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); + build_editor(buffer.clone(), cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -1589,9 +1626,10 @@ fn test_newline(cx: &mut TestAppContext) { fn test_newline_with_old_selections(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple( - " + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple( + " a b( X @@ -1600,19 +1638,20 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) { X ) " - .unindent() - .as_str(), - cx, - ); - let mut editor = build_editor(buffer.clone(), cx); - editor.change_selections(None, cx, |s| { - s.select_ranges([ - Point::new(2, 4)..Point::new(2, 5), - Point::new(5, 4)..Point::new(5, 5), - ]) - }); - editor - }); + .unindent() + .as_str(), + cx, + ); + let mut editor = build_editor(buffer.clone(), cx); + editor.change_selections(None, cx, |s| { + s.select_ranges([ + Point::new(2, 4)..Point::new(2, 5), + Point::new(5, 4)..Point::new(5, 5), + ]) + }); + editor + }) + .detach(cx); editor.update(cx, |editor, cx| { // Edit the buffer directly, deleting ranges surrounding the editor's selections @@ -1817,12 +1856,14 @@ async fn test_newline_comments(cx: &mut gpui::TestAppContext) { fn test_insert_with_old_selections(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); - let mut editor = build_editor(buffer.clone(), cx); - editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20])); - editor - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); + let mut editor = build_editor(buffer.clone(), cx); + editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20])); + editor + }) + .detach(cx); editor.update(cx, |editor, cx| { // Edit the buffer directly, deleting ranges surrounding the editor's selections @@ -2329,10 +2370,12 @@ async fn test_delete(cx: &mut gpui::TestAppContext) { fn test_delete_line(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -2352,10 +2395,12 @@ fn test_delete_line(cx: &mut TestAppContext) { ); }); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)]) @@ -2654,10 +2699,12 @@ async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) { fn test_duplicate_line(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -2680,10 +2727,12 @@ fn test_duplicate_line(cx: &mut TestAppContext) { ); }); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -2707,10 +2756,12 @@ fn test_duplicate_line(cx: &mut TestAppContext) { fn test_move_line_up_down(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.fold_ranges( vec![ @@ -2806,10 +2857,12 @@ fn test_move_line_up_down(cx: &mut TestAppContext) { fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); - build_editor(buffer, cx) - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); + build_editor(buffer, cx) + }) + .detach(cx); editor.update(cx, |editor, cx| { let snapshot = editor.buffer.read(cx).snapshot(cx); editor.insert_blocks( @@ -2834,102 +2887,94 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) { fn test_transpose(cx: &mut TestAppContext) { init_test(cx, |_| {}); - _ = cx - .add_window(|cx| { - let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx); + _ = cx.add_window(|cx| { + let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx); - editor.change_selections(None, cx, |s| s.select_ranges([1..1])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selections.ranges(cx), [2..2]); + editor.change_selections(None, cx, |s| s.select_ranges([1..1])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bac"); + assert_eq!(editor.selections.ranges(cx), [2..2]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bca"); - assert_eq!(editor.selections.ranges(cx), [3..3]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bca"); + assert_eq!(editor.selections.ranges(cx), [3..3]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selections.ranges(cx), [3..3]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bac"); + assert_eq!(editor.selections.ranges(cx), [3..3]); - editor - }) - .1; + editor + }); - _ = cx - .add_window(|cx| { - let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); + _ = cx.add_window(|cx| { + let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - editor.change_selections(None, cx, |s| s.select_ranges([3..3])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acb\nde"); - assert_eq!(editor.selections.ranges(cx), [3..3]); + editor.change_selections(None, cx, |s| s.select_ranges([3..3])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acb\nde"); + assert_eq!(editor.selections.ranges(cx), [3..3]); - editor.change_selections(None, cx, |s| s.select_ranges([4..4])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selections.ranges(cx), [5..5]); + editor.change_selections(None, cx, |s| s.select_ranges([4..4])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acbd\ne"); + assert_eq!(editor.selections.ranges(cx), [5..5]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acbde\n"); - assert_eq!(editor.selections.ranges(cx), [6..6]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acbde\n"); + assert_eq!(editor.selections.ranges(cx), [6..6]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selections.ranges(cx), [6..6]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acbd\ne"); + assert_eq!(editor.selections.ranges(cx), [6..6]); - editor - }) - .1; + editor + }); - _ = cx - .add_window(|cx| { - let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); + _ = cx.add_window(|cx| { + let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bacd\ne"); - assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]); + editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bacd\ne"); + assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcade\n"); + assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcda\ne"); - assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcda\ne"); + assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcade\n"); + assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcaed\n"); - assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcaed\n"); + assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]); - editor - }) - .1; + editor + }); - _ = cx - .add_window(|cx| { - let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx); + _ = cx.add_window(|cx| { + let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx); - editor.change_selections(None, cx, |s| s.select_ranges([4..4])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selections.ranges(cx), [8..8]); + editor.change_selections(None, cx, |s| s.select_ranges([4..4])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "🏀🍐✋"); + assert_eq!(editor.selections.ranges(cx), [8..8]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "🏀✋🍐"); - assert_eq!(editor.selections.ranges(cx), [11..11]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "🏀✋🍐"); + assert_eq!(editor.selections.ranges(cx), [11..11]); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selections.ranges(cx), [11..11]); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "🏀🍐✋"); + assert_eq!(editor.selections.ranges(cx), [11..11]); - editor - }) - .1; + editor + }); } #[gpui::test] @@ -3132,10 +3177,12 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) { fn test_select_all(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.select_all(&SelectAll, cx); assert_eq!( @@ -3149,10 +3196,12 @@ fn test_select_all(cx: &mut TestAppContext) { fn test_select_line(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -3196,10 +3245,12 @@ fn test_select_line(cx: &mut TestAppContext) { fn test_split_selection_into_lines(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.fold_ranges( vec![ @@ -3267,10 +3318,12 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) { fn test_add_selection_above_below(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, view) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); - build_editor(buffer, cx) - }); + let view = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); + build_editor(buffer, cx) + }) + .detach(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -3555,7 +3608,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); + let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -3718,7 +3771,7 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); editor .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx)) .await; @@ -4281,7 +4334,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); + let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -4429,7 +4482,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); editor .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -4519,7 +4572,7 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { ); let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); editor.update(cx, |editor, cx| { let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap(); @@ -4649,7 +4702,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { let fake_server = fake_servers.next().await.unwrap(); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); assert!(cx.read(|cx| editor.is_dirty(cx))); @@ -4761,7 +4814,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { let fake_server = fake_servers.next().await.unwrap(); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); assert!(cx.read(|cx| editor.is_dirty(cx))); @@ -4875,7 +4928,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { let fake_server = fake_servers.next().await.unwrap(); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); let format = editor.update(cx, |editor, cx| { @@ -5653,7 +5706,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { multibuffer }); - let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx)); + let view = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); view.update(cx, |view, cx| { assert_eq!(view.text(cx), "aaaa\nbbbb"); view.change_selections(None, cx, |s| { @@ -5723,7 +5776,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { multibuffer }); - let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx)); + let view = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); view.update(cx, |view, cx| { let (expected_text, selection_ranges) = marked_text_ranges( indoc! {" @@ -5799,22 +5852,24 @@ fn test_refresh_selections(cx: &mut TestAppContext) { multibuffer }); - let (_, editor) = cx.add_window(|cx| { - let mut editor = build_editor(multibuffer.clone(), cx); - let snapshot = editor.snapshot(cx); - editor.change_selections(None, cx, |s| { - s.select_ranges([Point::new(1, 3)..Point::new(1, 3)]) - }); - editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx); - assert_eq!( - editor.selections.ranges(cx), - [ - Point::new(1, 3)..Point::new(1, 3), - Point::new(2, 1)..Point::new(2, 1), - ] - ); - editor - }); + let editor = cx + .add_window(|cx| { + let mut editor = build_editor(multibuffer.clone(), cx); + let snapshot = editor.snapshot(cx); + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(1, 3)..Point::new(1, 3)]) + }); + editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx); + assert_eq!( + editor.selections.ranges(cx), + [ + Point::new(1, 3)..Point::new(1, 3), + Point::new(2, 1)..Point::new(2, 1), + ] + ); + editor + }) + .detach(cx); // Refreshing selections is a no-op when excerpts haven't changed. editor.update(cx, |editor, cx| { @@ -5884,16 +5939,18 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) { multibuffer }); - let (_, editor) = cx.add_window(|cx| { - let mut editor = build_editor(multibuffer.clone(), cx); - let snapshot = editor.snapshot(cx); - editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx); - assert_eq!( - editor.selections.ranges(cx), - [Point::new(1, 3)..Point::new(1, 3)] - ); - editor - }); + let editor = cx + .add_window(|cx| { + let mut editor = build_editor(multibuffer.clone(), cx); + let snapshot = editor.snapshot(cx); + editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx); + assert_eq!( + editor.selections.ranges(cx), + [Point::new(1, 3)..Point::new(1, 3)] + ); + editor + }) + .detach(cx); multibuffer.update(cx, |multibuffer, cx| { multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx); @@ -5956,7 +6013,7 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); + let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -5992,10 +6049,12 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { fn test_highlighted_ranges(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); - build_editor(buffer.clone(), cx) - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); + build_editor(buffer.clone(), cx) + }) + .detach(cx); editor.update(cx, |editor, cx| { struct Type1; @@ -6084,16 +6143,20 @@ async fn test_following(cx: &mut gpui::TestAppContext) { .unwrap(); cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)) }); - let (_, leader) = cx.add_window(|cx| build_editor(buffer.clone(), cx)); - let (_, follower) = cx.update(|cx| { - cx.add_window( - WindowOptions { - bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))), - ..Default::default() - }, - |cx| build_editor(buffer.clone(), cx), - ) - }); + let leader = cx + .add_window(|cx| build_editor(buffer.clone(), cx)) + .detach(cx); + let follower = cx + .update(|cx| { + cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))), + ..Default::default() + }, + |cx| build_editor(buffer.clone(), cx), + ) + }) + .detach(cx); let is_still_following = Rc::new(RefCell::new(true)); let follower_edit_event_count = Rc::new(RefCell::new(0)); @@ -6224,7 +6287,9 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let leader = pane.update(cx, |_, cx| { @@ -6968,7 +7033,7 @@ async fn test_copilot_multibuffer( ); multibuffer }); - let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx)); + let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); handle_copilot_completion_request( &copilot_lsp, @@ -7098,7 +7163,7 @@ async fn test_copilot_disabled_globs( ); multibuffer }); - let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx)); + let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); let mut copilot_requests = copilot_lsp .handle_request::(move |_params, _cx| async move { @@ -7177,7 +7242,9 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) { .await; let project = Project::test(fs, ["/a".as_ref()], cx).await; project.update(cx, |project, _| project.languages().add(Arc::new(language))); - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -7282,7 +7349,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test .await; let project = Project::test(fs, ["/a".as_ref()], cx).await; project.update(cx, |project, _| project.languages().add(Arc::new(language))); - let (_, _workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let _buffer = project .update(cx, |project, cx| { project.open_local_buffer("/a/main.rs", cx) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 750beaea1380ee676c15a3d895c633a91bbe6c12..dc40e7fb8568aeec2bf5d4fe6f4168e4847c3558 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3002,10 +3002,12 @@ mod tests { fn test_layout_line_numbers(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); - Editor::new(EditorMode::Full, buffer, None, None, cx) - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); + Editor::new(EditorMode::Full, buffer, None, None, cx) + }) + .detach(cx); let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let layouts = editor.update(cx, |editor, cx| { @@ -3021,10 +3023,12 @@ mod tests { fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("", cx); - Editor::new(EditorMode::Full, buffer, None, None, cx) - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple("", cx); + Editor::new(EditorMode::Full, buffer, None, None, cx) + }) + .detach(cx); editor.update(cx, |editor, cx| { editor.set_placeholder_text("hello", cx); @@ -3231,10 +3235,12 @@ mod tests { info!( "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" ); - let (_, editor) = cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple(&input_text, cx); - Editor::new(editor_mode, buffer, None, None, cx) - }); + let editor = cx + .add_window(|cx| { + let buffer = MultiBuffer::build_simple(&input_text, cx); + Editor::new(editor_mode, buffer, None, None, cx) + }) + .detach(cx); let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let (_, layout_state) = editor.update(cx, |editor, cx| { diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 63076ba234d52c53556bb9fc9a8a43acbf314a96..089cbb29958c727a0b7760018833a279534a225a 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -1136,7 +1136,9 @@ mod tests { ) .await; let project = Project::test(fs, ["/a".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -1836,7 +1838,9 @@ mod tests { .await; let project = Project::test(fs, ["/a".as_ref()], cx).await; project.update(cx, |project, _| project.languages().add(Arc::new(language))); - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -1989,7 +1993,9 @@ mod tests { project.update(cx, |project, _| { project.languages().add(Arc::clone(&language)) }); - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -2075,8 +2081,9 @@ mod tests { deterministic.run_until_parked(); cx.foreground().run_until_parked(); - let (_, editor) = - cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)); + let editor = cx + .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)) + .detach(cx); let editor_edited = Arc::new(AtomicBool::new(false)); let fake_server = fake_servers.next().await.unwrap(); let closure_editor_edited = Arc::clone(&editor_edited); @@ -2328,7 +2335,9 @@ all hints should be invalidated and requeried for all of its visible excerpts" project.update(cx, |project, _| { project.languages().add(Arc::clone(&language)) }); - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -2373,8 +2382,9 @@ all hints should be invalidated and requeried for all of its visible excerpts" deterministic.run_until_parked(); cx.foreground().run_until_parked(); - let (_, editor) = - cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)); + let editor = cx + .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)) + .detach(cx); let editor_edited = Arc::new(AtomicBool::new(false)); let fake_server = fake_servers.next().await.unwrap(); let closure_editor_edited = Arc::clone(&editor_edited); @@ -2562,7 +2572,9 @@ all hints should be invalidated and requeried for all of its visible excerpts" let project = Project::test(fs, ["/a".as_ref()], cx).await; project.update(cx, |project, _| project.languages().add(Arc::new(language))); - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index 0fe49d4d0462216b28ea9273e9490e96a7bc3a9d..f53115f224a1cfe6cb4a3c634786c7e7e8c18305 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -69,7 +69,8 @@ impl<'a> EditorLspTestContext<'a> { .insert_tree("/root", json!({ "dir": { file_name.clone(): "" }})) .await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); project .update(cx, |project, cx| { project.find_or_create_local_worktree("/root", true, cx) @@ -98,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> { Self { cx: EditorTestContext { cx, - window_id, + window_id: window.id(), editor, }, lsp, diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index bac70f139a6be8732e9b2b8bde9bce2b63144418..c7ea1b4f3852301e300c5ff3f1db1dff65f342a2 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -32,16 +32,14 @@ impl<'a> EditorTestContext<'a> { let buffer = project .update(cx, |project, cx| project.create_buffer("", None, cx)) .unwrap(); - let (window_id, editor) = cx.update(|cx| { - cx.add_window(Default::default(), |cx| { - cx.focus_self(); - build_editor(MultiBuffer::build_from_buffer(buffer, cx), cx) - }) + let window = cx.add_window(|cx| { + cx.focus_self(); + build_editor(MultiBuffer::build_from_buffer(buffer, cx), cx) }); - + let editor = window.root(cx); Self { cx, - window_id, + window_id: window.id(), editor, } } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index b6701f12d6eb057d6bb7e8ae2a91fca3dbe37f58..2c9d9c0c71e390c256e7db785ee06d707039a8a1 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -617,8 +617,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - cx.dispatch_action(window_id, Toggle); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + cx.dispatch_action(window.id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); finder @@ -631,8 +632,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window_id, SelectNext); - cx.dispatch_action(window_id, Confirm); + cx.dispatch_action(window.id(), SelectNext); + cx.dispatch_action(window.id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -671,8 +672,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - cx.dispatch_action(window_id, Toggle); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + cx.dispatch_action(window.id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -704,8 +706,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window_id, SelectNext); - cx.dispatch_action(window_id, Confirm); + cx.dispatch_action(window.id(), SelectNext); + cx.dispatch_action(window.id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -754,8 +756,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - cx.dispatch_action(window_id, Toggle); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + cx.dispatch_action(window.id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -787,8 +790,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window_id, SelectNext); - cx.dispatch_action(window_id, Confirm); + cx.dispatch_action(window.id(), SelectNext); + cx.dispatch_action(window.id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -837,19 +840,23 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - let (_, finder) = cx.add_window(|cx| { - Picker::new( - FileFinderDelegate::new( - workspace.downgrade(), - workspace.read(cx).project().clone(), - None, - Vec::new(), + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); + let finder = cx + .add_window(|cx| { + Picker::new( + FileFinderDelegate::new( + workspace.downgrade(), + workspace.read(cx).project().clone(), + None, + Vec::new(), + cx, + ), cx, - ), - cx, - ) - }); + ) + }) + .detach(cx); let query = test_path_like("hi"); finder @@ -931,19 +938,23 @@ mod tests { cx, ) .await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - let (_, finder) = cx.add_window(|cx| { - Picker::new( - FileFinderDelegate::new( - workspace.downgrade(), - workspace.read(cx).project().clone(), - None, - Vec::new(), + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); + let finder = cx + .add_window(|cx| { + Picker::new( + FileFinderDelegate::new( + workspace.downgrade(), + workspace.read(cx).project().clone(), + None, + Vec::new(), + cx, + ), cx, - ), - cx, - ) - }); + ) + }) + .detach(cx); finder .update(cx, |f, cx| { f.delegate_mut().spawn_search(test_path_like("hi"), cx) @@ -967,19 +978,23 @@ mod tests { cx, ) .await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - let (_, finder) = cx.add_window(|cx| { - Picker::new( - FileFinderDelegate::new( - workspace.downgrade(), - workspace.read(cx).project().clone(), - None, - Vec::new(), + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); + let finder = cx + .add_window(|cx| { + Picker::new( + FileFinderDelegate::new( + workspace.downgrade(), + workspace.read(cx).project().clone(), + None, + Vec::new(), + cx, + ), cx, - ), - cx, - ) - }); + ) + }) + .detach(cx); // Even though there is only one worktree, that worktree's filename // is included in the matching, because the worktree is a single file. @@ -1015,61 +1030,6 @@ mod tests { finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0)); } - #[gpui::test] - async fn test_multiple_matches_with_same_relative_path(cx: &mut TestAppContext) { - let app_state = init_test(cx); - app_state - .fs - .as_fake() - .insert_tree( - "/root", - json!({ - "dir1": { "a.txt": "" }, - "dir2": { "a.txt": "" } - }), - ) - .await; - - let project = Project::test( - app_state.fs.clone(), - ["/root/dir1".as_ref(), "/root/dir2".as_ref()], - cx, - ) - .await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - - let (_, finder) = cx.add_window(|cx| { - Picker::new( - FileFinderDelegate::new( - workspace.downgrade(), - workspace.read(cx).project().clone(), - None, - Vec::new(), - cx, - ), - cx, - ) - }); - - // Run a search that matches two files with the same relative path. - finder - .update(cx, |f, cx| { - f.delegate_mut().spawn_search(test_path_like("a.t"), cx) - }) - .await; - - // Can switch between different matches with the same relative path. - finder.update(cx, |finder, cx| { - let delegate = finder.delegate_mut(); - assert_eq!(delegate.matches.len(), 2); - assert_eq!(delegate.selected_index(), 0); - delegate.set_selected_index(1, cx); - assert_eq!(delegate.selected_index(), 1); - delegate.set_selected_index(0, cx); - assert_eq!(delegate.selected_index(), 0); - }); - } - #[gpui::test] async fn test_path_distance_ordering(cx: &mut TestAppContext) { let app_state = init_test(cx); @@ -1089,7 +1049,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1); @@ -1103,18 +1065,20 @@ mod tests { worktree_id, path: Arc::from(Path::new("/root/dir2/b.txt")), })); - let (_, finder) = cx.add_window(|cx| { - Picker::new( - FileFinderDelegate::new( - workspace.downgrade(), - workspace.read(cx).project().clone(), - b_path, - Vec::new(), + let finder = cx + .add_window(|cx| { + Picker::new( + FileFinderDelegate::new( + workspace.downgrade(), + workspace.read(cx).project().clone(), + b_path, + Vec::new(), + cx, + ), cx, - ), - cx, - ) - }); + ) + }) + .detach(cx); finder .update(cx, |f, cx| { @@ -1151,19 +1115,23 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); - let (_, finder) = cx.add_window(|cx| { - Picker::new( - FileFinderDelegate::new( - workspace.downgrade(), - workspace.read(cx).project().clone(), - None, - Vec::new(), + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); + let finder = cx + .add_window(|cx| { + Picker::new( + FileFinderDelegate::new( + workspace.downgrade(), + workspace.read(cx).project().clone(), + None, + Vec::new(), + cx, + ), cx, - ), - cx, - ) - }); + ) + }) + .detach(cx); finder .update(cx, |f, cx| { f.delegate_mut().spawn_search(test_path_like("dir"), cx) @@ -1198,7 +1166,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1); @@ -1404,7 +1374,9 @@ mod tests { .detach(); deterministic.run_until_parked(); - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1,); diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 9c0e50647c6138fc94ee388220cb65d0112e3dd6..45169ed3af4af5e8f6806c3aaefcaa0f422a6059 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1424,7 +1424,7 @@ impl AppContext { &mut self, window_id: usize, build_root_view: F, - ) -> Option> + ) -> Option> where V: View, F: FnOnce(&mut ViewContext) -> V, @@ -3826,6 +3826,15 @@ impl WindowHandle { self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap()) } + /// Keep this window open until it's explicitly closed. + // + // TODO: Implement window dropping behavior when we don't call this. + pub fn detach(mut self, cx: &impl BorrowAppContext) -> ViewHandle { + let root = self.root(cx); + self.any_handle.ref_counts.take(); + root + } + pub fn read_with(&self, cx: &C, read: F) -> R where C: BorrowAppContext, @@ -3893,7 +3902,7 @@ impl WindowHandle { pub struct AnyWindowHandle { window_id: usize, root_view_type: TypeId, - ref_counts: Arc>, + ref_counts: Option>>, #[cfg(any(test, feature = "test-support"))] handle_id: usize, @@ -3913,7 +3922,7 @@ impl AnyWindowHandle { Self { window_id, root_view_type: TypeId::of::(), - ref_counts, + ref_counts: Some(ref_counts), #[cfg(any(test, feature = "test-support"))] handle_id, } @@ -3937,7 +3946,16 @@ impl AnyWindowHandle { impl Drop for AnyWindowHandle { fn drop(&mut self) { - self.ref_counts.lock().dec_window(self.window_id) + if let Some(ref_counts) = self.ref_counts.as_ref() { + ref_counts.lock().dec_window(self.window_id); + + #[cfg(any(test, feature = "test-support"))] + ref_counts + .lock() + .leak_detector + .lock() + .handle_dropped(self.window_id, self.handle_id); + } } } diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 9dc5d99bc5e8ac0b0994aae8b66169151d9b0b93..7cdcbc2c8fc593452fcc9025fc71545eda3f5ceb 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -15,7 +15,7 @@ use crate::{ util::post_inc, Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect, Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription, - View, ViewContext, ViewHandle, WindowInvalidation, + View, ViewContext, ViewHandle, WindowHandle, WindowInvalidation, }; use anyhow::{anyhow, bail, Result}; use collections::{HashMap, HashSet}; @@ -1151,15 +1151,15 @@ impl<'a> WindowContext<'a> { self.window.platform_window.prompt(level, msg, answers) } - pub fn replace_root_view(&mut self, build_root_view: F) -> ViewHandle + pub fn replace_root_view(&mut self, build_root_view: F) -> WindowHandle where V: View, F: FnOnce(&mut ViewContext) -> V, { let root_view = self.add_view(|cx| build_root_view(cx)); - self.window.root_view = Some(root_view.clone().into_any()); self.window.focused_view_id = Some(root_view.id()); - root_view + self.window.root_view = Some(root_view.into_any()); + WindowHandle::new(self.window_id, self.ref_counts.clone()) } pub fn add_view(&mut self, build_view: F) -> ViewHandle diff --git a/crates/language_tools/src/lsp_log_tests.rs b/crates/language_tools/src/lsp_log_tests.rs index d4a16b5758a2a905c0e621b368a9653114828ec4..ce05a417ad8fc6cebc61ee236d2289b8b73e2170 100644 --- a/crates/language_tools/src/lsp_log_tests.rs +++ b/crates/language_tools/src/lsp_log_tests.rs @@ -61,7 +61,9 @@ async fn test_lsp_logs(cx: &mut TestAppContext) { .receive_notification::() .await; - let (_, log_view) = cx.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx)); + let log_view = cx + .add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx)) + .detach(cx); language_server.notify::(lsp::LogMessageParams { message: "hello from the server".into(), diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 0be52646e631ef1446f168dc3ecf1ce7b9ef0075..fdc5ea108a812459f374fdd0a1386fe31bb9830a 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1780,7 +1780,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); assert_eq!( visible_entries_as_strings(&panel, 0..50, cx), @@ -1868,7 +1870,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -2219,7 +2223,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -2319,7 +2325,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/root1".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); panel.update(cx, |panel, cx| { @@ -2392,7 +2400,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); toggle_expand_dir(&panel, "src/test", cx); @@ -2481,7 +2491,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "src/", cx); @@ -2627,7 +2639,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let new_search_events_count = Arc::new(AtomicUsize::new(0)); @@ -2714,7 +2728,9 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); panel.update(cx, |panel, cx| { diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index cbf914230d3b316370083b2cdcb3535631344dba..8471f3a3a7014d2034438839f76ae9b4214ded52 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -326,7 +326,9 @@ mod tests { }, ); - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let window_id = window.id(); // Create the project symbols view. let symbols = cx.add_view(window_id, |cx| { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 45842aa5617b4f87cf306aebdead2d9ae96d455f..1e635432bd5c138f81410f3ac40ee461830ddb17 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -849,11 +849,13 @@ mod tests { cx, ) }); - let (window_id, _root_view) = cx.add_window(|_| EmptyView); + let window = cx.add_window(|_| EmptyView); - let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); + let editor = cx.add_view(window.id(), |cx| { + Editor::for_buffer(buffer.clone(), None, cx) + }); - let search_bar = cx.add_view(window_id, |cx| { + let search_bar = cx.add_view(window.id(), |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); @@ -1229,7 +1231,8 @@ mod tests { "Should pick a query with multiple results" ); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); - let (window_id, _root_view) = cx.add_window(|_| EmptyView); + let window = cx.add_window(|_| EmptyView); + let window_id = window.id(); let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); @@ -1416,11 +1419,13 @@ mod tests { "# .unindent(); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); - let (window_id, _root_view) = cx.add_window(|_| EmptyView); + let window = cx.add_window(|_| EmptyView); - let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); + let editor = cx.add_view(window.id(), |cx| { + Editor::for_buffer(buffer.clone(), None, cx) + }); - let search_bar = cx.add_view(window_id, |cx| { + let search_bar = cx.add_view(window.id(), |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 1b4e32f4b832483c160293a5d792b39d62a1a628..e57edd3b14709aea1056965b59e173bc9e404a23 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1447,7 +1447,9 @@ pub mod tests { .await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); - let (_, search_view) = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx)); + let search_view = cx + .add_window(|cx| ProjectSearchView::new(search.clone(), cx)) + .detach(cx); search_view.update(cx, |search_view, cx| { search_view @@ -1564,7 +1566,9 @@ pub mod tests { ) .await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); let active_item = cx.read(|cx| { workspace @@ -1748,7 +1752,9 @@ pub mod tests { let worktree_id = project.read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() }); - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); let active_item = cx.read(|cx| { workspace @@ -1866,7 +1872,9 @@ pub mod tests { ) .await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); workspace.update(cx, |workspace, cx| { ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) }); diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index e108a05ccc00bf3a47127c0ced234f0cc4dd332b..874978b4fc567526e6bd17d4f6edd3fa9a670c3c 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -1070,7 +1070,9 @@ mod tests { }); let project = Project::test(params.fs.clone(), [], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); (project, workspace) } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ee658c9cc92b3a4fa24a56f986e895547a190ad1..98883fac3372246d462585be76eb0bfd1a20bbae 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1972,7 +1972,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); pane.update(cx, |pane, cx| { @@ -1987,7 +1988,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // 1. Add with a destination index @@ -2065,7 +2067,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // 1. Add with a destination index @@ -2141,7 +2144,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // singleton view @@ -2209,7 +2213,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); add_labeled_item(&pane, "A", false, cx); @@ -2256,7 +2261,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); @@ -2276,7 +2282,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); add_labeled_item(&pane, "A", true, cx); @@ -2299,7 +2306,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); @@ -2319,7 +2327,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); @@ -2339,7 +2348,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); add_labeled_item(&pane, "A", false, cx); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 434975216ad977df75cdd960f27fdb6ed4306f02..3222ea2eb8cab97976c81ad6ad5c43668cf46655 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -793,7 +793,7 @@ impl Workspace { DB.next_id().await.unwrap_or(0) }; - let workspace = requesting_window_id + let window = requesting_window_id .and_then(|window_id| { cx.update(|cx| { cx.replace_root_view(window_id, |cx| { @@ -852,9 +852,9 @@ impl Workspace { ) }, ) - .1 }); + let workspace = window.root(&cx); (app_state.initialize_workspace)( workspace.downgrade(), serialized_workspace.is_some(), @@ -864,7 +864,7 @@ impl Workspace { .await .log_err(); - cx.update_window(workspace.window_id(), |cx| cx.activate_window()); + window.update(&mut cx, |cx| cx.activate_window()); let workspace = workspace.downgrade(); notify_if_database_failed(&workspace, &mut cx); @@ -3977,7 +3977,7 @@ pub fn join_remote_project( .await?; let window_bounds_override = window_bounds_env_override(&cx); - let (_, workspace) = cx.add_window( + let window = cx.add_window( (app_state.build_window_options)( window_bounds_override, None, @@ -3985,6 +3985,7 @@ pub fn join_remote_project( ), |cx| Workspace::new(0, project, app_state.clone(), cx), ); + let workspace = window.root(&cx); (app_state.initialize_workspace)( workspace.downgrade(), false, @@ -4113,10 +4114,11 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); // Adding an item with no ambiguity renders the tab without detail. - let item1 = cx.add_view(window_id, |_| { + let item1 = window.add_view(cx, |_| { let mut item = TestItem::new(); item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]); item @@ -4128,7 +4130,7 @@ mod tests { // Adding an item that creates ambiguity increases the level of detail on // both tabs. - let item2 = cx.add_view(window_id, |_| { + let item2 = window.add_view(cx, |_| { let mut item = TestItem::new(); item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); item @@ -4142,7 +4144,7 @@ mod tests { // Adding an item that creates ambiguity increases the level of detail only // on the ambiguous tabs. In this case, the ambiguity can't be resolved so // we stop at the highest detail available. - let item3 = cx.add_view(window_id, |_| { + let item3 = window.add_view(cx, |_| { let mut item = TestItem::new(); item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); item @@ -4177,16 +4179,17 @@ mod tests { .await; let project = Project::test(fs, ["root1".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let worktree_id = project.read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() }); - let item1 = cx.add_view(window_id, |cx| { + let item1 = window.add_view(cx, |cx| { TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]) }); - let item2 = cx.add_view(window_id, |cx| { + let item2 = window.add_view(cx, |cx| { TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)]) }); @@ -4201,14 +4204,14 @@ mod tests { ); }); assert_eq!( - cx.current_window_title(window_id).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root1") ); // Add a second item to a non-empty pane workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx)); assert_eq!( - cx.current_window_title(window_id).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("two.txt — root1") ); project.read_with(cx, |project, cx| { @@ -4227,7 +4230,7 @@ mod tests { .await .unwrap(); assert_eq!( - cx.current_window_title(window_id).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root1") ); project.read_with(cx, |project, cx| { @@ -4247,14 +4250,14 @@ mod tests { .await .unwrap(); assert_eq!( - cx.current_window_title(window_id).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root1, root2") ); // Remove a project folder project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); assert_eq!( - cx.current_window_title(window_id).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root2") ); } @@ -4267,18 +4270,19 @@ mod tests { fs.insert_tree("/root", json!({ "one": "" })).await; let project = Project::test(fs, ["root".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); // When there are no dirty items, there's nothing to do. - let item1 = cx.add_view(window_id, |_| TestItem::new()); + let item1 = window.add_view(cx, |_| TestItem::new()); workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx)); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); assert!(task.await.unwrap()); // When there are dirty untitled items, prompt to save each one. If the user // cancels any prompt, then abort. - let item2 = cx.add_view(window_id, |_| TestItem::new().with_dirty(true)); - let item3 = cx.add_view(window_id, |cx| { + let item2 = window.add_view(cx, |_| TestItem::new().with_dirty(true)); + let item3 = window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) @@ -4289,9 +4293,9 @@ mod tests { }); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); cx.foreground().run_until_parked(); - cx.simulate_prompt_answer(window_id, 2 /* cancel */); + cx.simulate_prompt_answer(window.id(), 2 /* cancel */); cx.foreground().run_until_parked(); - assert!(!cx.has_pending_prompt(window_id)); + assert!(!cx.has_pending_prompt(window.id())); assert!(!task.await.unwrap()); } @@ -4302,26 +4306,27 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); - let item1 = cx.add_view(window_id, |cx| { + let item1 = window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) }); - let item2 = cx.add_view(window_id, |cx| { + let item2 = window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_conflict(true) .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]) }); - let item3 = cx.add_view(window_id, |cx| { + let item3 = window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_conflict(true) .with_project_items(&[TestProjectItem::new(3, "3.txt", cx)]) }); - let item4 = cx.add_view(window_id, |cx| { + let item4 = window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_project_items(&[TestProjectItem::new_untitled(cx)]) @@ -4349,10 +4354,10 @@ mod tests { assert_eq!(pane.items_len(), 4); assert_eq!(pane.active_item().unwrap().id(), item1.id()); }); - assert!(cx.has_pending_prompt(window_id)); + assert!(cx.has_pending_prompt(window.id())); // Confirm saving item 1. - cx.simulate_prompt_answer(window_id, 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); // Item 1 is saved. There's a prompt to save item 3. @@ -4363,10 +4368,10 @@ mod tests { assert_eq!(pane.items_len(), 3); assert_eq!(pane.active_item().unwrap().id(), item3.id()); }); - assert!(cx.has_pending_prompt(window_id)); + assert!(cx.has_pending_prompt(window.id())); // Cancel saving item 3. - cx.simulate_prompt_answer(window_id, 1); + cx.simulate_prompt_answer(window.id(), 1); cx.foreground().run_until_parked(); // Item 3 is reloaded. There's a prompt to save item 4. @@ -4377,10 +4382,10 @@ mod tests { assert_eq!(pane.items_len(), 2); assert_eq!(pane.active_item().unwrap().id(), item4.id()); }); - assert!(cx.has_pending_prompt(window_id)); + assert!(cx.has_pending_prompt(window.id())); // Confirm saving item 4. - cx.simulate_prompt_answer(window_id, 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); // There's a prompt for a path for item 4. @@ -4404,13 +4409,14 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); // Create several workspace items with single project entries, and two // workspace items with multiple project entries. let single_entry_items = (0..=4) .map(|project_entry_id| { - cx.add_view(window_id, |cx| { + window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_project_items(&[TestProjectItem::new( @@ -4421,7 +4427,7 @@ mod tests { }) }) .collect::>(); - let item_2_3 = cx.add_view(window_id, |cx| { + let item_2_3 = window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_singleton(false) @@ -4430,7 +4436,7 @@ mod tests { single_entry_items[3].read(cx).project_items[0].clone(), ]) }); - let item_3_4 = cx.add_view(window_id, |cx| { + let item_3_4 = window.add_view(cx, |cx| { TestItem::new() .with_dirty(true) .with_singleton(false) @@ -4482,7 +4488,7 @@ mod tests { &[ProjectEntryId::from_proto(0)] ); }); - cx.simulate_prompt_answer(window_id, 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); left_pane.read_with(cx, |pane, cx| { @@ -4491,7 +4497,7 @@ mod tests { &[ProjectEntryId::from_proto(2)] ); }); - cx.simulate_prompt_answer(window_id, 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); close.await.unwrap(); @@ -4507,10 +4513,11 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - let item = cx.add_view(window_id, |cx| { + let item = window.add_view(cx, |cx| { TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) }); let item_id = item.id(); @@ -4550,7 +4557,7 @@ mod tests { item.read_with(cx, |item, _| assert_eq!(item.save_count, 2)); // Deactivating the window still saves the file. - cx.simulate_window_activation(Some(window_id)); + cx.simulate_window_activation(Some(window.id())); item.update(cx, |item, cx| { cx.focus_self(); item.is_dirty = true; @@ -4592,7 +4599,7 @@ mod tests { pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)) .await .unwrap(); - assert!(!cx.has_pending_prompt(window_id)); + assert!(!cx.has_pending_prompt(window.id())); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); // Add the item again, ensuring autosave is prevented if the underlying file has been deleted. @@ -4613,7 +4620,7 @@ mod tests { let _close_items = pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)); deterministic.run_until_parked(); - assert!(cx.has_pending_prompt(window_id)); + assert!(cx.has_pending_prompt(window.id())); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); } @@ -4624,9 +4631,10 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); - let item = cx.add_view(window_id, |cx| { + let item = window.add_view(cx, |cx| { TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) }); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); @@ -4677,7 +4685,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); let panel = workspace.update(cx, |workspace, cx| { let panel = cx.add_view(|_| TestPanel::new(DockPosition::Right)); @@ -4824,7 +4833,8 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| { // Add panel_1 on the left, panel_2 on the right. @@ -4979,7 +4989,7 @@ mod tests { // If focus is transferred to another view that's not a panel or another pane, we still show // the panel as zoomed. - let focus_receiver = cx.add_view(window_id, |_| EmptyView); + let focus_receiver = window.add_view(cx, |_| EmptyView); focus_receiver.update(cx, |_, cx| cx.focus_self()); workspace.read_with(cx, |workspace, _| { assert_eq!(workspace.zoomed, Some(panel_1.downgrade().into_any())); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 4b0bf1cd4c6aec42aa2d636b474828ef452cbe03..1770c5648e1fd2f8fe2a5ed31354f5525629a04f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -983,7 +983,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1295,7 +1297,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); // Open a file within an existing worktree. workspace @@ -1336,7 +1340,9 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; project.update(cx, |project, _| project.languages().add(rust_lang())); - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); // Create a new untitled buffer @@ -1429,7 +1435,9 @@ mod tests { let project = Project::test(app_state.fs.clone(), [], cx).await; project.update(cx, |project, _| project.languages().add(rust_lang())); - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); // Create a new untitled buffer cx.dispatch_action(window_id, NewFile); @@ -1480,7 +1488,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); + let window_id = window.id(); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1554,7 +1564,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .detach(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let entries = cx.read(|cx| workspace.file_project_paths(cx)); @@ -1831,7 +1843,9 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = cx + .add_window(|cx| Workspace::test_new(project, cx)) + .detach(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let entries = cx.read(|cx| workspace.file_project_paths(cx)); @@ -2073,7 +2087,8 @@ mod tests { cx.foreground().run_until_parked(); - let (window_id, _view) = cx.add_window(|_| TestView); + let window = cx.add_window(|_| TestView); + let window_id = window.id(); // Test loading the keymap base at all assert_key_bindings_for( @@ -2243,7 +2258,8 @@ mod tests { cx.foreground().run_until_parked(); - let (window_id, _view) = cx.add_window(|_| TestView); + let window = cx.add_window(|_| TestView); + let window_id = window.id(); // Test loading the keymap base at all assert_key_bindings_for( From 8e36da1382e84c87c5d3576a848eb06645bb21ab Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Aug 2023 15:02:55 -0600 Subject: [PATCH 08/53] Get tests passing --- crates/gpui/src/app.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 45169ed3af4af5e8f6806c3aaefcaa0f422a6059..bd615522c2610ee1d8e76c4675c98d738ef8a460 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3831,7 +3831,14 @@ impl WindowHandle { // TODO: Implement window dropping behavior when we don't call this. pub fn detach(mut self, cx: &impl BorrowAppContext) -> ViewHandle { let root = self.root(cx); - self.any_handle.ref_counts.take(); + let ref_counts = self.any_handle.ref_counts.take(); + #[cfg(any(test, feature = "test-support"))] + ref_counts + .unwrap() + .lock() + .leak_detector + .lock() + .handle_dropped(self.id(), self.any_handle.handle_id); root } From df4480ba52aede20b416afb87a0b0e17eb025682 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 2 Aug 2023 17:33:56 -0400 Subject: [PATCH 09/53] Use the same font size for hovered state of LSP status This element is used for the update state as well for some reason so while we don't normally ever see this state, it is used when the status is acting as the restart to update button --- styles/src/style_tree/status_bar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/style_tree/status_bar.ts b/styles/src/style_tree/status_bar.ts index 6261939994ac4e7ae7e06090912218d5dcfb7f54..d35b721c6cefcc50fc19ea526164ad967fa2578e 100644 --- a/styles/src/style_tree/status_bar.ts +++ b/styles/src/style_tree/status_bar.ts @@ -49,7 +49,7 @@ export default function status_bar(): any { }, state: { hovered: { - message: text(layer, "sans"), + message: text(layer, "sans", { size: "xs" }), icon_color: foreground(layer), background: background(layer, "hovered"), }, From 3c938a7377bbc2a3147f71da53ef05a1f45eaa5a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Aug 2023 08:10:16 -0600 Subject: [PATCH 10/53] WIP --- crates/gpui/src/app.rs | 16 ++++++++++++---- crates/gpui/src/app/test_app_context.rs | 2 ++ crates/gpui/src/app/window.rs | 2 ++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index bd615522c2610ee1d8e76c4675c98d738ef8a460..9b847e9c0cfdea426902ea59e93cec3a0c349fdf 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -130,10 +130,12 @@ pub trait BorrowAppContext { } pub trait BorrowWindowContext { - fn read_with(&self, window_id: usize, f: F) -> T + type Return; + + fn read_with(&self, window_id: usize, f: F) -> Self::Return where F: FnOnce(&WindowContext) -> T; - fn update(&mut self, window_id: usize, f: F) -> T + fn update(&mut self, window_id: usize, f: F) -> Self::Return where F: FnOnce(&mut WindowContext) -> T; } @@ -3358,6 +3360,8 @@ impl BorrowAppContext for ViewContext<'_, '_, V> { } impl BorrowWindowContext for ViewContext<'_, '_, V> { + type Return = T; + fn read_with T>(&self, window_id: usize, f: F) -> T { BorrowWindowContext::read_with(&*self.window_context, window_id, f) } @@ -3463,6 +3467,8 @@ impl BorrowAppContext for LayoutContext<'_, '_, '_, V> { } impl BorrowWindowContext for LayoutContext<'_, '_, '_, V> { + type Return = T; + fn read_with T>(&self, window_id: usize, f: F) -> T { BorrowWindowContext::read_with(&*self.view_context, window_id, f) } @@ -3515,6 +3521,8 @@ impl BorrowAppContext for EventContext<'_, '_, '_, V> { } impl BorrowWindowContext for EventContext<'_, '_, '_, V> { + type Return = T; + fn read_with T>(&self, window_id: usize, f: F) -> T { BorrowWindowContext::read_with(&*self.view_context, window_id, f) } @@ -4013,7 +4021,7 @@ impl ViewHandle { cx.read_view(self) } - pub fn read_with(&self, cx: &C, read: F) -> S + pub fn read_with(&self, cx: &C, read: F) -> C::Return where C: BorrowWindowContext, F: FnOnce(&T, &ViewContext) -> S, @@ -4024,7 +4032,7 @@ impl ViewHandle { }) } - pub fn update(&self, cx: &mut C, update: F) -> S + pub fn update(&self, cx: &mut C, update: F) -> C::Return where C: BorrowWindowContext, F: FnOnce(&mut T, &mut ViewContext) -> S, diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 80f10374665f9c2215ca4b2582088dd43df3bb04..5c7947a44827d3eb23e6a490c7fdfaacd9e90bfb 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -406,6 +406,8 @@ impl BorrowAppContext for TestAppContext { } impl BorrowWindowContext for TestAppContext { + type Return = T; + fn read_with T>(&self, window_id: usize, f: F) -> T { self.cx .borrow() diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 7cdcbc2c8fc593452fcc9025fc71545eda3f5ceb..671d2b38c77c9d71201e77c87fd8ef21d077b24b 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -142,6 +142,8 @@ impl BorrowAppContext for WindowContext<'_> { } impl BorrowWindowContext for WindowContext<'_> { + type Return = T; + fn read_with T>(&self, window_id: usize, f: F) -> T { if self.window_id == window_id { f(self) From ee1b4a52cc41cb7c331ad9936f1c1cbfb220057e Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Thu, 3 Aug 2023 18:57:43 -0400 Subject: [PATCH 11/53] Add `PathExt` trait (#2823) This PR adds a `PathExt` trait. It pulls in our existing `compact()` function, as a method, and then adds a method, and testing, for `icon_suffix()`. A test was added to fix: - https://github.com/zed-industries/community/issues/1877 Release Notes: - Fixed a bug where file icons would not be registered for files with with `.` characters in their name ([#1877](https://github.com/zed-industries/community/issues/1877)). --- crates/editor/src/items.rs | 9 +- crates/project_panel/src/file_associations.rs | 11 +- .../src/highlighted_workspace_location.rs | 3 +- crates/recent_projects/src/recent_projects.rs | 3 +- crates/util/src/paths.rs | 118 ++++++++++++------ 5 files changed, 88 insertions(+), 56 deletions(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 7c8fe12aa068cfd97566142d6436f0ce4f0300d2..b99977a60eb45dc3a0a616169067063e6c4e691f 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -28,7 +28,10 @@ use std::{ path::{Path, PathBuf}, }; use text::Selection; -use util::{paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt}; +use util::{ + paths::{PathExt, FILE_ROW_COLUMN_DELIMITER}, + ResultExt, TryFutureExt, +}; use workspace::item::{BreadcrumbText, FollowableItemHandle}; use workspace::{ item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem}, @@ -546,9 +549,7 @@ impl Item for Editor { .and_then(|f| f.as_local())? .abs_path(cx); - let file_path = util::paths::compact(&file_path) - .to_string_lossy() - .to_string(); + let file_path = file_path.compact().to_string_lossy().to_string(); Some(file_path.into()) } diff --git a/crates/project_panel/src/file_associations.rs b/crates/project_panel/src/file_associations.rs index 2694fa1697f31d76fb4f37e1632c184ac2e9ce8f..f2692b96db2c317e1fc4d7e96c9d5109ac502258 100644 --- a/crates/project_panel/src/file_associations.rs +++ b/crates/project_panel/src/file_associations.rs @@ -4,7 +4,7 @@ use collections::HashMap; use gpui::{AppContext, AssetSource}; use serde_derive::Deserialize; -use util::iife; +use util::{iife, paths::PathExt}; #[derive(Deserialize, Debug)] struct TypeConfig { @@ -48,14 +48,7 @@ impl FileAssociations { // FIXME: Associate a type with the languages and have the file's langauge // override these associations iife!({ - let suffix = path - .file_name() - .and_then(|os_str| os_str.to_str()) - .and_then(|file_name| { - file_name - .find('.') - .and_then(|dot_index| file_name.get(dot_index + 1..)) - })?; + let suffix = path.icon_suffix()?; this.suffixes .get(suffix) diff --git a/crates/recent_projects/src/highlighted_workspace_location.rs b/crates/recent_projects/src/highlighted_workspace_location.rs index d3ecfb74fb637bf64d283c99ca8d939ec4acd5e3..f915cb24edf89cd1c6f0797e2fc7b5f8c790399f 100644 --- a/crates/recent_projects/src/highlighted_workspace_location.rs +++ b/crates/recent_projects/src/highlighted_workspace_location.rs @@ -5,6 +5,7 @@ use gpui::{ elements::{Label, LabelStyle}, AnyElement, Element, View, }; +use util::paths::PathExt; use workspace::WorkspaceLocation; pub struct HighlightedText { @@ -61,7 +62,7 @@ impl HighlightedWorkspaceLocation { .paths() .iter() .map(|path| { - let path = util::paths::compact(&path); + let path = path.compact(); let highlighted_text = Self::highlights_for_path( path.as_ref(), &string_match.positions, diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 5bf9ba6ccfefb6f89c46a3ed1b934e931d7ce827..7a09ac259f5039ec9ff4f5656ab6b10bd5a3527d 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -11,6 +11,7 @@ use highlighted_workspace_location::HighlightedWorkspaceLocation; use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate, PickerEvent}; use std::sync::Arc; +use util::paths::PathExt; use workspace::{ notifications::simple_message_notification::MessageNotification, Workspace, WorkspaceLocation, WORKSPACE_DB, @@ -134,7 +135,7 @@ impl PickerDelegate for RecentProjectsDelegate { let combined_string = location .paths() .iter() - .map(|path| util::paths::compact(&path).to_string_lossy().into_owned()) + .map(|path| path.compact().to_string_lossy().into_owned()) .collect::>() .join(""); StringMatchCandidate::new(id, combined_string) diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 5df0ed12e96dd13af148bafc1d2682585389cc57..7e0b240570f249b7dc97b17ea0fc85581fd44410 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -30,49 +30,47 @@ pub mod legacy { } } -/// Compacts a given file path by replacing the user's home directory -/// prefix with a tilde (`~`). -/// -/// # Arguments -/// -/// * `path` - A reference to a `Path` representing the file path to compact. -/// -/// # Examples -/// -/// ``` -/// use std::path::{Path, PathBuf}; -/// use util::paths::compact; -/// let path: PathBuf = [ -/// util::paths::HOME.to_string_lossy().to_string(), -/// "some_file.txt".to_string(), -/// ] -/// .iter() -/// .collect(); -/// if cfg!(target_os = "linux") || cfg!(target_os = "macos") { -/// assert_eq!(compact(&path).to_str(), Some("~/some_file.txt")); -/// } else { -/// assert_eq!(compact(&path).to_str(), path.to_str()); -/// } -/// ``` -/// -/// # Returns -/// -/// * A `PathBuf` containing the compacted file path. If the input path -/// does not have the user's home directory prefix, or if we are not on -/// Linux or macOS, the original path is returned unchanged. -pub fn compact(path: &Path) -> PathBuf { - if cfg!(target_os = "linux") || cfg!(target_os = "macos") { - match path.strip_prefix(HOME.as_path()) { - Ok(relative_path) => { - let mut shortened_path = PathBuf::new(); - shortened_path.push("~"); - shortened_path.push(relative_path); - shortened_path +pub trait PathExt { + fn compact(&self) -> PathBuf; + fn icon_suffix(&self) -> Option<&str>; +} + +impl> PathExt for T { + /// Compacts a given file path by replacing the user's home directory + /// prefix with a tilde (`~`). + /// + /// # Returns + /// + /// * A `PathBuf` containing the compacted file path. If the input path + /// does not have the user's home directory prefix, or if we are not on + /// Linux or macOS, the original path is returned unchanged. + fn compact(&self) -> PathBuf { + if cfg!(target_os = "linux") || cfg!(target_os = "macos") { + match self.as_ref().strip_prefix(HOME.as_path()) { + Ok(relative_path) => { + let mut shortened_path = PathBuf::new(); + shortened_path.push("~"); + shortened_path.push(relative_path); + shortened_path + } + Err(_) => self.as_ref().to_path_buf(), } - Err(_) => path.to_path_buf(), + } else { + self.as_ref().to_path_buf() } - } else { - path.to_path_buf() + } + + fn icon_suffix(&self) -> Option<&str> { + let file_name = self.as_ref().file_name()?.to_str()?; + + if file_name.starts_with(".") { + return file_name.strip_prefix("."); + } + + self.as_ref() + .extension() + .map(|extension| extension.to_str()) + .flatten() } } @@ -279,4 +277,42 @@ mod tests { ); } } + + #[test] + fn test_path_compact() { + let path: PathBuf = [ + HOME.to_string_lossy().to_string(), + "some_file.txt".to_string(), + ] + .iter() + .collect(); + if cfg!(target_os = "linux") || cfg!(target_os = "macos") { + assert_eq!(path.compact().to_str(), Some("~/some_file.txt")); + } else { + assert_eq!(path.compact().to_str(), path.to_str()); + } + } + + #[test] + fn test_path_suffix() { + // No dots in name + let path = Path::new("/a/b/c/file_name.rs"); + assert_eq!(path.icon_suffix(), Some("rs")); + + // Single dot in name + let path = Path::new("/a/b/c/file.name.rs"); + assert_eq!(path.icon_suffix(), Some("rs")); + + // Multiple dots in name + let path = Path::new("/a/b/c/long.file.name.rs"); + assert_eq!(path.icon_suffix(), Some("rs")); + + // Hidden file, no extension + let path = Path::new("/a/b/c/.gitignore"); + assert_eq!(path.icon_suffix(), Some("gitignore")); + + // Hidden file, with extension + let path = Path::new("/a/b/c/.eslintrc.js"); + assert_eq!(path.icon_suffix(), Some("eslintrc.js")); + } } From afcc0d621b8524d4b3cfa9a5ed19b00c11666348 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Aug 2023 17:03:39 -0600 Subject: [PATCH 12/53] WIP --- crates/editor/src/editor_tests.rs | 98 ++++---- crates/editor/src/element.rs | 6 +- crates/editor/src/inlay_hint_cache.rs | 14 +- crates/file_finder/src/file_finder.rs | 20 +- crates/gpui/src/app.rs | 264 ++++++++++++--------- crates/gpui/src/app/test_app_context.rs | 10 +- crates/gpui/src/app/window.rs | 10 +- crates/language_tools/src/lsp_log_tests.rs | 2 +- crates/project_panel/src/project_panel.rs | 8 +- crates/search/src/project_search.rs | 4 +- crates/terminal_view/src/terminal_view.rs | 2 +- crates/workspace/src/workspace.rs | 108 ++++----- crates/zed/src/zed.rs | 6 +- 13 files changed, 295 insertions(+), 257 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 96921643d45a1b7c1083dab5df471b3bd95adb2d..a114cd437b16bbad580d6e732bb004ac352d822a 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -64,7 +64,7 @@ fn test_edit_events(cx: &mut TestAppContext) { Editor::for_buffer(buffer.clone(), None, cx) } }) - .detach(cx); + .root(cx); let editor2 = cx .add_window({ let events = events.clone(); @@ -81,7 +81,7 @@ fn test_edit_events(cx: &mut TestAppContext) { Editor::for_buffer(buffer.clone(), None, cx) } }) - .detach(cx); + .root(cx); assert_eq!(mem::take(&mut *events.borrow_mut()), []); // Mutating editor 1 will emit an `Edited` event only for that editor. @@ -179,7 +179,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let editor = cx .add_window(|cx| build_editor(buffer.clone(), cx)) - .detach(cx); + .root(cx); editor.update(cx, |editor, cx| { editor.start_transaction_at(now, cx); @@ -354,7 +354,7 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); editor.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); }); @@ -423,7 +423,7 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx); @@ -471,7 +471,7 @@ fn test_clone(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple(&text, cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); editor.update(cx, |editor, cx| { editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone())); @@ -489,7 +489,7 @@ fn test_clone(cx: &mut TestAppContext) { .update(cx, |editor, cx| { cx.add_window(Default::default(), |cx| editor.clone(cx)) }) - .detach(cx); + .root(cx); let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)); let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)); @@ -639,7 +639,7 @@ fn test_cancel(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx); @@ -704,7 +704,7 @@ fn test_fold_action(cx: &mut TestAppContext) { ); build_editor(buffer.clone(), cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -774,7 +774,7 @@ fn test_move_cursor(cx: &mut TestAppContext) { let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx)); let view = cx .add_window(|cx| build_editor(buffer.clone(), cx)) - .detach(cx); + .root(cx); buffer.update(cx, |buffer, cx| { buffer.edit( @@ -854,7 +854,7 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); build_editor(buffer.clone(), cx) }) - .detach(cx); + .root(cx); assert_eq!('ⓐ'.len_utf8(), 3); assert_eq!('α'.len_utf8(), 2); @@ -961,7 +961,7 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); build_editor(buffer.clone(), cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]); @@ -1013,7 +1013,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("abc\n def", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -1178,7 +1178,7 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -1233,7 +1233,7 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) { MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.set_wrap_width(Some(140.), cx); @@ -1568,7 +1568,7 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("one two three four", cx); build_editor(buffer.clone(), cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -1606,7 +1606,7 @@ fn test_newline(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); build_editor(buffer.clone(), cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -1651,7 +1651,7 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) { }); editor }) - .detach(cx); + .root(cx); editor.update(cx, |editor, cx| { // Edit the buffer directly, deleting ranges surrounding the editor's selections @@ -1863,7 +1863,7 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) { editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20])); editor }) - .detach(cx); + .root(cx); editor.update(cx, |editor, cx| { // Edit the buffer directly, deleting ranges surrounding the editor's selections @@ -2375,7 +2375,7 @@ fn test_delete_line(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -2400,7 +2400,7 @@ fn test_delete_line(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)]) @@ -2704,7 +2704,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -2732,7 +2732,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -2761,7 +2761,7 @@ fn test_move_line_up_down(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.fold_ranges( vec![ @@ -2862,7 +2862,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); editor.update(cx, |editor, cx| { let snapshot = editor.buffer.read(cx).snapshot(cx); editor.insert_blocks( @@ -3182,7 +3182,7 @@ fn test_select_all(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.select_all(&SelectAll, cx); assert_eq!( @@ -3201,7 +3201,7 @@ fn test_select_line(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { s.select_display_ranges([ @@ -3250,7 +3250,7 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.fold_ranges( vec![ @@ -3323,7 +3323,7 @@ fn test_add_selection_above_below(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); build_editor(buffer, cx) }) - .detach(cx); + .root(cx); view.update(cx, |view, cx| { view.change_selections(None, cx, |s| { @@ -3608,7 +3608,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -3771,7 +3771,7 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); editor .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx)) .await; @@ -4334,7 +4334,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -4482,7 +4482,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); editor .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -4572,7 +4572,7 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { ); let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); - let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); editor.update(cx, |editor, cx| { let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap(); @@ -4702,7 +4702,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { let fake_server = fake_servers.next().await.unwrap(); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); assert!(cx.read(|cx| editor.is_dirty(cx))); @@ -4814,7 +4814,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { let fake_server = fake_servers.next().await.unwrap(); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); assert!(cx.read(|cx| editor.is_dirty(cx))); @@ -4928,7 +4928,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { let fake_server = fake_servers.next().await.unwrap(); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let editor = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); let format = editor.update(cx, |editor, cx| { @@ -5706,7 +5706,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { multibuffer }); - let view = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); + let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx); view.update(cx, |view, cx| { assert_eq!(view.text(cx), "aaaa\nbbbb"); view.change_selections(None, cx, |s| { @@ -5776,7 +5776,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { multibuffer }); - let view = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); + let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx); view.update(cx, |view, cx| { let (expected_text, selection_ranges) = marked_text_ranges( indoc! {" @@ -5869,7 +5869,7 @@ fn test_refresh_selections(cx: &mut TestAppContext) { ); editor }) - .detach(cx); + .root(cx); // Refreshing selections is a no-op when excerpts haven't changed. editor.update(cx, |editor, cx| { @@ -5950,7 +5950,7 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) { ); editor }) - .detach(cx); + .root(cx); multibuffer.update(cx, |multibuffer, cx| { multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx); @@ -6013,7 +6013,7 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let view = cx.add_window(|cx| build_editor(buffer, cx)).detach(cx); + let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) .await; @@ -6054,7 +6054,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); build_editor(buffer.clone(), cx) }) - .detach(cx); + .root(cx); editor.update(cx, |editor, cx| { struct Type1; @@ -6145,7 +6145,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) { }); let leader = cx .add_window(|cx| build_editor(buffer.clone(), cx)) - .detach(cx); + .root(cx); let follower = cx .update(|cx| { cx.add_window( @@ -6156,7 +6156,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) { |cx| build_editor(buffer.clone(), cx), ) }) - .detach(cx); + .root(cx); let is_still_following = Rc::new(RefCell::new(true)); let follower_edit_event_count = Rc::new(RefCell::new(0)); @@ -6289,7 +6289,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let leader = pane.update(cx, |_, cx| { @@ -7033,7 +7033,7 @@ async fn test_copilot_multibuffer( ); multibuffer }); - let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx); handle_copilot_completion_request( &copilot_lsp, @@ -7163,7 +7163,7 @@ async fn test_copilot_disabled_globs( ); multibuffer }); - let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).detach(cx); + let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx); let mut copilot_requests = copilot_lsp .handle_request::(move |_params, _cx| async move { @@ -7244,7 +7244,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) { project.update(cx, |project, _| project.languages().add(Arc::new(language))); let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index dc40e7fb8568aeec2bf5d4fe6f4168e4847c3558..2d4b273f5ef0523f8ef8d9d9cd107c7e30bd659d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3007,7 +3007,7 @@ mod tests { let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); Editor::new(EditorMode::Full, buffer, None, None, cx) }) - .detach(cx); + .root(cx); let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let layouts = editor.update(cx, |editor, cx| { @@ -3028,7 +3028,7 @@ mod tests { let buffer = MultiBuffer::build_simple("", cx); Editor::new(EditorMode::Full, buffer, None, None, cx) }) - .detach(cx); + .root(cx); editor.update(cx, |editor, cx| { editor.set_placeholder_text("hello", cx); @@ -3240,7 +3240,7 @@ mod tests { let buffer = MultiBuffer::build_simple(&input_text, cx); Editor::new(editor_mode, buffer, None, None, cx) }) - .detach(cx); + .root(cx); let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let (_, layout_state) = editor.update(cx, |editor, cx| { diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 089cbb29958c727a0b7760018833a279534a225a..47a27c049f91658fdd848381f86207642bce15c9 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -1138,7 +1138,7 @@ mod tests { let project = Project::test(fs, ["/a".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -1840,7 +1840,7 @@ mod tests { project.update(cx, |project, _| project.languages().add(Arc::new(language))); let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -1995,7 +1995,7 @@ mod tests { }); let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -2083,7 +2083,7 @@ mod tests { cx.foreground().run_until_parked(); let editor = cx .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)) - .detach(cx); + .root(cx); let editor_edited = Arc::new(AtomicBool::new(false)); let fake_server = fake_servers.next().await.unwrap(); let closure_editor_edited = Arc::clone(&editor_edited); @@ -2337,7 +2337,7 @@ all hints should be invalidated and requeried for all of its visible excerpts" }); let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() @@ -2384,7 +2384,7 @@ all hints should be invalidated and requeried for all of its visible excerpts" cx.foreground().run_until_parked(); let editor = cx .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)) - .detach(cx); + .root(cx); let editor_edited = Arc::new(AtomicBool::new(false)); let fake_server = fake_servers.next().await.unwrap(); let closure_editor_edited = Arc::clone(&editor_edited); @@ -2574,7 +2574,7 @@ all hints should be invalidated and requeried for all of its visible excerpts" project.update(cx, |project, _| project.languages().add(Arc::new(language))); let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let worktree_id = workspace.update(cx, |workspace, cx| { workspace.project().read_with(cx, |project, cx| { project.worktrees(cx).next().unwrap().read(cx).id() diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 2c9d9c0c71e390c256e7db785ee06d707039a8a1..12bf3242627619d390409bba044c0fa9cdcce3d4 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -842,7 +842,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let finder = cx .add_window(|cx| { Picker::new( @@ -856,7 +856,7 @@ mod tests { cx, ) }) - .detach(cx); + .root(cx); let query = test_path_like("hi"); finder @@ -940,7 +940,7 @@ mod tests { .await; let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let finder = cx .add_window(|cx| { Picker::new( @@ -954,7 +954,7 @@ mod tests { cx, ) }) - .detach(cx); + .root(cx); finder .update(cx, |f, cx| { f.delegate_mut().spawn_search(test_path_like("hi"), cx) @@ -980,7 +980,7 @@ mod tests { .await; let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let finder = cx .add_window(|cx| { Picker::new( @@ -994,7 +994,7 @@ mod tests { cx, ) }) - .detach(cx); + .root(cx); // Even though there is only one worktree, that worktree's filename // is included in the matching, because the worktree is a single file. @@ -1051,7 +1051,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1); @@ -1078,7 +1078,7 @@ mod tests { cx, ) }) - .detach(cx); + .root(cx); finder .update(cx, |f, cx| { @@ -1117,7 +1117,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let finder = cx .add_window(|cx| { Picker::new( @@ -1131,7 +1131,7 @@ mod tests { cx, ) }) - .detach(cx); + .root(cx); finder .update(cx, |f, cx| { f.delegate_mut().spawn_search(test_path_like("dir"), cx) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 9b847e9c0cfdea426902ea59e93cec3a0c349fdf..dce0b0e5f0ceca28e7503027720cfc39be351e24 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -130,12 +130,12 @@ pub trait BorrowAppContext { } pub trait BorrowWindowContext { - type Return; + type Result; - fn read_with(&self, window_id: usize, f: F) -> Self::Return + fn read_window_with(&self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&WindowContext) -> T; - fn update(&mut self, window_id: usize, f: F) -> Self::Return + fn update_window(&mut self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T; } @@ -458,6 +458,26 @@ impl BorrowAppContext for AsyncAppContext { } } +impl BorrowWindowContext for AsyncAppContext { + type Result = Option; + + fn read_window_with(&self, window_id: usize, f: F) -> Self::Result + where + F: FnOnce(&WindowContext) -> T, + { + self.0.borrow().read_with(|cx| cx.read_window(window_id, f)) + } + + fn update_window(&mut self, window_id: usize, f: F) -> Self::Result + where + F: FnOnce(&mut WindowContext) -> T, + { + self.0 + .borrow_mut() + .update(|cx| cx.update_window(window_id, f)) + } +} + type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize); type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext); @@ -2162,6 +2182,24 @@ impl BorrowAppContext for AppContext { } } +impl BorrowWindowContext for AppContext { + type Result = Option; + + fn read_window_with(&self, window_id: usize, f: F) -> Self::Result + where + F: FnOnce(&WindowContext) -> T, + { + AppContext::read_window(self, window_id, f) + } + + fn update_window(&mut self, window_id: usize, f: F) -> Self::Result + where + F: FnOnce(&mut WindowContext) -> T, + { + AppContext::update_window(self, window_id, f) + } +} + #[derive(Debug)] pub enum ParentId { View(usize), @@ -3360,14 +3398,18 @@ impl BorrowAppContext for ViewContext<'_, '_, V> { } impl BorrowWindowContext for ViewContext<'_, '_, V> { - type Return = T; + type Result = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_with(&*self.window_context, window_id, f) + fn read_window_with T>(&self, window_id: usize, f: F) -> T { + BorrowWindowContext::read_window_with(&*self.window_context, window_id, f) } - fn update T>(&mut self, window_id: usize, f: F) -> T { - BorrowWindowContext::update(&mut *self.window_context, window_id, f) + fn update_window T>( + &mut self, + window_id: usize, + f: F, + ) -> T { + BorrowWindowContext::update_window(&mut *self.window_context, window_id, f) } } @@ -3467,14 +3509,18 @@ impl BorrowAppContext for LayoutContext<'_, '_, '_, V> { } impl BorrowWindowContext for LayoutContext<'_, '_, '_, V> { - type Return = T; + type Result = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_with(&*self.view_context, window_id, f) + fn read_window_with T>(&self, window_id: usize, f: F) -> T { + BorrowWindowContext::read_window_with(&*self.view_context, window_id, f) } - fn update T>(&mut self, window_id: usize, f: F) -> T { - BorrowWindowContext::update(&mut *self.view_context, window_id, f) + fn update_window T>( + &mut self, + window_id: usize, + f: F, + ) -> T { + BorrowWindowContext::update_window(&mut *self.view_context, window_id, f) } } @@ -3521,14 +3567,18 @@ impl BorrowAppContext for EventContext<'_, '_, '_, V> { } impl BorrowWindowContext for EventContext<'_, '_, '_, V> { - type Return = T; + type Result = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_with(&*self.view_context, window_id, f) + fn read_window_with T>(&self, window_id: usize, f: F) -> T { + BorrowWindowContext::read_window_with(&*self.view_context, window_id, f) } - fn update T>(&mut self, window_id: usize, f: F) -> T { - BorrowWindowContext::update(&mut *self.view_context, window_id, f) + fn update_window T>( + &mut self, + window_id: usize, + f: F, + ) -> T { + BorrowWindowContext::update_window(&mut *self.view_context, window_id, f) } } @@ -3830,32 +3880,16 @@ impl WindowHandle { self.any_handle.id() } - pub fn root(&self, cx: &impl BorrowAppContext) -> ViewHandle { + pub fn root(&self, cx: &C) -> C::Result> { self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap()) } - /// Keep this window open until it's explicitly closed. - // - // TODO: Implement window dropping behavior when we don't call this. - pub fn detach(mut self, cx: &impl BorrowAppContext) -> ViewHandle { - let root = self.root(cx); - let ref_counts = self.any_handle.ref_counts.take(); - #[cfg(any(test, feature = "test-support"))] - ref_counts - .unwrap() - .lock() - .leak_detector - .lock() - .handle_dropped(self.id(), self.any_handle.handle_id); - root - } - - pub fn read_with(&self, cx: &C, read: F) -> R + pub fn read_with(&self, cx: &C, read: F) -> C::Result where - C: BorrowAppContext, + C: BorrowWindowContext, F: FnOnce(&WindowContext) -> R, { - cx.read_with(|cx| cx.read_window(self.id(), read).unwrap()) + cx.read_window_with(self.id(), |cx| read(cx)) } pub fn update(&self, cx: &mut C, update: F) -> R @@ -3891,9 +3925,9 @@ impl WindowHandle { root_view.read(cx) } - pub fn read_root_with(&self, cx: &C, read: F) -> R + pub fn read_root_with(&self, cx: &C, read: F) -> C::Result where - C: BorrowAppContext, + C: BorrowWindowContext, F: FnOnce(&V, &ViewContext) -> R, { self.read_with(cx, |cx| { @@ -4021,25 +4055,25 @@ impl ViewHandle { cx.read_view(self) } - pub fn read_with(&self, cx: &C, read: F) -> C::Return + pub fn read_with(&self, cx: &C, read: F) -> C::Result where C: BorrowWindowContext, F: FnOnce(&T, &ViewContext) -> S, { - cx.read_with(self.window_id, |cx| { + cx.read_window_with(self.window_id, |cx| { let cx = ViewContext::immutable(cx, self.view_id); read(cx.read_view(self), &cx) }) } - pub fn update(&self, cx: &mut C, update: F) -> C::Return + pub fn update(&self, cx: &mut C, update: F) -> C::Result where C: BorrowWindowContext, F: FnOnce(&mut T, &mut ViewContext) -> S, { let mut update = Some(update); - cx.update(self.window_id, |cx| { + cx.update_window(self.window_id, |cx| { cx.update_view(self, &mut |view, cx| { let update = update.take().unwrap(); update(view, cx) @@ -5005,7 +5039,7 @@ mod tests { } #[crate::test(self)] - fn test_entity_release_hooks(cx: &mut AppContext) { + fn test_entity_release_hooks(cx: &mut TestAppContext) { struct Model { released: Rc>, } @@ -5048,7 +5082,7 @@ mod tests { let model = cx.add_model(|_| Model { released: model_released.clone(), }); - let window = cx.add_window(Default::default(), |_| View { + let window = cx.add_window(|_| View { released: view_released.clone(), }); let view = window.root(cx); @@ -5056,16 +5090,18 @@ mod tests { assert!(!model_released.get()); assert!(!view_released.get()); - cx.observe_release(&model, { - let model_release_observed = model_release_observed.clone(); - move |_, _| model_release_observed.set(true) - }) - .detach(); - cx.observe_release(&view, { - let view_release_observed = view_release_observed.clone(); - move |_, _| view_release_observed.set(true) - }) - .detach(); + cx.update(|cx| { + cx.observe_release(&model, { + let model_release_observed = model_release_observed.clone(); + move |_, _| model_release_observed.set(true) + }) + .detach(); + cx.observe_release(&view, { + let view_release_observed = view_release_observed.clone(); + move |_, _| view_release_observed.set(true) + }) + .detach(); + }); cx.update(move |_| { drop(model); @@ -5795,7 +5831,7 @@ mod tests { } #[crate::test(self)] - fn test_dispatch_action(cx: &mut AppContext) { + fn test_dispatch_action(cx: &mut TestAppContext) { struct ViewA { id: usize, child: Option, @@ -5846,68 +5882,70 @@ mod tests { impl_actions!(test, [Action]); let actions = Rc::new(RefCell::new(Vec::new())); + let observed_actions = Rc::new(RefCell::new(Vec::new())); - cx.add_global_action({ - let actions = actions.clone(); - move |_: &Action, _: &mut AppContext| { - actions.borrow_mut().push("global".to_string()); - } - }); + cx.update(|cx| { + cx.add_global_action({ + let actions = actions.clone(); + move |_: &Action, _: &mut AppContext| { + actions.borrow_mut().push("global".to_string()); + } + }); - cx.add_action({ - let actions = actions.clone(); - move |view: &mut ViewA, action: &Action, cx| { - assert_eq!(action.0, "bar"); - cx.propagate_action(); - actions.borrow_mut().push(format!("{} a", view.id)); - } - }); + cx.add_action({ + let actions = actions.clone(); + move |view: &mut ViewA, action: &Action, cx| { + assert_eq!(action.0, "bar"); + cx.propagate_action(); + actions.borrow_mut().push(format!("{} a", view.id)); + } + }); - cx.add_action({ - let actions = actions.clone(); - move |view: &mut ViewA, _: &Action, cx| { - if view.id != 1 { - cx.add_view(|cx| { - cx.propagate_action(); // Still works on a nested ViewContext - ViewB { id: 5, child: None } - }); + cx.add_action({ + let actions = actions.clone(); + move |view: &mut ViewA, _: &Action, cx| { + if view.id != 1 { + cx.add_view(|cx| { + cx.propagate_action(); // Still works on a nested ViewContext + ViewB { id: 5, child: None } + }); + } + actions.borrow_mut().push(format!("{} b", view.id)); } - actions.borrow_mut().push(format!("{} b", view.id)); - } - }); + }); - cx.add_action({ - let actions = actions.clone(); - move |view: &mut ViewB, _: &Action, cx| { - cx.propagate_action(); - actions.borrow_mut().push(format!("{} c", view.id)); - } - }); + cx.add_action({ + let actions = actions.clone(); + move |view: &mut ViewB, _: &Action, cx| { + cx.propagate_action(); + actions.borrow_mut().push(format!("{} c", view.id)); + } + }); - cx.add_action({ - let actions = actions.clone(); - move |view: &mut ViewB, _: &Action, cx| { - cx.propagate_action(); - actions.borrow_mut().push(format!("{} d", view.id)); - } - }); + cx.add_action({ + let actions = actions.clone(); + move |view: &mut ViewB, _: &Action, cx| { + cx.propagate_action(); + actions.borrow_mut().push(format!("{} d", view.id)); + } + }); - cx.capture_action({ - let actions = actions.clone(); - move |view: &mut ViewA, _: &Action, cx| { - cx.propagate_action(); - actions.borrow_mut().push(format!("{} capture", view.id)); - } - }); + cx.capture_action({ + let actions = actions.clone(); + move |view: &mut ViewA, _: &Action, cx| { + cx.propagate_action(); + actions.borrow_mut().push(format!("{} capture", view.id)); + } + }); - let observed_actions = Rc::new(RefCell::new(Vec::new())); - cx.observe_actions({ - let observed_actions = observed_actions.clone(); - move |action_id, _| observed_actions.borrow_mut().push(action_id) - }) - .detach(); + cx.observe_actions({ + let observed_actions = observed_actions.clone(); + move |action_id, _| observed_actions.borrow_mut().push(action_id) + }) + .detach(); + }); - let window = cx.add_window(Default::default(), |_| ViewA { id: 1, child: None }); + let window = cx.add_window(|_| ViewA { id: 1, child: None }); let view_1 = window.root(cx); let view_2 = window.update(cx, |cx| { let child = cx.add_view(|_| ViewB { id: 2, child: None }); @@ -5956,7 +5994,7 @@ mod tests { // Remove view_1, which doesn't propagate the action - let window = cx.add_window(Default::default(), |_| ViewB { id: 2, child: None }); + let window = cx.add_window(|_| ViewB { id: 2, child: None }); let view_2 = window.root(cx); let view_3 = window.update(cx, |cx| { let child = cx.add_view(|_| ViewA { id: 3, child: None }); @@ -6457,7 +6495,7 @@ mod tests { } #[crate::test(self)] - fn test_refresh_windows(cx: &mut AppContext) { + fn test_refresh_windows(cx: &mut TestAppContext) { struct View(usize); impl super::Entity for View { @@ -6474,7 +6512,7 @@ mod tests { } } - let window = cx.add_window(Default::default(), |_| View(0)); + let window = cx.add_window(|_| View(0)); let root_view = window.root(cx); window.update(cx, |cx| { assert_eq!( diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 5c7947a44827d3eb23e6a490c7fdfaacd9e90bfb..0165c52e9f3fff9453c43484208c202a6a39138a 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -406,16 +406,20 @@ impl BorrowAppContext for TestAppContext { } impl BorrowWindowContext for TestAppContext { - type Return = T; + type Result = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { + fn read_window_with T>(&self, window_id: usize, f: F) -> T { self.cx .borrow() .read_window(window_id, f) .expect("window was closed") } - fn update T>(&mut self, window_id: usize, f: F) -> T { + fn update_window T>( + &mut self, + window_id: usize, + f: F, + ) -> T { self.cx .borrow_mut() .update_window(window_id, f) diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 671d2b38c77c9d71201e77c87fd8ef21d077b24b..0149c310daaadd773e54dd58314261eb1864ecb6 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -142,9 +142,9 @@ impl BorrowAppContext for WindowContext<'_> { } impl BorrowWindowContext for WindowContext<'_> { - type Return = T; + type Result = T; - fn read_with T>(&self, window_id: usize, f: F) -> T { + fn read_window_with T>(&self, window_id: usize, f: F) -> T { if self.window_id == window_id { f(self) } else { @@ -152,7 +152,11 @@ impl BorrowWindowContext for WindowContext<'_> { } } - fn update T>(&mut self, window_id: usize, f: F) -> T { + fn update_window T>( + &mut self, + window_id: usize, + f: F, + ) -> T { if self.window_id == window_id { f(self) } else { diff --git a/crates/language_tools/src/lsp_log_tests.rs b/crates/language_tools/src/lsp_log_tests.rs index ce05a417ad8fc6cebc61ee236d2289b8b73e2170..d26000ebc73b022c15dbf02ccdb3053763264118 100644 --- a/crates/language_tools/src/lsp_log_tests.rs +++ b/crates/language_tools/src/lsp_log_tests.rs @@ -63,7 +63,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) { let log_view = cx .add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx)) - .detach(cx); + .root(cx); language_server.notify::(lsp::LogMessageParams { message: "hello from the server".into(), diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index fdc5ea108a812459f374fdd0a1386fe31bb9830a..021ea2d3bc3a817342e9eac684496ffeee5026bb 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1782,7 +1782,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); assert_eq!( visible_entries_as_strings(&panel, 0..50, cx), @@ -2327,7 +2327,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); panel.update(cx, |panel, cx| { @@ -2641,7 +2641,7 @@ mod tests { let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let new_search_events_count = Arc::new(AtomicUsize::new(0)); @@ -2730,7 +2730,7 @@ mod tests { let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); panel.update(cx, |panel, cx| { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index e57edd3b14709aea1056965b59e173bc9e404a23..0db66b4e378a56c97e2e2d1290742e52e1329bda 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1449,7 +1449,7 @@ pub mod tests { let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); let search_view = cx .add_window(|cx| ProjectSearchView::new(search.clone(), cx)) - .detach(cx); + .root(cx); search_view.update(cx, |search_view, cx| { search_view @@ -1754,7 +1754,7 @@ pub mod tests { }); let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let active_item = cx.read(|cx| { workspace diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 874978b4fc567526e6bd17d4f6edd3fa9a670c3c..a600046ac2861fb7b0b1250c65d4b31c1af3166c 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -1072,7 +1072,7 @@ mod tests { let project = Project::test(params.fs.clone(), [], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); (project, workspace) } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3222ea2eb8cab97976c81ad6ad5c43668cf46655..2efa9f8daac69c872bda6cb3f7936d52d8d0222b 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -793,68 +793,60 @@ impl Workspace { DB.next_id().await.unwrap_or(0) }; - let window = requesting_window_id - .and_then(|window_id| { - cx.update(|cx| { - cx.replace_root_view(window_id, |cx| { - Workspace::new( - workspace_id, - project_handle.clone(), - app_state.clone(), - cx, - ) - }) + let window = requesting_window_id.and_then(|window_id| { + cx.update(|cx| { + cx.replace_root_view(window_id, |cx| { + Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) }) }) - .unwrap_or_else(|| { - let window_bounds_override = window_bounds_env_override(&cx); - let (bounds, display) = if let Some(bounds) = window_bounds_override { - (Some(bounds), None) - } else { - serialized_workspace - .as_ref() - .and_then(|serialized_workspace| { - let display = serialized_workspace.display?; - let mut bounds = serialized_workspace.bounds?; - - // Stored bounds are relative to the containing display. - // So convert back to global coordinates if that screen still exists - if let WindowBounds::Fixed(mut window_bounds) = bounds { - if let Some(screen) = cx.platform().screen_by_id(display) { - let screen_bounds = screen.bounds(); - window_bounds.set_origin_x( - window_bounds.origin_x() + screen_bounds.origin_x(), - ); - window_bounds.set_origin_y( - window_bounds.origin_y() + screen_bounds.origin_y(), - ); - bounds = WindowBounds::Fixed(window_bounds); - } else { - // Screen no longer exists. Return none here. - return None; - } + }); + let window = window.unwrap_or_else(|| { + let window_bounds_override = window_bounds_env_override(&cx); + let (bounds, display) = if let Some(bounds) = window_bounds_override { + (Some(bounds), None) + } else { + serialized_workspace + .as_ref() + .and_then(|serialized_workspace| { + let display = serialized_workspace.display?; + let mut bounds = serialized_workspace.bounds?; + + // Stored bounds are relative to the containing display. + // So convert back to global coordinates if that screen still exists + if let WindowBounds::Fixed(mut window_bounds) = bounds { + if let Some(screen) = cx.platform().screen_by_id(display) { + let screen_bounds = screen.bounds(); + window_bounds.set_origin_x( + window_bounds.origin_x() + screen_bounds.origin_x(), + ); + window_bounds.set_origin_y( + window_bounds.origin_y() + screen_bounds.origin_y(), + ); + bounds = WindowBounds::Fixed(window_bounds); + } else { + // Screen no longer exists. Return none here. + return None; } + } - Some((bounds, display)) - }) - .unzip() - }; - - // Use the serialized workspace to construct the new window - cx.add_window( - (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), - |cx| { - Workspace::new( - workspace_id, - project_handle.clone(), - app_state.clone(), - cx, - ) - }, - ) - }); + Some((bounds, display)) + }) + .unzip() + }; + + // Use the serialized workspace to construct the new window + cx.add_window( + (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), + |cx| { + Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) + }, + ) + }); + + // We haven't yielded the main thread since obtaining the window handle, + // so the window exists. + let workspace = window.root(&cx).unwrap(); - let workspace = window.root(&cx); (app_state.initialize_workspace)( workspace.downgrade(), serialized_workspace.is_some(), @@ -3985,7 +3977,7 @@ pub fn join_remote_project( ), |cx| Workspace::new(0, project, app_state.clone(), cx), ); - let workspace = window.root(&cx); + let workspace = window.root(&cx).unwrap(); (app_state.initialize_workspace)( workspace.downgrade(), false, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1770c5648e1fd2f8fe2a5ed31354f5525629a04f..a459122cfc9a29b209f8d6cbcf7031f739781857 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -985,7 +985,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1566,7 +1566,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project.clone(), cx)) - .detach(cx); + .root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let entries = cx.read(|cx| workspace.file_project_paths(cx)); @@ -1845,7 +1845,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let workspace = cx .add_window(|cx| Workspace::test_new(project, cx)) - .detach(cx); + .root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let entries = cx.read(|cx| workspace.file_project_paths(cx)); From 485c0a482ee8e7b2a2014f1129ca060d4554af0c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Aug 2023 17:11:47 -0600 Subject: [PATCH 13/53] Don't refcount window handles --- crates/collab/src/tests/integration_tests.rs | 4 +- .../src/incoming_call_notification.rs | 2 +- .../src/project_shared_notification.rs | 2 +- crates/command_palette/src/command_palette.rs | 2 +- crates/copilot/src/sign_in.rs | 4 +- crates/diagnostics/src/diagnostics.rs | 4 +- crates/editor/src/editor_tests.rs | 2 +- .../src/test/editor_lsp_test_context.rs | 2 +- crates/editor/src/test/editor_test_context.rs | 2 +- crates/file_finder/src/file_finder.rs | 22 ++-- crates/gpui/src/app.rs | 100 ++++-------------- crates/gpui/src/app/ref_counts.rs | 22 ---- crates/gpui/src/app/test_app_context.rs | 4 +- crates/gpui/src/app/window.rs | 2 +- crates/project_panel/src/project_panel.rs | 8 +- crates/project_symbols/src/project_symbols.rs | 2 +- crates/search/src/buffer_search.rs | 10 +- crates/search/src/project_search.rs | 4 +- crates/workspace/src/workspace.rs | 36 +++---- crates/zed/src/zed.rs | 12 +-- 20 files changed, 83 insertions(+), 163 deletions(-) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 1a8e6d938d6d6caac2b8caee501e1088ddeea5b3..037f97f71abc8a5961b3693c461522af985fbb55 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3446,7 +3446,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx)); let mut editor_cx_a = EditorTestContext { cx: cx_a, - window_id: window_a.id(), + window_id: window_a.window_id(), editor: editor_a, }; @@ -3459,7 +3459,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx)); let mut editor_cx_b = EditorTestContext { cx: cx_b, - window_id: window_b.id(), + window_id: window_b.window_id(), editor: editor_b, }; diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index a9c5e697a5f60d0cb6f9c0e6baf7cac50aba192a..770f5d5795c980df75a4ce0f888f72745f63970e 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -49,7 +49,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { |_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()), ); - notification_windows.push(window.id()); + notification_windows.push(window.window_id()); } } } diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 03ab91623b43270bf2592ced0f460b474fd0c435..5be7e33bf3cea049569ce294dbb3d8b1942cf38f 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -52,7 +52,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { notification_windows .entry(*project_id) .or_insert(Vec::new()) - .push(window.id()); + .push(window.window_id()); } } room::Event::RemoteProjectUnshared { project_id } => { diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 7d4b4126b702774bf8c295d3b61f904ff7ebf80c..935358c2a126bfd59f90ffe89f7b7825063af8c9 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -297,7 +297,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), [], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let editor = cx.add_view(window_id, |cx| { let mut editor = Editor::single_line(None, cx); editor.set_text("abc", cx); diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index 659bee7445f0696d32bf7532c902907d7ddcf11c..0d5bb28ed688897e67ecccd816215f68ce573fd7 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -25,7 +25,7 @@ pub fn init(cx: &mut AppContext) { match &status { crate::Status::SigningIn { prompt } => { if let Some(code_verification_handle) = code_verification.as_mut() { - let window_id = code_verification_handle.id(); + let window_id = code_verification_handle.window_id(); let updated = cx.update_window(window_id, |cx| { code_verification_handle.update_root(cx, |code_verification, cx| { code_verification.set_status(status.clone(), cx) @@ -41,7 +41,7 @@ pub fn init(cx: &mut AppContext) { } Status::Authorized | Status::Unauthorized => { if let Some(code_verification) = code_verification.as_ref() { - let window_id = code_verification.id(); + let window_id = code_verification.window_id(); cx.update_window(window_id, |cx| { code_verification.update_root(cx, |code_verification, cx| { code_verification.set_status(status, cx) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 2444465be666124adb706bcf1833c50c77cee081..f2db0a7763054d19ea85b591de6e8b8d63272740 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -857,7 +857,7 @@ mod tests { let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); // Create some diagnostics project.update(cx, |project, cx| { @@ -1252,7 +1252,7 @@ mod tests { let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let view = cx.add_view(window_id, |cx| { ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index a114cd437b16bbad580d6e732bb004ac352d822a..e8913505cab0d579afa7d1621fa54398a985844e 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -525,7 +525,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) { let project = Project::test(fs, [], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); cx.add_view(window_id, |cx| { let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index f53115f224a1cfe6cb4a3c634786c7e7e8c18305..d25ca7bb88a86383166288fcb1ac6cd5cfd5997f 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -99,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> { Self { cx: EditorTestContext { cx, - window_id: window.id(), + window_id: window.window_id(), editor, }, lsp, diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index c7ea1b4f3852301e300c5ff3f1db1dff65f342a2..ac519764fdd8b671a92070118ec9b4937d135c64 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -39,7 +39,7 @@ impl<'a> EditorTestContext<'a> { let editor = window.root(cx); Self { cx, - window_id: window.id(), + window_id: window.window_id(), editor, } } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 12bf3242627619d390409bba044c0fa9cdcce3d4..84a45b083e6fdef0a2b4145e756f495fdc255300 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -619,7 +619,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.id(), Toggle); + cx.dispatch_action(window.window_id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); finder @@ -632,8 +632,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.id(), SelectNext); - cx.dispatch_action(window.id(), Confirm); + cx.dispatch_action(window.window_id(), SelectNext); + cx.dispatch_action(window.window_id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -674,7 +674,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.id(), Toggle); + cx.dispatch_action(window.window_id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -706,8 +706,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.id(), SelectNext); - cx.dispatch_action(window.id(), Confirm); + cx.dispatch_action(window.window_id(), SelectNext); + cx.dispatch_action(window.window_id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -758,7 +758,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.id(), Toggle); + cx.dispatch_action(window.window_id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -790,8 +790,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.id(), SelectNext); - cx.dispatch_action(window.id(), Confirm); + cx.dispatch_action(window.window_id(), SelectNext); + cx.dispatch_action(window.window_id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -1168,7 +1168,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1); @@ -1376,7 +1376,7 @@ mod tests { let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1,); diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index dce0b0e5f0ceca28e7503027720cfc39be351e24..90f910d255ff595bd1e2c0e1173296180e10c8fc 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1337,7 +1337,7 @@ impl AppContext { .open_window(window_id, window_options, this.foreground.clone()); let window = this.build_window(window_id, platform_window, build_root_view); this.windows.insert(window_id, window); - WindowHandle::new(window_id, this.ref_counts.clone()) + WindowHandle::new(window_id) }) } @@ -3863,21 +3863,21 @@ impl Clone for WeakModelHandle { impl Copy for WeakModelHandle {} pub struct WindowHandle { - any_handle: AnyWindowHandle, - view_type: PhantomData, + window_id: usize, + root_view_type: PhantomData, } #[allow(dead_code)] impl WindowHandle { - fn new(window_id: usize, ref_counts: Arc>) -> Self { + fn new(window_id: usize) -> Self { WindowHandle { - any_handle: AnyWindowHandle::new::(window_id, ref_counts), - view_type: PhantomData, + window_id, + root_view_type: PhantomData, } } - pub fn id(&self) -> usize { - self.any_handle.id() + pub fn window_id(&self) -> usize { + self.window_id } pub fn root(&self, cx: &C) -> C::Result> { @@ -3889,7 +3889,7 @@ impl WindowHandle { C: BorrowWindowContext, F: FnOnce(&WindowContext) -> R, { - cx.read_window_with(self.id(), |cx| read(cx)) + cx.read_window_with(self.window_id(), |cx| read(cx)) } pub fn update(&self, cx: &mut C, update: F) -> R @@ -3897,7 +3897,7 @@ impl WindowHandle { C: BorrowAppContext, F: FnOnce(&mut WindowContext) -> R, { - cx.update(|cx| cx.update_window(self.id(), update).unwrap()) + cx.update(|cx| cx.update_window(self.window_id(), update).unwrap()) } pub fn update_root(&self, cx: &mut C, update: F) -> R @@ -3905,7 +3905,7 @@ impl WindowHandle { C: BorrowAppContext, F: FnOnce(&mut V, &mut ViewContext) -> R, { - let window_id = self.id(); + let window_id = self.window_id(); cx.update(|cx| { cx.update_window(window_id, |cx| { cx.root_view() @@ -3920,7 +3920,9 @@ impl WindowHandle { pub fn read_root<'a>(&self, cx: &'a AppContext) -> &'a V { let root_view = cx - .read_window(self.id(), |cx| cx.root_view().clone().downcast().unwrap()) + .read_window(self.window_id(), |cx| { + cx.root_view().clone().downcast().unwrap() + }) .unwrap(); root_view.read(cx) } @@ -3948,66 +3950,6 @@ impl WindowHandle { } } -pub struct AnyWindowHandle { - window_id: usize, - root_view_type: TypeId, - ref_counts: Option>>, - - #[cfg(any(test, feature = "test-support"))] - handle_id: usize, -} - -impl AnyWindowHandle { - fn new(window_id: usize, ref_counts: Arc>) -> Self { - ref_counts.lock().inc_window(window_id); - - #[cfg(any(test, feature = "test-support"))] - let handle_id = ref_counts - .lock() - .leak_detector - .lock() - .handle_created(None, window_id); - - Self { - window_id, - root_view_type: TypeId::of::(), - ref_counts: Some(ref_counts), - #[cfg(any(test, feature = "test-support"))] - handle_id, - } - } - - pub fn id(&self) -> usize { - self.window_id - } - - pub fn downcast(self) -> Option> { - if TypeId::of::() == self.root_view_type { - Some(WindowHandle { - any_handle: self, - view_type: PhantomData, - }) - } else { - None - } - } -} - -impl Drop for AnyWindowHandle { - fn drop(&mut self) { - if let Some(ref_counts) = self.ref_counts.as_ref() { - ref_counts.lock().dec_window(self.window_id); - - #[cfg(any(test, feature = "test-support"))] - ref_counts - .lock() - .leak_detector - .lock() - .handle_dropped(self.window_id, self.handle_id); - } - } -} - #[repr(transparent)] pub struct ViewHandle { any_handle: AnyViewHandle, @@ -6281,7 +6223,7 @@ mod tests { // Check that global actions do not have a binding, even if a binding does exist in another view assert_eq!( - &available_actions(window.id(), view_1.id(), cx), + &available_actions(window.window_id(), view_1.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::GlobalAction", vec![]) @@ -6290,7 +6232,7 @@ mod tests { // Check that view 1 actions and bindings are available even when called from view 2 assert_eq!( - &available_actions(window.id(), view_2.id(), cx), + &available_actions(window.window_id(), view_2.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action2", vec![Keystroke::parse("b").unwrap()]), @@ -6353,7 +6295,7 @@ mod tests { ]); }); - let actions = cx.available_actions(window.id(), view.id()); + let actions = cx.available_actions(window.window_id(), view.id()); assert_eq!( actions[0].1.as_any().downcast_ref::(), Some(&ActionWithArg { arg: false }) @@ -6639,25 +6581,25 @@ mod tests { [("window 2", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_2.id())); + cx.simulate_window_activation(Some(window_2.window_id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 3", false), ("window 2", true)] ); - cx.simulate_window_activation(Some(window_1.id())); + cx.simulate_window_activation(Some(window_1.window_id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 2", false), ("window 1", true)] ); - cx.simulate_window_activation(Some(window_3.id())); + cx.simulate_window_activation(Some(window_3.window_id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 1", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_3.id())); + cx.simulate_window_activation(Some(window_3.window_id())); assert_eq!(mem::take(&mut *events.borrow_mut()), []); } diff --git a/crates/gpui/src/app/ref_counts.rs b/crates/gpui/src/app/ref_counts.rs index 74563d05bc00d403dd768fe08fd269da9a8bfb5a..f0c1699f165ea8100ccdfe1facbfb8a3ac1a2d8e 100644 --- a/crates/gpui/src/app/ref_counts.rs +++ b/crates/gpui/src/app/ref_counts.rs @@ -25,7 +25,6 @@ struct ElementStateRefCount { pub struct RefCounts { entity_counts: HashMap, element_state_counts: HashMap, - dropped_windows: HashSet, dropped_models: HashSet, dropped_views: HashSet<(usize, usize)>, dropped_element_states: HashSet, @@ -44,18 +43,6 @@ impl RefCounts { } } - pub fn inc_window(&mut self, window_id: usize) { - match self.entity_counts.entry(window_id) { - Entry::Occupied(mut entry) => { - *entry.get_mut() += 1; - } - Entry::Vacant(entry) => { - entry.insert(1); - self.dropped_windows.remove(&window_id); - } - } - } - pub fn inc_model(&mut self, model_id: usize) { match self.entity_counts.entry(model_id) { Entry::Occupied(mut entry) => { @@ -98,15 +85,6 @@ impl RefCounts { } } - pub fn dec_window(&mut self, window_id: usize) { - let count = self.entity_counts.get_mut(&window_id).unwrap(); - *count -= 1; - if *count == 0 { - self.entity_counts.remove(&window_id); - self.dropped_windows.insert(window_id); - } - } - pub fn dec_model(&mut self, model_id: usize) { let count = self.entity_counts.get_mut(&model_id).unwrap(); *count -= 1; diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 0165c52e9f3fff9453c43484208c202a6a39138a..3b574eb03ee3f6cfcae23cffff8f1c5359828c66 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -157,9 +157,9 @@ impl TestAppContext { .cx .borrow_mut() .add_window(Default::default(), build_root_view); - self.simulate_window_activation(Some(window.id())); + self.simulate_window_activation(Some(window.window_id())); - WindowHandle::new(window.id(), self.cx.borrow_mut().ref_counts.clone()) + WindowHandle::new(window.window_id()) } pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 0149c310daaadd773e54dd58314261eb1864ecb6..789341d46fa4788e24c7b4d7b1e44d0558e1f3b7 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -1165,7 +1165,7 @@ impl<'a> WindowContext<'a> { let root_view = self.add_view(|cx| build_root_view(cx)); self.window.focused_view_id = Some(root_view.id()); self.window.root_view = Some(root_view.into_any()); - WindowHandle::new(self.window_id, self.ref_counts.clone()) + WindowHandle::new(self.window_id) } pub fn add_view(&mut self, build_view: F) -> ViewHandle diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 021ea2d3bc3a817342e9eac684496ffeee5026bb..80847f9f4b97b3dc2cf2c32d6d11f0ec7dcbcc3a 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1872,7 +1872,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -2225,7 +2225,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -2402,7 +2402,7 @@ mod tests { let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); toggle_expand_dir(&panel, "src/test", cx); @@ -2493,7 +2493,7 @@ mod tests { let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "src/", cx); diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 8471f3a3a7014d2034438839f76ae9b4214ded52..4bd186fc98b3edb93bb4b2daa1b3e90633e3a0df 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -328,7 +328,7 @@ mod tests { let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); // Create the project symbols view. let symbols = cx.add_view(window_id, |cx| { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 1e635432bd5c138f81410f3ac40ee461830ddb17..265e4f02061539a685322a446fef9f7d94b00ffb 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -851,11 +851,11 @@ mod tests { }); let window = cx.add_window(|_| EmptyView); - let editor = cx.add_view(window.id(), |cx| { + let editor = cx.add_view(window.window_id(), |cx| { Editor::for_buffer(buffer.clone(), None, cx) }); - let search_bar = cx.add_view(window.id(), |cx| { + let search_bar = cx.add_view(window.window_id(), |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); @@ -1232,7 +1232,7 @@ mod tests { ); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let window = cx.add_window(|_| EmptyView); - let window_id = window.id(); + let window_id = window.window_id(); let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); @@ -1421,11 +1421,11 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let window = cx.add_window(|_| EmptyView); - let editor = cx.add_view(window.id(), |cx| { + let editor = cx.add_view(window.window_id(), |cx| { Editor::for_buffer(buffer.clone(), None, cx) }); - let search_bar = cx.add_view(window.id(), |cx| { + let search_bar = cx.add_view(window.window_id(), |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 0db66b4e378a56c97e2e2d1290742e52e1329bda..febd564050ddd0ea6355e83f69f505e80d924029 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1568,7 +1568,7 @@ pub mod tests { let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let active_item = cx.read(|cx| { workspace @@ -1874,7 +1874,7 @@ pub mod tests { let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); workspace.update(cx, |workspace, cx| { ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) }); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 2efa9f8daac69c872bda6cb3f7936d52d8d0222b..09e4c4c2193e8521cd8c7dc9841a361933497e73 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4196,14 +4196,14 @@ mod tests { ); }); assert_eq!( - cx.current_window_title(window.id()).as_deref(), + cx.current_window_title(window.window_id()).as_deref(), Some("one.txt — root1") ); // Add a second item to a non-empty pane workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx)); assert_eq!( - cx.current_window_title(window.id()).as_deref(), + cx.current_window_title(window.window_id()).as_deref(), Some("two.txt — root1") ); project.read_with(cx, |project, cx| { @@ -4222,7 +4222,7 @@ mod tests { .await .unwrap(); assert_eq!( - cx.current_window_title(window.id()).as_deref(), + cx.current_window_title(window.window_id()).as_deref(), Some("one.txt — root1") ); project.read_with(cx, |project, cx| { @@ -4242,14 +4242,14 @@ mod tests { .await .unwrap(); assert_eq!( - cx.current_window_title(window.id()).as_deref(), + cx.current_window_title(window.window_id()).as_deref(), Some("one.txt — root1, root2") ); // Remove a project folder project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); assert_eq!( - cx.current_window_title(window.id()).as_deref(), + cx.current_window_title(window.window_id()).as_deref(), Some("one.txt — root2") ); } @@ -4285,9 +4285,9 @@ mod tests { }); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); cx.foreground().run_until_parked(); - cx.simulate_prompt_answer(window.id(), 2 /* cancel */); + cx.simulate_prompt_answer(window.window_id(), 2 /* cancel */); cx.foreground().run_until_parked(); - assert!(!cx.has_pending_prompt(window.id())); + assert!(!cx.has_pending_prompt(window.window_id())); assert!(!task.await.unwrap()); } @@ -4346,10 +4346,10 @@ mod tests { assert_eq!(pane.items_len(), 4); assert_eq!(pane.active_item().unwrap().id(), item1.id()); }); - assert!(cx.has_pending_prompt(window.id())); + assert!(cx.has_pending_prompt(window.window_id())); // Confirm saving item 1. - cx.simulate_prompt_answer(window.id(), 0); + cx.simulate_prompt_answer(window.window_id(), 0); cx.foreground().run_until_parked(); // Item 1 is saved. There's a prompt to save item 3. @@ -4360,10 +4360,10 @@ mod tests { assert_eq!(pane.items_len(), 3); assert_eq!(pane.active_item().unwrap().id(), item3.id()); }); - assert!(cx.has_pending_prompt(window.id())); + assert!(cx.has_pending_prompt(window.window_id())); // Cancel saving item 3. - cx.simulate_prompt_answer(window.id(), 1); + cx.simulate_prompt_answer(window.window_id(), 1); cx.foreground().run_until_parked(); // Item 3 is reloaded. There's a prompt to save item 4. @@ -4374,10 +4374,10 @@ mod tests { assert_eq!(pane.items_len(), 2); assert_eq!(pane.active_item().unwrap().id(), item4.id()); }); - assert!(cx.has_pending_prompt(window.id())); + assert!(cx.has_pending_prompt(window.window_id())); // Confirm saving item 4. - cx.simulate_prompt_answer(window.id(), 0); + cx.simulate_prompt_answer(window.window_id(), 0); cx.foreground().run_until_parked(); // There's a prompt for a path for item 4. @@ -4480,7 +4480,7 @@ mod tests { &[ProjectEntryId::from_proto(0)] ); }); - cx.simulate_prompt_answer(window.id(), 0); + cx.simulate_prompt_answer(window.window_id(), 0); cx.foreground().run_until_parked(); left_pane.read_with(cx, |pane, cx| { @@ -4489,7 +4489,7 @@ mod tests { &[ProjectEntryId::from_proto(2)] ); }); - cx.simulate_prompt_answer(window.id(), 0); + cx.simulate_prompt_answer(window.window_id(), 0); cx.foreground().run_until_parked(); close.await.unwrap(); @@ -4549,7 +4549,7 @@ mod tests { item.read_with(cx, |item, _| assert_eq!(item.save_count, 2)); // Deactivating the window still saves the file. - cx.simulate_window_activation(Some(window.id())); + cx.simulate_window_activation(Some(window.window_id())); item.update(cx, |item, cx| { cx.focus_self(); item.is_dirty = true; @@ -4591,7 +4591,7 @@ mod tests { pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)) .await .unwrap(); - assert!(!cx.has_pending_prompt(window.id())); + assert!(!cx.has_pending_prompt(window.window_id())); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); // Add the item again, ensuring autosave is prevented if the underlying file has been deleted. @@ -4612,7 +4612,7 @@ mod tests { let _close_items = pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)); deterministic.run_until_parked(); - assert!(cx.has_pending_prompt(window.id())); + assert!(cx.has_pending_prompt(window.window_id())); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a459122cfc9a29b209f8d6cbcf7031f739781857..1c65317c413b67f6f3764c5dad2b71e2e9581d01 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1299,7 +1299,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); // Open a file within an existing worktree. workspace @@ -1342,7 +1342,7 @@ mod tests { project.update(cx, |project, _| project.languages().add(rust_lang())); let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); // Create a new untitled buffer @@ -1437,7 +1437,7 @@ mod tests { project.update(cx, |project, _| project.languages().add(rust_lang())); let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); // Create a new untitled buffer cx.dispatch_action(window_id, NewFile); @@ -1490,7 +1490,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); + let window_id = window.window_id(); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -2088,7 +2088,7 @@ mod tests { cx.foreground().run_until_parked(); let window = cx.add_window(|_| TestView); - let window_id = window.id(); + let window_id = window.window_id(); // Test loading the keymap base at all assert_key_bindings_for( @@ -2259,7 +2259,7 @@ mod tests { cx.foreground().run_until_parked(); let window = cx.add_window(|_| TestView); - let window_id = window.id(); + let window_id = window.window_id(); // Test loading the keymap base at all assert_key_bindings_for( From 2d96388be369259c5e8b6006bfe81cfe979889a3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Aug 2023 17:46:34 -0600 Subject: [PATCH 14/53] Use WindowHandles in a couple places --- crates/copilot/src/sign_in.rs | 44 ++++++++++++++++---------------- crates/gpui/src/app.rs | 47 ++++++++++++++++------------------- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index 0d5bb28ed688897e67ecccd816215f68ce573fd7..d03a2d393bfd345a478d66aa68998ae3759d7940 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -18,42 +18,42 @@ const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot"; pub fn init(cx: &mut AppContext) { if let Some(copilot) = Copilot::global(cx) { - let mut code_verification: Option> = None; + let mut verification_window: Option> = None; cx.observe(&copilot, move |copilot, cx| { let status = copilot.read(cx).status(); match &status { crate::Status::SigningIn { prompt } => { - if let Some(code_verification_handle) = code_verification.as_mut() { - let window_id = code_verification_handle.window_id(); - let updated = cx.update_window(window_id, |cx| { - code_verification_handle.update_root(cx, |code_verification, cx| { - code_verification.set_status(status.clone(), cx) - }); - cx.activate_window(); - }); - if updated.is_none() { - code_verification = Some(create_copilot_auth_window(cx, &status)); + if let Some(window) = verification_window.as_mut() { + let updated = window + .root(cx) + .map(|root| { + root.update(cx, |verification, cx| { + verification.set_status(status.clone(), cx); + cx.activate_window(); + }) + }) + .is_some(); + if !updated { + verification_window = Some(create_copilot_auth_window(cx, &status)); } } else if let Some(_prompt) = prompt { - code_verification = Some(create_copilot_auth_window(cx, &status)); + verification_window = Some(create_copilot_auth_window(cx, &status)); } } Status::Authorized | Status::Unauthorized => { - if let Some(code_verification) = code_verification.as_ref() { - let window_id = code_verification.window_id(); - cx.update_window(window_id, |cx| { - code_verification.update_root(cx, |code_verification, cx| { - code_verification.set_status(status, cx) + if let Some(window) = verification_window.as_ref() { + if let Some(verification) = window.root(cx) { + verification.update(cx, |verification, cx| { + verification.set_status(status, cx); + cx.platform().activate(true); + cx.activate_window(); }); - - cx.platform().activate(true); - cx.activate_window(); - }); + } } } _ => { - if let Some(code_verification) = code_verification.take() { + if let Some(code_verification) = verification_window.take() { code_verification.update(cx, |cx| cx.remove_window()); } } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 90f910d255ff595bd1e2c0e1173296180e10c8fc..d98033820bb60853403960adba3183f10782ce95 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -808,7 +808,7 @@ impl AppContext { result } - pub fn read_window T>( + fn read_window T>( &self, window_id: usize, callback: F, @@ -3892,31 +3892,26 @@ impl WindowHandle { cx.read_window_with(self.window_id(), |cx| read(cx)) } - pub fn update(&self, cx: &mut C, update: F) -> R + pub fn update(&self, cx: &mut C, update: F) -> C::Result where - C: BorrowAppContext, + C: BorrowWindowContext, F: FnOnce(&mut WindowContext) -> R, { - cx.update(|cx| cx.update_window(self.window_id(), update).unwrap()) - } - - pub fn update_root(&self, cx: &mut C, update: F) -> R - where - C: BorrowAppContext, - F: FnOnce(&mut V, &mut ViewContext) -> R, - { - let window_id = self.window_id(); - cx.update(|cx| { - cx.update_window(window_id, |cx| { - cx.root_view() - .clone() - .downcast::() - .unwrap() - .update(cx, update) - }) - .unwrap() - }) - } + cx.update_window(self.window_id(), update) + } + + // pub fn update_root(&self, cx: &mut C, update: F) -> C::Result> + // where + // C: BorrowWindowContext, + // F: FnOnce(&mut V, &mut ViewContext) -> R, + // { + // cx.update_window(self.window_id, |cx| { + // cx.root_view() + // .clone() + // .downcast::() + // .map(|v| v.update(cx, update)) + // }) + // } pub fn read_root<'a>(&self, cx: &'a AppContext) -> &'a V { let root_view = cx @@ -3940,9 +3935,9 @@ impl WindowHandle { }) } - pub fn add_view(&self, cx: &mut C, build_view: F) -> ViewHandle + pub fn add_view(&self, cx: &mut C, build_view: F) -> C::Result> where - C: BorrowAppContext, + C: BorrowWindowContext, U: View, F: FnOnce(&mut ViewContext) -> U, { @@ -4836,7 +4831,7 @@ mod tests { let called_defer = Rc::new(AtomicBool::new(false)); let called_after_window_update = Rc::new(AtomicBool::new(false)); - window.update_root(cx, |this, cx| { + window.root(cx).update(cx, |this, cx| { assert_eq!(this.render_count, 1); cx.defer({ let called_defer = called_defer.clone(); From 8c98b02e457ffb2fb0f457e9e2834ebbc9bebdd7 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Fri, 4 Aug 2023 15:09:08 -0400 Subject: [PATCH 15/53] Add `convert to {upper,lower} case` commands Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com> --- crates/editor/src/editor.rs | 66 +++++++++++++++++++++++++++++++ crates/editor/src/editor_tests.rs | 59 +++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a4d9259a6d7e7a63dc1606450ecef9d2aa4c53f4..ab952ba6e727fe7ced14fdcc37280bcb31ccfc9d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -231,6 +231,8 @@ actions!( SortLinesCaseInsensitive, ReverseLines, ShuffleLines, + ConvertToUpperCase, + ConvertToLowerCase, Transpose, Cut, Copy, @@ -353,6 +355,8 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::sort_lines_case_insensitive); cx.add_action(Editor::reverse_lines); cx.add_action(Editor::shuffle_lines); + cx.add_action(Editor::convert_to_upper_case); + cx.add_action(Editor::convert_to_lower_case); cx.add_action(Editor::delete_to_previous_word_start); cx.add_action(Editor::delete_to_previous_subword_start); cx.add_action(Editor::delete_to_next_word_end); @@ -4306,6 +4310,68 @@ impl Editor { }); } + pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext) { + self.manipulate_text(cx, |text| text.to_uppercase()) + } + + pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext) { + self.manipulate_text(cx, |text| text.to_lowercase()) + } + + fn manipulate_text(&mut self, cx: &mut ViewContext, mut callback: Fn) + where + Fn: FnMut(&str) -> String, + { + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let buffer = self.buffer.read(cx).snapshot(cx); + + let mut new_selections = Vec::new(); + let mut edits = Vec::new(); + for selection in self.selections.all::(cx) { + let selection_is_empty = selection.is_empty(); + + let (start, end) = if selection_is_empty { + let word_range = movement::surrounding_word( + &display_map, + selection.start.to_display_point(&display_map), + ); + let start = word_range.start.to_offset(&display_map, Bias::Left); + let end = word_range.end.to_offset(&display_map, Bias::Left); + (start, end) + } else { + (selection.start, selection.end) + }; + + let text = buffer.text_for_range(start..end).collect::(); + let text = callback(&text); + + if selection_is_empty { + new_selections.push(selection); + } else { + new_selections.push(Selection { + start, + end: start + text.len(), + goal: SelectionGoal::None, + ..selection + }); + } + + edits.push((start..end, text)); + } + + self.transact(cx, |this, cx| { + this.buffer.update(cx, |buffer, cx| { + buffer.edit(edits, None, cx); + }); + + this.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.select(new_selections); + }); + + this.request_autoscroll(Autoscroll::fit(), cx); + }); + } + pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index e8913505cab0d579afa7d1621fa54398a985844e..11d64f085cd35141aa98ff2bbafe20b5c51d96d4 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2695,6 +2695,65 @@ async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) { "}); } +#[gpui::test] +async fn test_manipulate_text(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let mut cx = EditorTestContext::new(cx).await; + + // Test convert_to_upper_case() + cx.set_state(indoc! {" + «hello worldˇ» + "}); + cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx)); + cx.assert_editor_state(indoc! {" + «HELLO WORLDˇ» + "}); + + // Test convert_to_lower_case() + cx.set_state(indoc! {" + «HELLO WORLDˇ» + "}); + cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx)); + cx.assert_editor_state(indoc! {" + «hello worldˇ» + "}); + + // From here on out, test more complex cases of manipulate_text() with a single driver method: convert_to_upper_case() + + // Test no selection case - should affect words cursors are in + // Cursor at beginning, middle, and end of word + cx.set_state(indoc! {" + ˇhello big beauˇtiful worldˇ + "}); + cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx)); + cx.assert_editor_state(indoc! {" + ˇHELLO big BEAUˇTIFUL WORLDˇ + "}); + + // Test multiple selections on a single line and across multiple lines + cx.set_state(indoc! {" + «Theˇ» quick «brown + foxˇ» jumps «overˇ» + the «lazyˇ» dog + "}); + cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx)); + cx.assert_editor_state(indoc! {" + «THEˇ» quick «BROWN + FOXˇ» jumps «OVERˇ» + the «LAZYˇ» dog + "}); + + // Test case where text length grows + cx.set_state(indoc! {" + «tschüߡ» + "}); + cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx)); + cx.assert_editor_state(indoc! {" + «TSCHÜSSˇ» + "}); +} + #[gpui::test] fn test_duplicate_line(cx: &mut TestAppContext) { init_test(cx, |_| {}); From 12e8f417e4ca3ff1d7aa019825b40c3b3d779483 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Fri, 4 Aug 2023 22:37:29 -0400 Subject: [PATCH 16/53] Add more convert to case commands ConvertToTitleCase ConvertToSnakeCase ConvertToKebabCase ConvertToUpperCamelCase ConvertToLowerCamelCase --- Cargo.lock | 10 ++++++++++ crates/editor/Cargo.toml | 3 ++- crates/editor/src/editor.rs | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fbf4e750c62c920a7158c712d48c4f6cd380bf6f..5afa1ebe6252ddabeb77067c2786d89301041a99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1649,6 +1649,15 @@ dependencies = [ "theme", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "copilot" version = "0.1.0" @@ -2314,6 +2323,7 @@ dependencies = [ "clock", "collections", "context_menu", + "convert_case", "copilot", "ctor", "db", diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index bc1c904404972cb847947b8c60147bcf4a41186b..2fdeee56c18150aa1f38f97176f288fe54412a21 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -47,6 +47,7 @@ workspace = { path = "../workspace" } aho-corasick = "0.7" anyhow.workspace = true +convert_case = "0.6.0" futures.workspace = true indoc = "1.0.4" itertools = "0.10" @@ -56,12 +57,12 @@ ordered-float.workspace = true parking_lot.workspace = true postage.workspace = true pulldown-cmark = { version = "0.9.2", default-features = false } +rand.workspace = true schemars.workspace = true serde.workspace = true serde_derive.workspace = true smallvec.workspace = true smol.workspace = true -rand.workspace = true tree-sitter-rust = { workspace = true, optional = true } tree-sitter-html = { workspace = true, optional = true } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ab952ba6e727fe7ced14fdcc37280bcb31ccfc9d..c70a1a8e048a62c87b9576547da8f6d6c62fb013 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -28,6 +28,7 @@ use blink_manager::BlinkManager; use client::{ClickhouseEvent, TelemetrySettings}; use clock::{Global, ReplicaId}; use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque}; +use convert_case::{Case, Casing}; use copilot::Copilot; pub use display_map::DisplayPoint; use display_map::*; @@ -233,6 +234,11 @@ actions!( ShuffleLines, ConvertToUpperCase, ConvertToLowerCase, + ConvertToTitleCase, + ConvertToSnakeCase, + ConvertToKebabCase, + ConvertToUpperCamelCase, + ConvertToLowerCamelCase, Transpose, Cut, Copy, @@ -357,6 +363,11 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::shuffle_lines); cx.add_action(Editor::convert_to_upper_case); cx.add_action(Editor::convert_to_lower_case); + cx.add_action(Editor::convert_to_title_case); + cx.add_action(Editor::convert_to_snake_case); + cx.add_action(Editor::convert_to_kebab_case); + cx.add_action(Editor::convert_to_upper_camel_case); + cx.add_action(Editor::convert_to_lower_camel_case); cx.add_action(Editor::delete_to_previous_word_start); cx.add_action(Editor::delete_to_previous_subword_start); cx.add_action(Editor::delete_to_next_word_end); @@ -4318,6 +4329,34 @@ impl Editor { self.manipulate_text(cx, |text| text.to_lowercase()) } + pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext) { + self.manipulate_text(cx, |text| text.to_case(Case::Title)) + } + + pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext) { + self.manipulate_text(cx, |text| text.to_case(Case::Snake)) + } + + pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext) { + self.manipulate_text(cx, |text| text.to_case(Case::Kebab)) + } + + pub fn convert_to_upper_camel_case( + &mut self, + _: &ConvertToUpperCamelCase, + cx: &mut ViewContext, + ) { + self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel)) + } + + pub fn convert_to_lower_camel_case( + &mut self, + _: &ConvertToLowerCamelCase, + cx: &mut ViewContext, + ) { + self.manipulate_text(cx, |text| text.to_case(Case::Camel)) + } + fn manipulate_text(&mut self, cx: &mut ViewContext, mut callback: Fn) where Fn: FnMut(&str) -> String, From 1abb6a0176feb0b1fe78553a08a2f6675654a8cf Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Sat, 5 Aug 2023 11:31:21 -0400 Subject: [PATCH 17/53] Expand empty selections to cover full word and fix bugs --- crates/editor/src/editor.rs | 21 +++++++++++---------- crates/editor/src/editor_tests.rs | 23 +++++++++++++++++++++-- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c70a1a8e048a62c87b9576547da8f6d6c62fb013..cd5e86b91098b35cad385331d5e8f28a380d3139 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4366,6 +4366,8 @@ impl Editor { let mut new_selections = Vec::new(); let mut edits = Vec::new(); + let mut selection_adjustment = 0i32; + for selection in self.selections.all::(cx) { let selection_is_empty = selection.is_empty(); @@ -4382,18 +4384,17 @@ impl Editor { }; let text = buffer.text_for_range(start..end).collect::(); + let old_length = text.len() as i32; let text = callback(&text); - if selection_is_empty { - new_selections.push(selection); - } else { - new_selections.push(Selection { - start, - end: start + text.len(), - goal: SelectionGoal::None, - ..selection - }); - } + new_selections.push(Selection { + start: (start as i32 - selection_adjustment) as usize, + end: ((start + text.len()) as i32 - selection_adjustment) as usize, + goal: SelectionGoal::None, + ..selection + }); + + selection_adjustment += old_length - text.len() as i32; edits.push((start..end, text)); } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 11d64f085cd35141aa98ff2bbafe20b5c51d96d4..7e39647ac6370bcb3bba1ff0721764c6e3ee542d 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2719,7 +2719,7 @@ async fn test_manipulate_text(cx: &mut TestAppContext) { «hello worldˇ» "}); - // From here on out, test more complex cases of manipulate_text() with a single driver method: convert_to_upper_case() + // From here on out, test more complex cases of manipulate_text() // Test no selection case - should affect words cursors are in // Cursor at beginning, middle, and end of word @@ -2728,7 +2728,7 @@ async fn test_manipulate_text(cx: &mut TestAppContext) { "}); cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx)); cx.assert_editor_state(indoc! {" - ˇHELLO big BEAUˇTIFUL WORLDˇ + «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ» "}); // Test multiple selections on a single line and across multiple lines @@ -2752,6 +2752,25 @@ async fn test_manipulate_text(cx: &mut TestAppContext) { cx.assert_editor_state(indoc! {" «TSCHÜSSˇ» "}); + + // Test to make sure we don't crash when text shrinks + cx.set_state(indoc! {" + aaa_bbbˇ + "}); + cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx)); + cx.assert_editor_state(indoc! {" + «aaaBbbˇ» + "}); + + // Test to make sure we all aware of the fact that each word can grow and shrink + // Final selections should be aware of this fact + cx.set_state(indoc! {" + aaa_bˇbb bbˇb_ccc ˇccc_ddd + "}); + cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx)); + cx.assert_editor_state(indoc! {" + «aaaBbbˇ» «bbbCccˇ» «cccDddˇ» + "}); } #[gpui::test] From dcf8b00656eab32299338c2a63b69dc128fff19d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 5 Aug 2023 18:00:44 -0600 Subject: [PATCH 18/53] WIP --- crates/gpui/src/app.rs | 59 ++++++++++++++++++++--------------- crates/gpui/src/app/window.rs | 2 +- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index d98033820bb60853403960adba3183f10782ce95..62d7d1d48aef6413dbfa3aed4e05d93dc32cf413 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1442,7 +1442,7 @@ impl AppContext { window } - pub fn replace_root_view( + pub(crate) fn replace_root_view( &mut self, window_id: usize, build_root_view: F, @@ -3900,28 +3900,6 @@ impl WindowHandle { cx.update_window(self.window_id(), update) } - // pub fn update_root(&self, cx: &mut C, update: F) -> C::Result> - // where - // C: BorrowWindowContext, - // F: FnOnce(&mut V, &mut ViewContext) -> R, - // { - // cx.update_window(self.window_id, |cx| { - // cx.root_view() - // .clone() - // .downcast::() - // .map(|v| v.update(cx, update)) - // }) - // } - - pub fn read_root<'a>(&self, cx: &'a AppContext) -> &'a V { - let root_view = cx - .read_window(self.window_id(), |cx| { - cx.root_view().clone().downcast().unwrap() - }) - .unwrap(); - root_view.read(cx) - } - pub fn read_root_with(&self, cx: &C, read: F) -> C::Result where C: BorrowWindowContext, @@ -3935,6 +3913,20 @@ impl WindowHandle { }) } + pub fn update_root(&self, cx: &mut C, update: F) -> C::Result + where + C: BorrowWindowContext, + F: FnOnce(&mut V, &mut ViewContext) -> R, + { + cx.update_window(self.window_id, |cx| { + cx.root_view() + .clone() + .downcast::() + .unwrap() + .update(cx, update) + }) + } + pub fn add_view(&self, cx: &mut C, build_view: F) -> C::Result> where C: BorrowWindowContext, @@ -3943,6 +3935,23 @@ impl WindowHandle { { self.update(cx, |cx| cx.add_view(build_view)) } + + pub(crate) fn replace_root_view( + &self, + cx: &mut C, + build_root_view: F, + ) -> C::Result> + where + C: BorrowWindowContext, + F: FnOnce(&mut ViewContext) -> V, + { + cx.update_window(self.window_id, |cx| { + let root_view = self.add_view(cx, |cx| build_root_view(cx)); + cx.window.root_view = Some(root_view.clone().into_any()); + cx.window.focused_view_id = Some(root_view.id()); + root_view + }) + } } #[repr(transparent)] @@ -5329,7 +5338,7 @@ mod tests { TestView { events: Vec::new() } }); - assert_eq!(window.read_root(cx).events, ["before emit"]); + window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before emit"])); } #[crate::test(self)] @@ -5381,7 +5390,7 @@ mod tests { TestView { events: Vec::new() } }); - assert_eq!(window.read_root(cx).events, ["before notify"]); + assert_eq!(window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before notify"]))); } #[crate::test(self)] diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 789341d46fa4788e24c7b4d7b1e44d0558e1f3b7..20992bfbaad03e03888e9971c8f4f88f829cdf46 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -1157,7 +1157,7 @@ impl<'a> WindowContext<'a> { self.window.platform_window.prompt(level, msg, answers) } - pub fn replace_root_view(&mut self, build_root_view: F) -> WindowHandle + pub(crate) fn replace_root_view(&mut self, build_root_view: F) -> WindowHandle where V: View, F: FnOnce(&mut ViewContext) -> V, From ef5b982ea5901f9f5ef78854e1bcba93b77dd2b6 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Sun, 6 Aug 2023 02:20:31 -0400 Subject: [PATCH 19/53] Fix bash path_suffixes and add line_comment --- crates/zed/src/languages/bash/config.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/zed/src/languages/bash/config.toml b/crates/zed/src/languages/bash/config.toml index 80b8753d80105f91ea9c0b802a2fe11ea61557c4..d03897a071d78c55c48ac97c4e829c7d2e05db1f 100644 --- a/crates/zed/src/languages/bash/config.toml +++ b/crates/zed/src/languages/bash/config.toml @@ -1,5 +1,6 @@ name = "Shell Script" -path_suffixes = [".sh", ".bash", ".bashrc", ".bash_profile", ".bash_aliases", ".bash_logout", ".profile", ".zsh", ".zshrc", ".zshenv", ".zsh_profile", ".zsh_aliases", ".zsh_histfile", ".zlogin"] +path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin"] +line_comment = "# " first_line_pattern = "^#!.*\\b(?:ba|z)?sh\\b" brackets = [ { start = "[", end = "]", close = true, newline = false }, From adc50469ff0c938415c4995ce8ed7fb761021a68 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 6 Aug 2023 12:45:31 -0600 Subject: [PATCH 20/53] WIP --- Cargo.lock | 39 ++- Cargo.toml | 1 + crates/collab/src/tests/integration_tests.rs | 4 +- .../src/incoming_call_notification.rs | 2 +- .../src/project_shared_notification.rs | 2 +- crates/command_palette/src/command_palette.rs | 2 +- crates/diagnostics/src/diagnostics.rs | 4 +- crates/editor/src/editor_tests.rs | 2 +- .../src/test/editor_lsp_test_context.rs | 2 +- crates/editor/src/test/editor_test_context.rs | 2 +- crates/file_finder/src/file_finder.rs | 22 +- crates/gpui/Cargo.toml | 1 + crates/gpui/src/app.rs | 264 +++++++++--------- crates/gpui/src/app/test_app_context.rs | 33 ++- crates/gpui/src/app/window.rs | 21 +- crates/project_panel/src/project_panel.rs | 8 +- crates/project_symbols/src/project_symbols.rs | 2 +- crates/search/src/buffer_search.rs | 10 +- crates/search/src/project_search.rs | 4 +- crates/workspace/src/workspace.rs | 165 +++++------ crates/zed/src/zed.rs | 20 +- 21 files changed, 331 insertions(+), 279 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fbf4e750c62c920a7158c712d48c4f6cd380bf6f..6cd36252d1b8f09fd0a88027b09d4e4da0d7524b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1649,6 +1649,12 @@ dependencies = [ "theme", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "copilot" version = "0.1.0" @@ -2133,6 +2139,19 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn 1.0.109", +] + [[package]] name = "dhat" version = "0.3.2" @@ -3096,6 +3115,7 @@ dependencies = [ "core-graphics", "core-text", "ctor", + "derive_more", "dhat", "env_logger 0.9.3", "etagere", @@ -5106,7 +5126,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" dependencies = [ - "rustc_version", + "rustc_version 0.3.3", ] [[package]] @@ -6276,7 +6296,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver", + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.18", ] [[package]] @@ -6716,6 +6745,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + [[package]] name = "semver-parser" version = "0.10.2" diff --git a/Cargo.toml b/Cargo.toml index 6e79c6b6576c290a4a0e85e56a80cfe9b100ff2b..1938e832e9cdb74c26a96abfa150f09b23a97d75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,7 @@ resolver = "2" anyhow = { version = "1.0.57" } async-trait = { version = "0.1" } ctor = { version = "0.1" } +derive_more = { version = "0.99.17" } env_logger = { version = "0.9" } futures = { version = "0.3" } globset = { version = "0.4" } diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 037f97f71abc8a5961b3693c461522af985fbb55..1a8e6d938d6d6caac2b8caee501e1088ddeea5b3 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3446,7 +3446,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx)); let mut editor_cx_a = EditorTestContext { cx: cx_a, - window_id: window_a.window_id(), + window_id: window_a.id(), editor: editor_a, }; @@ -3459,7 +3459,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx)); let mut editor_cx_b = EditorTestContext { cx: cx_b, - window_id: window_b.window_id(), + window_id: window_b.id(), editor: editor_b, }; diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 770f5d5795c980df75a4ce0f888f72745f63970e..a9c5e697a5f60d0cb6f9c0e6baf7cac50aba192a 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -49,7 +49,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { |_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()), ); - notification_windows.push(window.window_id()); + notification_windows.push(window.id()); } } } diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 5be7e33bf3cea049569ce294dbb3d8b1942cf38f..03ab91623b43270bf2592ced0f460b474fd0c435 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -52,7 +52,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { notification_windows .entry(*project_id) .or_insert(Vec::new()) - .push(window.window_id()); + .push(window.id()); } } room::Event::RemoteProjectUnshared { project_id } => { diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 935358c2a126bfd59f90ffe89f7b7825063af8c9..7d4b4126b702774bf8c295d3b61f904ff7ebf80c 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -297,7 +297,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), [], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let editor = cx.add_view(window_id, |cx| { let mut editor = Editor::single_line(None, cx); editor.set_text("abc", cx); diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index f2db0a7763054d19ea85b591de6e8b8d63272740..2444465be666124adb706bcf1833c50c77cee081 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -857,7 +857,7 @@ mod tests { let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); // Create some diagnostics project.update(cx, |project, cx| { @@ -1252,7 +1252,7 @@ mod tests { let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let view = cx.add_view(window_id, |cx| { ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index e8913505cab0d579afa7d1621fa54398a985844e..a114cd437b16bbad580d6e732bb004ac352d822a 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -525,7 +525,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) { let project = Project::test(fs, [], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); cx.add_view(window_id, |cx| { let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index d25ca7bb88a86383166288fcb1ac6cd5cfd5997f..f53115f224a1cfe6cb4a3c634786c7e7e8c18305 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -99,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> { Self { cx: EditorTestContext { cx, - window_id: window.window_id(), + window_id: window.id(), editor, }, lsp, diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index ac519764fdd8b671a92070118ec9b4937d135c64..c7ea1b4f3852301e300c5ff3f1db1dff65f342a2 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -39,7 +39,7 @@ impl<'a> EditorTestContext<'a> { let editor = window.root(cx); Self { cx, - window_id: window.window_id(), + window_id: window.id(), editor, } } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 84a45b083e6fdef0a2b4145e756f495fdc255300..12bf3242627619d390409bba044c0fa9cdcce3d4 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -619,7 +619,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.window_id(), Toggle); + cx.dispatch_action(window.id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); finder @@ -632,8 +632,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.window_id(), SelectNext); - cx.dispatch_action(window.window_id(), Confirm); + cx.dispatch_action(window.id(), SelectNext); + cx.dispatch_action(window.id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -674,7 +674,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.window_id(), Toggle); + cx.dispatch_action(window.id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -706,8 +706,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.window_id(), SelectNext); - cx.dispatch_action(window.window_id(), Confirm); + cx.dispatch_action(window.id(), SelectNext); + cx.dispatch_action(window.id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -758,7 +758,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.window_id(), Toggle); + cx.dispatch_action(window.id(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -790,8 +790,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.window_id(), SelectNext); - cx.dispatch_action(window.window_id(), Confirm); + cx.dispatch_action(window.id(), SelectNext); + cx.dispatch_action(window.id(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -1168,7 +1168,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1); @@ -1376,7 +1376,7 @@ mod tests { let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1,); diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 31b7db008d28d56849c7ce36eb3f23336fe82fad..5bd7d0318439d2cbd46667f585be37ae25efa33e 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -22,6 +22,7 @@ sqlez = { path = "../sqlez" } async-task = "4.0.3" backtrace = { version = "0.3", optional = true } ctor.workspace = true +derive_more.workspace = true dhat = { version = "0.3", optional = true } env_logger = { version = "0.9", optional = true } etagere = "0.2" diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 62d7d1d48aef6413dbfa3aed4e05d93dc32cf413..f967f134039081c1a89c71385e2fc25124a32bf3 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -23,6 +23,7 @@ use std::{ }; use anyhow::{anyhow, Context, Result}; +use derive_more::Deref; use parking_lot::Mutex; use postage::oneshot; use smallvec::SmallVec; @@ -132,11 +133,13 @@ pub trait BorrowAppContext { pub trait BorrowWindowContext { type Result; - fn read_window_with(&self, window_id: usize, f: F) -> Self::Result + fn read_window_with(&self, window_handle: H, f: F) -> Self::Result where + H: Into, F: FnOnce(&WindowContext) -> T; - fn update_window(&mut self, window_id: usize, f: F) -> Self::Result + fn update_window(&mut self, window_handle: H, f: F) -> Self::Result where + H: Into, F: FnOnce(&mut WindowContext) -> T; } @@ -300,13 +303,13 @@ impl App { result } - fn update_window T>( - &mut self, - window_id: usize, - callback: F, - ) -> Option { + fn update_window(&mut self, handle: H, callback: F) -> Option + where + H: Into, + F: FnOnce(&mut WindowContext) -> T, + { let mut state = self.0.borrow_mut(); - let result = state.update_window(window_id, callback); + let result = state.update_window(handle, callback); state.pending_notifications.clear(); result } @@ -333,6 +336,10 @@ impl AsyncAppContext { self.0.borrow_mut().update(callback) } + pub fn windows(&self) -> Vec { + self.0.borrow().windows().collect() + } + pub fn read_window T>( &self, window_id: usize, @@ -343,10 +350,10 @@ impl AsyncAppContext { pub fn update_window T>( &mut self, - window_id: usize, + handle: AnyWindowHandle, callback: F, ) -> Option { - self.0.borrow_mut().update_window(window_id, callback) + self.0.borrow_mut().update_window(handle, callback) } pub fn debug_elements(&self, window_id: usize) -> Option { @@ -359,14 +366,14 @@ impl AsyncAppContext { pub fn dispatch_action( &mut self, - window_id: usize, + window: impl Into, view_id: usize, action: &dyn Action, ) -> Result<()> { self.0 .borrow_mut() - .update_window(window_id, |window| { - window.dispatch_action(Some(view_id), action); + .update_window(window, |cx| { + cx.dispatch_action(Some(view_id), action); }) .ok_or_else(|| anyhow!("window not found")) } @@ -380,22 +387,6 @@ impl AsyncAppContext { .unwrap_or_default() } - pub fn has_window(&self, window_id: usize) -> bool { - self.read(|cx| cx.windows.contains_key(&window_id)) - } - - pub fn window_is_active(&self, window_id: usize) -> bool { - self.read(|cx| cx.windows.get(&window_id).map_or(false, |w| w.is_active)) - } - - pub fn root_view(&self, window_id: usize) -> Option { - self.read(|cx| cx.windows.get(&window_id).map(|w| w.root_view().clone())) - } - - pub fn window_ids(&self) -> Vec { - self.read(|cx| cx.windows.keys().copied().collect()) - } - pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, @@ -501,7 +492,7 @@ pub struct AppContext { models: HashMap>, views: HashMap<(usize, usize), Box>, views_metadata: HashMap<(usize, usize), ViewMetadata>, - windows: HashMap, + windows: HashMap, globals: HashMap>, element_states: HashMap>, background: Arc, @@ -818,22 +809,6 @@ impl AppContext { Some(callback(&window_context)) } - pub fn update_window T>( - &mut self, - window_id: usize, - callback: F, - ) -> Option { - self.update(|app_context| { - let mut window = app_context.windows.remove(&window_id)?; - let mut window_context = WindowContext::mutable(app_context, &mut window, window_id); - let result = callback(&mut window_context); - if !window_context.removed { - app_context.windows.insert(window_id, window); - } - Some(result) - }) - } - pub fn update_active_window T>( &mut self, callback: F, @@ -1331,43 +1306,40 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let window_id = post_inc(&mut this.next_id); + let window = WindowHandle::::new(post_inc(&mut this.next_id)); let platform_window = this.platform - .open_window(window_id, window_options, this.foreground.clone()); - let window = this.build_window(window_id, platform_window, build_root_view); - this.windows.insert(window_id, window); - WindowHandle::new(window_id) + .open_window(window, window_options, this.foreground.clone()); + let window = this.build_window(window, platform_window, build_root_view); + this.windows.insert(window.into(), window); + window }) } - pub fn add_status_bar_item(&mut self, build_root_view: F) -> (usize, ViewHandle) + pub fn add_status_bar_item(&mut self, build_root_view: F) -> WindowHandle where V: View, F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let window_id = post_inc(&mut this.next_id); - let platform_window = this.platform.add_status_item(window_id); - let window = this.build_window(window_id, platform_window, build_root_view); - let root_view = window.root_view().clone().downcast::().unwrap(); - - this.windows.insert(window_id, window); - this.update_window(window_id, |cx| { - root_view.update(cx, |view, cx| view.focus_in(cx.handle().into_any(), cx)) - }); + let handle = WindowHandle::::new(post_inc(&mut this.next_id)); + let platform_window = this.platform.add_status_item(handle.id()); + let window = this.build_window(handle, platform_window, build_root_view); - (window_id, root_view) + this.windows.insert(handle.into(), window); + handle.update_root(this, |view, cx| view.focus_in(cx.handle().into_any(), cx)); + handle }) } - pub fn build_window( + pub fn build_window( &mut self, - window_id: usize, + handle: H, mut platform_window: Box, build_root_view: F, ) -> Window where + H: Into, V: View, F: FnOnce(&mut ViewContext) -> V, { @@ -1375,7 +1347,7 @@ impl AppContext { let mut app = self.upgrade(); platform_window.on_event(Box::new(move |event| { - app.update_window(window_id, |cx| { + app.update_window(handle, |cx| { if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { if cx.dispatch_keystroke(keystroke) { return true; @@ -1391,35 +1363,35 @@ impl AppContext { { let mut app = self.upgrade(); platform_window.on_active_status_change(Box::new(move |is_active| { - app.update(|cx| cx.window_changed_active_status(window_id, is_active)) + app.update(|cx| cx.window_changed_active_status(handle, is_active)) })); } { let mut app = self.upgrade(); platform_window.on_resize(Box::new(move || { - app.update(|cx| cx.window_was_resized(window_id)) + app.update(|cx| cx.window_was_resized(handle)) })); } { let mut app = self.upgrade(); platform_window.on_moved(Box::new(move || { - app.update(|cx| cx.window_was_moved(window_id)) + app.update(|cx| cx.window_was_moved(handle)) })); } { let mut app = self.upgrade(); platform_window.on_fullscreen(Box::new(move |is_fullscreen| { - app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen)) + app.update(|cx| cx.window_was_fullscreen_changed(handle, is_fullscreen)) })); } { let mut app = self.upgrade(); platform_window.on_close(Box::new(move || { - app.update(|cx| cx.update_window(window_id, |cx| cx.remove_window())); + app.update(|cx| cx.update_window(handle, |cx| cx.remove_window())); })); } @@ -1431,27 +1403,20 @@ impl AppContext { platform_window.set_input_handler(Box::new(WindowInputHandler { app: self.upgrade().0, - window_id, + window_id: handle, })); - let mut window = Window::new(window_id, platform_window, self, build_root_view); - let mut cx = WindowContext::mutable(self, &mut window, window_id); + let mut window = Window::new(handle, platform_window, self, build_root_view); + let mut cx = WindowContext::mutable(self, &mut window, handle); cx.layout(false).expect("initial layout should not error"); let scene = cx.paint().expect("initial paint should not error"); window.platform_window.present_scene(scene); window } - pub(crate) fn replace_root_view( - &mut self, - window_id: usize, - build_root_view: F, - ) -> Option> - where - V: View, - F: FnOnce(&mut ViewContext) -> V, - { - self.update_window(window_id, |cx| cx.replace_root_view(build_root_view)) + pub fn windows(&self) -> impl Iterator { + todo!(); + None.into_iter() } pub fn read_view(&self, handle: &ViewHandle) -> &T { @@ -2192,11 +2157,20 @@ impl BorrowWindowContext for AppContext { AppContext::read_window(self, window_id, f) } - fn update_window(&mut self, window_id: usize, f: F) -> Self::Result + fn update_window(&mut self, window: usize, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T, { - AppContext::update_window(self, window_id, f) + self.update(|app_context| { + let mut window = app_context.windows.remove(&window_handle)?; + let mut window_context = + WindowContext::mutable(app_context, &mut window, window_handle); + let result = callback(&mut window_context); + if !window_context.removed { + app_context.windows.insert(window_handle, window); + } + Some(result) + }) } } @@ -3862,44 +3836,28 @@ impl Clone for WeakModelHandle { impl Copy for WeakModelHandle {} +#[derive(Deref, Copy, Clone)] pub struct WindowHandle { - window_id: usize, + #[deref] + any_handle: AnyWindowHandle, root_view_type: PhantomData, } -#[allow(dead_code)] impl WindowHandle { fn new(window_id: usize) -> Self { WindowHandle { - window_id, + any_handle: AnyWindowHandle { + window_id, + root_view_type: TypeId::of::(), + }, root_view_type: PhantomData, } } - pub fn window_id(&self) -> usize { - self.window_id - } - pub fn root(&self, cx: &C) -> C::Result> { self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap()) } - pub fn read_with(&self, cx: &C, read: F) -> C::Result - where - C: BorrowWindowContext, - F: FnOnce(&WindowContext) -> R, - { - cx.read_window_with(self.window_id(), |cx| read(cx)) - } - - pub fn update(&self, cx: &mut C, update: F) -> C::Result - where - C: BorrowWindowContext, - F: FnOnce(&mut WindowContext) -> R, - { - cx.update_window(self.window_id(), update) - } - pub fn read_root_with(&self, cx: &C, read: F) -> C::Result where C: BorrowWindowContext, @@ -3918,7 +3876,7 @@ impl WindowHandle { C: BorrowWindowContext, F: FnOnce(&mut V, &mut ViewContext) -> R, { - cx.update_window(self.window_id, |cx| { + cx.update_window(*self, |cx| { cx.root_view() .clone() .downcast::() @@ -3927,6 +3885,53 @@ impl WindowHandle { }) } + pub fn replace_root(&self, cx: &mut C, build_root: F) -> C::Result> + where + C: BorrowWindowContext, + F: FnOnce(&mut ViewContext) -> V, + { + cx.update_window(self.into_any(), |cx| { + let root_view = self.add_view(cx, |cx| build_root(cx)); + cx.window.root_view = Some(root_view.clone().into_any()); + cx.window.focused_view_id = Some(root_view.id()); + root_view + }) + } +} + +impl Into for WindowHandle { + fn into(self) -> AnyWindowHandle { + self.any_handle + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct AnyWindowHandle { + window_id: usize, + root_view_type: TypeId, +} + +impl AnyWindowHandle { + pub fn id(&self) -> usize { + self.window_id + } + + pub fn read_with(&self, cx: &C, read: F) -> C::Result + where + C: BorrowWindowContext, + F: FnOnce(&WindowContext) -> R, + { + cx.read_window_with(self.window_id, |cx| read(cx)) + } + + pub fn update(&self, cx: &mut C, update: F) -> C::Result + where + C: BorrowWindowContext, + F: FnOnce(&mut WindowContext) -> R, + { + cx.update_window(self.window_id, update) + } + pub fn add_view(&self, cx: &mut C, build_view: F) -> C::Result> where C: BorrowWindowContext, @@ -3936,21 +3941,16 @@ impl WindowHandle { self.update(cx, |cx| cx.add_view(build_view)) } - pub(crate) fn replace_root_view( - &self, - cx: &mut C, - build_root_view: F, - ) -> C::Result> - where - C: BorrowWindowContext, - F: FnOnce(&mut ViewContext) -> V, - { - cx.update_window(self.window_id, |cx| { - let root_view = self.add_view(cx, |cx| build_root_view(cx)); - cx.window.root_view = Some(root_view.clone().into_any()); - cx.window.focused_view_id = Some(root_view.id()); - root_view - }) + pub fn root_is(&self) -> bool { + self.root_view_type == TypeId::of::() + } + + pub fn is_active(&self, cx: &C) -> C::Result { + self.read_with(cx, |cx| cx.window.is_active) + } + + pub fn remove(&self, cx: &mut C) -> C::Result<()> { + self.update(cx, |cx| cx.remove_window()) } } @@ -5390,7 +5390,7 @@ mod tests { TestView { events: Vec::new() } }); - assert_eq!(window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before notify"]))); + window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before notify"])); } #[crate::test(self)] @@ -6227,7 +6227,7 @@ mod tests { // Check that global actions do not have a binding, even if a binding does exist in another view assert_eq!( - &available_actions(window.window_id(), view_1.id(), cx), + &available_actions(window.id(), view_1.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::GlobalAction", vec![]) @@ -6236,7 +6236,7 @@ mod tests { // Check that view 1 actions and bindings are available even when called from view 2 assert_eq!( - &available_actions(window.window_id(), view_2.id(), cx), + &available_actions(window.id(), view_2.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action2", vec![Keystroke::parse("b").unwrap()]), @@ -6299,7 +6299,7 @@ mod tests { ]); }); - let actions = cx.available_actions(window.window_id(), view.id()); + let actions = cx.available_actions(window.id(), view.id()); assert_eq!( actions[0].1.as_any().downcast_ref::(), Some(&ActionWithArg { arg: false }) @@ -6585,25 +6585,25 @@ mod tests { [("window 2", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_2.window_id())); + cx.simulate_window_activation(Some(window_2.id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 3", false), ("window 2", true)] ); - cx.simulate_window_activation(Some(window_1.window_id())); + cx.simulate_window_activation(Some(window_1.id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 2", false), ("window 1", true)] ); - cx.simulate_window_activation(Some(window_3.window_id())); + cx.simulate_window_activation(Some(window_3.id())); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 1", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_3.window_id())); + cx.simulate_window_activation(Some(window_3.id())); assert_eq!(mem::take(&mut *events.borrow_mut()), []); } diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 3b574eb03ee3f6cfcae23cffff8f1c5359828c66..5b005d7d6f2cd527321c97b3690576433ed132e7 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -4,9 +4,9 @@ use crate::{ keymap_matcher::{Binding, Keystroke}, platform, platform::{Event, InputHandler, KeyDownEvent, Platform}, - Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle, - ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle, - WindowContext, WindowHandle, + Action, AnyWindowHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, + Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, + WeakHandle, WindowContext, WindowHandle, }; use collections::BTreeMap; use futures::Future; @@ -124,6 +124,13 @@ impl TestAppContext { } } + pub fn window(&self, window_id: usize) -> Option> { + self.cx + .borrow() + .read_window(window_id, |cx| cx.window()) + .flatten() + } + pub fn read_window T>( &self, window_id: usize, @@ -157,9 +164,9 @@ impl TestAppContext { .cx .borrow_mut() .add_window(Default::default(), build_root_view); - self.simulate_window_activation(Some(window.window_id())); + self.simulate_window_activation(Some(window.id())); - WindowHandle::new(window.window_id()) + WindowHandle::new(window.id()) } pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle @@ -191,10 +198,14 @@ impl TestAppContext { self.cx.borrow_mut().subscribe_global(callback) } - pub fn window_ids(&self) -> Vec { - self.cx.borrow().windows.keys().copied().collect() + pub fn windows(&self) -> impl Iterator { + self.cx.borrow().windows() } + // pub fn window_ids(&self) -> Vec { + // self.cx.borrow().windows.keys().copied().collect() + // } + pub fn remove_all_windows(&mut self) { self.update(|cx| cx.windows.clear()); } @@ -311,15 +322,15 @@ impl TestAppContext { pub fn simulate_window_activation(&self, to_activate: Option) { self.cx.borrow_mut().update(|cx| { - let other_window_ids = cx + let other_windows = cx .windows .keys() - .filter(|window_id| Some(**window_id) != to_activate) + .filter(|window| Some(window.id()) != to_activate) .copied() .collect::>(); - for window_id in other_window_ids { - cx.window_changed_active_status(window_id, false) + for window in other_windows { + cx.window_changed_active_status(window.id(), false) } if let Some(to_activate) = to_activate { diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 20992bfbaad03e03888e9971c8f4f88f829cdf46..cc8468edbd7dbced86061e70a2b17d7ee4578691 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -196,6 +196,16 @@ impl<'a> WindowContext<'a> { self.window_id } + pub fn window(&self) -> Option> { + self.window.root_view.as_ref().and_then(|root_view| { + if root_view.is::() { + Some(WindowHandle::new(self.window_id)) + } else { + None + } + }) + } + pub fn app_context(&mut self) -> &mut AppContext { &mut self.app_context } @@ -1157,17 +1167,6 @@ impl<'a> WindowContext<'a> { self.window.platform_window.prompt(level, msg, answers) } - pub(crate) fn replace_root_view(&mut self, build_root_view: F) -> WindowHandle - where - V: View, - F: FnOnce(&mut ViewContext) -> V, - { - let root_view = self.add_view(|cx| build_root_view(cx)); - self.window.focused_view_id = Some(root_view.id()); - self.window.root_view = Some(root_view.into_any()); - WindowHandle::new(self.window_id) - } - pub fn add_view(&mut self, build_view: F) -> ViewHandle where T: View, diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 80847f9f4b97b3dc2cf2c32d6d11f0ec7dcbcc3a..021ea2d3bc3a817342e9eac684496ffeee5026bb 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1872,7 +1872,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -2225,7 +2225,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -2402,7 +2402,7 @@ mod tests { let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); toggle_expand_dir(&panel, "src/test", cx); @@ -2493,7 +2493,7 @@ mod tests { let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "src/", cx); diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 4bd186fc98b3edb93bb4b2daa1b3e90633e3a0df..8471f3a3a7014d2034438839f76ae9b4214ded52 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -328,7 +328,7 @@ mod tests { let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); // Create the project symbols view. let symbols = cx.add_view(window_id, |cx| { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 265e4f02061539a685322a446fef9f7d94b00ffb..1e635432bd5c138f81410f3ac40ee461830ddb17 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -851,11 +851,11 @@ mod tests { }); let window = cx.add_window(|_| EmptyView); - let editor = cx.add_view(window.window_id(), |cx| { + let editor = cx.add_view(window.id(), |cx| { Editor::for_buffer(buffer.clone(), None, cx) }); - let search_bar = cx.add_view(window.window_id(), |cx| { + let search_bar = cx.add_view(window.id(), |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); @@ -1232,7 +1232,7 @@ mod tests { ); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let window = cx.add_window(|_| EmptyView); - let window_id = window.window_id(); + let window_id = window.id(); let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); @@ -1421,11 +1421,11 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let window = cx.add_window(|_| EmptyView); - let editor = cx.add_view(window.window_id(), |cx| { + let editor = cx.add_view(window.id(), |cx| { Editor::for_buffer(buffer.clone(), None, cx) }); - let search_bar = cx.add_view(window.window_id(), |cx| { + let search_bar = cx.add_view(window.id(), |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index febd564050ddd0ea6355e83f69f505e80d924029..0db66b4e378a56c97e2e2d1290742e52e1329bda 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1568,7 +1568,7 @@ pub mod tests { let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let active_item = cx.read(|cx| { workspace @@ -1874,7 +1874,7 @@ pub mod tests { let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); workspace.update(cx, |workspace, cx| { ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) }); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 09e4c4c2193e8521cd8c7dc9841a361933497e73..37fab1ee462d7a242c331ea5df169babbae3a6d8 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -37,7 +37,7 @@ use gpui::{ }, AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, - WeakViewHandle, WindowContext, + WeakViewHandle, WindowContext, WindowHandle, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; use itertools::Itertools; @@ -749,7 +749,7 @@ impl Workspace { fn new_local( abs_paths: Vec, app_state: Arc, - requesting_window_id: Option, + requesting_window: Option>, cx: &mut AppContext, ) -> Task<( WeakViewHandle, @@ -793,55 +793,60 @@ impl Workspace { DB.next_id().await.unwrap_or(0) }; - let window = requesting_window_id.and_then(|window_id| { - cx.update(|cx| { - cx.replace_root_view(window_id, |cx| { - Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) - }) - }) - }); - let window = window.unwrap_or_else(|| { - let window_bounds_override = window_bounds_env_override(&cx); - let (bounds, display) = if let Some(bounds) = window_bounds_override { - (Some(bounds), None) - } else { - serialized_workspace - .as_ref() - .and_then(|serialized_workspace| { - let display = serialized_workspace.display?; - let mut bounds = serialized_workspace.bounds?; - - // Stored bounds are relative to the containing display. - // So convert back to global coordinates if that screen still exists - if let WindowBounds::Fixed(mut window_bounds) = bounds { - if let Some(screen) = cx.platform().screen_by_id(display) { - let screen_bounds = screen.bounds(); - window_bounds.set_origin_x( - window_bounds.origin_x() + screen_bounds.origin_x(), - ); - window_bounds.set_origin_y( - window_bounds.origin_y() + screen_bounds.origin_y(), - ); - bounds = WindowBounds::Fixed(window_bounds); - } else { - // Screen no longer exists. Return none here. - return None; + let window = if let Some(window) = requesting_window { + window.replace_root(&mut cx, |cx| { + Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) + }); + window + } else { + { + let window_bounds_override = window_bounds_env_override(&cx); + let (bounds, display) = if let Some(bounds) = window_bounds_override { + (Some(bounds), None) + } else { + serialized_workspace + .as_ref() + .and_then(|serialized_workspace| { + let display = serialized_workspace.display?; + let mut bounds = serialized_workspace.bounds?; + + // Stored bounds are relative to the containing display. + // So convert back to global coordinates if that screen still exists + if let WindowBounds::Fixed(mut window_bounds) = bounds { + if let Some(screen) = cx.platform().screen_by_id(display) { + let screen_bounds = screen.bounds(); + window_bounds.set_origin_x( + window_bounds.origin_x() + screen_bounds.origin_x(), + ); + window_bounds.set_origin_y( + window_bounds.origin_y() + screen_bounds.origin_y(), + ); + bounds = WindowBounds::Fixed(window_bounds); + } else { + // Screen no longer exists. Return none here. + return None; + } } - } - Some((bounds, display)) - }) - .unzip() - }; - - // Use the serialized workspace to construct the new window - cx.add_window( - (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), - |cx| { - Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) - }, - ) - }); + Some((bounds, display)) + }) + .unzip() + }; + + // Use the serialized workspace to construct the new window + cx.add_window( + (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), + |cx| { + Workspace::new( + workspace_id, + project_handle.clone(), + app_state.clone(), + cx, + ) + }, + ) + } + }; // We haven't yielded the main thread since obtaining the window handle, // so the window exists. @@ -1227,14 +1232,14 @@ impl Workspace { pub fn close_global(_: &CloseWindow, cx: &mut AppContext) { cx.spawn(|mut cx| async move { - let id = cx - .window_ids() + let window = cx + .windows() .into_iter() - .find(|&id| cx.window_is_active(id)); - if let Some(id) = id { + .find(|window| window.is_active(&cx).unwrap_or(false)); + if let Some(window) = window { //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); + window.remove(&mut cx); } }) .detach(); @@ -1265,9 +1270,9 @@ impl Workspace { cx.spawn(|this, mut cx| async move { let workspace_count = cx - .window_ids() + .windows() .into_iter() - .filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::()) + .filter(|window| window.root_is::()) .count(); if let Some(active_call) = active_call { @@ -1385,7 +1390,7 @@ impl Workspace { paths: Vec, cx: &mut ViewContext, ) -> Task> { - let window_id = cx.window_id(); + let window = cx.window::(); let is_remote = self.project.read(cx).is_remote(); let has_worktree = self.project.read(cx).worktrees(cx).next().is_some(); let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx)); @@ -1397,15 +1402,15 @@ impl Workspace { let app_state = self.app_state.clone(); cx.spawn(|_, mut cx| async move { - let window_id_to_replace = if let Some(close_task) = close_task { + let window_to_replace = if let Some(close_task) = close_task { if !close_task.await? { return Ok(()); } - Some(window_id) + window } else { None }; - cx.update(|cx| open_paths(&paths, &app_state, window_id_to_replace, cx)) + cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx)) .await?; Ok(()) }) @@ -3851,7 +3856,7 @@ pub async fn last_opened_workspace_paths() -> Option { pub fn open_paths( abs_paths: &[PathBuf], app_state: &Arc, - requesting_window_id: Option, + requesting_window: Option>, cx: &mut AppContext, ) -> Task< Result<( @@ -3879,7 +3884,7 @@ pub fn open_paths( } else { Ok(cx .update(|cx| { - Workspace::new_local(abs_paths, app_state.clone(), requesting_window_id, cx) + Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx) }) .await) } @@ -4196,14 +4201,14 @@ mod tests { ); }); assert_eq!( - cx.current_window_title(window.window_id()).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root1") ); // Add a second item to a non-empty pane workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx)); assert_eq!( - cx.current_window_title(window.window_id()).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("two.txt — root1") ); project.read_with(cx, |project, cx| { @@ -4222,7 +4227,7 @@ mod tests { .await .unwrap(); assert_eq!( - cx.current_window_title(window.window_id()).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root1") ); project.read_with(cx, |project, cx| { @@ -4242,14 +4247,14 @@ mod tests { .await .unwrap(); assert_eq!( - cx.current_window_title(window.window_id()).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root1, root2") ); // Remove a project folder project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); assert_eq!( - cx.current_window_title(window.window_id()).as_deref(), + cx.current_window_title(window.id()).as_deref(), Some("one.txt — root2") ); } @@ -4285,9 +4290,9 @@ mod tests { }); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); cx.foreground().run_until_parked(); - cx.simulate_prompt_answer(window.window_id(), 2 /* cancel */); + cx.simulate_prompt_answer(window.id(), 2 /* cancel */); cx.foreground().run_until_parked(); - assert!(!cx.has_pending_prompt(window.window_id())); + assert!(!cx.has_pending_prompt(window.id())); assert!(!task.await.unwrap()); } @@ -4346,10 +4351,10 @@ mod tests { assert_eq!(pane.items_len(), 4); assert_eq!(pane.active_item().unwrap().id(), item1.id()); }); - assert!(cx.has_pending_prompt(window.window_id())); + assert!(cx.has_pending_prompt(window.id())); // Confirm saving item 1. - cx.simulate_prompt_answer(window.window_id(), 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); // Item 1 is saved. There's a prompt to save item 3. @@ -4360,10 +4365,10 @@ mod tests { assert_eq!(pane.items_len(), 3); assert_eq!(pane.active_item().unwrap().id(), item3.id()); }); - assert!(cx.has_pending_prompt(window.window_id())); + assert!(cx.has_pending_prompt(window.id())); // Cancel saving item 3. - cx.simulate_prompt_answer(window.window_id(), 1); + cx.simulate_prompt_answer(window.id(), 1); cx.foreground().run_until_parked(); // Item 3 is reloaded. There's a prompt to save item 4. @@ -4374,10 +4379,10 @@ mod tests { assert_eq!(pane.items_len(), 2); assert_eq!(pane.active_item().unwrap().id(), item4.id()); }); - assert!(cx.has_pending_prompt(window.window_id())); + assert!(cx.has_pending_prompt(window.id())); // Confirm saving item 4. - cx.simulate_prompt_answer(window.window_id(), 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); // There's a prompt for a path for item 4. @@ -4480,7 +4485,7 @@ mod tests { &[ProjectEntryId::from_proto(0)] ); }); - cx.simulate_prompt_answer(window.window_id(), 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); left_pane.read_with(cx, |pane, cx| { @@ -4489,7 +4494,7 @@ mod tests { &[ProjectEntryId::from_proto(2)] ); }); - cx.simulate_prompt_answer(window.window_id(), 0); + cx.simulate_prompt_answer(window.id(), 0); cx.foreground().run_until_parked(); close.await.unwrap(); @@ -4549,7 +4554,7 @@ mod tests { item.read_with(cx, |item, _| assert_eq!(item.save_count, 2)); // Deactivating the window still saves the file. - cx.simulate_window_activation(Some(window.window_id())); + cx.simulate_window_activation(Some(window.id())); item.update(cx, |item, cx| { cx.focus_self(); item.is_dirty = true; @@ -4591,7 +4596,7 @@ mod tests { pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)) .await .unwrap(); - assert!(!cx.has_pending_prompt(window.window_id())); + assert!(!cx.has_pending_prompt(window.id())); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); // Add the item again, ensuring autosave is prevented if the underlying file has been deleted. @@ -4612,7 +4617,7 @@ mod tests { let _close_items = pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)); deterministic.run_until_parked(); - assert!(cx.has_pending_prompt(window.window_id())); + assert!(cx.has_pending_prompt(window.id())); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 46fcc014a138a727e2c3ceec01dbc0e3d6abee05..a8a287fb4e6fc964346378fad4eee09bc5eb057a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -813,11 +813,12 @@ mod tests { // Replace existing windows let window_id = cx.window_ids()[0]; + let window = cx.read_window(window_id, |cx| cx.window()).flatten(); cx.update(|cx| { open_paths( &[PathBuf::from("/root/c"), PathBuf::from("/root/d")], &app_state, - Some(window_id), + window, cx, ) }) @@ -982,9 +983,8 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let workspace = cx - .add_window(|cx| Workspace::test_new(project, cx)) - .root(cx); + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + let workspace = window.root(cx); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1298,7 +1298,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); // Open a file within an existing worktree. workspace @@ -1341,7 +1341,7 @@ mod tests { project.update(cx, |project, _| project.languages().add(rust_lang())); let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); // Create a new untitled buffer @@ -1436,7 +1436,7 @@ mod tests { project.update(cx, |project, _| project.languages().add(rust_lang())); let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); // Create a new untitled buffer cx.dispatch_action(window_id, NewFile); @@ -1489,7 +1489,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.window_id(); + let window_id = window.id(); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -2087,7 +2087,7 @@ mod tests { cx.foreground().run_until_parked(); let window = cx.add_window(|_| TestView); - let window_id = window.window_id(); + let window_id = window.id(); // Test loading the keymap base at all assert_key_bindings_for( @@ -2258,7 +2258,7 @@ mod tests { cx.foreground().run_until_parked(); let window = cx.add_window(|_| TestView); - let window_id = window.window_id(); + let window_id = window.id(); // Test loading the keymap base at all assert_key_bindings_for( From d4d32611fe3422e9dbb03d432fb8e746f7b8198c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sun, 6 Aug 2023 18:57:02 -0600 Subject: [PATCH 21/53] WIP --- crates/gpui/src/app.rs | 58 +++++++++++++++---------- crates/gpui/src/app/menu.rs | 6 +-- crates/gpui/src/app/test_app_context.rs | 12 ++--- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index f967f134039081c1a89c71385e2fc25124a32bf3..c2d2397d9c4f4e00ff73cb92358de2423f85a383 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -133,13 +133,11 @@ pub trait BorrowAppContext { pub trait BorrowWindowContext { type Result; - fn read_window_with(&self, window_handle: H, f: F) -> Self::Result + fn read_window_with(&self, window_id: usize, f: F) -> Self::Result where - H: Into, F: FnOnce(&WindowContext) -> T; - fn update_window(&mut self, window_handle: H, f: F) -> Self::Result + fn update_window(&mut self, window_id: usize, f: F) -> Self::Result where - H: Into, F: FnOnce(&mut WindowContext) -> T; } @@ -303,13 +301,12 @@ impl App { result } - fn update_window(&mut self, handle: H, callback: F) -> Option + fn update_window(&mut self, window_id: usize, callback: F) -> Option where - H: Into, F: FnOnce(&mut WindowContext) -> T, { let mut state = self.0.borrow_mut(); - let result = state.update_window(handle, callback); + let result = state.update_window(window_id, callback); state.pending_notifications.clear(); result } @@ -350,10 +347,10 @@ impl AsyncAppContext { pub fn update_window T>( &mut self, - handle: AnyWindowHandle, + window_id: usize, callback: F, ) -> Option { - self.0.borrow_mut().update_window(handle, callback) + self.0.borrow_mut().update_window(window_id, callback) } pub fn debug_elements(&self, window_id: usize) -> Option { @@ -366,13 +363,13 @@ impl AsyncAppContext { pub fn dispatch_action( &mut self, - window: impl Into, + window_id: usize, view_id: usize, action: &dyn Action, ) -> Result<()> { self.0 .borrow_mut() - .update_window(window, |cx| { + .update_window(window_id, |cx| { cx.dispatch_action(Some(view_id), action); }) .ok_or_else(|| anyhow!("window not found")) @@ -492,7 +489,7 @@ pub struct AppContext { models: HashMap>, views: HashMap<(usize, usize), Box>, views_metadata: HashMap<(usize, usize), ViewMetadata>, - windows: HashMap, + windows: HashMap, globals: HashMap>, element_states: HashMap>, background: Arc, @@ -1414,9 +1411,18 @@ impl AppContext { window } - pub fn windows(&self) -> impl Iterator { - todo!(); - None.into_iter() + pub fn main_window(&self) -> Option { + self.platform.main_window_id().and_then(|main_window_id| { + self.windows + .get(&main_window_id) + .map(|window| AnyWindowHandle::new(main_window_id, window.root_view().type_id())) + }) + } + + pub fn windows(&self) -> impl '_ + Iterator { + self.windows.iter().map(|(window_id, window)| { + AnyWindowHandle::new(*window_id, window.root_view().type_id()) + }) } pub fn read_view(&self, handle: &ViewHandle) -> &T { @@ -2157,17 +2163,16 @@ impl BorrowWindowContext for AppContext { AppContext::read_window(self, window_id, f) } - fn update_window(&mut self, window: usize, f: F) -> Self::Result + fn update_window(&mut self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T, { self.update(|app_context| { - let mut window = app_context.windows.remove(&window_handle)?; - let mut window_context = - WindowContext::mutable(app_context, &mut window, window_handle); - let result = callback(&mut window_context); + let mut window = app_context.windows.remove(&window_id)?; + let mut window_context = WindowContext::mutable(app_context, &mut window, window_id); + let result = f(&mut window_context); if !window_context.removed { - app_context.windows.insert(window_handle, window); + app_context.windows.insert(window_id, window); } Some(result) }) @@ -3876,7 +3881,7 @@ impl WindowHandle { C: BorrowWindowContext, F: FnOnce(&mut V, &mut ViewContext) -> R, { - cx.update_window(*self, |cx| { + cx.update_window(self.id(), |cx| { cx.root_view() .clone() .downcast::() @@ -3890,7 +3895,7 @@ impl WindowHandle { C: BorrowWindowContext, F: FnOnce(&mut ViewContext) -> V, { - cx.update_window(self.into_any(), |cx| { + cx.update_window(self.id(), |cx| { let root_view = self.add_view(cx, |cx| build_root(cx)); cx.window.root_view = Some(root_view.clone().into_any()); cx.window.focused_view_id = Some(root_view.id()); @@ -3912,6 +3917,13 @@ pub struct AnyWindowHandle { } impl AnyWindowHandle { + fn new(window_id: usize, root_view_type: TypeId) -> Self { + Self { + window_id, + root_view_type, + } + } + pub fn id(&self) -> usize { self.window_id } diff --git a/crates/gpui/src/app/menu.rs b/crates/gpui/src/app/menu.rs index a2ac13984bee5fcc7c248e5750080c84a5353aa0..1d8908b8fdda9627d2eb7df433a7c5b819f024ed 100644 --- a/crates/gpui/src/app/menu.rs +++ b/crates/gpui/src/app/menu.rs @@ -77,9 +77,9 @@ pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform, let cx = app.0.clone(); move |action| { let mut cx = cx.borrow_mut(); - if let Some(main_window_id) = cx.platform.main_window_id() { - let dispatched = cx - .update_window(main_window_id, |cx| { + if let Some(main_window) = cx.main_window() { + let dispatched = main_window + .update(&mut *cx, |cx| { if let Some(view_id) = cx.focused_view_id() { cx.dispatch_action(Some(view_id), action); true diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 5b005d7d6f2cd527321c97b3690576433ed132e7..dcbe810804b6ac8331a3a3da20868040c10fcd8d 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -198,8 +198,8 @@ impl TestAppContext { self.cx.borrow_mut().subscribe_global(callback) } - pub fn windows(&self) -> impl Iterator { - self.cx.borrow().windows() + pub fn windows(&self) -> Vec { + self.cx.borrow().windows().collect() } // pub fn window_ids(&self) -> Vec { @@ -322,15 +322,15 @@ impl TestAppContext { pub fn simulate_window_activation(&self, to_activate: Option) { self.cx.borrow_mut().update(|cx| { - let other_windows = cx + let other_window_ids = cx .windows .keys() - .filter(|window| Some(window.id()) != to_activate) + .filter(|window_id| Some(**window_id) != to_activate) .copied() .collect::>(); - for window in other_windows { - cx.window_changed_active_status(window.id(), false) + for window_id in other_window_ids { + cx.window_changed_active_status(window_id, false) } if let Some(to_activate) = to_activate { From 3e0d0e5c010f3f5f0c8e964af5fcfce621705b14 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 7 Aug 2023 13:54:47 -0600 Subject: [PATCH 22/53] WIP --- crates/gpui/src/app.rs | 186 ++++++++++++++++---- crates/gpui/src/app/test_app_context.rs | 59 ++++--- crates/gpui/src/app/window.rs | 16 +- crates/gpui/src/app/window_input_handler.rs | 27 ++- crates/workspace/src/workspace.rs | 11 +- 5 files changed, 221 insertions(+), 78 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index c2d2397d9c4f4e00ff73cb92358de2423f85a383..730d0da5a054ea808dedd236892d4ca14aeefc30 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -133,12 +133,18 @@ pub trait BorrowAppContext { pub trait BorrowWindowContext { type Result; - fn read_window_with(&self, window_id: usize, f: F) -> Self::Result + fn read_window(&self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&WindowContext) -> T; + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option; fn update_window(&mut self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T; + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option; } #[derive(Clone)] @@ -449,13 +455,22 @@ impl BorrowAppContext for AsyncAppContext { impl BorrowWindowContext for AsyncAppContext { type Result = Option; - fn read_window_with(&self, window_id: usize, f: F) -> Self::Result + fn read_window(&self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&WindowContext) -> T, { self.0.borrow().read_with(|cx| cx.read_window(window_id, f)) } + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option, + { + self.0 + .borrow_mut() + .update(|cx| cx.read_window_optional(window_id, f)) + } + fn update_window(&mut self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T, @@ -464,6 +479,15 @@ impl BorrowWindowContext for AsyncAppContext { .borrow_mut() .update(|cx| cx.update_window(window_id, f)) } + + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option, + { + self.0 + .borrow_mut() + .update(|cx| cx.update_window_optional(window_id, f)) + } } type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize); @@ -1303,13 +1327,14 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let window = WindowHandle::::new(post_inc(&mut this.next_id)); + let window_id = post_inc(&mut this.next_id); let platform_window = this.platform - .open_window(window, window_options, this.foreground.clone()); - let window = this.build_window(window, platform_window, build_root_view); - this.windows.insert(window.into(), window); - window + .open_window(window_id, window_options, this.foreground.clone()); + let handle = WindowHandle::::new(window_id); + let window = this.build_window(handle, platform_window, build_root_view); + this.windows.insert(window_id, window); + handle }) } @@ -1319,11 +1344,11 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let handle = WindowHandle::::new(post_inc(&mut this.next_id)); - let platform_window = this.platform.add_status_item(handle.id()); + let window_id = post_inc(&mut this.next_id); + let platform_window = this.platform.add_status_item(window_id); + let handle = WindowHandle::::new(window_id); let window = this.build_window(handle, platform_window, build_root_view); - - this.windows.insert(handle.into(), window); + this.windows.insert(window_id, window); handle.update_root(this, |view, cx| view.focus_in(cx.handle().into_any(), cx)); handle }) @@ -1340,11 +1365,14 @@ impl AppContext { V: View, F: FnOnce(&mut ViewContext) -> V, { + let handle: AnyWindowHandle = handle.into(); + let window_id = handle.id(); + { let mut app = self.upgrade(); platform_window.on_event(Box::new(move |event| { - app.update_window(handle, |cx| { + app.update_window(window_id, |cx| { if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { if cx.dispatch_keystroke(keystroke) { return true; @@ -1360,35 +1388,35 @@ impl AppContext { { let mut app = self.upgrade(); platform_window.on_active_status_change(Box::new(move |is_active| { - app.update(|cx| cx.window_changed_active_status(handle, is_active)) + app.update(|cx| cx.window_changed_active_status(window_id, is_active)) })); } { let mut app = self.upgrade(); platform_window.on_resize(Box::new(move || { - app.update(|cx| cx.window_was_resized(handle)) + app.update(|cx| cx.window_was_resized(window_id)) })); } { let mut app = self.upgrade(); platform_window.on_moved(Box::new(move || { - app.update(|cx| cx.window_was_moved(handle)) + app.update(|cx| cx.window_was_moved(window_id)) })); } { let mut app = self.upgrade(); platform_window.on_fullscreen(Box::new(move |is_fullscreen| { - app.update(|cx| cx.window_was_fullscreen_changed(handle, is_fullscreen)) + app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen)) })); } { let mut app = self.upgrade(); platform_window.on_close(Box::new(move || { - app.update(|cx| cx.update_window(handle, |cx| cx.remove_window())); + app.update(|cx| cx.update_window(window_id, |cx| cx.remove_window())); })); } @@ -1400,11 +1428,11 @@ impl AppContext { platform_window.set_input_handler(Box::new(WindowInputHandler { app: self.upgrade().0, - window_id: handle, + window: handle, })); - let mut window = Window::new(handle, platform_window, self, build_root_view); - let mut cx = WindowContext::mutable(self, &mut window, handle); + let mut window = Window::new(window_id, platform_window, self, build_root_view); + let mut cx = WindowContext::mutable(self, &mut window, window_id); cx.layout(false).expect("initial layout should not error"); let scene = cx.paint().expect("initial paint should not error"); window.platform_window.present_scene(scene); @@ -2156,13 +2184,20 @@ impl BorrowAppContext for AppContext { impl BorrowWindowContext for AppContext { type Result = Option; - fn read_window_with(&self, window_id: usize, f: F) -> Self::Result + fn read_window(&self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&WindowContext) -> T, { AppContext::read_window(self, window_id, f) } + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option, + { + AppContext::read_window(self, window_id, f).flatten() + } + fn update_window(&mut self, window_id: usize, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T, @@ -2177,6 +2212,13 @@ impl BorrowWindowContext for AppContext { Some(result) }) } + + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option, + { + AppContext::update_window(self, window_id, f).flatten() + } } #[derive(Debug)] @@ -3379,8 +3421,15 @@ impl BorrowAppContext for ViewContext<'_, '_, V> { impl BorrowWindowContext for ViewContext<'_, '_, V> { type Result = T; - fn read_window_with T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_window_with(&*self.window_context, window_id, f) + fn read_window T>(&self, window_id: usize, f: F) -> T { + BorrowWindowContext::read_window(&*self.window_context, window_id, f) + } + + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option, + { + BorrowWindowContext::read_window_optional(&*self.window_context, window_id, f) } fn update_window T>( @@ -3390,6 +3439,13 @@ impl BorrowWindowContext for ViewContext<'_, '_, V> { ) -> T { BorrowWindowContext::update_window(&mut *self.window_context, window_id, f) } + + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option, + { + BorrowWindowContext::update_window_optional(&mut *self.window_context, window_id, f) + } } pub struct LayoutContext<'a, 'b, 'c, V: View> { @@ -3490,8 +3546,15 @@ impl BorrowAppContext for LayoutContext<'_, '_, '_, V> { impl BorrowWindowContext for LayoutContext<'_, '_, '_, V> { type Result = T; - fn read_window_with T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_window_with(&*self.view_context, window_id, f) + fn read_window T>(&self, window_id: usize, f: F) -> T { + BorrowWindowContext::read_window(&*self.view_context, window_id, f) + } + + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option, + { + BorrowWindowContext::read_window_optional(&*self.view_context, window_id, f) } fn update_window T>( @@ -3501,6 +3564,13 @@ impl BorrowWindowContext for LayoutContext<'_, '_, '_, V> { ) -> T { BorrowWindowContext::update_window(&mut *self.view_context, window_id, f) } + + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option, + { + BorrowWindowContext::update_window_optional(&mut *self.view_context, window_id, f) + } } pub struct EventContext<'a, 'b, 'c, V: View> { @@ -3548,8 +3618,15 @@ impl BorrowAppContext for EventContext<'_, '_, '_, V> { impl BorrowWindowContext for EventContext<'_, '_, '_, V> { type Result = T; - fn read_window_with T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_window_with(&*self.view_context, window_id, f) + fn read_window T>(&self, window_id: usize, f: F) -> T { + BorrowWindowContext::read_window(&*self.view_context, window_id, f) + } + + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option, + { + BorrowWindowContext::read_window_optional(&*self.view_context, window_id, f) } fn update_window T>( @@ -3559,6 +3636,13 @@ impl BorrowWindowContext for EventContext<'_, '_, '_, V> { ) -> T { BorrowWindowContext::update_window(&mut *self.view_context, window_id, f) } + + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option, + { + BorrowWindowContext::update_window_optional(&mut *self.view_context, window_id, f) + } } pub(crate) enum Reference<'a, T> { @@ -3841,13 +3925,24 @@ impl Clone for WeakModelHandle { impl Copy for WeakModelHandle {} -#[derive(Deref, Copy, Clone)] -pub struct WindowHandle { +#[derive(Deref)] +pub struct WindowHandle { #[deref] any_handle: AnyWindowHandle, - root_view_type: PhantomData, + root_view_type: PhantomData, +} + +impl Clone for WindowHandle { + fn clone(&self) -> Self { + Self { + any_handle: self.any_handle.clone(), + root_view_type: PhantomData, + } + } } +impl Copy for WindowHandle {} + impl WindowHandle { fn new(window_id: usize) -> Self { WindowHandle { @@ -3933,7 +4028,15 @@ impl AnyWindowHandle { C: BorrowWindowContext, F: FnOnce(&WindowContext) -> R, { - cx.read_window_with(self.window_id, |cx| read(cx)) + cx.read_window(self.window_id, |cx| read(cx)) + } + + pub fn read_optional_with(&self, cx: &C, read: F) -> Option + where + C: BorrowWindowContext, + F: FnOnce(&WindowContext) -> Option, + { + cx.read_window_optional(self.window_id, |cx| read(cx)) } pub fn update(&self, cx: &mut C, update: F) -> C::Result @@ -3944,6 +4047,14 @@ impl AnyWindowHandle { cx.update_window(self.window_id, update) } + pub fn update_optional(&self, cx: &mut C, update: F) -> Option + where + C: BorrowWindowContext, + F: FnOnce(&mut WindowContext) -> Option, + { + cx.update_window_optional(self.window_id, update) + } + pub fn add_view(&self, cx: &mut C, build_view: F) -> C::Result> where C: BorrowWindowContext, @@ -3953,6 +4064,17 @@ impl AnyWindowHandle { self.update(cx, |cx| cx.add_view(build_view)) } + pub fn downcast(self) -> Option> { + if self.root_view_type == TypeId::of::() { + Some(WindowHandle { + any_handle: self, + root_view_type: PhantomData, + }) + } else { + None + } + } + pub fn root_is(&self) -> bool { self.root_view_type == TypeId::of::() } @@ -4018,7 +4140,7 @@ impl ViewHandle { C: BorrowWindowContext, F: FnOnce(&T, &ViewContext) -> S, { - cx.read_window_with(self.window_id, |cx| { + cx.read_window(self.window_id, |cx| { let cx = ViewContext::immutable(cx, self.view_id); read(cx.read_view(self), &cx) }) diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index dcbe810804b6ac8331a3a3da20868040c10fcd8d..0331c7922e680fe1c45460bc2569c85ab77a3f19 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -92,33 +92,34 @@ impl TestAppContext { self.update(|cx| cx.dispatch_global_action_any(&action)); } - pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) { - let handled = self - .cx - .borrow_mut() - .update_window(window_id, |cx| { - if cx.dispatch_keystroke(&keystroke) { - return true; - } + pub fn dispatch_keystroke( + &mut self, + window: AnyWindowHandle, + keystroke: Keystroke, + is_held: bool, + ) { + let handled = window.update(self, |cx| { + if cx.dispatch_keystroke(&keystroke) { + return true; + } - if cx.dispatch_event( - Event::KeyDown(KeyDownEvent { - keystroke: keystroke.clone(), - is_held, - }), - false, - ) { - return true; - } + if cx.dispatch_event( + Event::KeyDown(KeyDownEvent { + keystroke: keystroke.clone(), + is_held, + }), + false, + ) { + return true; + } - false - }) - .unwrap_or(false); + false + }); if !handled && !keystroke.cmd && !keystroke.ctrl { WindowInputHandler { app: self.cx.clone(), - window_id, + window, } .replace_text_in_range(None, &keystroke.key) } @@ -419,13 +420,20 @@ impl BorrowAppContext for TestAppContext { impl BorrowWindowContext for TestAppContext { type Result = T; - fn read_window_with T>(&self, window_id: usize, f: F) -> T { + fn read_window T>(&self, window_id: usize, f: F) -> T { self.cx .borrow() .read_window(window_id, f) .expect("window was closed") } + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option, + { + BorrowWindowContext::read_window(self, window_id, f) + } + fn update_window T>( &mut self, window_id: usize, @@ -436,6 +444,13 @@ impl BorrowWindowContext for TestAppContext { .update_window(window_id, f) .expect("window was closed") } + + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option, + { + BorrowWindowContext::update_window(self, window_id, f) + } } impl ModelHandle { diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index cc8468edbd7dbced86061e70a2b17d7ee4578691..d960b9da16a7c84d46fda2b17cd2650204aa1c08 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -144,7 +144,7 @@ impl BorrowAppContext for WindowContext<'_> { impl BorrowWindowContext for WindowContext<'_> { type Result = T; - fn read_window_with T>(&self, window_id: usize, f: F) -> T { + fn read_window T>(&self, window_id: usize, f: F) -> T { if self.window_id == window_id { f(self) } else { @@ -152,6 +152,13 @@ impl BorrowWindowContext for WindowContext<'_> { } } + fn read_window_optional(&self, window_id: usize, f: F) -> Option + where + F: FnOnce(&WindowContext) -> Option, + { + BorrowWindowContext::read_window(self, window_id, f) + } + fn update_window T>( &mut self, window_id: usize, @@ -163,6 +170,13 @@ impl BorrowWindowContext for WindowContext<'_> { panic!("update called with id of window that does not belong to this context") } } + + fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + where + F: FnOnce(&mut WindowContext) -> Option, + { + BorrowWindowContext::update_window_optional(self, window_id, f) + } } impl<'a> WindowContext<'a> { diff --git a/crates/gpui/src/app/window_input_handler.rs b/crates/gpui/src/app/window_input_handler.rs index 8ee9f7eeff5c2a3e4f4d63ca55c9338f02e3ed69..d7c65b11fae6f6c66de0c4c0a1c4302400d651ba 100644 --- a/crates/gpui/src/app/window_input_handler.rs +++ b/crates/gpui/src/app/window_input_handler.rs @@ -2,11 +2,11 @@ use std::{cell::RefCell, ops::Range, rc::Rc}; use pathfinder_geometry::rect::RectF; -use crate::{platform::InputHandler, window::WindowContext, AnyView, AppContext}; +use crate::{platform::InputHandler, window::WindowContext, AnyView, AnyWindowHandle, AppContext}; pub struct WindowInputHandler { pub app: Rc>, - pub window_id: usize, + pub window: AnyWindowHandle, } impl WindowInputHandler { @@ -21,13 +21,12 @@ impl WindowInputHandler { // // See https://github.com/zed-industries/community/issues/444 let mut app = self.app.try_borrow_mut().ok()?; - app.update_window(self.window_id, |cx| { + self.window.update_optional(&mut *app, |cx| { let view_id = cx.window.focused_view_id?; - let view = cx.views.get(&(self.window_id, view_id))?; + let view = cx.views.get(&(self.window.id(), view_id))?; let result = f(view.as_ref(), &cx); Some(result) }) - .flatten() } fn update_focused_view(&mut self, f: F) -> Option @@ -35,11 +34,12 @@ impl WindowInputHandler { F: FnOnce(&mut dyn AnyView, &mut WindowContext, usize) -> T, { let mut app = self.app.try_borrow_mut().ok()?; - app.update_window(self.window_id, |cx| { - let view_id = cx.window.focused_view_id?; - cx.update_any_view(view_id, |view, cx| f(view, cx, view_id)) - }) - .flatten() + self.window + .update(&mut *app, |cx| { + let view_id = cx.window.focused_view_id?; + cx.update_any_view(view_id, |view, cx| f(view, cx, view_id)) + }) + .flatten() } } @@ -83,9 +83,8 @@ impl InputHandler for WindowInputHandler { } fn rect_for_range(&self, range_utf16: Range) -> Option { - self.app - .borrow() - .read_window(self.window_id, |cx| cx.rect_for_text_range(range_utf16)) - .flatten() + self.window.read_optional_with(&*self.app.borrow(), |cx| { + cx.rect_for_text_range(range_utf16) + }) } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 37fab1ee462d7a242c331ea5df169babbae3a6d8..c5e0e7e1ad6e2cb83b6c9cdb3dbd01f8f1687d3c 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4035,16 +4035,9 @@ pub fn restart(_: &Restart, cx: &mut AppContext) { let should_confirm = settings::get::(cx).confirm_quit; cx.spawn(|mut cx| async move { let mut workspaces = cx - .window_ids() + .windows() .into_iter() - .filter_map(|window_id| { - Some( - cx.root_view(window_id)? - .clone() - .downcast::()? - .downgrade(), - ) - }) + .filter_map(|window| Some(window.downcast::()?.root(&cx)?.downgrade())) .collect::>(); // If multiple windows have unsaved changes, and need a save prompt, From 4e33654abaafd22de5140cc6e40f342351047a2a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 7 Aug 2023 13:53:41 -0700 Subject: [PATCH 23/53] Make LspAdapter::process_diagnostics synchronous Co-authored-by: Nathan --- crates/language/src/language.rs | 14 ++++---------- crates/project/src/project.rs | 29 +++++++++++++---------------- crates/zed/src/languages/rust.rs | 4 ++-- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 125e14d445f3872e9718cb2759016a09b0c28439..100ab275717066251973c3f6d547e32f72ff0b4c 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -182,8 +182,8 @@ impl CachedLspAdapter { self.adapter.workspace_configuration(cx) } - pub async fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) { - self.adapter.process_diagnostics(params).await + pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) { + self.adapter.process_diagnostics(params) } pub async fn process_completion(&self, completion_item: &mut lsp::CompletionItem) { @@ -262,7 +262,7 @@ pub trait LspAdapter: 'static + Send + Sync { container_dir: PathBuf, ) -> Option; - async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} + fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} async fn process_completion(&self, _: &mut lsp::CompletionItem) {} @@ -1487,12 +1487,6 @@ impl Language { None } - pub async fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams) { - for adapter in &self.adapters { - adapter.process_diagnostics(diagnostics).await; - } - } - pub async fn process_completion(self: &Arc, completion: &mut lsp::CompletionItem) { for adapter in &self.adapters { adapter.process_completion(completion).await; @@ -1756,7 +1750,7 @@ impl LspAdapter for Arc { unreachable!(); } - async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} + fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} async fn disk_based_diagnostic_sources(&self) -> Vec { self.disk_based_diagnostics_sources.clone() diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 6b905a1faa876bafd793b5e6fc02be4aacc437c2..1aa2a2dd40c2d01b98860d39420c6ec4ab894728 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2769,24 +2769,21 @@ impl Project { language_server .on_notification::({ let adapter = adapter.clone(); - move |mut params, cx| { + move |mut params, mut cx| { let this = this; let adapter = adapter.clone(); - cx.spawn(|mut cx| async move { - adapter.process_diagnostics(&mut params).await; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.update_diagnostics( - server_id, - params, - &adapter.disk_based_diagnostic_sources, - cx, - ) - .log_err(); - }); - } - }) - .detach(); + adapter.process_diagnostics(&mut params); + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.update_diagnostics( + server_id, + params, + &adapter.disk_based_diagnostic_sources, + cx, + ) + .log_err(); + }); + } } }) .detach(); diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 97549b00583d45f3b03a730d2a10f43dc9b2f978..3c7f84fec7dced7f8241ff7009160b0d748191f4 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -102,7 +102,7 @@ impl LspAdapter for RustLspAdapter { Some("rust-analyzer/flycheck".into()) } - async fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) { + fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) { lazy_static! { static ref REGEX: Regex = Regex::new("(?m)`([^`]+)\n`$").unwrap(); } @@ -310,7 +310,7 @@ mod tests { }, ], }; - RustLspAdapter.process_diagnostics(&mut params).await; + RustLspAdapter.process_diagnostics(&mut params); assert_eq!(params.diagnostics[0].message, "use of moved value `a`"); From 580c2ea8eb7ec308ba0dc1d003b8addb212bc371 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 7 Aug 2023 17:07:01 -0400 Subject: [PATCH 24/53] Fix test name --- crates/util/src/paths.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 7e0b240570f249b7dc97b17ea0fc85581fd44410..f231669197fcfdd560011132e406772da64e3050 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -294,7 +294,7 @@ mod tests { } #[test] - fn test_path_suffix() { + fn test_icon_suffix() { // No dots in name let path = Path::new("/a/b/c/file_name.rs"); assert_eq!(path.icon_suffix(), Some("rs")); From dbf25ea2ffbb77c8ad2276860c4e24a1da4fd70d Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 7 Aug 2023 17:24:22 -0400 Subject: [PATCH 25/53] Add syntax highlighting for Cargo.toml files --- crates/zed/src/languages/toml/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zed/src/languages/toml/config.toml b/crates/zed/src/languages/toml/config.toml index 4e89f5cabd99fb2faf120b32943be31d20d2a514..188239a8e0d2b518c99b9b6f69a14632bae37926 100644 --- a/crates/zed/src/languages/toml/config.toml +++ b/crates/zed/src/languages/toml/config.toml @@ -1,5 +1,5 @@ name = "TOML" -path_suffixes = ["toml"] +path_suffixes = ["Cargo.lock", "toml"] line_comment = "# " autoclose_before = ",]}" brackets = [ From ca21626064d078867c7241d7eb39b09f763fa894 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 7 Aug 2023 23:32:27 +0200 Subject: [PATCH 26/53] Baseline: Improve selection rendering for large quantities from 270ms to 90ms --- crates/editor/src/editor.rs | 64 +++++++++++++++++++++++++++++++++- crates/editor/src/element.rs | 67 +++++++++++++----------------------- 2 files changed, 86 insertions(+), 45 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index cd5e86b91098b35cad385331d5e8f28a380d3139..8f8511deb697913c1e8e1fcebd6702177d504c34 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -90,7 +90,7 @@ use std::{ cmp::{self, Ordering, Reverse}, mem, num::NonZeroU32, - ops::{ControlFlow, Deref, DerefMut, Range}, + ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive}, path::Path, sync::Arc, time::{Duration, Instant}, @@ -7549,6 +7549,68 @@ impl Editor { results } + pub fn selected_rows( + &self, + search_range: Range, + display_snapshot: &DisplaySnapshot, + theme: &Theme, + ) -> Vec> { + let mut results = Vec::new(); + let buffer = &display_snapshot.buffer_snapshot; + let Some((color_fetcher, ranges)) = self.background_highlights + .get(&TypeId::of::()) else { + return vec![]; + }; + + let color = color_fetcher(theme); + let start_ix = match ranges.binary_search_by(|probe| { + let cmp = probe.end.cmp(&search_range.start, buffer); + if cmp.is_gt() { + Ordering::Greater + } else { + Ordering::Less + } + }) { + Ok(i) | Err(i) => i, + }; + let mut push_region = |start, end| { + if let (Some(start_display), Some(end_display)) = (start, end) { + results.push(start_display..=end_display); + } + }; + let mut start_row = None; + let mut end_row = None; + for range in &ranges[start_ix..] { + if range.start.cmp(&search_range.end, buffer).is_ge() { + break; + } + let start = range.start.to_point(buffer).row; + let end = range.end.to_point(buffer).row; + if start_row.is_none() { + assert_eq!(end_row, None); + start_row = Some(start); + end_row = Some(end); + continue; + } + if let Some(current_end) = end_row.as_mut() { + if start > *current_end + 1 { + push_region(start_row, end_row); + start_row = Some(start); + end_row = Some(end); + } else { + // Merge two hunks. + *current_end = end; + } + } else { + unreachable!(); + } + } + // We might still have a hunk that was not rendered (if there was a search hit on the last line) + push_region(start_row, end_row); + + results + } + pub fn highlight_text( &mut self, ranges: Vec>, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 2d4b273f5ef0523f8ef8d9d9cd107c7e30bd659d..9ef95ec929a9ebb1425eed4c049f159e95c00466 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1107,8 +1107,6 @@ impl EditorElement { if layout.is_singleton && scrollbar_settings.selections { let start_anchor = Anchor::min(); let end_anchor = Anchor::max(); - let mut start_row = None; - let mut end_row = None; let color = scrollbar_theme.selections; let border = Border { width: 1., @@ -1119,54 +1117,35 @@ impl EditorElement { bottom: false, left: true, }; - let mut push_region = |start, end| { - if let (Some(start_display), Some(end_display)) = (start, end) { - let start_y = y_for_row(start_display as f32); - let mut end_y = y_for_row(end_display as f32); - if end_y - start_y < 1. { - end_y = start_y + 1.; - } - let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y)); - - scene.push_quad(Quad { - bounds, - background: Some(color), - border, - corner_radius: style.thumb.corner_radius, - }) + let mut push_region = |start: u32, end: u32| { + let start_y = y_for_row(start as f32); + let mut end_y = y_for_row(end as f32); + if end_y - start_y < 1. { + end_y = start_y + 1.; } + let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y)); + + scene.push_quad(Quad { + bounds, + background: Some(color), + border, + corner_radius: style.thumb.corner_radius, + }) }; - for (row, _) in &editor - .background_highlights_in_range_for::( + let start = std::time::Instant::now(); + + let background_ranges = editor + .selected_rows::( start_anchor..end_anchor, &layout.position_map.snapshot, &theme, - ) - { - let start_display = row.start; - let end_display = row.end; - - if start_row.is_none() { - assert_eq!(end_row, None); - start_row = Some(start_display.row()); - end_row = Some(end_display.row()); - continue; - } - if let Some(current_end) = end_row.as_mut() { - if start_display.row() > *current_end + 1 { - push_region(start_row, end_row); - start_row = Some(start_display.row()); - end_row = Some(end_display.row()); - } else { - // Merge two hunks. - *current_end = end_display.row(); - } - } else { - unreachable!(); - } + ); + dbg!(start.elapsed().as_millis()); + for row in background_ranges { + let start = row.start(); + let end = row.end(); + push_region(*start, *end); } - // We might still have a hunk that was not rendered (if there was a search hit on the last line) - push_region(start_row, end_row); } if layout.is_singleton && scrollbar_settings.git_diff { From fa168959764defa98ed2c7ce5173f38afb6135aa Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 00:27:38 +0200 Subject: [PATCH 27/53] Do not query start of range if it's end is the same as the previous hunk's --- crates/editor/src/editor.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8f8511deb697913c1e8e1fcebd6702177d504c34..c12bdc9c55c9b97a96da6452c6ca8ea9a8512098 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7557,12 +7557,11 @@ impl Editor { ) -> Vec> { let mut results = Vec::new(); let buffer = &display_snapshot.buffer_snapshot; - let Some((color_fetcher, ranges)) = self.background_highlights + let Some((_, ranges)) = self.background_highlights .get(&TypeId::of::()) else { return vec![]; }; - let color = color_fetcher(theme); let start_ix = match ranges.binary_search_by(|probe| { let cmp = probe.end.cmp(&search_range.start, buffer); if cmp.is_gt() { @@ -7584,8 +7583,14 @@ impl Editor { if range.start.cmp(&search_range.end, buffer).is_ge() { break; } - let start = range.start.to_point(buffer).row; let end = range.end.to_point(buffer).row; + if let Some(current_row) = &end_row { + if end == *current_row { + continue; + } + } + let start = range.start.to_point(buffer).row; + if start_row.is_none() { assert_eq!(end_row, None); start_row = Some(start); From 42e12213571d475b5019113b49c1a88b3f0f0a30 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 02:17:11 +0200 Subject: [PATCH 28/53] Add upper bound limit. Remove dbg! statements --- crates/editor/src/editor.rs | 4 +++- crates/editor/src/element.rs | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c12bdc9c55c9b97a96da6452c6ca8ea9a8512098..682974d61fb3b2993048db2105c740617cfba04e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7553,6 +7553,7 @@ impl Editor { &self, search_range: Range, display_snapshot: &DisplaySnapshot, + count: usize, theme: &Theme, ) -> Vec> { let mut results = Vec::new(); @@ -7572,6 +7573,7 @@ impl Editor { }) { Ok(i) | Err(i) => i, }; + let end_ix = count.min(ranges.len()); let mut push_region = |start, end| { if let (Some(start_display), Some(end_display)) = (start, end) { results.push(start_display..=end_display); @@ -7579,7 +7581,7 @@ impl Editor { }; let mut start_row = None; let mut end_row = None; - for range in &ranges[start_ix..] { + for range in &ranges[start_ix..end_ix] { if range.start.cmp(&search_range.end, buffer).is_ge() { break; } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 9ef95ec929a9ebb1425eed4c049f159e95c00466..735abbbd13f0dd834c461f3792bd4adedd1b3f9b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1132,15 +1132,13 @@ impl EditorElement { corner_radius: style.thumb.corner_radius, }) }; - let start = std::time::Instant::now(); - let background_ranges = editor .selected_rows::( start_anchor..end_anchor, &layout.position_map.snapshot, + 50000, &theme, ); - dbg!(start.elapsed().as_millis()); for row in background_ranges { let start = row.start(); let end = row.end(); From 241d3951b8193d2ee1ba8fd177815531334da33b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 02:25:30 +0200 Subject: [PATCH 29/53] Remove redundant argument --- crates/editor/src/editor.rs | 1 - crates/editor/src/element.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 682974d61fb3b2993048db2105c740617cfba04e..0711e5eb024b6cfa9ea0b3f5bc6ee313d4c2cd52 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7554,7 +7554,6 @@ impl Editor { search_range: Range, display_snapshot: &DisplaySnapshot, count: usize, - theme: &Theme, ) -> Vec> { let mut results = Vec::new(); let buffer = &display_snapshot.buffer_snapshot; diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 735abbbd13f0dd834c461f3792bd4adedd1b3f9b..c3c8681bcf0d572419c144ce410bdc32b52426b5 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1137,7 +1137,6 @@ impl EditorElement { start_anchor..end_anchor, &layout.position_map.snapshot, 50000, - &theme, ); for row in background_ranges { let start = row.start(); From b0fc6da55b495dc5f43a934ee8b2d67eb1fbe62e Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 02:37:27 +0200 Subject: [PATCH 30/53] Use display maps --- crates/editor/src/editor.rs | 21 ++++++++++++--------- crates/editor/src/element.rs | 6 +++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 0711e5eb024b6cfa9ea0b3f5bc6ee313d4c2cd52..8f4f97ad604dc15c47dbc2373495919059497dab 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7554,7 +7554,7 @@ impl Editor { search_range: Range, display_snapshot: &DisplaySnapshot, count: usize, - ) -> Vec> { + ) -> Vec> { let mut results = Vec::new(); let buffer = &display_snapshot.buffer_snapshot; let Some((_, ranges)) = self.background_highlights @@ -7573,24 +7573,27 @@ impl Editor { Ok(i) | Err(i) => i, }; let end_ix = count.min(ranges.len()); - let mut push_region = |start, end| { + let mut push_region = |start: Option, end: Option| { if let (Some(start_display), Some(end_display)) = (start, end) { - results.push(start_display..=end_display); + results.push( + start_display.to_display_point(display_snapshot) + ..=end_display.to_display_point(display_snapshot), + ); } }; - let mut start_row = None; - let mut end_row = None; + let mut start_row: Option = None; + let mut end_row: Option = None; for range in &ranges[start_ix..end_ix] { if range.start.cmp(&search_range.end, buffer).is_ge() { break; } - let end = range.end.to_point(buffer).row; + let end = range.end.to_point(buffer); if let Some(current_row) = &end_row { - if end == *current_row { + if end.row == current_row.row { continue; } } - let start = range.start.to_point(buffer).row; + let start = range.start.to_point(buffer); if start_row.is_none() { assert_eq!(end_row, None); @@ -7599,7 +7602,7 @@ impl Editor { continue; } if let Some(current_end) = end_row.as_mut() { - if start > *current_end + 1 { + if start.row > current_end.row + 1 { push_region(start_row, end_row); start_row = Some(start); end_row = Some(end); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index c3c8681bcf0d572419c144ce410bdc32b52426b5..30a9cba85c3e0bd92bc2fa0c2e121dc6e9949835 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1117,9 +1117,9 @@ impl EditorElement { bottom: false, left: true, }; - let mut push_region = |start: u32, end: u32| { - let start_y = y_for_row(start as f32); - let mut end_y = y_for_row(end as f32); + let mut push_region = |start: DisplayPoint, end: DisplayPoint| { + let start_y = y_for_row(start.row() as f32); + let mut end_y = y_for_row(end.row() as f32); if end_y - start_y < 1. { end_y = start_y + 1.; } From 371c669e003f8082896706261779231b3c2dce11 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 02:45:15 +0200 Subject: [PATCH 31/53] Address review feedback. Rename selected_rows to background_highlight_row_ranges. Do not return any ranges if there are more than 50k results --- crates/editor/src/editor.rs | 6 ++++-- crates/editor/src/element.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8f4f97ad604dc15c47dbc2373495919059497dab..17195cb22b5de1bc60c21c8169f929e020cc78f7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7549,7 +7549,7 @@ impl Editor { results } - pub fn selected_rows( + pub fn background_highlight_row_ranges( &self, search_range: Range, display_snapshot: &DisplaySnapshot, @@ -7616,7 +7616,9 @@ impl Editor { } // We might still have a hunk that was not rendered (if there was a search hit on the last line) push_region(start_row, end_row); - + if results.len() > count { + return vec![]; + } results } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 30a9cba85c3e0bd92bc2fa0c2e121dc6e9949835..33ebd4c7bd632d5137b6f8f565e6e7010899abe1 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1133,7 +1133,7 @@ impl EditorElement { }) }; let background_ranges = editor - .selected_rows::( + .background_highlight_row_ranges::( start_anchor..end_anchor, &layout.position_map.snapshot, 50000, From 486f5bc6ca186b07c415aea5794631c5f6cbcf80 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 7 Aug 2023 19:08:58 -0600 Subject: [PATCH 32/53] Get compiling --- crates/collab/src/tests/integration_tests.rs | 4 +- .../src/project_shared_notification.rs | 2 +- .../collab_ui/src/sharing_status_indicator.rs | 8 +- crates/editor/src/editor_tests.rs | 6 +- .../src/test/editor_lsp_test_context.rs | 2 +- crates/editor/src/test/editor_test_context.rs | 10 ++- crates/go_to_line/src/go_to_line.rs | 18 ++-- crates/gpui/src/app.rs | 7 ++ crates/gpui/src/app/window.rs | 2 +- crates/vim/src/editor_events.rs | 2 +- crates/vim/src/mode_indicator.rs | 2 +- crates/vim/src/test/vim_test_context.rs | 2 +- crates/workspace/src/workspace.rs | 52 ++++++----- crates/zed/src/zed.rs | 87 +++++++------------ 14 files changed, 97 insertions(+), 107 deletions(-) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 1a8e6d938d6d6caac2b8caee501e1088ddeea5b3..92ccee91c0ecb5cd6d8a0ca2af5928f9b6c3a72f 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3446,7 +3446,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx)); let mut editor_cx_a = EditorTestContext { cx: cx_a, - window_id: window_a.id(), + window: window_a.into(), editor: editor_a, }; @@ -3459,7 +3459,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor( let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx)); let mut editor_cx_b = EditorTestContext { cx: cx_b, - window_id: window_b.id(), + window: window_b.into(), editor: editor_b, }; diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 03ab91623b43270bf2592ced0f460b474fd0c435..d5e7c877f7067480688faedeb9b36be964878a72 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -5,7 +5,7 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions}, - AppContext, Entity, View, ViewContext, + AppContext, BorrowWindowContext, Entity, View, ViewContext, }; use std::sync::{Arc, Weak}; use workspace::AppState; diff --git a/crates/collab_ui/src/sharing_status_indicator.rs b/crates/collab_ui/src/sharing_status_indicator.rs index 3a1dde072f867b0791ae8fab90d6c6328857bb2a..a39ffc457a04cacacf351fcc2d85cac8c0a391cd 100644 --- a/crates/collab_ui/src/sharing_status_indicator.rs +++ b/crates/collab_ui/src/sharing_status_indicator.rs @@ -20,11 +20,11 @@ pub fn init(cx: &mut AppContext) { { status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator)); } - } else if let Some((window_id, _)) = status_indicator.take() { - cx.update_window(window_id, |cx| cx.remove_window()); + } else if let Some(window) = status_indicator.take() { + window.update(cx, |cx| cx.remove_window()); } - } else if let Some((window_id, _)) = status_indicator.take() { - cx.update_window(window_id, |cx| cx.remove_window()); + } else if let Some(window) = status_indicator.take() { + window.update(cx, |cx| cx.remove_window()); } }) .detach(); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index a114cd437b16bbad580d6e732bb004ac352d822a..9a65e2e9532c4c74c855b58ae714bd8c7d0970bf 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1290,7 +1290,7 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppCon let mut cx = EditorTestContext::new(cx).await; let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); - cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height)); + cx.simulate_window_resize(cx.window.id(), vec2f(100., 4. * line_height)); cx.set_state( &r#"ˇone @@ -1401,7 +1401,7 @@ async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); let mut cx = EditorTestContext::new(cx).await; let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); - cx.simulate_window_resize(cx.window_id, vec2f(1000., 4. * line_height + 0.5)); + cx.simulate_window_resize(cx.window.id(), vec2f(1000., 4. * line_height + 0.5)); cx.set_state( &r#"ˇone @@ -1439,7 +1439,7 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx).await; let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); - cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height)); + cx.simulate_window_resize(cx.window.id(), vec2f(100., 4. * line_height)); cx.set_state( &r#" diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index f53115f224a1cfe6cb4a3c634786c7e7e8c18305..83aaa3b703491b5f5334d173ca8d90e731aeb5e8 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -99,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> { Self { cx: EditorTestContext { cx, - window_id: window.id(), + window: window.into(), editor, }, lsp, diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index c7ea1b4f3852301e300c5ff3f1db1dff65f342a2..118cddaa9226a543ca479f577428237d77539d5d 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -3,7 +3,8 @@ use crate::{ }; use futures::Future; use gpui::{ - keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle, + keymap_matcher::Keystroke, AnyWindowHandle, AppContext, ContextHandle, ModelContext, + ViewContext, ViewHandle, }; use indoc::indoc; use language::{Buffer, BufferSnapshot}; @@ -21,7 +22,7 @@ use super::build_editor; pub struct EditorTestContext<'a> { pub cx: &'a mut gpui::TestAppContext, - pub window_id: usize, + pub window: AnyWindowHandle, pub editor: ViewHandle, } @@ -39,7 +40,7 @@ impl<'a> EditorTestContext<'a> { let editor = window.root(cx); Self { cx, - window_id: window.id(), + window: window.into(), editor, } } @@ -111,7 +112,8 @@ impl<'a> EditorTestContext<'a> { let keystroke_under_test_handle = self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text)); let keystroke = Keystroke::parse(keystroke_text).unwrap(); - self.cx.dispatch_keystroke(self.window_id, keystroke, false); + + self.cx.dispatch_keystroke(self.window, keystroke, false); keystroke_under_test_handle } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 769f2eda55b4a165e138ee094adc4a581963a641..fa42a523741679c191c5fab053417243f4d5d4df 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -135,14 +135,16 @@ impl Entity for GoToLine { fn release(&mut self, cx: &mut AppContext) { let scroll_position = self.prev_scroll_position.take(); - cx.update_window(self.active_editor.window_id(), |cx| { - self.active_editor.update(cx, |editor, cx| { - editor.highlight_rows(None); - if let Some(scroll_position) = scroll_position { - editor.set_scroll_position(scroll_position, cx); - } - }) - }); + if let Some(window) = self.active_editor.window(cx) { + window.update(cx, |cx| { + self.active_editor.update(cx, |editor, cx| { + editor.highlight_rows(None); + if let Some(scroll_position) = scroll_position { + editor.set_scroll_position(scroll_position, cx); + } + }) + }); + } } } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 730d0da5a054ea808dedd236892d4ca14aeefc30..b95cb0179a84131afcccf2f07bd9929d166684d8 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -23,6 +23,7 @@ use std::{ }; use anyhow::{anyhow, Context, Result}; + use derive_more::Deref; use parking_lot::Mutex; use postage::oneshot; @@ -4127,6 +4128,12 @@ impl ViewHandle { self.window_id } + pub fn window(&self, cx: &C) -> C::Result { + cx.read_window(self.window_id, |cx| { + AnyWindowHandle::new(self.window_id, cx.window.root_view.type_id()) + }) + } + pub fn id(&self) -> usize { self.view_id } diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index d960b9da16a7c84d46fda2b17cd2650204aa1c08..70d02c1fa1265b5449b127a6736c8a81573be1e1 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -175,7 +175,7 @@ impl BorrowWindowContext for WindowContext<'_> { where F: FnOnce(&mut WindowContext) -> Option, { - BorrowWindowContext::update_window_optional(self, window_id, f) + BorrowWindowContext::update_window(self, window_id, f) } } diff --git a/crates/vim/src/editor_events.rs b/crates/vim/src/editor_events.rs index 60e63f982347b9fee2a10087d530527a1db69ad4..4fef6bd715eedbe49b1347b679a2ebe6d3a89f33 100644 --- a/crates/vim/src/editor_events.rs +++ b/crates/vim/src/editor_events.rs @@ -1,6 +1,6 @@ use crate::Vim; use editor::{EditorBlurred, EditorFocused, EditorReleased}; -use gpui::AppContext; +use gpui::{AppContext, BorrowWindowContext}; pub fn init(cx: &mut AppContext) { cx.subscribe_global(focused).detach(); diff --git a/crates/vim/src/mode_indicator.rs b/crates/vim/src/mode_indicator.rs index 639a7594f113c61bc8c232325ac8d769b2ac89e5..afd60af848be052060988ba961bb75e811f33d3b 100644 --- a/crates/vim/src/mode_indicator.rs +++ b/crates/vim/src/mode_indicator.rs @@ -1,6 +1,6 @@ use gpui::{ elements::{Empty, Label}, - AnyElement, Element, Entity, Subscription, View, ViewContext, + AnyElement, Element, Entity, Subscription, View, ViewContext, BorrowWindowContext }; use settings::SettingsStore; use workspace::{item::ItemHandle, StatusItemView}; diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index ea09e550916c4355b10d12b6d3a5a2207112dd16..839ab3aafc87e535a8b09096be6b58a5837063da 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -85,7 +85,7 @@ impl<'a> VimTestContext<'a> { } pub fn set_state(&mut self, text: &str, mode: Mode) -> ContextHandle { - let window_id = self.window_id; + let window_id = self.window.id(); self.update_window(window_id, |cx| { Vim::update(cx, |vim, cx| { vim.switch_mode(mode, false, cx); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c5e0e7e1ad6e2cb83b6c9cdb3dbd01f8f1687d3c..679c34611b37a4c7dd88e86c39338b75d9bfe9df 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3827,9 +3827,9 @@ pub fn activate_workspace_for_project( cx: &mut AsyncAppContext, predicate: impl Fn(&mut Project, &mut ModelContext) -> bool, ) -> Option> { - for window_id in cx.window_ids() { - let handle = cx - .update_window(window_id, |cx| { + for window in cx.windows() { + let handle = window + .update(cx, |cx| { if let Some(workspace_handle) = cx.root_view().clone().downcast::() { let project = workspace_handle.read(cx).project.clone(); if project.update(cx, &predicate) { @@ -3945,18 +3945,23 @@ pub fn join_remote_project( ) -> Task> { cx.spawn(|mut cx| async move { let existing_workspace = cx - .window_ids() + .windows() .into_iter() - .filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::()) - .find(|workspace| { - cx.read_window(workspace.window_id(), |cx| { - workspace.read(cx).project().read(cx).remote_id() == Some(project_id) + .find_map(|window| { + window.downcast::().and_then(|window| { + window.read_root_with(&cx, |workspace, cx| { + if workspace.project().read(cx).remote_id() == Some(project_id) { + Some(cx.handle().downgrade()) + } else { + None + } + }) }) - .unwrap_or(false) - }); + }) + .flatten(); let workspace = if let Some(existing_workspace) = existing_workspace { - existing_workspace.downgrade() + existing_workspace } else { let active_call = cx.read(ActiveCall::global); let room = active_call @@ -4034,19 +4039,19 @@ pub fn join_remote_project( pub fn restart(_: &Restart, cx: &mut AppContext) { let should_confirm = settings::get::(cx).confirm_quit; cx.spawn(|mut cx| async move { - let mut workspaces = cx + let mut workspace_windows = cx .windows() .into_iter() - .filter_map(|window| Some(window.downcast::()?.root(&cx)?.downgrade())) + .filter_map(|window| window.downcast::()) .collect::>(); // If multiple windows have unsaved changes, and need a save prompt, // prompt in the active window before switching to a different window. - workspaces.sort_by_key(|workspace| !cx.window_is_active(workspace.window_id())); + workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false)); - if let (true, Some(workspace)) = (should_confirm, workspaces.first()) { + if let (true, Some(window)) = (should_confirm, workspace_windows.first()) { let answer = cx.prompt( - workspace.window_id(), + window.id(), PromptLevel::Info, "Are you sure you want to restart?", &["Restart", "Cancel"], @@ -4061,14 +4066,13 @@ pub fn restart(_: &Restart, cx: &mut AppContext) { } // If the user cancels any save prompt, then keep the app open. - for workspace in workspaces { - if !workspace - .update(&mut cx, |workspace, cx| { - workspace.prepare_to_close(true, cx) - })? - .await? - { - return Ok(()); + for window in workspace_windows { + if let Some(close) = window.update_root(&mut cx, |workspace, cx| { + workspace.prepare_to_close(true, cx) + }) { + if !close.await? { + return Ok(()); + } } } cx.platform().restart(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a8a287fb4e6fc964346378fad4eee09bc5eb057a..2b9321b303e8610bed75b9da53b770dbc6267c3a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -406,26 +406,19 @@ pub fn build_window_options( fn quit(_: &Quit, cx: &mut gpui::AppContext) { let should_confirm = settings::get::(cx).confirm_quit; cx.spawn(|mut cx| async move { - let mut workspaces = cx - .window_ids() + let mut workspace_windows = cx + .windows() .into_iter() - .filter_map(|window_id| { - Some( - cx.root_view(window_id)? - .clone() - .downcast::()? - .downgrade(), - ) - }) + .filter_map(|window| window.downcast::()) .collect::>(); // If multiple windows have unsaved changes, and need a save prompt, // prompt in the active window before switching to a different window. - workspaces.sort_by_key(|workspace| !cx.window_is_active(workspace.window_id())); + workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false)); - if let (true, Some(workspace)) = (should_confirm, workspaces.first()) { + if let (true, Some(window)) = (should_confirm, workspace_windows.first()) { let answer = cx.prompt( - workspace.window_id(), + window.id(), PromptLevel::Info, "Are you sure you want to quit?", &["Quit", "Cancel"], @@ -440,14 +433,13 @@ fn quit(_: &Quit, cx: &mut gpui::AppContext) { } // If the user cancels any save prompt, then keep the app open. - for workspace in workspaces { - if !workspace - .update(&mut cx, |workspace, cx| { - workspace.prepare_to_close(true, cx) - })? - .await? - { - return Ok(()); + for window in workspace_windows { + if let Some(close) = window.update_root(&mut cx, |workspace, cx| { + workspace.prepare_to_close(false, cx) + }) { + if close.await? { + return Ok(()); + } } } cx.platform().quit(); @@ -782,17 +774,13 @@ mod tests { }) .await .unwrap(); - assert_eq!(cx.window_ids().len(), 1); + assert_eq!(cx.windows().len(), 1); cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) .await .unwrap(); - assert_eq!(cx.window_ids().len(), 1); - let workspace_1 = cx - .read_window(cx.window_ids()[0], |cx| cx.root_view().clone()) - .unwrap() - .downcast::() - .unwrap(); + assert_eq!(cx.windows().len(), 1); + let workspace_1 = cx.windows()[0].downcast::().unwrap().root(cx); workspace_1.update(cx, |workspace, cx| { assert_eq!(workspace.worktrees(cx).count(), 2); assert!(workspace.left_dock().read(cx).is_open()); @@ -809,28 +797,22 @@ mod tests { }) .await .unwrap(); - assert_eq!(cx.window_ids().len(), 2); + assert_eq!(cx.windows().len(), 2); // Replace existing windows - let window_id = cx.window_ids()[0]; - let window = cx.read_window(window_id, |cx| cx.window()).flatten(); + let window = cx.windows()[0].downcast::().unwrap(); cx.update(|cx| { open_paths( &[PathBuf::from("/root/c"), PathBuf::from("/root/d")], &app_state, - window, + Some(window), cx, ) }) .await .unwrap(); - assert_eq!(cx.window_ids().len(), 2); - let workspace_1 = cx - .read_window(cx.window_ids()[0], |cx| cx.root_view().clone()) - .unwrap() - .clone() - .downcast::() - .unwrap(); + assert_eq!(cx.windows().len(), 2); + let workspace_1 = cx.windows()[0].downcast::().unwrap().root(cx); workspace_1.update(cx, |workspace, cx| { assert_eq!( workspace @@ -856,14 +838,10 @@ mod tests { cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) .await .unwrap(); - assert_eq!(cx.window_ids().len(), 1); + assert_eq!(cx.windows().len(), 1); // When opening the workspace, the window is not in a edited state. - let workspace = cx - .read_window(cx.window_ids()[0], |cx| cx.root_view().clone()) - .unwrap() - .downcast::() - .unwrap(); + let workspace = cx.windows()[0].downcast::().unwrap().root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let editor = workspace.read_with(cx, |workspace, cx| { workspace @@ -917,12 +895,12 @@ mod tests { // buffer having unsaved changes. assert!(!cx.simulate_window_close(workspace.window_id())); executor.run_until_parked(); - assert_eq!(cx.window_ids().len(), 1); + assert_eq!(cx.windows().len(), 1); // The window is successfully closed after the user dismisses the prompt. cx.simulate_prompt_answer(workspace.window_id(), 1); executor.run_until_parked(); - assert_eq!(cx.window_ids().len(), 0); + assert_eq!(cx.windows().len(), 0); } #[gpui::test] @@ -935,12 +913,13 @@ mod tests { }) .await; - let window_id = *cx.window_ids().first().unwrap(); - let workspace = cx - .read_window(window_id, |cx| cx.root_view().clone()) + let window = cx + .windows() + .first() .unwrap() .downcast::() .unwrap(); + let workspace = window.root(cx); let editor = workspace.update(cx, |workspace, cx| { workspace @@ -1105,12 +1084,8 @@ mod tests { cx.update(|cx| open_paths(&[PathBuf::from("/dir1/")], &app_state, None, cx)) .await .unwrap(); - assert_eq!(cx.window_ids().len(), 1); - let workspace = cx - .read_window(cx.window_ids()[0], |cx| cx.root_view().clone()) - .unwrap() - .downcast::() - .unwrap(); + assert_eq!(cx.windows().len(), 1); + let workspace = cx.windows()[0].downcast::().unwrap().root(cx); #[track_caller] fn assert_project_panel_selection( From 0197d49230832118cf78f4b5c94d47d390b27d44 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 7 Aug 2023 19:45:43 -0600 Subject: [PATCH 33/53] Move activation simulation to AnyWindowHandle --- crates/gpui/src/app.rs | 31 +++++++++++++++++++++---- crates/gpui/src/app/test_app_context.rs | 28 ++++------------------ crates/workspace/src/workspace.rs | 6 ++--- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index b95cb0179a84131afcccf2f07bd9929d166684d8..bd3fbb8149fb6ecfd00bb461f8c4a21a22289047 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -4087,6 +4087,29 @@ impl AnyWindowHandle { pub fn remove(&self, cx: &mut C) -> C::Result<()> { self.update(cx, |cx| cx.remove_window()) } + + pub fn simulate_activation(&self, cx: &mut TestAppContext) { + self.update(cx, |cx| { + let other_window_ids = cx + .windows + .keys() + .filter(|window_id| **window_id != self.window_id) + .copied() + .collect::>(); + + for window_id in other_window_ids { + cx.window_changed_active_status(window_id, false) + } + + cx.window_changed_active_status(self.window_id, true) + }); + } + + pub fn simulate_deactivation(&self, cx: &mut TestAppContext) { + self.update(cx, |cx| { + cx.window_changed_active_status(self.window_id, false); + }) + } } #[repr(transparent)] @@ -6726,25 +6749,25 @@ mod tests { [("window 2", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_2.id())); + window_2.simulate_activation(cx); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 3", false), ("window 2", true)] ); - cx.simulate_window_activation(Some(window_1.id())); + window_1.simulate_activation(cx); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 2", false), ("window 1", true)] ); - cx.simulate_window_activation(Some(window_3.id())); + window_3.simulate_activation(cx); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 1", false), ("window 3", true)] ); - cx.simulate_window_activation(Some(window_3.id())); + window_3.simulate_activation(cx); assert_eq!(mem::take(&mut *events.borrow_mut()), []); } diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 0331c7922e680fe1c45460bc2569c85ab77a3f19..675d8b8528dc0b2c95b2c140207be6a4e3067563 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -156,17 +156,16 @@ impl TestAppContext { self.cx.borrow_mut().add_model(build_model) } - pub fn add_window(&mut self, build_root_view: F) -> WindowHandle + pub fn add_window(&mut self, build_root_view: F) -> WindowHandle where - T: View, - F: FnOnce(&mut ViewContext) -> T, + V: View, + F: FnOnce(&mut ViewContext) -> V, { let window = self .cx .borrow_mut() .add_window(Default::default(), build_root_view); - self.simulate_window_activation(Some(window.id())); - + window.simulate_activation(self); WindowHandle::new(window.id()) } @@ -321,25 +320,6 @@ impl TestAppContext { self.platform_window_mut(window_id).resize_handlers = handlers; } - pub fn simulate_window_activation(&self, to_activate: Option) { - self.cx.borrow_mut().update(|cx| { - let other_window_ids = cx - .windows - .keys() - .filter(|window_id| Some(**window_id) != to_activate) - .copied() - .collect::>(); - - for window_id in other_window_ids { - cx.window_changed_active_status(window_id, false) - } - - if let Some(to_activate) = to_activate { - cx.window_changed_active_status(to_activate, true) - } - }); - } - pub fn is_window_edited(&self, window_id: usize) -> bool { self.platform_window_mut(window_id).edited } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 679c34611b37a4c7dd88e86c39338b75d9bfe9df..dc52614ecf497542306a7705e3be2f938d825315 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4530,7 +4530,7 @@ mod tests { }); // Deactivating the window saves the file. - cx.simulate_window_activation(None); + window.simulate_deactivation(cx); deterministic.run_until_parked(); item.read_with(cx, |item, _| assert_eq!(item.save_count, 1)); @@ -4551,12 +4551,12 @@ mod tests { item.read_with(cx, |item, _| assert_eq!(item.save_count, 2)); // Deactivating the window still saves the file. - cx.simulate_window_activation(Some(window.id())); + window.simulate_activation(cx); item.update(cx, |item, cx| { cx.focus_self(); item.is_dirty = true; }); - cx.simulate_window_activation(None); + window.simulate_deactivation(cx); deterministic.run_until_parked(); item.read_with(cx, |item, _| assert_eq!(item.save_count, 3)); From f2be3181a98aab8c711d6c4f92b43dc891dc6ce0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 7 Aug 2023 20:23:04 -0600 Subject: [PATCH 34/53] Move window-related methods from TestAppContext to AnyWindowHandle --- crates/collab/src/tests/integration_tests.rs | 4 +- crates/editor/src/editor_tests.rs | 9 +- crates/gpui/src/app/test_app_context.rs | 132 +++++++++---------- crates/project_panel/src/project_panel.rs | 26 ++-- crates/workspace/src/workspace.rs | 46 +++---- crates/zed/src/zed.rs | 29 ++-- 6 files changed, 118 insertions(+), 128 deletions(-) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 92ccee91c0ecb5cd6d8a0ca2af5928f9b6c3a72f..29dcd95eae076cd38ae36fb950a41eabe98905a0 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -1510,7 +1510,7 @@ async fn test_host_disconnect( .unwrap(); assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx))); editor_b.update(cx_b, |editor, cx| editor.insert("X", cx)); - assert!(cx_b.is_window_edited(workspace_b.window_id())); + assert!(window_b.is_edited(cx_b)); // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared. server.forbid_connections(); @@ -1525,7 +1525,7 @@ async fn test_host_disconnect( window_b.read_with(cx_b, |cx| { assert_eq!(cx.focused_view_id(), None); }); - assert!(!cx_b.is_window_edited(workspace_b.window_id())); + assert!(!window_b.is_edited(cx_b)); // Ensure client B is not prompted to save edits when closing window after disconnecting. let can_close = workspace_b diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 9a65e2e9532c4c74c855b58ae714bd8c7d0970bf..61fa952f944d1059c210629aa93eba0736d62459 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1290,7 +1290,8 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppCon let mut cx = EditorTestContext::new(cx).await; let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); - cx.simulate_window_resize(cx.window.id(), vec2f(100., 4. * line_height)); + let window = cx.window; + window.simulate_resize(vec2f(100., 4. * line_height), &mut cx); cx.set_state( &r#"ˇone @@ -1401,7 +1402,8 @@ async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); let mut cx = EditorTestContext::new(cx).await; let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); - cx.simulate_window_resize(cx.window.id(), vec2f(1000., 4. * line_height + 0.5)); + let window = cx.window; + window.simulate_resize(vec2f(1000., 4. * line_height + 0.5), &mut cx); cx.set_state( &r#"ˇone @@ -1439,7 +1441,8 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx).await; let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); - cx.simulate_window_resize(cx.window.id(), vec2f(100., 4. * line_height)); + let window = cx.window; + window.simulate_resize(vec2f(100., 4. * line_height), &mut cx); cx.set_state( &r#" diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 675d8b8528dc0b2c95b2c140207be6a4e3067563..e298224caa2d0fc4829443a836cc9bbb91ef6087 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -202,10 +202,6 @@ impl TestAppContext { self.cx.borrow().windows().collect() } - // pub fn window_ids(&self) -> Vec { - // self.cx.borrow().windows.keys().copied().collect() - // } - pub fn remove_all_windows(&mut self) { self.update(|cx| cx.windows.clear()); } @@ -273,57 +269,6 @@ impl TestAppContext { self.foreground_platform.as_ref().did_prompt_for_new_path() } - pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) { - use postage::prelude::Sink as _; - - let mut done_tx = self - .platform_window_mut(window_id) - .pending_prompts - .borrow_mut() - .pop_front() - .expect("prompt was not called"); - done_tx.try_send(answer).ok(); - } - - pub fn has_pending_prompt(&self, window_id: usize) -> bool { - let window = self.platform_window_mut(window_id); - let prompts = window.pending_prompts.borrow_mut(); - !prompts.is_empty() - } - - pub fn current_window_title(&self, window_id: usize) -> Option { - self.platform_window_mut(window_id).title.clone() - } - - pub fn simulate_window_close(&self, window_id: usize) -> bool { - let handler = self - .platform_window_mut(window_id) - .should_close_handler - .take(); - if let Some(mut handler) = handler { - let should_close = handler(); - self.platform_window_mut(window_id).should_close_handler = Some(handler); - should_close - } else { - false - } - } - - pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) { - let mut window = self.platform_window_mut(window_id); - window.size = size; - let mut handlers = mem::take(&mut window.resize_handlers); - drop(window); - for handler in &mut handlers { - handler(); - } - self.platform_window_mut(window_id).resize_handlers = handlers; - } - - pub fn is_window_edited(&self, window_id: usize) -> bool { - self.platform_window_mut(window_id).edited - } - pub fn leak_detector(&self) -> Arc> { self.cx.borrow().leak_detector() } @@ -344,18 +289,6 @@ impl TestAppContext { self.assert_dropped(weak); } - fn platform_window_mut(&self, window_id: usize) -> std::cell::RefMut { - std::cell::RefMut::map(self.cx.borrow_mut(), |state| { - let window = state.windows.get_mut(&window_id).unwrap(); - let test_window = window - .platform_window - .as_any_mut() - .downcast_mut::() - .unwrap(); - test_window - }) - } - pub fn set_condition_duration(&mut self, duration: Option) { self.condition_duration = duration; } @@ -545,6 +478,71 @@ impl ModelHandle { } } +impl AnyWindowHandle { + pub fn has_pending_prompt(&self, cx: &mut TestAppContext) -> bool { + let window = self.platform_window_mut(cx); + let prompts = window.pending_prompts.borrow_mut(); + !prompts.is_empty() + } + + pub fn current_title(&self, cx: &mut TestAppContext) -> Option { + self.platform_window_mut(cx).title.clone() + } + + pub fn simulate_close(&self, cx: &mut TestAppContext) -> bool { + let handler = self.platform_window_mut(cx).should_close_handler.take(); + if let Some(mut handler) = handler { + let should_close = handler(); + self.platform_window_mut(cx).should_close_handler = Some(handler); + should_close + } else { + false + } + } + + pub fn simulate_resize(&self, size: Vector2F, cx: &mut TestAppContext) { + let mut window = self.platform_window_mut(cx); + window.size = size; + let mut handlers = mem::take(&mut window.resize_handlers); + drop(window); + for handler in &mut handlers { + handler(); + } + self.platform_window_mut(cx).resize_handlers = handlers; + } + + pub fn is_edited(&self, cx: &mut TestAppContext) -> bool { + self.platform_window_mut(cx).edited + } + + pub fn simulate_prompt_answer(&self, answer: usize, cx: &mut TestAppContext) { + use postage::prelude::Sink as _; + + let mut done_tx = self + .platform_window_mut(cx) + .pending_prompts + .borrow_mut() + .pop_front() + .expect("prompt was not called"); + done_tx.try_send(answer).ok(); + } + + fn platform_window_mut<'a>( + &self, + cx: &'a mut TestAppContext, + ) -> std::cell::RefMut<'a, platform::test::Window> { + std::cell::RefMut::map(cx.cx.borrow_mut(), |state| { + let window = state.windows.get_mut(&self.window_id).unwrap(); + let test_window = window + .platform_window + .as_any_mut() + .downcast_mut::() + .unwrap(); + test_window + }) + } +} + impl ViewHandle { pub fn next_notification(&self, cx: &TestAppContext) -> impl Future { use postage::prelude::{Sink as _, Stream as _}; diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 021ea2d3bc3a817342e9eac684496ffeee5026bb..07aaea812a98d5758bb15b801c5cd8bdf1688e8f 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1726,7 +1726,7 @@ impl ClipboardEntry { #[cfg(test)] mod tests { use super::*; - use gpui::{TestAppContext, ViewHandle}; + use gpui::{AnyWindowHandle, TestAppContext, ViewHandle}; use pretty_assertions::assert_eq; use project::FakeFs; use serde_json::json; @@ -2421,7 +2421,7 @@ mod tests { ); ensure_single_file_is_opened(window_id, &workspace, "test/first.rs", cx); - submit_deletion(window_id, &panel, cx); + submit_deletion(window.into(), &panel, cx); assert_eq!( visible_entries_as_strings(&panel, 0..10, cx), &[ @@ -2432,7 +2432,7 @@ mod tests { ], "Project panel should have no deleted file, no other file is selected in it" ); - ensure_no_open_items_and_panes(window_id, &workspace, cx); + ensure_no_open_items_and_panes(window.into(), &workspace, cx); select_path(&panel, "src/test/second.rs", cx); panel.update(cx, |panel, cx| panel.open_file(&Open, cx)); @@ -2464,13 +2464,13 @@ mod tests { .expect("Open item should be an editor"); open_editor.update(cx, |editor, cx| editor.set_text("Another text!", cx)); }); - submit_deletion(window_id, &panel, cx); + submit_deletion(window.into(), &panel, cx); assert_eq!( visible_entries_as_strings(&panel, 0..10, cx), &["v src", " v test", " third.rs"], "Project panel should have no deleted file, with one last file remaining" ); - ensure_no_open_items_and_panes(window_id, &workspace, cx); + ensure_no_open_items_and_panes(window.into(), &workspace, cx); } #[gpui::test] @@ -2910,12 +2910,12 @@ mod tests { } fn submit_deletion( - window_id: usize, + window: AnyWindowHandle, panel: &ViewHandle, cx: &mut TestAppContext, ) { assert!( - !cx.has_pending_prompt(window_id), + !window.has_pending_prompt(cx), "Should have no prompts before the deletion" ); panel.update(cx, |panel, cx| { @@ -2925,27 +2925,27 @@ mod tests { .detach_and_log_err(cx); }); assert!( - cx.has_pending_prompt(window_id), + window.has_pending_prompt(cx), "Should have a prompt after the deletion" ); - cx.simulate_prompt_answer(window_id, 0); + window.simulate_prompt_answer(0, cx); assert!( - !cx.has_pending_prompt(window_id), + !window.has_pending_prompt(cx), "Should have no prompts after prompt was replied to" ); cx.foreground().run_until_parked(); } fn ensure_no_open_items_and_panes( - window_id: usize, + window: AnyWindowHandle, workspace: &ViewHandle, cx: &mut TestAppContext, ) { assert!( - !cx.has_pending_prompt(window_id), + !window.has_pending_prompt(cx), "Should have no prompts after deletion operation closes the file" ); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { let open_project_paths = workspace .read(cx) .panes() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index dc52614ecf497542306a7705e3be2f938d825315..da708c6ea727de82b1c288a9549e6d627fad07b3 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4197,17 +4197,11 @@ mod tests { .map(|e| e.id) ); }); - assert_eq!( - cx.current_window_title(window.id()).as_deref(), - Some("one.txt — root1") - ); + assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1")); // Add a second item to a non-empty pane workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx)); - assert_eq!( - cx.current_window_title(window.id()).as_deref(), - Some("two.txt — root1") - ); + assert_eq!(window.current_title(cx).as_deref(), Some("two.txt — root1")); project.read_with(cx, |project, cx| { assert_eq!( project.active_entry(), @@ -4223,10 +4217,7 @@ mod tests { }) .await .unwrap(); - assert_eq!( - cx.current_window_title(window.id()).as_deref(), - Some("one.txt — root1") - ); + assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1")); project.read_with(cx, |project, cx| { assert_eq!( project.active_entry(), @@ -4244,16 +4235,13 @@ mod tests { .await .unwrap(); assert_eq!( - cx.current_window_title(window.id()).as_deref(), + window.current_title(cx).as_deref(), Some("one.txt — root1, root2") ); // Remove a project folder project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); - assert_eq!( - cx.current_window_title(window.id()).as_deref(), - Some("one.txt — root2") - ); + assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root2")); } #[gpui::test] @@ -4287,9 +4275,9 @@ mod tests { }); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); cx.foreground().run_until_parked(); - cx.simulate_prompt_answer(window.id(), 2 /* cancel */); + window.simulate_prompt_answer(2, cx); // cancel cx.foreground().run_until_parked(); - assert!(!cx.has_pending_prompt(window.id())); + assert!(!window.has_pending_prompt(cx)); assert!(!task.await.unwrap()); } @@ -4348,10 +4336,10 @@ mod tests { assert_eq!(pane.items_len(), 4); assert_eq!(pane.active_item().unwrap().id(), item1.id()); }); - assert!(cx.has_pending_prompt(window.id())); + assert!(window.has_pending_prompt(cx)); // Confirm saving item 1. - cx.simulate_prompt_answer(window.id(), 0); + window.simulate_prompt_answer(0, cx); cx.foreground().run_until_parked(); // Item 1 is saved. There's a prompt to save item 3. @@ -4362,10 +4350,10 @@ mod tests { assert_eq!(pane.items_len(), 3); assert_eq!(pane.active_item().unwrap().id(), item3.id()); }); - assert!(cx.has_pending_prompt(window.id())); + assert!(window.has_pending_prompt(cx)); // Cancel saving item 3. - cx.simulate_prompt_answer(window.id(), 1); + window.simulate_prompt_answer(1, cx); cx.foreground().run_until_parked(); // Item 3 is reloaded. There's a prompt to save item 4. @@ -4376,10 +4364,10 @@ mod tests { assert_eq!(pane.items_len(), 2); assert_eq!(pane.active_item().unwrap().id(), item4.id()); }); - assert!(cx.has_pending_prompt(window.id())); + assert!(window.has_pending_prompt(cx)); // Confirm saving item 4. - cx.simulate_prompt_answer(window.id(), 0); + window.simulate_prompt_answer(0, cx); cx.foreground().run_until_parked(); // There's a prompt for a path for item 4. @@ -4482,7 +4470,7 @@ mod tests { &[ProjectEntryId::from_proto(0)] ); }); - cx.simulate_prompt_answer(window.id(), 0); + window.simulate_prompt_answer(0, cx); cx.foreground().run_until_parked(); left_pane.read_with(cx, |pane, cx| { @@ -4491,7 +4479,7 @@ mod tests { &[ProjectEntryId::from_proto(2)] ); }); - cx.simulate_prompt_answer(window.id(), 0); + window.simulate_prompt_answer(0, cx); cx.foreground().run_until_parked(); close.await.unwrap(); @@ -4593,7 +4581,7 @@ mod tests { pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)) .await .unwrap(); - assert!(!cx.has_pending_prompt(window.id())); + assert!(!window.has_pending_prompt(cx)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); // Add the item again, ensuring autosave is prevented if the underlying file has been deleted. @@ -4614,7 +4602,7 @@ mod tests { let _close_items = pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)); deterministic.run_until_parked(); - assert!(cx.has_pending_prompt(window.id())); + assert!(window.has_pending_prompt(cx)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 2b9321b303e8610bed75b9da53b770dbc6267c3a..3435727b1e0345ab1eef4cb63870b6b71f03c7f9 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -841,7 +841,8 @@ mod tests { assert_eq!(cx.windows().len(), 1); // When opening the workspace, the window is not in a edited state. - let workspace = cx.windows()[0].downcast::().unwrap().root(cx); + let window = cx.windows()[0].downcast::().unwrap(); + let workspace = window.root(cx); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let editor = workspace.read_with(cx, |workspace, cx| { workspace @@ -850,19 +851,19 @@ mod tests { .downcast::() .unwrap() }); - assert!(!cx.is_window_edited(workspace.window_id())); + assert!(!window.is_edited(cx)); // Editing a buffer marks the window as edited. editor.update(cx, |editor, cx| editor.insert("EDIT", cx)); - assert!(cx.is_window_edited(workspace.window_id())); + assert!(window.is_edited(cx)); // Undoing the edit restores the window's edited state. editor.update(cx, |editor, cx| editor.undo(&Default::default(), cx)); - assert!(!cx.is_window_edited(workspace.window_id())); + assert!(!window.is_edited(cx)); // Redoing the edit marks the window as edited again. editor.update(cx, |editor, cx| editor.redo(&Default::default(), cx)); - assert!(cx.is_window_edited(workspace.window_id())); + assert!(window.is_edited(cx)); // Closing the item restores the window's edited state. let close = pane.update(cx, |pane, cx| { @@ -870,9 +871,10 @@ mod tests { pane.close_active_item(&Default::default(), cx).unwrap() }); executor.run_until_parked(); - cx.simulate_prompt_answer(workspace.window_id(), 1); + + window.simulate_prompt_answer(1, cx); close.await.unwrap(); - assert!(!cx.is_window_edited(workspace.window_id())); + assert!(!window.is_edited(cx)); // Opening the buffer again doesn't impact the window's edited state. cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) @@ -885,20 +887,20 @@ mod tests { .downcast::() .unwrap() }); - assert!(!cx.is_window_edited(workspace.window_id())); + assert!(!window.is_edited(cx)); // Editing the buffer marks the window as edited. editor.update(cx, |editor, cx| editor.insert("EDIT", cx)); - assert!(cx.is_window_edited(workspace.window_id())); + assert!(window.is_edited(cx)); // Ensure closing the window via the mouse gets preempted due to the // buffer having unsaved changes. - assert!(!cx.simulate_window_close(workspace.window_id())); + assert!(!window.simulate_close(cx)); executor.run_until_parked(); assert_eq!(cx.windows().len(), 1); // The window is successfully closed after the user dismisses the prompt. - cx.simulate_prompt_answer(workspace.window_id(), 1); + window.simulate_prompt_answer(1, cx); executor.run_until_parked(); assert_eq!(cx.windows().len(), 0); } @@ -1273,7 +1275,6 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); // Open a file within an existing worktree. workspace @@ -1299,7 +1300,7 @@ mod tests { cx.read(|cx| assert!(editor.is_dirty(cx))); let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx)); - cx.simulate_prompt_answer(window_id, 0); + window.simulate_prompt_answer(0, cx); save_task.await.unwrap(); editor.read_with(cx, |editor, cx| { assert!(!editor.is_dirty(cx)); @@ -1506,7 +1507,7 @@ mod tests { cx.dispatch_action(window_id, workspace::CloseActiveItem); cx.foreground().run_until_parked(); - cx.simulate_prompt_answer(window_id, 1); + window.simulate_prompt_answer(1, cx); cx.foreground().run_until_parked(); workspace.read_with(cx, |workspace, cx| { From 0f332238b3076d17fbaf2ff920cf9eaeda6ac2ac Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 7 Aug 2023 22:08:44 -0600 Subject: [PATCH 35/53] Remove unused method --- crates/gpui/src/app/test_app_context.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index e298224caa2d0fc4829443a836cc9bbb91ef6087..e0fd0cb8e9f15407f25a22aaa0e768ff786caecc 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -125,13 +125,6 @@ impl TestAppContext { } } - pub fn window(&self, window_id: usize) -> Option> { - self.cx - .borrow() - .read_window(window_id, |cx| cx.window()) - .flatten() - } - pub fn read_window T>( &self, window_id: usize, From f0da6b05fdfec04e4583be1231c7d755dad24646 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 7 Aug 2023 22:15:53 -0600 Subject: [PATCH 36/53] Remove TestAppContext::add_view Instead, we now call this on window handles. --- crates/collab/src/tests.rs | 44 ++----------------- crates/collab/src/tests/integration_tests.rs | 40 ++++++++--------- crates/command_palette/src/command_palette.rs | 3 +- crates/diagnostics/src/diagnostics.rs | 6 +-- crates/editor/src/editor_tests.rs | 3 +- crates/gpui/src/app/test_app_context.rs | 9 ---- crates/project_symbols/src/project_symbols.rs | 3 +- crates/search/src/buffer_search.rs | 17 +++---- crates/search/src/project_search.rs | 3 +- 9 files changed, 35 insertions(+), 93 deletions(-) diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 4804f5b0f1f24033fc79c12a6af5d87096a49a84..febe43ce5e1c78357c9ba4fa42c5a0f328b4ba9f 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -12,10 +12,7 @@ use client::{ use collections::{HashMap, HashSet}; use fs::FakeFs; use futures::{channel::oneshot, StreamExt as _}; -use gpui::{ - elements::*, executor::Deterministic, AnyElement, Entity, ModelHandle, TestAppContext, View, - ViewContext, ViewHandle, WeakViewHandle, -}; +use gpui::{executor::Deterministic, ModelHandle, TestAppContext, WindowHandle}; use language::LanguageRegistry; use parking_lot::Mutex; use project::{Project, WorktreeId}; @@ -466,43 +463,8 @@ impl TestClient { &self, project: &ModelHandle, cx: &mut TestAppContext, - ) -> ViewHandle { - struct WorkspaceContainer { - workspace: Option>, - } - - impl Entity for WorkspaceContainer { - type Event = (); - } - - impl View for WorkspaceContainer { - fn ui_name() -> &'static str { - "WorkspaceContainer" - } - - fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - if let Some(workspace) = self - .workspace - .as_ref() - .and_then(|workspace| workspace.upgrade(cx)) - { - ChildView::new(&workspace, cx).into_any() - } else { - Empty::new().into_any() - } - } - } - - // We use a workspace container so that we don't need to remove the window in order to - // drop the workspace and we can use a ViewHandle instead. - let window = cx.add_window(|_| WorkspaceContainer { workspace: None }); - let container = window.root(cx); - let workspace = window.add_view(cx, |cx| Workspace::test_new(project.clone(), cx)); - container.update(cx, |container, cx| { - container.workspace = Some(workspace.downgrade()); - cx.notify(); - }); - workspace + ) -> WindowHandle { + cx.add_window(|cx| Workspace::test_new(project.clone(), cx)) } } diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 29dcd95eae076cd38ae36fb950a41eabe98905a0..8ad7864adfc774888c395d9f14508a4e066edf63 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -6441,8 +6441,10 @@ async fn test_basic_following( .await .unwrap(); - let workspace_a = client_a.build_workspace(&project_a, cx_a); - let workspace_b = client_b.build_workspace(&project_b, cx_b); + let window_a = client_a.build_workspace(&project_a, cx_a); + let workspace_a = window_a.root(cx_a); + let window_b = client_b.build_workspace(&project_b, cx_b); + let workspace_b = window_b.root(cx_b); // Client A opens some editors. let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone()); @@ -6525,7 +6527,7 @@ async fn test_basic_following( cx_c.foreground().run_until_parked(); let active_call_c = cx_c.read(ActiveCall::global); let project_c = client_c.build_remote_project(project_id, cx_c).await; - let workspace_c = client_c.build_workspace(&project_c, cx_c); + let workspace_c = client_c.build_workspace(&project_c, cx_c).root(cx_c); active_call_c .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx)) .await @@ -6543,7 +6545,7 @@ async fn test_basic_following( cx_d.foreground().run_until_parked(); let active_call_d = cx_d.read(ActiveCall::global); let project_d = client_d.build_remote_project(project_id, cx_d).await; - let workspace_d = client_d.build_workspace(&project_d, cx_d); + let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d); active_call_d .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx)) .await @@ -6870,9 +6872,7 @@ async fn test_basic_following( }); // Client B activates a panel, and the previously-opened screen-sharing item gets activated. - let panel = cx_b.add_view(workspace_b.window_id(), |_| { - TestPanel::new(DockPosition::Left) - }); + let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left)); workspace_b.update(cx_b, |workspace, cx| { workspace.add_panel(panel, cx); workspace.toggle_panel_focus::(cx); @@ -6900,7 +6900,7 @@ async fn test_basic_following( // Client B activates an item that doesn't implement following, // so the previously-opened screen-sharing item gets activated. - let unfollowable_item = cx_b.add_view(workspace_b.window_id(), |_| TestItem::new()); + let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new()); workspace_b.update(cx_b, |workspace, cx| { workspace.active_pane().update(cx, |pane, cx| { pane.add_item(Box::new(unfollowable_item), true, true, None, cx) @@ -7062,10 +7062,10 @@ async fn test_following_tab_order( .await .unwrap(); - let workspace_a = client_a.build_workspace(&project_a, cx_a); + let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a); let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone()); - let workspace_b = client_b.build_workspace(&project_b, cx_b); + let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b); let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone()); let client_b_id = project_a.read_with(cx_a, |project, _| { @@ -7188,7 +7188,7 @@ async fn test_peers_following_each_other( .unwrap(); // Client A opens some editors. - let workspace_a = client_a.build_workspace(&project_a, cx_a); + let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a); let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone()); let _editor_a1 = workspace_a .update(cx_a, |workspace, cx| { @@ -7200,7 +7200,7 @@ async fn test_peers_following_each_other( .unwrap(); // Client B opens an editor. - let workspace_b = client_b.build_workspace(&project_b, cx_b); + let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b); let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone()); let _editor_b1 = workspace_b .update(cx_b, |workspace, cx| { @@ -7359,7 +7359,7 @@ async fn test_auto_unfollowing( .unwrap(); // Client A opens some editors. - let workspace_a = client_a.build_workspace(&project_a, cx_a); + let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a); let _editor_a1 = workspace_a .update(cx_a, |workspace, cx| { workspace.open_path((worktree_id, "1.txt"), None, true, cx) @@ -7370,7 +7370,7 @@ async fn test_auto_unfollowing( .unwrap(); // Client B starts following client A. - let workspace_b = client_b.build_workspace(&project_b, cx_b); + let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b); let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone()); let leader_id = project_b.read_with(cx_b, |project, _| { project.collaborators().values().next().unwrap().peer_id @@ -7498,14 +7498,14 @@ async fn test_peers_simultaneously_following_each_other( client_a.fs.insert_tree("/a", json!({})).await; let (project_a, _) = client_a.build_local_project("/a", cx_a).await; - let workspace_a = client_a.build_workspace(&project_a, cx_a); + let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a); let project_id = active_call_a .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; - let workspace_b = client_b.build_workspace(&project_b, cx_b); + let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b); deterministic.run_until_parked(); let client_a_id = project_b.read_with(cx_b, |project, _| { @@ -7887,7 +7887,7 @@ async fn test_mutual_editor_inlay_hint_cache_update( .await .unwrap(); - let workspace_a = client_a.build_workspace(&project_a, cx_a); + let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a); cx_a.foreground().start_waiting(); let _buffer_a = project_a @@ -7955,7 +7955,7 @@ async fn test_mutual_editor_inlay_hint_cache_update( "Host editor update the cache version after every cache/view change", ); }); - let workspace_b = client_b.build_workspace(&project_b, cx_b); + let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b); let editor_b = workspace_b .update(cx_b, |workspace, cx| { workspace.open_path((worktree_id, "main.rs"), None, true, cx) @@ -8194,8 +8194,8 @@ async fn test_inlay_hint_refresh_is_forwarded( .await .unwrap(); - let workspace_a = client_a.build_workspace(&project_a, cx_a); - let workspace_b = client_b.build_workspace(&project_b, cx_b); + let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a); + let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b); cx_a.foreground().start_waiting(); cx_b.foreground().start_waiting(); diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 7d4b4126b702774bf8c295d3b61f904ff7ebf80c..f51a6c923802c93b197f059aed2af24f7cfde0e9 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -297,8 +297,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), [], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); - let editor = cx.add_view(window_id, |cx| { + let editor = window.add_view(cx, |cx| { let mut editor = Editor::single_line(None, cx); editor.set_text("abc", cx); editor diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 2444465be666124adb706bcf1833c50c77cee081..16a7340fae485b1c1b329315700437e4470c3af9 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -857,7 +857,6 @@ mod tests { let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); // Create some diagnostics project.update(cx, |project, cx| { @@ -944,7 +943,7 @@ mod tests { }); // Open the project diagnostics view while there are already diagnostics. - let view = cx.add_view(window_id, |cx| { + let view = window.add_view(cx, |cx| { ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) }); @@ -1252,9 +1251,8 @@ mod tests { let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); - let view = cx.add_view(window_id, |cx| { + let view = window.add_view(cx, |cx| { ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) }); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index c9688011a443fe0cb75fadbd9124b1feace579d6..ec1cc1249895881485489b56474c61aafd78d31d 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -525,9 +525,8 @@ async fn test_navigation_history(cx: &mut TestAppContext) { let project = Project::test(fs, [], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - cx.add_view(window_id, |cx| { + window.add_view(cx, |cx| { let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); let mut editor = build_editor(buffer.clone(), cx); let handle = cx.handle(); diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index e0fd0cb8e9f15407f25a22aaa0e768ff786caecc..ff988607b094f8d3253b8baaad2e6332bee4ad84 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -162,15 +162,6 @@ impl TestAppContext { WindowHandle::new(window.id()) } - pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle - where - T: View, - F: FnOnce(&mut ViewContext) -> T, - { - self.update_window(window_id, |cx| cx.add_view(build_view)) - .expect("window not found") - } - pub fn observe_global(&mut self, callback: F) -> Subscription where E: Any, diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 8471f3a3a7014d2034438839f76ae9b4214ded52..e88aee5dcfb6b9b5862ba92e6e8fc32fbc0da8cb 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -328,10 +328,9 @@ mod tests { let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); // Create the project symbols view. - let symbols = cx.add_view(window_id, |cx| { + let symbols = window.add_view(cx, |cx| { ProjectSymbols::new( ProjectSymbolsDelegate::new(workspace.downgrade(), project.clone()), cx, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 1e635432bd5c138f81410f3ac40ee461830ddb17..b20ffed6d7e1ad94e9c311503c332e066e5c9b91 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -850,12 +850,9 @@ mod tests { ) }); let window = cx.add_window(|_| EmptyView); + let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx)); - let editor = cx.add_view(window.id(), |cx| { - Editor::for_buffer(buffer.clone(), None, cx) - }); - - let search_bar = cx.add_view(window.id(), |cx| { + let search_bar = window.add_view(cx, |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); @@ -1234,9 +1231,9 @@ mod tests { let window = cx.add_window(|_| EmptyView); let window_id = window.id(); - let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); + let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx)); - let search_bar = cx.add_view(window_id, |cx| { + let search_bar = window.add_view(cx, |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); @@ -1421,11 +1418,9 @@ mod tests { let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let window = cx.add_window(|_| EmptyView); - let editor = cx.add_view(window.id(), |cx| { - Editor::for_buffer(buffer.clone(), None, cx) - }); + let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx)); - let search_bar = cx.add_view(window.id(), |cx| { + let search_bar = window.add_view(cx, |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(cx); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 0db66b4e378a56c97e2e2d1290742e52e1329bda..dffd88db14229871a805a282ccbc49670697206e 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1874,7 +1874,6 @@ pub mod tests { let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); workspace.update(cx, |workspace, cx| { ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) }); @@ -1889,7 +1888,7 @@ pub mod tests { .expect("Search view expected to appear after new search event trigger") }); - let search_bar = cx.add_view(window_id, |cx| { + let search_bar = window.add_view(cx, |cx| { let mut search_bar = ProjectSearchBar::new(); search_bar.set_active_pane_item(Some(&search_view), cx); // search_bar.show(cx); From dba2facd23d94ebb79aab8a32b4318ab28320c83 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 7 Aug 2023 22:58:01 -0600 Subject: [PATCH 37/53] Remove window via handles --- crates/collab_ui/src/incoming_call_notification.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index a9c5e697a5f60d0cb6f9c0e6baf7cac50aba192a..6f86a74300acc36e6fc7240dd023cb0d034e4f03 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -7,7 +7,7 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions}, - AnyElement, AppContext, Entity, View, ViewContext, + AnyElement, AppContext, Entity, View, ViewContext, WindowHandle, }; use util::ResultExt; use workspace::AppState; @@ -16,10 +16,10 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { let app_state = Arc::downgrade(app_state); let mut incoming_call = ActiveCall::global(cx).read(cx).incoming(); cx.spawn(|mut cx| async move { - let mut notification_windows = Vec::new(); + let mut notification_windows: Vec> = Vec::new(); while let Some(incoming_call) = incoming_call.next().await { - for window_id in notification_windows.drain(..) { - cx.remove_window(window_id); + for window in notification_windows.drain(..) { + window.remove(&mut cx); } if let Some(incoming_call) = incoming_call { @@ -49,7 +49,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { |_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()), ); - notification_windows.push(window.id()); + notification_windows.push(window); } } } From 1aff642981fd8b6fd9f4a0b58d7ca43758485edb Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 13:09:27 +0200 Subject: [PATCH 38/53] Do not highlgiht selections at all over the threshold --- crates/editor/src/editor.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 17195cb22b5de1bc60c21c8169f929e020cc78f7..02cd58524bb5e6b5b97a17b751d9a2304abbaf11 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7572,7 +7572,6 @@ impl Editor { }) { Ok(i) | Err(i) => i, }; - let end_ix = count.min(ranges.len()); let mut push_region = |start: Option, end: Option| { if let (Some(start_display), Some(end_display)) = (start, end) { results.push( @@ -7583,7 +7582,10 @@ impl Editor { }; let mut start_row: Option = None; let mut end_row: Option = None; - for range in &ranges[start_ix..end_ix] { + if ranges.len() > count { + return vec![]; + } + for range in &ranges[start_ix..] { if range.start.cmp(&search_range.end, buffer).is_ge() { break; } @@ -7616,9 +7618,6 @@ impl Editor { } // We might still have a hunk that was not rendered (if there was a search hit on the last line) push_region(start_row, end_row); - if results.len() > count { - return vec![]; - } results } From 49f1f1c6c263e1346f0534660c8ce2c8c70859be Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 09:13:17 -0600 Subject: [PATCH 39/53] Remove window when closing workspace in test --- crates/collab/src/tests/integration_tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 8ad7864adfc774888c395d9f14508a4e066edf63..ce7fd8a094abb4006c97701c612ed65cae49a105 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -6527,7 +6527,8 @@ async fn test_basic_following( cx_c.foreground().run_until_parked(); let active_call_c = cx_c.read(ActiveCall::global); let project_c = client_c.build_remote_project(project_id, cx_c).await; - let workspace_c = client_c.build_workspace(&project_c, cx_c).root(cx_c); + let window_c = client_c.build_workspace(&project_c, cx_c); + let workspace_c = window_c.root(cx_c); active_call_c .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx)) .await @@ -6643,6 +6644,7 @@ async fn test_basic_following( } // Client C closes the project. + window_c.remove(cx_c); cx_c.drop_last(workspace_c); // Clients A and B see that client B is following A, and client C is not present in the followers. From d896d89842ed11021482aaa477177d1203f56347 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:08:37 -0600 Subject: [PATCH 40/53] Store an AnyWindowHandle in WindowContext --- crates/collab_ui/src/contact_list.rs | 4 +- .../src/project_shared_notification.rs | 16 +- crates/command_palette/src/command_palette.rs | 8 +- crates/context_menu/src/context_menu.rs | 12 +- crates/file_finder/src/file_finder.rs | 42 +- crates/go_to_line/src/go_to_line.rs | 18 +- crates/gpui/src/app.rs | 621 +++++++++--------- crates/gpui/src/app/test_app_context.rs | 32 +- crates/gpui/src/app/window.rs | 149 ++--- crates/project_panel/src/project_panel.rs | 28 +- crates/search/src/buffer_search.rs | 15 +- crates/search/src/project_search.rs | 25 +- crates/vim/src/editor_events.rs | 10 +- crates/vim/src/mode_indicator.rs | 4 +- crates/vim/src/test/vim_test_context.rs | 4 +- crates/workspace/src/dock.rs | 12 +- crates/workspace/src/pane.rs | 4 +- crates/workspace/src/workspace.rs | 22 +- crates/zed/src/zed.rs | 59 +- 19 files changed, 526 insertions(+), 559 deletions(-) diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 428f2156d116133d063710fed99c890e8ad30869..a2b281856d923754e715e0e112eca2f5c682e605 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -305,7 +305,7 @@ impl ContactList { github_login ); let mut answer = cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]); - let window_id = cx.window_id(); + let window = cx.window(); cx.spawn(|_, mut cx| async move { if answer.next().await == Some(0) { if let Err(e) = user_store @@ -313,7 +313,7 @@ impl ContactList { .await { cx.prompt( - window_id, + window, PromptLevel::Info, &format!("Failed to remove contact: {}", e), &["Ok"], diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index d5e7c877f7067480688faedeb9b36be964878a72..63922f2b65c91df1053533584ac8f1bf3473c899 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -5,7 +5,7 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions}, - AppContext, BorrowWindowContext, Entity, View, ViewContext, + AppContext, Entity, View, ViewContext, }; use std::sync::{Arc, Weak}; use workspace::AppState; @@ -52,20 +52,20 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { notification_windows .entry(*project_id) .or_insert(Vec::new()) - .push(window.id()); + .push(window); } } room::Event::RemoteProjectUnshared { project_id } => { - if let Some(window_ids) = notification_windows.remove(&project_id) { - for window_id in window_ids { - cx.update_window(window_id, |cx| cx.remove_window()); + if let Some(windows) = notification_windows.remove(&project_id) { + for window in windows { + window.remove(cx); } } } room::Event::Left => { - for (_, window_ids) in notification_windows.drain() { - for window_id in window_ids { - cx.update_window(window_id, |cx| cx.remove_window()); + for (_, windows) in notification_windows.drain() { + for window in windows { + window.remove(cx); } } } diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index f51a6c923802c93b197f059aed2af24f7cfde0e9..b3703aa64a4549596caf47b57fc91971c985b8f0 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -80,11 +80,11 @@ impl PickerDelegate for CommandPaletteDelegate { query: String, cx: &mut ViewContext>, ) -> gpui::Task<()> { - let window_id = cx.window_id(); let view_id = self.focused_view_id; + let window = cx.window(); cx.spawn(move |picker, mut cx| async move { let actions = cx - .available_actions(window_id, view_id) + .available_actions(window, view_id) .into_iter() .filter_map(|(name, action, bindings)| { let filtered = cx.read(|cx| { @@ -162,13 +162,13 @@ impl PickerDelegate for CommandPaletteDelegate { fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { if !self.matches.is_empty() { - let window_id = cx.window_id(); + let window = cx.window(); let focused_view_id = self.focused_view_id; let action_ix = self.matches[self.selected_ix].candidate_id; let action = self.actions.remove(action_ix).action; cx.app_context() .spawn(move |mut cx| async move { - cx.dispatch_action(window_id, focused_view_id, action.as_ref()) + cx.dispatch_action(window, focused_view_id, action.as_ref()) }) .detach_and_log_err(cx); } diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index f58afab361f4db153824ad9488fb295588dac425..75cfd872b29aba9b03b1235faeaba24d8ac95ae7 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -218,12 +218,12 @@ impl ContextMenu { if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) { match action { ContextMenuItemAction::Action(action) => { - let window_id = cx.window_id(); + let window = cx.window(); let view_id = self.parent_view_id; let action = action.boxed_clone(); cx.app_context() .spawn(|mut cx| async move { - cx.dispatch_action(window_id, view_id, action.as_ref()) + cx.dispatch_action(window, view_id, action.as_ref()) }) .detach_and_log_err(cx); } @@ -480,17 +480,13 @@ impl ContextMenu { .on_down(MouseButton::Left, |_, _, _| {}) // Capture these events .on_click(MouseButton::Left, move |_, menu, cx| { menu.cancel(&Default::default(), cx); - let window_id = cx.window_id(); + let window = cx.window(); match &action { ContextMenuItemAction::Action(action) => { let action = action.boxed_clone(); cx.app_context() .spawn(|mut cx| async move { - cx.dispatch_action( - window_id, - view_id, - action.as_ref(), - ) + cx.dispatch_action(window, view_id, action.as_ref()) }) .detach_and_log_err(cx); } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 12bf3242627619d390409bba044c0fa9cdcce3d4..523d6e8a5caeb9edea4c990237263c66e7d384e4 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -619,7 +619,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.id(), Toggle); + cx.dispatch_action(window.into(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); finder @@ -632,8 +632,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.id(), SelectNext); - cx.dispatch_action(window.id(), Confirm); + cx.dispatch_action(window.into(), SelectNext); + cx.dispatch_action(window.into(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -674,7 +674,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.id(), Toggle); + cx.dispatch_action(window.into(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -706,8 +706,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.id(), SelectNext); - cx.dispatch_action(window.id(), Confirm); + cx.dispatch_action(window.into(), SelectNext); + cx.dispatch_action(window.into(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -758,7 +758,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - cx.dispatch_action(window.id(), Toggle); + cx.dispatch_action(window.into(), Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); let file_query = &first_file_name[..3]; @@ -790,8 +790,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window.id(), SelectNext); - cx.dispatch_action(window.id(), Confirm); + cx.dispatch_action(window.into(), SelectNext); + cx.dispatch_action(window.into(), Confirm); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) .await; @@ -1168,7 +1168,6 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1); @@ -1186,7 +1185,7 @@ mod tests { "fir", 1, "first.rs", - window_id, + window.into(), &workspace, &deterministic, cx, @@ -1201,7 +1200,7 @@ mod tests { "sec", 1, "second.rs", - window_id, + window.into(), &workspace, &deterministic, cx, @@ -1223,7 +1222,7 @@ mod tests { "thi", 1, "third.rs", - window_id, + window.into(), &workspace, &deterministic, cx, @@ -1255,7 +1254,7 @@ mod tests { "sec", 1, "second.rs", - window_id, + window.into(), &workspace, &deterministic, cx, @@ -1294,7 +1293,7 @@ mod tests { "thi", 1, "third.rs", - window_id, + window.into(), &workspace, &deterministic, cx, @@ -1376,7 +1375,6 @@ mod tests { let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1,); @@ -1411,7 +1409,7 @@ mod tests { "sec", 1, "second.rs", - window_id, + window.into(), &workspace, &deterministic, cx, @@ -1433,7 +1431,7 @@ mod tests { "fir", 1, "first.rs", - window_id, + window.into(), &workspace, &deterministic, cx, @@ -1465,12 +1463,12 @@ mod tests { input: &str, expected_matches: usize, expected_editor_title: &str, - window_id: usize, + window: gpui::AnyWindowHandle, workspace: &ViewHandle, deterministic: &gpui::executor::Deterministic, cx: &mut gpui::TestAppContext, ) -> Vec { - cx.dispatch_action(window_id, Toggle); + cx.dispatch_action(window, Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); finder .update(cx, |finder, cx| { @@ -1487,8 +1485,8 @@ mod tests { }); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(window_id, SelectNext); - cx.dispatch_action(window_id, Confirm); + cx.dispatch_action(window, SelectNext); + cx.dispatch_action(window, Confirm); deterministic.run_until_parked(); active_pane .condition(cx, |pane, _| pane.active_item().is_some()) diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index fa42a523741679c191c5fab053417243f4d5d4df..1d3b44fa4359ec31fbb3c6aab5ba9b161d23f693 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -135,16 +135,14 @@ impl Entity for GoToLine { fn release(&mut self, cx: &mut AppContext) { let scroll_position = self.prev_scroll_position.take(); - if let Some(window) = self.active_editor.window(cx) { - window.update(cx, |cx| { - self.active_editor.update(cx, |editor, cx| { - editor.highlight_rows(None); - if let Some(scroll_position) = scroll_position { - editor.set_scroll_position(scroll_position, cx); - } - }) - }); - } + self.active_editor.window().update(cx, |cx| { + self.active_editor.update(cx, |editor, cx| { + editor.highlight_rows(None); + if let Some(scroll_position) = scroll_position { + editor.set_scroll_position(scroll_position, cx); + } + }) + }); } } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index ff8e343c2caaf361c2b178f16d1c94b492973cb8..e0f46b036a07322844daf0fd7ca30c4d546e185f 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -134,16 +134,16 @@ pub trait BorrowAppContext { pub trait BorrowWindowContext { type Result; - fn read_window(&self, window_id: usize, f: F) -> Self::Result + fn read_window(&self, window: AnyWindowHandle, f: F) -> Self::Result where F: FnOnce(&WindowContext) -> T; - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option; - fn update_window(&mut self, window_id: usize, f: F) -> Self::Result + fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T; - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option; } @@ -308,12 +308,12 @@ impl App { result } - fn update_window(&mut self, window_id: usize, callback: F) -> Option + fn update_window(&mut self, window: AnyWindowHandle, callback: F) -> Option where F: FnOnce(&mut WindowContext) -> T, { let mut state = self.0.borrow_mut(); - let result = state.update_window(window_id, callback); + let result = state.update_window(window, callback); state.pending_notifications.clear(); result } @@ -346,22 +346,22 @@ impl AsyncAppContext { pub fn read_window T>( &self, - window_id: usize, + window: AnyWindowHandle, callback: F, ) -> Option { - self.0.borrow_mut().read_window(window_id, callback) + self.0.borrow_mut().read_window(window, callback) } pub fn update_window T>( &mut self, - window_id: usize, + window: AnyWindowHandle, callback: F, ) -> Option { - self.0.borrow_mut().update_window(window_id, callback) + self.0.borrow_mut().update_window(window, callback) } - pub fn debug_elements(&self, window_id: usize) -> Option { - self.0.borrow().read_window(window_id, |cx| { + pub fn debug_elements(&self, window: AnyWindowHandle) -> Option { + self.0.borrow().read_window(window, |cx| { let root_view = cx.window.root_view(); let root_element = cx.window.rendered_views.get(&root_view.id())?; root_element.debug(cx).log_err() @@ -370,13 +370,13 @@ impl AsyncAppContext { pub fn dispatch_action( &mut self, - window_id: usize, + window: AnyWindowHandle, view_id: usize, action: &dyn Action, ) -> Result<()> { self.0 .borrow_mut() - .update_window(window_id, |cx| { + .update_window(window, |cx| { cx.dispatch_action(Some(view_id), action); }) .ok_or_else(|| anyhow!("window not found")) @@ -384,10 +384,10 @@ impl AsyncAppContext { pub fn available_actions( &self, - window_id: usize, + window: AnyWindowHandle, view_id: usize, ) -> Vec<(&'static str, Box, SmallVec<[Binding; 1]>)> { - self.read_window(window_id, |cx| cx.available_actions(view_id)) + self.read_window(window, |cx| cx.available_actions(view_id)) .unwrap_or_default() } @@ -411,23 +411,23 @@ impl AsyncAppContext { self.update(|cx| cx.add_window(window_options, build_root_view)) } - pub fn remove_window(&mut self, window_id: usize) { - self.update_window(window_id, |cx| cx.remove_window()); + pub fn remove_window(&mut self, window: AnyWindowHandle) { + self.update_window(window, |cx| cx.remove_window()); } - pub fn activate_window(&mut self, window_id: usize) { - self.update_window(window_id, |cx| cx.activate_window()); + pub fn activate_window(&mut self, window: AnyWindowHandle) { + self.update_window(window, |cx| cx.activate_window()); } // TODO: Can we eliminate this method and move it to WindowContext then call it with update_window?s pub fn prompt( &mut self, - window_id: usize, + window: AnyWindowHandle, level: PromptLevel, msg: &str, answers: &[&str], ) -> Option> { - self.update_window(window_id, |cx| cx.prompt(level, msg, answers)) + self.update_window(window, |cx| cx.prompt(level, msg, answers)) } pub fn platform(&self) -> Arc { @@ -456,38 +456,36 @@ impl BorrowAppContext for AsyncAppContext { impl BorrowWindowContext for AsyncAppContext { type Result = Option; - fn read_window(&self, window_id: usize, f: F) -> Self::Result + fn read_window(&self, window: AnyWindowHandle, f: F) -> Self::Result where F: FnOnce(&WindowContext) -> T, { - self.0.borrow().read_with(|cx| cx.read_window(window_id, f)) + self.0.borrow().read_with(|cx| cx.read_window(window, f)) } - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { self.0 .borrow_mut() - .update(|cx| cx.read_window_optional(window_id, f)) + .update(|cx| cx.read_window_optional(window, f)) } - fn update_window(&mut self, window_id: usize, f: F) -> Self::Result + fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T, { - self.0 - .borrow_mut() - .update(|cx| cx.update_window(window_id, f)) + self.0.borrow_mut().update(|cx| cx.update_window(window, f)) } - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option, { self.0 .borrow_mut() - .update(|cx| cx.update_window_optional(window_id, f)) + .update(|cx| cx.update_window_optional(window, f)) } } @@ -534,7 +532,7 @@ pub struct AppContext { global_actions: HashMap>, keystroke_matcher: KeymapMatcher, next_id: usize, - // next_window_id: usize, + // next_window: AnyWindowHandle, next_subscription_id: usize, frame_count: usize, @@ -794,13 +792,13 @@ impl AppContext { } } - pub fn view_ui_name(&self, window_id: usize, view_id: usize) -> Option<&'static str> { - Some(self.views.get(&(window_id, view_id))?.ui_name()) + pub fn view_ui_name(&self, window: AnyWindowHandle, view_id: usize) -> Option<&'static str> { + Some(self.views.get(&(window.id(), view_id))?.ui_name()) } - pub fn view_type_id(&self, window_id: usize, view_id: usize) -> Option { + pub fn view_type_id(&self, window: AnyWindowHandle, view_id: usize) -> Option { self.views_metadata - .get(&(window_id, view_id)) + .get(&(window.id(), view_id)) .map(|metadata| metadata.type_id) } @@ -823,11 +821,11 @@ impl AppContext { fn read_window T>( &self, - window_id: usize, + handle: AnyWindowHandle, callback: F, ) -> Option { - let window = self.windows.get(&window_id)?; - let window_context = WindowContext::immutable(self, &window, window_id); + let window = self.windows.get(&handle.id())?; + let window_context = WindowContext::immutable(self, &window, handle); Some(callback(&window_context)) } @@ -835,9 +833,8 @@ impl AppContext { &mut self, callback: F, ) -> Option { - self.platform - .main_window_id() - .and_then(|id| self.update_window(id, callback)) + self.main_window() + .and_then(|window| window.update(self, callback)) } pub fn prompt_for_paths( @@ -1075,10 +1072,10 @@ impl AppContext { } } - fn notify_view(&mut self, window_id: usize, view_id: usize) { + fn notify_view(&mut self, window: AnyWindowHandle, view_id: usize) { if self.pending_notifications.insert(view_id) { self.pending_effects - .push_back(Effect::ViewNotification { window_id, view_id }); + .push_back(Effect::ViewNotification { window, view_id }); } } @@ -1096,13 +1093,13 @@ impl AppContext { pub fn is_action_available(&self, action: &dyn Action) -> bool { let mut available_in_window = false; let action_id = action.id(); - if let Some(window_id) = self.platform.main_window_id() { + if let Some(window) = self.main_window() { available_in_window = self - .read_window(window_id, |cx| { + .read_window(window, |cx| { if let Some(focused_view_id) = cx.focused_view_id() { for view_id in cx.ancestors(focused_view_id) { if let Some(view_metadata) = - cx.views_metadata.get(&(window_id, view_id)) + cx.views_metadata.get(&(cx.window_handle.id(), view_id)) { if let Some(actions) = cx.actions.get(&view_metadata.type_id) { if actions.contains_key(&action_id) { @@ -1367,13 +1364,12 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { let handle: AnyWindowHandle = handle.into(); - let window_id = handle.id(); { let mut app = self.upgrade(); platform_window.on_event(Box::new(move |event| { - app.update_window(window_id, |cx| { + app.update_window(handle, |cx| { if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { if cx.dispatch_keystroke(keystroke) { return true; @@ -1389,35 +1385,35 @@ impl AppContext { { let mut app = self.upgrade(); platform_window.on_active_status_change(Box::new(move |is_active| { - app.update(|cx| cx.window_changed_active_status(window_id, is_active)) + app.update(|cx| cx.window_changed_active_status(handle, is_active)) })); } { let mut app = self.upgrade(); platform_window.on_resize(Box::new(move || { - app.update(|cx| cx.window_was_resized(window_id)) + app.update(|cx| cx.window_was_resized(handle)) })); } { let mut app = self.upgrade(); platform_window.on_moved(Box::new(move || { - app.update(|cx| cx.window_was_moved(window_id)) + app.update(|cx| cx.window_was_moved(handle)) })); } { let mut app = self.upgrade(); platform_window.on_fullscreen(Box::new(move |is_fullscreen| { - app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen)) + app.update(|cx| cx.window_was_fullscreen_changed(handle, is_fullscreen)) })); } { let mut app = self.upgrade(); platform_window.on_close(Box::new(move || { - app.update(|cx| cx.update_window(window_id, |cx| cx.remove_window())); + app.update(|cx| cx.update_window(handle, |cx| cx.remove_window())); })); } @@ -1432,8 +1428,8 @@ impl AppContext { window: handle, })); - let mut window = Window::new(window_id, platform_window, self, build_root_view); - let mut cx = WindowContext::mutable(self, &mut window, window_id); + let mut window = Window::new(handle, platform_window, self, build_root_view); + let mut cx = WindowContext::mutable(self, &mut window, handle); cx.layout(false).expect("initial layout should not error"); let scene = cx.paint().expect("initial paint should not error"); window.platform_window.present_scene(scene); @@ -1455,7 +1451,7 @@ impl AppContext { } pub fn read_view(&self, handle: &ViewHandle) -> &T { - if let Some(view) = self.views.get(&(handle.window_id, handle.view_id)) { + if let Some(view) = self.views.get(&(handle.window.id(), handle.view_id)) { view.as_any().downcast_ref().expect("downcast is type safe") } else { panic!("circular view reference for type {}", type_name::()); @@ -1465,7 +1461,7 @@ impl AppContext { fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { if self.ref_counts.lock().is_entity_alive(handle.view_id) { Some(ViewHandle::new( - handle.window_id, + handle.window, handle.view_id, &self.ref_counts, )) @@ -1477,7 +1473,7 @@ impl AppContext { fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option { if self.ref_counts.lock().is_entity_alive(handle.view_id) { Some(AnyViewHandle::new( - handle.window_id, + handle.window, handle.view_id, handle.view_type, self.ref_counts.clone(), @@ -1585,9 +1581,10 @@ impl AppContext { observations.emit(model_id, |callback| callback(self)); } - Effect::ViewNotification { window_id, view_id } => { - self.handle_view_notification_effect(window_id, view_id) - } + Effect::ViewNotification { + window: window_id, + view_id, + } => self.handle_view_notification_effect(window_id, view_id), Effect::GlobalNotification { type_id } => { let mut subscriptions = self.global_observations.clone(); @@ -1618,13 +1615,13 @@ impl AppContext { Effect::Focus(mut effect) => { if focus_effects - .get(&effect.window_id()) + .get(&effect.window().id()) .map_or(false, |prev_effect| prev_effect.is_forced()) { effect.force(); } - focus_effects.insert(effect.window_id(), effect); + focus_effects.insert(effect.window().id(), effect); } Effect::FocusObservation { @@ -1639,42 +1636,38 @@ impl AppContext { ); } - Effect::ResizeWindow { window_id } => { - if let Some(window) = self.windows.get_mut(&window_id) { + Effect::ResizeWindow { window } => { + if let Some(window) = self.windows.get_mut(&window.id()) { window .invalidation .get_or_insert(WindowInvalidation::default()); } - self.handle_window_moved(window_id); + self.handle_window_moved(window); } - Effect::MoveWindow { window_id } => { - self.handle_window_moved(window_id); + Effect::MoveWindow { window } => { + self.handle_window_moved(window); } Effect::WindowActivationObservation { - window_id, + window, subscription_id, callback, } => self.window_activation_observations.add_callback( - window_id, + window.id(), subscription_id, callback, ), - Effect::ActivateWindow { - window_id, - is_active, - } => { - if self.handle_window_activation_effect(window_id, is_active) - && is_active + Effect::ActivateWindow { window, is_active } => { + if self.handle_window_activation_effect(window, is_active) && is_active { focus_effects - .entry(window_id) + .entry(window.id()) .or_insert_with(|| FocusEffect::View { - window_id, + window, view_id: self - .read_window(window_id, |cx| cx.focused_view_id()) + .read_window(window, |cx| cx.focused_view_id()) .flatten(), is_forced: true, }) @@ -1683,26 +1676,26 @@ impl AppContext { } Effect::WindowFullscreenObservation { - window_id, + window, subscription_id, callback, } => self.window_fullscreen_observations.add_callback( - window_id, + window.id(), subscription_id, callback, ), Effect::FullscreenWindow { - window_id, + window, is_fullscreen, - } => self.handle_fullscreen_effect(window_id, is_fullscreen), + } => self.handle_fullscreen_effect(window, is_fullscreen), Effect::WindowBoundsObservation { - window_id, + window, subscription_id, callback, } => self.window_bounds_observations.add_callback( - window_id, + window.id(), subscription_id, callback, ), @@ -1714,18 +1707,15 @@ impl AppContext { Effect::ActionDispatchNotification { action_id } => { self.handle_action_dispatch_notification_effect(action_id) } - Effect::WindowShouldCloseSubscription { - window_id, - callback, - } => { - self.handle_window_should_close_subscription_effect(window_id, callback) + Effect::WindowShouldCloseSubscription { window, callback } => { + self.handle_window_should_close_subscription_effect(window, callback) } Effect::Keystroke { - window_id, + window, keystroke, handled_by, result, - } => self.handle_keystroke_effect(window_id, keystroke, handled_by, result), + } => self.handle_keystroke_effect(window, keystroke, handled_by, result), Effect::ActiveLabeledTasksChanged => { self.handle_active_labeled_tasks_changed_effect() } @@ -1740,8 +1730,8 @@ impl AppContext { } self.pending_notifications.clear(); } else { - for window_id in self.windows.keys().cloned().collect::>() { - self.update_window(window_id, |cx| { + for window in self.windows().collect::>() { + self.update_window(window, |cx| { let invalidation = if refreshing { let mut invalidation = cx.window.invalidation.take().unwrap_or_default(); @@ -1757,7 +1747,7 @@ impl AppContext { let appearance = cx.window.platform_window.appearance(); cx.invalidate(invalidation, appearance); if let Some(old_parents) = cx.layout(refreshing).log_err() { - updated_windows.insert(window_id); + updated_windows.insert(window); if let Some(focused_view_id) = cx.focused_view_id() { let old_ancestors = std::iter::successors( @@ -1772,7 +1762,7 @@ impl AppContext { for old_ancestor in old_ancestors.iter().copied() { if !new_ancestors.contains(&old_ancestor) { if let Some(mut view) = - cx.views.remove(&(window_id, old_ancestor)) + cx.views.remove(&(window.id(), old_ancestor)) { view.focus_out( focused_view_id, @@ -1780,7 +1770,7 @@ impl AppContext { old_ancestor, ); cx.views - .insert((window_id, old_ancestor), view); + .insert((window.id(), old_ancestor), view); } } } @@ -1789,7 +1779,7 @@ impl AppContext { for new_ancestor in new_ancestors.iter().copied() { if !old_ancestors.contains(&new_ancestor) { if let Some(mut view) = - cx.views.remove(&(window_id, new_ancestor)) + cx.views.remove(&(window.id(), new_ancestor)) { view.focus_in( focused_view_id, @@ -1797,7 +1787,7 @@ impl AppContext { new_ancestor, ); cx.views - .insert((window_id, new_ancestor), view); + .insert((window.id(), new_ancestor), view); } } } @@ -1806,13 +1796,15 @@ impl AppContext { // there isn't any pending focus, focus the root view. let root_view_id = cx.window.root_view().id(); if focused_view_id != root_view_id - && !cx.views.contains_key(&(window_id, focused_view_id)) - && !focus_effects.contains_key(&window_id) + && !cx + .views + .contains_key(&(window.id(), focused_view_id)) + && !focus_effects.contains_key(&window.id()) { focus_effects.insert( - window_id, + window.id(), FocusEffect::View { - window_id, + window, view_id: Some(root_view_id), is_forced: false, }, @@ -1833,8 +1825,8 @@ impl AppContext { callback(self); } - for window_id in updated_windows.drain() { - self.update_window(window_id, |cx| { + for window in updated_windows.drain() { + self.update_window(window, |cx| { if let Some(scene) = cx.paint().log_err() { cx.window.platform_window.present_scene(scene); } @@ -1855,39 +1847,37 @@ impl AppContext { } } - fn window_was_resized(&mut self, window_id: usize) { + fn window_was_resized(&mut self, window: AnyWindowHandle) { self.pending_effects - .push_back(Effect::ResizeWindow { window_id }); + .push_back(Effect::ResizeWindow { window }); } - fn window_was_moved(&mut self, window_id: usize) { + fn window_was_moved(&mut self, window: AnyWindowHandle) { self.pending_effects - .push_back(Effect::MoveWindow { window_id }); + .push_back(Effect::MoveWindow { window }); } - fn window_was_fullscreen_changed(&mut self, window_id: usize, is_fullscreen: bool) { + fn window_was_fullscreen_changed(&mut self, window: AnyWindowHandle, is_fullscreen: bool) { self.pending_effects.push_back(Effect::FullscreenWindow { - window_id, + window, is_fullscreen, }); } - fn window_changed_active_status(&mut self, window_id: usize, is_active: bool) { - self.pending_effects.push_back(Effect::ActivateWindow { - window_id, - is_active, - }); + fn window_changed_active_status(&mut self, window: AnyWindowHandle, is_active: bool) { + self.pending_effects + .push_back(Effect::ActivateWindow { window, is_active }); } fn keystroke( &mut self, - window_id: usize, + window: AnyWindowHandle, keystroke: Keystroke, handled_by: Option>, result: MatchResult, ) { self.pending_effects.push_back(Effect::Keystroke { - window_id, + window, keystroke, handled_by, result, @@ -1910,16 +1900,16 @@ impl AppContext { fn handle_view_notification_effect( &mut self, - observed_window_id: usize, + observed_window: AnyWindowHandle, observed_view_id: usize, ) { - let view_key = (observed_window_id, observed_view_id); + let view_key = (observed_window.id(), observed_view_id); if let Some((view, mut view_metadata)) = self .views .remove(&view_key) .zip(self.views_metadata.remove(&view_key)) { - if let Some(window) = self.windows.get_mut(&observed_window_id) { + if let Some(window) = self.windows.get_mut(&observed_window.id()) { window .invalidation .get_or_insert_with(Default::default) @@ -1946,17 +1936,17 @@ impl AppContext { }) } - fn handle_fullscreen_effect(&mut self, window_id: usize, is_fullscreen: bool) { - self.update_window(window_id, |cx| { + fn handle_fullscreen_effect(&mut self, window: AnyWindowHandle, is_fullscreen: bool) { + self.update_window(window, |cx| { cx.window.is_fullscreen = is_fullscreen; let mut fullscreen_observations = cx.window_fullscreen_observations.clone(); - fullscreen_observations.emit(window_id, |callback| callback(is_fullscreen, cx)); + fullscreen_observations.emit(window.id(), |callback| callback(is_fullscreen, cx)); if let Some(uuid) = cx.window_display_uuid() { let bounds = cx.window_bounds(); let mut bounds_observations = cx.window_bounds_observations.clone(); - bounds_observations.emit(window_id, |callback| callback(bounds, uuid, cx)); + bounds_observations.emit(window.id(), |callback| callback(bounds, uuid, cx)); } Some(()) @@ -1965,42 +1955,42 @@ impl AppContext { fn handle_keystroke_effect( &mut self, - window_id: usize, + window: AnyWindowHandle, keystroke: Keystroke, handled_by: Option>, result: MatchResult, ) { - self.update_window(window_id, |cx| { + self.update_window(window, |cx| { let mut observations = cx.keystroke_observations.clone(); - observations.emit(window_id, move |callback| { + observations.emit(window.id(), move |callback| { callback(&keystroke, &result, handled_by.as_ref(), cx) }); }); } - fn handle_window_activation_effect(&mut self, window_id: usize, active: bool) -> bool { - self.update_window(window_id, |cx| { + fn handle_window_activation_effect(&mut self, window: AnyWindowHandle, active: bool) -> bool { + self.update_window(window, |cx| { if cx.window.is_active == active { return false; } cx.window.is_active = active; let mut observations = cx.window_activation_observations.clone(); - observations.emit(window_id, |callback| callback(active, cx)); + observations.emit(window.id(), |callback| callback(active, cx)); true }) .unwrap_or(false) } fn handle_focus_effect(&mut self, effect: FocusEffect) { - let window_id = effect.window_id(); - self.update_window(window_id, |cx| { + let window = effect.window(); + self.update_window(window, |cx| { // Ensure the newly-focused view still exists, otherwise focus // the root view instead. let focused_id = match effect { FocusEffect::View { view_id, .. } => { if let Some(view_id) = view_id { - if cx.views.contains_key(&(window_id, view_id)) { + if cx.views.contains_key(&(window.id(), view_id)) { Some(view_id) } else { Some(cx.root_view().id()) @@ -2025,9 +2015,9 @@ impl AppContext { if focus_changed { if let Some(blurred_id) = blurred_id { for view_id in cx.ancestors(blurred_id).collect::>() { - if let Some(mut view) = cx.views.remove(&(window_id, view_id)) { + if let Some(mut view) = cx.views.remove(&(window.id(), view_id)) { view.focus_out(blurred_id, cx, view_id); - cx.views.insert((window_id, view_id), view); + cx.views.insert((window.id(), view_id), view); } } @@ -2039,9 +2029,9 @@ impl AppContext { if focus_changed || effect.is_forced() { if let Some(focused_id) = focused_id { for view_id in cx.ancestors(focused_id).collect::>() { - if let Some(mut view) = cx.views.remove(&(window_id, view_id)) { + if let Some(mut view) = cx.views.remove(&(window.id(), view_id)) { view.focus_in(focused_id, cx, view_id); - cx.views.insert((window_id, view_id), view); + cx.views.insert((window.id(), view_id), view); } } @@ -2063,24 +2053,24 @@ impl AppContext { fn handle_window_should_close_subscription_effect( &mut self, - window_id: usize, + window: AnyWindowHandle, mut callback: WindowShouldCloseSubscriptionCallback, ) { let mut app = self.upgrade(); - if let Some(window) = self.windows.get_mut(&window_id) { + if let Some(window) = self.windows.get_mut(&window.id()) { window .platform_window .on_should_close(Box::new(move || app.update(|cx| callback(cx)))) } } - fn handle_window_moved(&mut self, window_id: usize) { - self.update_window(window_id, |cx| { + fn handle_window_moved(&mut self, window: AnyWindowHandle) { + self.update_window(window, |cx| { if let Some(display) = cx.window_display_uuid() { let bounds = cx.window_bounds(); cx.window_bounds_observations .clone() - .emit(window_id, move |callback| { + .emit(window.id(), move |callback| { callback(bounds, display, cx); true }); @@ -2097,10 +2087,10 @@ impl AppContext { }); } - pub fn focus(&mut self, window_id: usize, view_id: Option) { + pub fn focus(&mut self, window: AnyWindowHandle, view_id: Option) { self.pending_effects .push_back(Effect::Focus(FocusEffect::View { - window_id, + window, view_id, is_forced: false, })); @@ -2185,40 +2175,40 @@ impl BorrowAppContext for AppContext { impl BorrowWindowContext for AppContext { type Result = Option; - fn read_window(&self, window_id: usize, f: F) -> Self::Result + fn read_window(&self, window: AnyWindowHandle, f: F) -> Self::Result where F: FnOnce(&WindowContext) -> T, { - AppContext::read_window(self, window_id, f) + AppContext::read_window(self, window, f) } - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { - AppContext::read_window(self, window_id, f).flatten() + AppContext::read_window(self, window, f).flatten() } - fn update_window(&mut self, window_id: usize, f: F) -> Self::Result + fn update_window(&mut self, handle: AnyWindowHandle, f: F) -> Self::Result where F: FnOnce(&mut WindowContext) -> T, { self.update(|app_context| { - let mut window = app_context.windows.remove(&window_id)?; - let mut window_context = WindowContext::mutable(app_context, &mut window, window_id); + let mut window = app_context.windows.remove(&handle.id())?; + let mut window_context = WindowContext::mutable(app_context, &mut window, handle); let result = f(&mut window_context); if !window_context.removed { - app_context.windows.insert(window_id, window); + app_context.windows.insert(handle.id(), window); } Some(result) }) } - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, handle: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option, { - AppContext::update_window(self, window_id, f).flatten() + AppContext::update_window(self, handle, f).flatten() } } @@ -2242,22 +2232,22 @@ pub struct WindowInvalidation { #[derive(Debug)] pub enum FocusEffect { View { - window_id: usize, + window: AnyWindowHandle, view_id: Option, is_forced: bool, }, ViewParent { - window_id: usize, + window: AnyWindowHandle, view_id: usize, is_forced: bool, }, } impl FocusEffect { - fn window_id(&self) -> usize { + fn window(&self) -> AnyWindowHandle { match self { - FocusEffect::View { window_id, .. } => *window_id, - FocusEffect::ViewParent { window_id, .. } => *window_id, + FocusEffect::View { window, .. } => *window, + FocusEffect::ViewParent { window, .. } => *window, } } @@ -2303,7 +2293,7 @@ pub enum Effect { model_id: usize, }, ViewNotification { - window_id: usize, + window: AnyWindowHandle, view_id: usize, }, Deferred { @@ -2328,36 +2318,36 @@ pub enum Effect { callback: FocusObservationCallback, }, ResizeWindow { - window_id: usize, + window: AnyWindowHandle, }, MoveWindow { - window_id: usize, + window: AnyWindowHandle, }, ActivateWindow { - window_id: usize, + window: AnyWindowHandle, is_active: bool, }, WindowActivationObservation { - window_id: usize, + window: AnyWindowHandle, subscription_id: usize, callback: WindowActivationCallback, }, FullscreenWindow { - window_id: usize, + window: AnyWindowHandle, is_fullscreen: bool, }, WindowFullscreenObservation { - window_id: usize, + window: AnyWindowHandle, subscription_id: usize, callback: WindowFullscreenCallback, }, WindowBoundsObservation { - window_id: usize, + window: AnyWindowHandle, subscription_id: usize, callback: WindowBoundsCallback, }, Keystroke { - window_id: usize, + window: AnyWindowHandle, keystroke: Keystroke, handled_by: Option>, result: MatchResult, @@ -2367,7 +2357,7 @@ pub enum Effect { action_id: TypeId, }, WindowShouldCloseSubscription { - window_id: usize, + window: AnyWindowHandle, callback: WindowShouldCloseSubscriptionCallback, }, ActiveLabeledTasksChanged, @@ -2419,9 +2409,9 @@ impl Debug for Effect { .debug_struct("Effect::ModelNotification") .field("model_id", model_id) .finish(), - Effect::ViewNotification { window_id, view_id } => f + Effect::ViewNotification { window, view_id } => f .debug_struct("Effect::ViewNotification") - .field("window_id", window_id) + .field("window_id", &window.id()) .field("view_id", view_id) .finish(), Effect::GlobalNotification { type_id } => f @@ -2451,71 +2441,68 @@ impl Debug for Effect { .debug_struct("Effect::ActionDispatchNotification") .field("action_id", action_id) .finish(), - Effect::ResizeWindow { window_id } => f + Effect::ResizeWindow { window } => f .debug_struct("Effect::RefreshWindow") - .field("window_id", window_id) + .field("window_id", &window.id()) .finish(), - Effect::MoveWindow { window_id } => f + Effect::MoveWindow { window } => f .debug_struct("Effect::MoveWindow") - .field("window_id", window_id) + .field("window_id", &window.id()) .finish(), Effect::WindowActivationObservation { - window_id, + window, subscription_id, .. } => f .debug_struct("Effect::WindowActivationObservation") - .field("window_id", window_id) + .field("window_id", &window.id()) .field("subscription_id", subscription_id) .finish(), - Effect::ActivateWindow { - window_id, - is_active, - } => f + Effect::ActivateWindow { window, is_active } => f .debug_struct("Effect::ActivateWindow") - .field("window_id", window_id) + .field("window_id", &window.id()) .field("is_active", is_active) .finish(), Effect::FullscreenWindow { - window_id, + window, is_fullscreen, } => f .debug_struct("Effect::FullscreenWindow") - .field("window_id", window_id) + .field("window_id", &window.id()) .field("is_fullscreen", is_fullscreen) .finish(), Effect::WindowFullscreenObservation { - window_id, + window, subscription_id, callback: _, } => f .debug_struct("Effect::WindowFullscreenObservation") - .field("window_id", window_id) + .field("window_id", &window.id()) .field("subscription_id", subscription_id) .finish(), Effect::WindowBoundsObservation { - window_id, + window, subscription_id, callback: _, } => f .debug_struct("Effect::WindowBoundsObservation") - .field("window_id", window_id) + .field("window_id", &window.id()) .field("subscription_id", subscription_id) .finish(), Effect::RefreshWindows => f.debug_struct("Effect::FullViewRefresh").finish(), - Effect::WindowShouldCloseSubscription { window_id, .. } => f + Effect::WindowShouldCloseSubscription { window, .. } => f .debug_struct("Effect::WindowShouldCloseSubscription") - .field("window_id", window_id) + .field("window_id", &window.id()) .finish(), Effect::Keystroke { - window_id, + window, keystroke, handled_by, result, } => f .debug_struct("Effect::Keystroke") - .field("window_id", window_id) + .field("window_id", &window.id()) .field("keystroke", keystroke) .field( "keystroke", @@ -2613,9 +2600,14 @@ pub trait AnyView { cx: &mut WindowContext, view_id: usize, ); - fn any_handle(&self, window_id: usize, view_id: usize, cx: &AppContext) -> AnyViewHandle { + fn any_handle( + &self, + window: AnyWindowHandle, + view_id: usize, + cx: &AppContext, + ) -> AnyViewHandle { AnyViewHandle::new( - window_id, + window, view_id, self.as_any().type_id(), cx.ref_counts.clone(), @@ -2653,7 +2645,7 @@ where fn render(&mut self, cx: &mut WindowContext, view_id: usize) -> Box { let mut view_context = ViewContext::mutable(cx, view_id); let element = V::render(self, &mut view_context); - let view = WeakViewHandle::new(cx.window_id, view_id); + let view = WeakViewHandle::new(cx.window_handle, view_id); Box::new(RootElement::new(element, view)) } @@ -2664,11 +2656,11 @@ where } else { let focused_type = cx .views_metadata - .get(&(cx.window_id, focused_id)) + .get(&(cx.window_handle.id(), focused_id)) .unwrap() .type_id; AnyViewHandle::new( - cx.window_id, + cx.window_handle, focused_id, focused_type, cx.ref_counts.clone(), @@ -2684,11 +2676,11 @@ where } else { let blurred_type = cx .views_metadata - .get(&(cx.window_id, blurred_id)) + .get(&(cx.window_handle.id(), blurred_id)) .unwrap() .type_id; AnyViewHandle::new( - cx.window_id, + cx.window_handle, blurred_id, blurred_type, cx.ref_counts.clone(), @@ -2995,18 +2987,22 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { pub fn handle(&self) -> ViewHandle { ViewHandle::new( - self.window_id, + self.window_handle, self.view_id, &self.window_context.ref_counts, ) } pub fn weak_handle(&self) -> WeakViewHandle { - WeakViewHandle::new(self.window_id, self.view_id) + WeakViewHandle::new(self.window_handle, self.view_id) } pub fn window_id(&self) -> usize { - self.window_id + self.window_handle.id() + } + + pub fn window(&self) -> AnyWindowHandle { + self.window_handle } pub fn view_id(&self) -> usize { @@ -3054,11 +3050,11 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { } pub fn focus_parent(&mut self) { - let window_id = self.window_id; + let window = self.window_handle; let view_id = self.view_id; self.pending_effects .push_back(Effect::Focus(FocusEffect::ViewParent { - window_id, + window, view_id, is_forced: false, })); @@ -3072,13 +3068,13 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { where F: 'static + FnMut(&mut V, &mut ViewContext) -> bool, { - let window_id = self.window_id; + let window = self.window_handle; let view = self.weak_handle(); self.pending_effects .push_back(Effect::WindowShouldCloseSubscription { - window_id, + window, callback: Box::new(move |cx| { - cx.update_window(window_id, |cx| { + cx.update_window(window, |cx| { if let Some(view) = view.upgrade(cx) { view.update(cx, |view, cx| callback(view, cx)) } else { @@ -3117,11 +3113,11 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { H: Handle, F: 'static + FnMut(&mut V, H, &mut ViewContext), { - let window_id = self.window_id; + let window = self.window_handle; let observer = self.weak_handle(); self.window_context .observe_internal(handle, move |observed, cx| { - cx.update_window(window_id, |cx| { + cx.update_window(window, |cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, observed, cx); @@ -3140,10 +3136,10 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { G: Any, F: 'static + FnMut(&mut V, &mut ViewContext), { - let window_id = self.window_id; + let window = self.window_handle; let observer = self.weak_handle(); self.window_context.observe_global::(move |cx| { - cx.update_window(window_id, |cx| { + cx.update_window(window, |cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| callback(observer, cx)); } @@ -3176,11 +3172,11 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { H: Handle, F: 'static + FnMut(&mut V, &E, &mut ViewContext), { - let window_id = self.window_id; + let window = self.window_handle; let observer = self.weak_handle(); self.window_context .observe_release(handle, move |released, cx| { - cx.update_window(window_id, |cx| { + cx.update_window(window, |cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, released, cx); @@ -3194,10 +3190,10 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { where F: 'static + FnMut(&mut V, TypeId, &mut ViewContext), { - let window_id = self.window_id; + let window = self.window_handle; let observer = self.weak_handle(); self.window_context.observe_actions(move |action_id, cx| { - cx.update_window(window_id, |cx| { + cx.update_window(window, |cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, action_id, cx); @@ -3289,10 +3285,10 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { where F: 'static + FnMut(&mut V, &mut ViewContext), { - let window_id = self.window_id; + let window = self.window_handle; let observer = self.weak_handle(); self.window_context.observe_active_labeled_tasks(move |cx| { - cx.update_window(window_id, |cx| { + cx.update_window(window, |cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, cx); @@ -3316,9 +3312,9 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { } pub fn notify(&mut self) { - let window_id = self.window_id; + let window = self.window_handle; let view_id = self.view_id; - self.window_context.notify_view(window_id, view_id); + self.window_context.notify_view(window, view_id); } pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext)) { @@ -3331,10 +3327,10 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { &mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext), ) { - let window_id = self.window_id; + let window = self.window_handle; let handle = self.handle(); self.window_context.after_window_update(move |cx| { - cx.update_window(window_id, |cx| { + cx.update_window(window, |cx| { handle.update(cx, |view, cx| { callback(view, cx); }) @@ -3422,30 +3418,30 @@ impl BorrowAppContext for ViewContext<'_, '_, V> { impl BorrowWindowContext for ViewContext<'_, '_, V> { type Result = T; - fn read_window T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_window(&*self.window_context, window_id, f) + fn read_window T>(&self, window: AnyWindowHandle, f: F) -> T { + BorrowWindowContext::read_window(&*self.window_context, window, f) } - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { - BorrowWindowContext::read_window_optional(&*self.window_context, window_id, f) + BorrowWindowContext::read_window_optional(&*self.window_context, window, f) } fn update_window T>( &mut self, - window_id: usize, + window: AnyWindowHandle, f: F, ) -> T { - BorrowWindowContext::update_window(&mut *self.window_context, window_id, f) + BorrowWindowContext::update_window(&mut *self.window_context, window, f) } - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option, { - BorrowWindowContext::update_window_optional(&mut *self.window_context, window_id, f) + BorrowWindowContext::update_window_optional(&mut *self.window_context, window, f) } } @@ -3483,11 +3479,11 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> { ) -> Option> { self.notify_if_view_ancestors_change(view_id); - let window_id = self.window_id; + let window = self.window_handle; let mut contexts = Vec::new(); let mut handler_depth = None; for (i, view_id) in self.ancestors(view_id).enumerate() { - if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) { + if let Some(view_metadata) = self.views_metadata.get(&(window.id(), view_id)) { if let Some(actions) = self.actions.get(&view_metadata.type_id) { if actions.contains_key(&action.id()) { handler_depth = Some(i); @@ -3547,30 +3543,30 @@ impl BorrowAppContext for LayoutContext<'_, '_, '_, V> { impl BorrowWindowContext for LayoutContext<'_, '_, '_, V> { type Result = T; - fn read_window T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_window(&*self.view_context, window_id, f) + fn read_window T>(&self, window: AnyWindowHandle, f: F) -> T { + BorrowWindowContext::read_window(&*self.view_context, window, f) } - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { - BorrowWindowContext::read_window_optional(&*self.view_context, window_id, f) + BorrowWindowContext::read_window_optional(&*self.view_context, window, f) } fn update_window T>( &mut self, - window_id: usize, + window: AnyWindowHandle, f: F, ) -> T { - BorrowWindowContext::update_window(&mut *self.view_context, window_id, f) + BorrowWindowContext::update_window(&mut *self.view_context, window, f) } - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option, { - BorrowWindowContext::update_window_optional(&mut *self.view_context, window_id, f) + BorrowWindowContext::update_window_optional(&mut *self.view_context, window, f) } } @@ -3619,30 +3615,30 @@ impl BorrowAppContext for EventContext<'_, '_, '_, V> { impl BorrowWindowContext for EventContext<'_, '_, '_, V> { type Result = T; - fn read_window T>(&self, window_id: usize, f: F) -> T { - BorrowWindowContext::read_window(&*self.view_context, window_id, f) + fn read_window T>(&self, window: AnyWindowHandle, f: F) -> T { + BorrowWindowContext::read_window(&*self.view_context, window, f) } - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { - BorrowWindowContext::read_window_optional(&*self.view_context, window_id, f) + BorrowWindowContext::read_window_optional(&*self.view_context, window, f) } fn update_window T>( &mut self, - window_id: usize, + window: AnyWindowHandle, f: F, ) -> T { - BorrowWindowContext::update_window(&mut *self.view_context, window_id, f) + BorrowWindowContext::update_window(&mut *self.view_context, window, f) } - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option, { - BorrowWindowContext::update_window_optional(&mut *self.view_context, window_id, f) + BorrowWindowContext::update_window_optional(&mut *self.view_context, window, f) } } @@ -3977,7 +3973,7 @@ impl WindowHandle { C: BorrowWindowContext, F: FnOnce(&mut V, &mut ViewContext) -> R, { - cx.update_window(self.id(), |cx| { + cx.update_window(self.any_handle, |cx| { cx.root_view() .clone() .downcast::() @@ -3991,7 +3987,7 @@ impl WindowHandle { C: BorrowWindowContext, F: FnOnce(&mut ViewContext) -> V, { - cx.update_window(self.id(), |cx| { + cx.update_window(self.any_handle, |cx| { let root_view = self.add_view(cx, |cx| build_root(cx)); cx.window.root_view = Some(root_view.clone().into_any()); cx.window.focused_view_id = Some(root_view.id()); @@ -4006,7 +4002,7 @@ impl Into for WindowHandle { } } -#[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct AnyWindowHandle { window_id: usize, root_view_type: TypeId, @@ -4029,7 +4025,7 @@ impl AnyWindowHandle { C: BorrowWindowContext, F: FnOnce(&WindowContext) -> R, { - cx.read_window(self.window_id, |cx| read(cx)) + cx.read_window(*self, |cx| read(cx)) } pub fn read_optional_with(&self, cx: &C, read: F) -> Option @@ -4037,7 +4033,7 @@ impl AnyWindowHandle { C: BorrowWindowContext, F: FnOnce(&WindowContext) -> Option, { - cx.read_window_optional(self.window_id, |cx| read(cx)) + cx.read_window_optional(*self, |cx| read(cx)) } pub fn update(&self, cx: &mut C, update: F) -> C::Result @@ -4045,7 +4041,7 @@ impl AnyWindowHandle { C: BorrowWindowContext, F: FnOnce(&mut WindowContext) -> R, { - cx.update_window(self.window_id, update) + cx.update_window(*self, update) } pub fn update_optional(&self, cx: &mut C, update: F) -> Option @@ -4053,7 +4049,7 @@ impl AnyWindowHandle { C: BorrowWindowContext, F: FnOnce(&mut WindowContext) -> Option, { - cx.update_window_optional(self.window_id, update) + cx.update_window_optional(*self, update) } pub fn add_view(&self, cx: &mut C, build_view: F) -> C::Result> @@ -4092,24 +4088,22 @@ impl AnyWindowHandle { pub fn simulate_activation(&self, cx: &mut TestAppContext) { self.update(cx, |cx| { let other_window_ids = cx - .windows - .keys() - .filter(|window_id| **window_id != self.window_id) - .copied() + .windows() + .filter(|window| *window != *self) .collect::>(); - for window_id in other_window_ids { - cx.window_changed_active_status(window_id, false) + for window in other_window_ids { + cx.window_changed_active_status(window, false) } - cx.window_changed_active_status(self.window_id, true) + cx.window_changed_active_status(*self, true) }); } #[cfg(any(test, feature = "test-support"))] pub fn simulate_deactivation(&self, cx: &mut TestAppContext) { self.update(cx, |cx| { - cx.window_changed_active_status(self.window_id, false); + cx.window_changed_active_status(*self, false); }) } } @@ -4129,20 +4123,15 @@ impl Deref for ViewHandle { } impl ViewHandle { - fn new(window_id: usize, view_id: usize, ref_counts: &Arc>) -> Self { + fn new(window: AnyWindowHandle, view_id: usize, ref_counts: &Arc>) -> Self { Self { - any_handle: AnyViewHandle::new( - window_id, - view_id, - TypeId::of::(), - ref_counts.clone(), - ), + any_handle: AnyViewHandle::new(window, view_id, TypeId::of::(), ref_counts.clone()), view_type: PhantomData, } } pub fn downgrade(&self) -> WeakViewHandle { - WeakViewHandle::new(self.window_id, self.view_id) + WeakViewHandle::new(self.window, self.view_id) } pub fn into_any(self) -> AnyViewHandle { @@ -4150,13 +4139,11 @@ impl ViewHandle { } pub fn window_id(&self) -> usize { - self.window_id + self.window.id() } - pub fn window(&self, cx: &C) -> C::Result { - cx.read_window(self.window_id, |cx| { - AnyWindowHandle::new(self.window_id, cx.window.root_view.type_id()) - }) + pub fn window(&self) -> AnyWindowHandle { + self.window } pub fn id(&self) -> usize { @@ -4172,7 +4159,7 @@ impl ViewHandle { C: BorrowWindowContext, F: FnOnce(&T, &ViewContext) -> S, { - cx.read_window(self.window_id, |cx| { + cx.read_window(self.window, |cx| { let cx = ViewContext::immutable(cx, self.view_id); read(cx.read_view(self), &cx) }) @@ -4185,7 +4172,7 @@ impl ViewHandle { { let mut update = Some(update); - cx.update_window(self.window_id, |cx| { + cx.update_window(self.window, |cx| { cx.update_view(self, &mut |view, cx| { let update = update.take().unwrap(); update(view, cx) @@ -4200,31 +4187,31 @@ impl ViewHandle { impl Clone for ViewHandle { fn clone(&self) -> Self { - ViewHandle::new(self.window_id, self.view_id, &self.ref_counts) + ViewHandle::new(self.window, self.view_id, &self.ref_counts) } } impl PartialEq for ViewHandle { fn eq(&self, other: &Self) -> bool { - self.window_id == other.window_id && self.view_id == other.view_id + self.window == other.window && self.view_id == other.view_id } } impl PartialEq for ViewHandle { fn eq(&self, other: &AnyViewHandle) -> bool { - self.window_id == other.window_id && self.view_id == other.view_id + self.window == other.window && self.view_id == other.view_id } } impl PartialEq> for ViewHandle { fn eq(&self, other: &WeakViewHandle) -> bool { - self.window_id == other.window_id && self.view_id == other.view_id + self.window == other.window && self.view_id == other.view_id } } impl PartialEq> for WeakViewHandle { fn eq(&self, other: &ViewHandle) -> bool { - self.window_id == other.window_id && self.view_id == other.view_id + self.window == other.window && self.view_id == other.view_id } } @@ -4232,7 +4219,7 @@ impl Eq for ViewHandle {} impl Hash for ViewHandle { fn hash(&self, state: &mut H) { - self.window_id.hash(state); + self.window.hash(state); self.view_id.hash(state); } } @@ -4240,7 +4227,7 @@ impl Hash for ViewHandle { impl Debug for ViewHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct(&format!("ViewHandle<{}>", type_name::())) - .field("window_id", &self.window_id) + .field("window_id", &self.window) .field("view_id", &self.view_id) .finish() } @@ -4254,7 +4241,7 @@ impl Handle for ViewHandle { } fn location(&self) -> EntityLocation { - EntityLocation::View(self.window_id, self.view_id) + EntityLocation::View(self.window.id(), self.view_id) } fn downgrade(&self) -> Self::Weak { @@ -4270,7 +4257,7 @@ impl Handle for ViewHandle { } pub struct AnyViewHandle { - window_id: usize, + window: AnyWindowHandle, view_id: usize, view_type: TypeId, ref_counts: Arc>, @@ -4281,12 +4268,12 @@ pub struct AnyViewHandle { impl AnyViewHandle { fn new( - window_id: usize, + window: AnyWindowHandle, view_id: usize, view_type: TypeId, ref_counts: Arc>, ) -> Self { - ref_counts.lock().inc_view(window_id, view_id); + ref_counts.lock().inc_view(window.id(), view_id); #[cfg(any(test, feature = "test-support"))] let handle_id = ref_counts @@ -4296,7 +4283,7 @@ impl AnyViewHandle { .handle_created(None, view_id); Self { - window_id, + window, view_id, view_type, ref_counts, @@ -4305,8 +4292,12 @@ impl AnyViewHandle { } } + pub fn window(&self) -> AnyWindowHandle { + self.window + } + pub fn window_id(&self) -> usize { - self.window_id + self.window.id() } pub fn id(&self) -> usize { @@ -4338,7 +4329,7 @@ impl AnyViewHandle { pub fn downgrade(&self) -> AnyWeakViewHandle { AnyWeakViewHandle { - window_id: self.window_id, + window: self.window, view_id: self.view_id, view_type: self.view_type, } @@ -4350,7 +4341,7 @@ impl AnyViewHandle { pub fn debug_json<'a, 'b>(&self, cx: &'b WindowContext<'a>) -> serde_json::Value { cx.views - .get(&(self.window_id, self.view_id)) + .get(&(self.window.id(), self.view_id)) .map_or_else(|| serde_json::Value::Null, |view| view.debug_json(cx)) } } @@ -4358,7 +4349,7 @@ impl AnyViewHandle { impl Clone for AnyViewHandle { fn clone(&self) -> Self { Self::new( - self.window_id, + self.window, self.view_id, self.view_type, self.ref_counts.clone(), @@ -4368,13 +4359,13 @@ impl Clone for AnyViewHandle { impl PartialEq for AnyViewHandle { fn eq(&self, other: &Self) -> bool { - self.window_id == other.window_id && self.view_id == other.view_id + self.window == other.window && self.view_id == other.view_id } } impl PartialEq> for AnyViewHandle { fn eq(&self, other: &ViewHandle) -> bool { - self.window_id == other.window_id && self.view_id == other.view_id + self.window == other.window && self.view_id == other.view_id } } @@ -4382,7 +4373,7 @@ impl Drop for AnyViewHandle { fn drop(&mut self) { self.ref_counts .lock() - .dec_view(self.window_id, self.view_id); + .dec_view(self.window.id(), self.view_id); #[cfg(any(test, feature = "test-support"))] self.ref_counts .lock() @@ -4395,7 +4386,7 @@ impl Drop for AnyViewHandle { impl Debug for AnyViewHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AnyViewHandle") - .field("window_id", &self.window_id) + .field("window_id", &self.window.id()) .field("view_id", &self.view_id) .finish() } @@ -4531,10 +4522,10 @@ impl WeakHandle for WeakViewHandle { } impl WeakViewHandle { - fn new(window_id: usize, view_id: usize) -> Self { + fn new(window: AnyWindowHandle, view_id: usize) -> Self { Self { any_handle: AnyWeakViewHandle { - window_id, + window, view_id, view_type: TypeId::of::(), }, @@ -4546,8 +4537,12 @@ impl WeakViewHandle { self.view_id } + pub fn window(&self) -> AnyWindowHandle { + self.window + } + pub fn window_id(&self) -> usize { - self.window_id + self.window.id() } pub fn into_any(self) -> AnyWeakViewHandle { @@ -4567,7 +4562,7 @@ impl WeakViewHandle { let handle = cx .upgrade_view_handle(self) .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?; - cx.read_window(self.window_id, |cx| handle.read_with(cx, read)) + cx.read_window(self.window, |cx| handle.read_with(cx, read)) .ok_or_else(|| anyhow!("window was removed")) }) } @@ -4581,7 +4576,7 @@ impl WeakViewHandle { let handle = cx .upgrade_view_handle(self) .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?; - cx.update_window(self.window_id, |cx| handle.update(cx, update)) + cx.update_window(self.window, |cx| handle.update(cx, update)) .ok_or_else(|| anyhow!("window was removed")) }) } @@ -4606,7 +4601,7 @@ impl Clone for WeakViewHandle { impl PartialEq for WeakViewHandle { fn eq(&self, other: &Self) -> bool { - self.window_id == other.window_id && self.view_id == other.view_id + self.window == other.window && self.view_id == other.view_id } } @@ -4620,7 +4615,7 @@ impl Hash for WeakViewHandle { #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct AnyWeakViewHandle { - window_id: usize, + window: AnyWindowHandle, view_id: usize, view_type: TypeId, } @@ -4652,7 +4647,7 @@ impl AnyWeakViewHandle { impl Hash for AnyWeakViewHandle { fn hash(&self, state: &mut H) { - self.window_id.hash(state); + self.window.hash(state); self.view_id.hash(state); self.view_type.hash(state); } @@ -6393,7 +6388,7 @@ mod tests { // Check that global actions do not have a binding, even if a binding does exist in another view assert_eq!( - &available_actions(window.id(), view_1.id(), cx), + &available_actions(window.into(), view_1.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::GlobalAction", vec![]) @@ -6402,7 +6397,7 @@ mod tests { // Check that view 1 actions and bindings are available even when called from view 2 assert_eq!( - &available_actions(window.id(), view_2.id(), cx), + &available_actions(window.into(), view_2.id(), cx), &[ ("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action2", vec![Keystroke::parse("b").unwrap()]), @@ -6412,11 +6407,11 @@ mod tests { // Produces a list of actions and key bindings fn available_actions( - window_id: usize, + window: AnyWindowHandle, view_id: usize, cx: &TestAppContext, ) -> Vec<(&'static str, Vec)> { - cx.available_actions(window_id, view_id) + cx.available_actions(window.into(), view_id) .into_iter() .map(|(action_name, _, bindings)| { ( @@ -6465,7 +6460,7 @@ mod tests { ]); }); - let actions = cx.available_actions(window.id(), view.id()); + let actions = cx.available_actions(window.into(), view.id()); assert_eq!( actions[0].1.as_any().downcast_ref::(), Some(&ActionWithArg { arg: false }) diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index ff988607b094f8d3253b8baaad2e6332bee4ad84..b1729d89b8591176f1fa9aa27ccc93cebdc65e8d 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -72,8 +72,8 @@ impl TestAppContext { cx } - pub fn dispatch_action(&mut self, window_id: usize, action: A) { - self.update_window(window_id, |window| { + pub fn dispatch_action(&mut self, window: AnyWindowHandle, action: A) { + self.update_window(window, |window| { window.dispatch_action(window.focused_view_id(), &action); }) .expect("window not found"); @@ -81,10 +81,10 @@ impl TestAppContext { pub fn available_actions( &self, - window_id: usize, + window: AnyWindowHandle, view_id: usize, ) -> Vec<(&'static str, Box, SmallVec<[Binding; 1]>)> { - self.read_window(window_id, |cx| cx.available_actions(view_id)) + self.read_window(window, |cx| cx.available_actions(view_id)) .unwrap_or_default() } @@ -127,18 +127,18 @@ impl TestAppContext { pub fn read_window T>( &self, - window_id: usize, + window: AnyWindowHandle, callback: F, ) -> Option { - self.cx.borrow().read_window(window_id, callback) + self.cx.borrow().read_window(window, callback) } pub fn update_window T>( &mut self, - window_id: usize, + window: AnyWindowHandle, callback: F, ) -> Option { - self.cx.borrow_mut().update_window(window_id, callback) + self.cx.borrow_mut().update_window(window, callback) } pub fn add_model(&mut self, build_model: F) -> ModelHandle @@ -317,36 +317,36 @@ impl BorrowAppContext for TestAppContext { impl BorrowWindowContext for TestAppContext { type Result = T; - fn read_window T>(&self, window_id: usize, f: F) -> T { + fn read_window T>(&self, window: AnyWindowHandle, f: F) -> T { self.cx .borrow() - .read_window(window_id, f) + .read_window(window, f) .expect("window was closed") } - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { - BorrowWindowContext::read_window(self, window_id, f) + BorrowWindowContext::read_window(self, window, f) } fn update_window T>( &mut self, - window_id: usize, + window: AnyWindowHandle, f: F, ) -> T { self.cx .borrow_mut() - .update_window(window_id, f) + .update_window(window, f) .expect("window was closed") } - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option, { - BorrowWindowContext::update_window(self, window_id, f) + BorrowWindowContext::update_window(self, window, f) } } diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 70d02c1fa1265b5449b127a6736c8a81573be1e1..44a3c5cfdc8ded2e004df2da56ee6ce510396871 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -13,9 +13,9 @@ use crate::{ }, text_layout::TextLayoutCache, util::post_inc, - Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect, - Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription, - View, ViewContext, ViewHandle, WindowHandle, WindowInvalidation, + Action, AnyView, AnyViewHandle, AnyWindowHandle, AppContext, BorrowAppContext, + BorrowWindowContext, Effect, Element, Entity, Handle, LayoutContext, MouseRegion, + MouseRegionId, SceneBuilder, Subscription, View, ViewContext, ViewHandle, WindowInvalidation, }; use anyhow::{anyhow, bail, Result}; use collections::{HashMap, HashSet}; @@ -60,7 +60,7 @@ pub struct Window { impl Window { pub fn new( - window_id: usize, + handle: AnyWindowHandle, platform_window: Box, cx: &mut AppContext, build_view: F, @@ -92,7 +92,7 @@ impl Window { appearance, }; - let mut window_context = WindowContext::mutable(cx, &mut window, window_id); + let mut window_context = WindowContext::mutable(cx, &mut window, handle); let root_view = window_context.add_view(|cx| build_view(cx)); if let Some(invalidation) = window_context.window.invalidation.take() { window_context.invalidate(invalidation, appearance); @@ -113,7 +113,7 @@ impl Window { pub struct WindowContext<'a> { pub(crate) app_context: Reference<'a, AppContext>, pub(crate) window: Reference<'a, Window>, - pub(crate) window_id: usize, + pub(crate) window_handle: AnyWindowHandle, pub(crate) removed: bool, } @@ -144,15 +144,15 @@ impl BorrowAppContext for WindowContext<'_> { impl BorrowWindowContext for WindowContext<'_> { type Result = T; - fn read_window T>(&self, window_id: usize, f: F) -> T { - if self.window_id == window_id { + fn read_window T>(&self, handle: AnyWindowHandle, f: F) -> T { + if self.window_handle == handle { f(self) } else { panic!("read_with called with id of window that does not belong to this context") } } - fn read_window_optional(&self, window_id: usize, f: F) -> Option + fn read_window_optional(&self, window_id: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { @@ -161,21 +161,21 @@ impl BorrowWindowContext for WindowContext<'_> { fn update_window T>( &mut self, - window_id: usize, + handle: AnyWindowHandle, f: F, ) -> T { - if self.window_id == window_id { + if self.window_handle == handle { f(self) } else { panic!("update called with id of window that does not belong to this context") } } - fn update_window_optional(&mut self, window_id: usize, f: F) -> Option + fn update_window_optional(&mut self, handle: AnyWindowHandle, f: F) -> Option where F: FnOnce(&mut WindowContext) -> Option, { - BorrowWindowContext::update_window(self, window_id, f) + BorrowWindowContext::update_window(self, handle, f) } } @@ -183,21 +183,25 @@ impl<'a> WindowContext<'a> { pub fn mutable( app_context: &'a mut AppContext, window: &'a mut Window, - window_id: usize, + handle: AnyWindowHandle, ) -> Self { Self { app_context: Reference::Mutable(app_context), window: Reference::Mutable(window), - window_id, + window_handle: handle, removed: false, } } - pub fn immutable(app_context: &'a AppContext, window: &'a Window, window_id: usize) -> Self { + pub fn immutable( + app_context: &'a AppContext, + window: &'a Window, + handle: AnyWindowHandle, + ) -> Self { Self { app_context: Reference::Immutable(app_context), window: Reference::Immutable(window), - window_id, + window_handle: handle, removed: false, } } @@ -207,17 +211,11 @@ impl<'a> WindowContext<'a> { } pub fn window_id(&self) -> usize { - self.window_id + self.window_handle.id() } - pub fn window(&self) -> Option> { - self.window.root_view.as_ref().and_then(|root_view| { - if root_view.is::() { - Some(WindowHandle::new(self.window_id)) - } else { - None - } - }) + pub fn window(&self) -> AnyWindowHandle { + self.window_handle } pub fn app_context(&mut self) -> &mut AppContext { @@ -240,10 +238,10 @@ impl<'a> WindowContext<'a> { where F: FnOnce(&mut dyn AnyView, &mut Self) -> T, { - let window_id = self.window_id; - let mut view = self.views.remove(&(window_id, view_id))?; + let handle = self.window_handle; + let mut view = self.views.remove(&(handle.id(), view_id))?; let result = f(view.as_mut(), self); - self.views.insert((window_id, view_id), view); + self.views.insert((handle.id(), view_id), view); Some(result) } @@ -268,9 +266,9 @@ impl<'a> WindowContext<'a> { } pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut WindowContext)) { - let window_id = self.window_id; + let handle = self.window_handle; self.app_context.defer(move |cx| { - cx.update_window(window_id, |cx| callback(cx)); + cx.update_window(handle, |cx| callback(cx)); }) } @@ -310,10 +308,10 @@ impl<'a> WindowContext<'a> { H: Handle, F: 'static + FnMut(H, &E::Event, &mut WindowContext) -> bool, { - let window_id = self.window_id; + let window_handle = self.window_handle; self.app_context .subscribe_internal(handle, move |emitter, event, cx| { - cx.update_window(window_id, |cx| callback(emitter, event, cx)) + cx.update_window(window_handle, |cx| callback(emitter, event, cx)) .unwrap_or(false) }) } @@ -322,17 +320,17 @@ impl<'a> WindowContext<'a> { where F: 'static + FnMut(bool, &mut WindowContext) -> bool, { - let window_id = self.window_id; + let handle = self.window_handle; let subscription_id = post_inc(&mut self.next_subscription_id); self.pending_effects .push_back(Effect::WindowActivationObservation { - window_id, + window: handle, subscription_id, callback: Box::new(callback), }); Subscription::WindowActivationObservation( self.window_activation_observations - .subscribe(window_id, subscription_id), + .subscribe(handle.id(), subscription_id), ) } @@ -340,17 +338,17 @@ impl<'a> WindowContext<'a> { where F: 'static + FnMut(bool, &mut WindowContext) -> bool, { - let window_id = self.window_id; + let window = self.window_handle; let subscription_id = post_inc(&mut self.next_subscription_id); self.pending_effects .push_back(Effect::WindowFullscreenObservation { - window_id, + window, subscription_id, callback: Box::new(callback), }); Subscription::WindowActivationObservation( self.window_activation_observations - .subscribe(window_id, subscription_id), + .subscribe(window.id(), subscription_id), ) } @@ -358,17 +356,17 @@ impl<'a> WindowContext<'a> { where F: 'static + FnMut(WindowBounds, Uuid, &mut WindowContext) -> bool, { - let window_id = self.window_id; + let window = self.window_handle; let subscription_id = post_inc(&mut self.next_subscription_id); self.pending_effects .push_back(Effect::WindowBoundsObservation { - window_id, + window, subscription_id, callback: Box::new(callback), }); Subscription::WindowBoundsObservation( self.window_bounds_observations - .subscribe(window_id, subscription_id), + .subscribe(window.id(), subscription_id), ) } @@ -377,13 +375,13 @@ impl<'a> WindowContext<'a> { F: 'static + FnMut(&Keystroke, &MatchResult, Option<&Box>, &mut WindowContext) -> bool, { - let window_id = self.window_id; + let window = self.window_handle; let subscription_id = post_inc(&mut self.next_subscription_id); self.keystroke_observations - .add_callback(window_id, subscription_id, Box::new(callback)); + .add_callback(window.id(), subscription_id, Box::new(callback)); Subscription::KeystrokeObservation( self.keystroke_observations - .subscribe(window_id, subscription_id), + .subscribe(window.id(), subscription_id), ) } @@ -391,11 +389,11 @@ impl<'a> WindowContext<'a> { &self, view_id: usize, ) -> Vec<(&'static str, Box, SmallVec<[Binding; 1]>)> { - let window_id = self.window_id; + let handle = self.window_handle; let mut contexts = Vec::new(); let mut handler_depths_by_action_id = HashMap::::default(); for (depth, view_id) in self.ancestors(view_id).enumerate() { - if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) { + if let Some(view_metadata) = self.views_metadata.get(&(handle.id(), view_id)) { contexts.push(view_metadata.keymap_context.clone()); if let Some(actions) = self.actions.get(&view_metadata.type_id) { handler_depths_by_action_id @@ -440,13 +438,13 @@ impl<'a> WindowContext<'a> { } pub(crate) fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool { - let window_id = self.window_id; + let handle = self.window_handle; if let Some(focused_view_id) = self.focused_view_id() { let dispatch_path = self .ancestors(focused_view_id) .filter_map(|view_id| { self.views_metadata - .get(&(window_id, view_id)) + .get(&(handle.id(), view_id)) .map(|view| (view_id, view.keymap_context.clone())) }) .collect(); @@ -471,15 +469,10 @@ impl<'a> WindowContext<'a> { } }; - self.keystroke( - window_id, - keystroke.clone(), - handled_by, - match_result.clone(), - ); + self.keystroke(handle, keystroke.clone(), handled_by, match_result.clone()); keystroke_handled } else { - self.keystroke(window_id, keystroke.clone(), None, MatchResult::None); + self.keystroke(handle, keystroke.clone(), None, MatchResult::None); false } } @@ -487,7 +480,7 @@ impl<'a> WindowContext<'a> { pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool { let mut mouse_events = SmallVec::<[_; 2]>::new(); let mut notified_views: HashSet = Default::default(); - let window_id = self.window_id; + let handle = self.window_handle; // 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events // get mapped into the mouse-specific MouseEvent type. @@ -851,19 +844,19 @@ impl<'a> WindowContext<'a> { } for view_id in notified_views { - self.notify_view(window_id, view_id); + self.notify_view(handle, view_id); } any_event_handled } pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool { - let window_id = self.window_id; + let handle = self.window_handle; if let Some(focused_view_id) = self.window.focused_view_id { for view_id in self.ancestors(focused_view_id).collect::>() { - if let Some(mut view) = self.views.remove(&(window_id, view_id)) { + if let Some(mut view) = self.views.remove(&(handle.id(), view_id)) { let handled = view.key_down(event, self, view_id); - self.views.insert((window_id, view_id), view); + self.views.insert((handle.id(), view_id), view); if handled { return true; } @@ -877,12 +870,12 @@ impl<'a> WindowContext<'a> { } pub(crate) fn dispatch_key_up(&mut self, event: &KeyUpEvent) -> bool { - let window_id = self.window_id; + let handle = self.window_handle; if let Some(focused_view_id) = self.window.focused_view_id { for view_id in self.ancestors(focused_view_id).collect::>() { - if let Some(mut view) = self.views.remove(&(window_id, view_id)) { + if let Some(mut view) = self.views.remove(&(handle.id(), view_id)) { let handled = view.key_up(event, self, view_id); - self.views.insert((window_id, view_id), view); + self.views.insert((handle.id(), view_id), view); if handled { return true; } @@ -896,12 +889,12 @@ impl<'a> WindowContext<'a> { } pub(crate) fn dispatch_modifiers_changed(&mut self, event: &ModifiersChangedEvent) -> bool { - let window_id = self.window_id; + let handle = self.window_handle; if let Some(focused_view_id) = self.window.focused_view_id { for view_id in self.ancestors(focused_view_id).collect::>() { - if let Some(mut view) = self.views.remove(&(window_id, view_id)) { + if let Some(mut view) = self.views.remove(&(handle.id(), view_id)) { let handled = view.modifiers_changed(event, self, view_id); - self.views.insert((window_id, view_id), view); + self.views.insert((handle.id(), view_id), view); if handled { return true; } @@ -936,14 +929,14 @@ impl<'a> WindowContext<'a> { } pub fn render_view(&mut self, params: RenderParams) -> Result> { - let window_id = self.window_id; + let handle = self.window_handle; let view_id = params.view_id; let mut view = self .views - .remove(&(window_id, view_id)) + .remove(&(handle.id(), view_id)) .ok_or_else(|| anyhow!("view not found"))?; let element = view.render(self, view_id); - self.views.insert((window_id, view_id), view); + self.views.insert((handle.id(), view_id), view); Ok(element) } @@ -971,9 +964,9 @@ impl<'a> WindowContext<'a> { } else if old_parent_id == new_parent_id { current_view_id = *old_parent_id.unwrap(); } else { - let window_id = self.window_id; + let handle = self.window_handle; for view_id_to_notify in view_ids_to_notify { - self.notify_view(window_id, view_id_to_notify); + self.notify_view(handle, view_id_to_notify); } break; } @@ -1141,7 +1134,7 @@ impl<'a> WindowContext<'a> { } pub fn focus(&mut self, view_id: Option) { - self.app_context.focus(self.window_id, view_id); + self.app_context.focus(self.window_handle, view_id); } pub fn window_bounds(&self) -> WindowBounds { @@ -1194,26 +1187,26 @@ impl<'a> WindowContext<'a> { T: View, F: FnOnce(&mut ViewContext) -> Option, { - let window_id = self.window_id; + let handle = self.window_handle; let view_id = post_inc(&mut self.next_id); let mut cx = ViewContext::mutable(self, view_id); let handle = if let Some(view) = build_view(&mut cx) { let mut keymap_context = KeymapContext::default(); view.update_keymap_context(&mut keymap_context, cx.app_context()); self.views_metadata.insert( - (window_id, view_id), + (handle.id(), view_id), ViewMetadata { type_id: TypeId::of::(), keymap_context, }, ); - self.views.insert((window_id, view_id), Box::new(view)); + self.views.insert((handle.id(), view_id), Box::new(view)); self.window .invalidation .get_or_insert_with(Default::default) .updated .insert(view_id); - Some(ViewHandle::new(window_id, view_id, &self.ref_counts)) + Some(ViewHandle::new(handle, view_id, &self.ref_counts)) } else { None }; @@ -1390,7 +1383,7 @@ pub struct ChildView { impl ChildView { pub fn new(view: &AnyViewHandle, cx: &AppContext) -> Self { - let view_name = cx.view_ui_name(view.window_id(), view.id()).unwrap(); + let view_name = cx.view_ui_name(view.window, view.id()).unwrap(); Self { view_id: view.id(), view_name, diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 07aaea812a98d5758bb15b801c5cd8bdf1688e8f..df4334a19fc7daf862504c6ef7ccfc6f3f9e3da0 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1726,7 +1726,7 @@ impl ClipboardEntry { #[cfg(test)] mod tests { use super::*; - use gpui::{AnyWindowHandle, TestAppContext, ViewHandle}; + use gpui::{AnyWindowHandle, TestAppContext, ViewHandle, WindowHandle}; use pretty_assertions::assert_eq; use project::FakeFs; use serde_json::json; @@ -1872,7 +1872,6 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -1894,7 +1893,7 @@ mod tests { // Add a file with the root folder selected. The filename editor is placed // before the first file in the root folder. panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx)); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { let panel = panel.read(cx); assert!(panel.filename_editor.is_focused(cx)); }); @@ -2225,7 +2224,6 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); @@ -2247,7 +2245,7 @@ mod tests { // Add a file with the root folder selected. The filename editor is placed // before the first file in the root folder. panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx)); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { let panel = panel.read(cx); assert!(panel.filename_editor.is_focused(cx)); }); @@ -2402,7 +2400,6 @@ mod tests { let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); toggle_expand_dir(&panel, "src/test", cx); @@ -2419,7 +2416,7 @@ mod tests { " third.rs" ] ); - ensure_single_file_is_opened(window_id, &workspace, "test/first.rs", cx); + ensure_single_file_is_opened(window, "test/first.rs", cx); submit_deletion(window.into(), &panel, cx); assert_eq!( @@ -2446,9 +2443,9 @@ mod tests { " third.rs" ] ); - ensure_single_file_is_opened(window_id, &workspace, "test/second.rs", cx); + ensure_single_file_is_opened(window, "test/second.rs", cx); - cx.update_window(window_id, |cx| { + window.update(cx, |cx| { let active_items = workspace .read(cx) .panes() @@ -2493,7 +2490,6 @@ mod tests { let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = window.root(cx); - let window_id = window.id(); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "src/", cx); @@ -2504,7 +2500,7 @@ mod tests { &["v src <== selected", " > test"] ); panel.update(cx, |panel, cx| panel.new_directory(&NewDirectory, cx)); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { let panel = panel.read(cx); assert!(panel.filename_editor.is_focused(cx)); }); @@ -2535,7 +2531,7 @@ mod tests { &["v src", " > test <== selected"] ); panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx)); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { let panel = panel.read(cx); assert!(panel.filename_editor.is_focused(cx)); }); @@ -2585,7 +2581,7 @@ mod tests { ], ); panel.update(cx, |panel, cx| panel.rename(&Rename, cx)); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { let panel = panel.read(cx); assert!(panel.filename_editor.is_focused(cx)); }); @@ -2882,13 +2878,11 @@ mod tests { } fn ensure_single_file_is_opened( - window_id: usize, - workspace: &ViewHandle, + window: WindowHandle, expected_path: &str, cx: &mut TestAppContext, ) { - cx.read_window(window_id, |cx| { - let workspace = workspace.read(cx); + window.update_root(cx, |workspace, cx| { let worktrees = workspace.worktrees(cx).collect::>(); assert_eq!(worktrees.len(), 1); let worktree_id = WorktreeId::from_usize(worktrees[0].id()); diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index b20ffed6d7e1ad94e9c311503c332e066e5c9b91..c3b4f5caa668c7a74ef446744cfc4aaa04ee6df7 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1229,8 +1229,6 @@ mod tests { ); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let window = cx.add_window(|_| EmptyView); - let window_id = window.id(); - let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx)); let search_bar = window.add_view(cx, |cx| { @@ -1249,12 +1247,13 @@ mod tests { search_bar.activate_current_match(cx); }); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { assert!( !editor.is_focused(cx), "Initially, the editor should not be focused" ); }); + let initial_selections = editor.update(cx, |editor, cx| { let initial_selections = editor.selections.display_ranges(cx); assert_eq!( @@ -1271,7 +1270,7 @@ mod tests { cx.focus(search_bar.query_editor.as_any()); search_bar.select_all_matches(&SelectAllMatches, cx); }); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { assert!( editor.is_focused(cx), "Should focus editor after successful SelectAllMatches" @@ -1295,7 +1294,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_next_match(&SelectNextMatch, cx); }); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { assert!( editor.is_focused(cx), "Should still have editor focused after SelectNextMatch" @@ -1324,7 +1323,7 @@ mod tests { cx.focus(search_bar.query_editor.as_any()); search_bar.select_all_matches(&SelectAllMatches, cx); }); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { assert!( editor.is_focused(cx), "Should focus editor after successful SelectAllMatches" @@ -1348,7 +1347,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_prev_match(&SelectPrevMatch, cx); }); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { assert!( editor.is_focused(cx), "Should still have editor focused after SelectPrevMatch" @@ -1384,7 +1383,7 @@ mod tests { search_bar.update(cx, |search_bar, cx| { search_bar.select_all_matches(&SelectAllMatches, cx); }); - cx.read_window(window_id, |cx| { + window.read_with(cx, |cx| { assert!( !editor.is_focused(cx), "Should not switch focus to editor if SelectAllMatches does not find any matches" diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index dffd88db14229871a805a282ccbc49670697206e..202d494560e7388b689a4b9f8750b07c62758697 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1568,7 +1568,6 @@ pub mod tests { let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); let active_item = cx.read(|cx| { workspace @@ -1599,9 +1598,9 @@ pub mod tests { }; let search_view_id = search_view.id(); - cx.spawn( - |mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) }, - ) + cx.spawn(|mut cx| async move { + cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) + }) .detach(); deterministic.run_until_parked(); search_view.update(cx, |search_view, cx| { @@ -1651,9 +1650,9 @@ pub mod tests { "Search view should be focused after mismatching query had been used in search", ); }); - cx.spawn( - |mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) }, - ) + cx.spawn(|mut cx| async move { + cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) + }) .detach(); deterministic.run_until_parked(); search_view.update(cx, |search_view, cx| { @@ -1683,9 +1682,9 @@ pub mod tests { "Search view with mismatching query should be focused after search results are available", ); }); - cx.spawn( - |mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) }, - ) + cx.spawn(|mut cx| async move { + cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) + }) .detach(); deterministic.run_until_parked(); search_view.update(cx, |search_view, cx| { @@ -1713,9 +1712,9 @@ pub mod tests { ); }); - cx.spawn( - |mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) }, - ) + cx.spawn(|mut cx| async move { + cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) + }) .detach(); deterministic.run_until_parked(); search_view.update(cx, |search_view, cx| { diff --git a/crates/vim/src/editor_events.rs b/crates/vim/src/editor_events.rs index 4fef6bd715eedbe49b1347b679a2ebe6d3a89f33..893f5e8a854aed40abc9ced5cb9219a57c8e1d53 100644 --- a/crates/vim/src/editor_events.rs +++ b/crates/vim/src/editor_events.rs @@ -1,6 +1,6 @@ use crate::Vim; use editor::{EditorBlurred, EditorFocused, EditorReleased}; -use gpui::{AppContext, BorrowWindowContext}; +use gpui::AppContext; pub fn init(cx: &mut AppContext) { cx.subscribe_global(focused).detach(); @@ -10,7 +10,7 @@ pub fn init(cx: &mut AppContext) { fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) { if let Some(previously_active_editor) = Vim::read(cx).active_editor.clone() { - cx.update_window(previously_active_editor.window_id(), |cx| { + previously_active_editor.window().update(cx, |cx| { Vim::update(cx, |vim, cx| { vim.update_active_editor(cx, |previously_active_editor, cx| { vim.unhook_vim_settings(previously_active_editor, cx) @@ -19,7 +19,7 @@ fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) { }); } - cx.update_window(editor.window_id(), |cx| { + editor.window().update(cx, |cx| { Vim::update(cx, |vim, cx| { vim.set_active_editor(editor.clone(), cx); }); @@ -27,7 +27,7 @@ fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) { } fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) { - cx.update_window(editor.window_id(), |cx| { + editor.window().update(cx, |cx| { Vim::update(cx, |vim, cx| { if let Some(previous_editor) = vim.active_editor.clone() { if previous_editor == editor.clone() { @@ -41,7 +41,7 @@ fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) { } fn released(EditorReleased(editor): &EditorReleased, cx: &mut AppContext) { - cx.update_window(editor.window_id(), |cx| { + editor.window().update(cx, |cx| { cx.update_default_global(|vim: &mut Vim, _| { if let Some(previous_editor) = vim.active_editor.clone() { if previous_editor == editor.clone() { diff --git a/crates/vim/src/mode_indicator.rs b/crates/vim/src/mode_indicator.rs index afd60af848be052060988ba961bb75e811f33d3b..5d54a66723da31ab271c55510820a87a48e207cd 100644 --- a/crates/vim/src/mode_indicator.rs +++ b/crates/vim/src/mode_indicator.rs @@ -1,6 +1,6 @@ use gpui::{ elements::{Empty, Label}, - AnyElement, Element, Entity, Subscription, View, ViewContext, BorrowWindowContext + AnyElement, Element, Entity, Subscription, View, ViewContext, }; use settings::SettingsStore; use workspace::{item::ItemHandle, StatusItemView}; @@ -20,7 +20,7 @@ impl ModeIndicator { if let Some(mode_indicator) = handle.upgrade(cx) { match event { VimEvent::ModeChanged { mode } => { - cx.update_window(mode_indicator.window_id(), |cx| { + mode_indicator.window().update(cx, |cx| { mode_indicator.update(cx, move |mode_indicator, cx| { mode_indicator.set_mode(mode, cx); }) diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index 839ab3aafc87e535a8b09096be6b58a5837063da..5eaaef900e4719e0f17ff7aebd62cb0377cd1980 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -85,8 +85,8 @@ impl<'a> VimTestContext<'a> { } pub fn set_state(&mut self, text: &str, mode: Mode) -> ContextHandle { - let window_id = self.window.id(); - self.update_window(window_id, |cx| { + let window = self.window; + window.update(self.cx.cx.cx, |cx| { Vim::update(cx, |vim, cx| { vim.switch_mode(mode, false, cx); }) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index ebaf399e22b3ecfa51b217327f5912a34dc7b542..5bb0fd93f8c9290c6f71f0ff94972115c9e3629c 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -203,7 +203,7 @@ impl Dock { pub fn panel_index_for_ui_name(&self, ui_name: &str, cx: &AppContext) -> Option { self.panel_entries.iter().position(|entry| { let panel = entry.panel.as_any(); - cx.view_ui_name(panel.window_id(), panel.id()) == Some(ui_name) + cx.view_ui_name(panel.window(), panel.id()) == Some(ui_name) }) } @@ -530,16 +530,12 @@ impl View for PanelButtons { tooltip_action.as_ref().map(|action| action.boxed_clone()); move |_, this, cx| { if let Some(tooltip_action) = &tooltip_action { - let window_id = cx.window_id(); + let window = cx.window(); let view_id = this.workspace.id(); let tooltip_action = tooltip_action.boxed_clone(); cx.spawn(|_, mut cx| async move { - cx.dispatch_action( - window_id, - view_id, - &*tooltip_action, - ) - .ok(); + cx.dispatch_action(window, view_id, &*tooltip_action) + .ok(); }) .detach(); } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 98883fac3372246d462585be76eb0bfd1a20bbae..ecbe25accad0ee5f1e52c785ddd80c588bb911df 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1917,8 +1917,8 @@ impl Element for PaneBackdrop { MouseRegion::new::(child_view_id, 0, visible_bounds).on_down( gpui::platform::MouseButton::Left, move |_, _: &mut V, cx| { - let window_id = cx.window_id(); - cx.app_context().focus(window_id, Some(child_view_id)) + let window = cx.window(); + cx.app_context().focus(window, Some(child_view_id)) }, ), ); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index da708c6ea727de82b1c288a9549e6d627fad07b3..f0990322653757344d459e2bc2ef1f8c569cbdcf 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1250,11 +1250,11 @@ impl Workspace { _: &CloseWindow, cx: &mut ViewContext, ) -> Option>> { - let window_id = cx.window_id(); + let window = cx.window(); let prepare = self.prepare_to_close(false, cx); Some(cx.spawn(|_, mut cx| async move { if prepare.await? { - cx.remove_window(window_id); + cx.remove_window(window); } Ok(()) })) @@ -1266,7 +1266,7 @@ impl Workspace { cx: &mut ViewContext, ) -> Task> { let active_call = self.active_call().cloned(); - let window_id = cx.window_id(); + let window = cx.window(); cx.spawn(|this, mut cx| async move { let workspace_count = cx @@ -1281,7 +1281,7 @@ impl Workspace { && active_call.read_with(&cx, |call, _| call.room().is_some()) { let answer = cx.prompt( - window_id, + window, PromptLevel::Warning, "Do you want to leave the current call?", &["Close window and hang up", "Cancel"], @@ -1390,7 +1390,7 @@ impl Workspace { paths: Vec, cx: &mut ViewContext, ) -> Task> { - let window = cx.window::(); + let window = cx.window().downcast::(); let is_remote = self.project.read(cx).is_remote(); let has_worktree = self.project.read(cx).worktrees(cx).next().is_some(); let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx)); @@ -3181,7 +3181,7 @@ impl Workspace { let left_visible = left_dock.is_open(); let left_active_panel = left_dock.visible_panel().and_then(|panel| { Some( - cx.view_ui_name(panel.as_any().window_id(), panel.id())? + cx.view_ui_name(panel.as_any().window(), panel.id())? .to_string(), ) }); @@ -3194,7 +3194,7 @@ impl Workspace { let right_visible = right_dock.is_open(); let right_active_panel = right_dock.visible_panel().and_then(|panel| { Some( - cx.view_ui_name(panel.as_any().window_id(), panel.id())? + cx.view_ui_name(panel.as_any().window(), panel.id())? .to_string(), ) }); @@ -3207,7 +3207,7 @@ impl Workspace { let bottom_visible = bottom_dock.is_open(); let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| { Some( - cx.view_ui_name(panel.as_any().window_id(), panel.id())? + cx.view_ui_name(panel.as_any().window(), panel.id())? .to_string(), ) }); @@ -4000,7 +4000,7 @@ pub fn join_remote_project( workspace.downgrade() }; - cx.activate_window(workspace.window_id()); + cx.activate_window(workspace.window()); cx.platform().activate(true); workspace.update(&mut cx, |workspace, cx| { @@ -4049,9 +4049,9 @@ pub fn restart(_: &Restart, cx: &mut AppContext) { // prompt in the active window before switching to a different window. workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false)); - if let (true, Some(window)) = (should_confirm, workspace_windows.first()) { + if let (true, Some(window)) = (should_confirm, workspace_windows.first().copied()) { let answer = cx.prompt( - window.id(), + window.into(), PromptLevel::Info, "Are you sure you want to restart?", &["Restart", "Cancel"], diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 3435727b1e0345ab1eef4cb63870b6b71f03c7f9..6397a1e6b2a818525b9e29879f0bd012dbc35e22 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -179,13 +179,12 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { move |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { let app_state = workspace.app_state().clone(); let markdown = app_state.languages.language_for_name("JSON"); - let window_id = cx.window_id(); + let window = cx.window(); cx.spawn(|workspace, mut cx| async move { let markdown = markdown.await.log_err(); - let content = to_string_pretty( - &cx.debug_elements(window_id) - .ok_or_else(|| anyhow!("could not debug elements for {window_id}"))?, - ) + let content = to_string_pretty(&cx.debug_elements(window).ok_or_else(|| { + anyhow!("could not debug elements for window {}", window.id()) + })?) .unwrap(); workspace .update(&mut cx, |workspace, cx| { @@ -416,9 +415,9 @@ fn quit(_: &Quit, cx: &mut gpui::AppContext) { // prompt in the active window before switching to a different window. workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false)); - if let (true, Some(window)) = (should_confirm, workspace_windows.first()) { + if let (true, Some(window)) = (should_confirm, workspace_windows.first().copied()) { let answer = cx.prompt( - window.id(), + window.into(), PromptLevel::Info, "Are you sure you want to quit?", &["Quit", "Cancel"], @@ -716,8 +715,8 @@ mod tests { use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor}; use fs::{FakeFs, Fs}; use gpui::{ - actions, elements::Empty, executor::Deterministic, Action, AnyElement, AppContext, - AssetSource, Element, Entity, TestAppContext, View, ViewHandle, + actions, elements::Empty, executor::Deterministic, Action, AnyElement, AnyWindowHandle, + AppContext, AssetSource, Element, Entity, TestAppContext, View, ViewHandle, }; use language::LanguageRegistry; use node_runtime::NodeRuntime; @@ -1317,11 +1316,10 @@ mod tests { project.update(cx, |project, _| project.languages().add(rust_lang())); let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); // Create a new untitled buffer - cx.dispatch_action(window_id, NewFile); + cx.dispatch_action(window.into(), NewFile); let editor = workspace.read_with(cx, |workspace, cx| { workspace .active_item(cx) @@ -1376,7 +1374,7 @@ mod tests { // Open the same newly-created file in another pane item. The new editor should reuse // the same buffer. - cx.dispatch_action(window_id, NewFile); + cx.dispatch_action(window.into(), NewFile); workspace .update(cx, |workspace, cx| { workspace.split_and_clone( @@ -1412,10 +1410,9 @@ mod tests { project.update(cx, |project, _| project.languages().add(rust_lang())); let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); // Create a new untitled buffer - cx.dispatch_action(window_id, NewFile); + cx.dispatch_action(window.into(), NewFile); let editor = workspace.read_with(cx, |workspace, cx| { workspace .active_item(cx) @@ -1465,7 +1462,6 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let workspace = window.root(cx); - let window_id = window.id(); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1487,7 +1483,7 @@ mod tests { (editor.downgrade(), buffer) }); - cx.dispatch_action(window_id, pane::SplitRight); + cx.dispatch_action(window.into(), pane::SplitRight); let editor_2 = cx.update(|cx| { let pane_2 = workspace.read(cx).active_pane().clone(); assert_ne!(pane_1, pane_2); @@ -1497,7 +1493,7 @@ mod tests { pane2_item.downcast::().unwrap().downgrade() }); - cx.dispatch_action(window_id, workspace::CloseActiveItem); + cx.dispatch_action(window.into(), workspace::CloseActiveItem); cx.foreground().run_until_parked(); workspace.read_with(cx, |workspace, _| { @@ -1505,7 +1501,7 @@ mod tests { assert_eq!(workspace.active_pane(), &pane_1); }); - cx.dispatch_action(window_id, workspace::CloseActiveItem); + cx.dispatch_action(window.into(), workspace::CloseActiveItem); cx.foreground().run_until_parked(); window.simulate_prompt_answer(1, cx); cx.foreground().run_until_parked(); @@ -2063,11 +2059,10 @@ mod tests { cx.foreground().run_until_parked(); let window = cx.add_window(|_| TestView); - let window_id = window.id(); // Test loading the keymap base at all assert_key_bindings_for( - window_id, + window.into(), cx, vec![("backspace", &A), ("k", &ActivatePreviousPane)], line!(), @@ -2094,7 +2089,7 @@ mod tests { cx.foreground().run_until_parked(); assert_key_bindings_for( - window_id, + window.into(), cx, vec![("backspace", &B), ("k", &ActivatePreviousPane)], line!(), @@ -2117,7 +2112,7 @@ mod tests { cx.foreground().run_until_parked(); assert_key_bindings_for( - window_id, + window.into(), cx, vec![("backspace", &B), ("[", &ActivatePrevItem)], line!(), @@ -2125,7 +2120,7 @@ mod tests { #[track_caller] fn assert_key_bindings_for<'a>( - window_id: usize, + window: AnyWindowHandle, cx: &TestAppContext, actions: Vec<(&'static str, &'a dyn Action)>, line: u32, @@ -2133,7 +2128,7 @@ mod tests { for (key, action) in actions { // assert that... assert!( - cx.available_actions(window_id, 0) + cx.available_actions(window, 0) .into_iter() .any(|(_, bound_action, b)| { // action names match... @@ -2234,11 +2229,10 @@ mod tests { cx.foreground().run_until_parked(); let window = cx.add_window(|_| TestView); - let window_id = window.id(); // Test loading the keymap base at all assert_key_bindings_for( - window_id, + window.into(), cx, vec![("backspace", &A), ("k", &ActivatePreviousPane)], line!(), @@ -2264,7 +2258,12 @@ mod tests { cx.foreground().run_until_parked(); - assert_key_bindings_for(window_id, cx, vec![("k", &ActivatePreviousPane)], line!()); + assert_key_bindings_for( + window.into(), + cx, + vec![("k", &ActivatePreviousPane)], + line!(), + ); // Test modifying the base, while retaining the users keymap fs.save( @@ -2282,11 +2281,11 @@ mod tests { cx.foreground().run_until_parked(); - assert_key_bindings_for(window_id, cx, vec![("[", &ActivatePrevItem)], line!()); + assert_key_bindings_for(window.into(), cx, vec![("[", &ActivatePrevItem)], line!()); #[track_caller] fn assert_key_bindings_for<'a>( - window_id: usize, + window: AnyWindowHandle, cx: &TestAppContext, actions: Vec<(&'static str, &'a dyn Action)>, line: u32, @@ -2294,7 +2293,7 @@ mod tests { for (key, action) in actions { // assert that... assert!( - cx.available_actions(window_id, 0) + cx.available_actions(window, 0) .into_iter() .any(|(_, bound_action, b)| { // action names match... From da7dc9c880b06cf465aafacd8756abd9a6f60f86 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:12:32 -0600 Subject: [PATCH 41/53] Work with window handles instead of ids in drag code --- crates/command_palette/src/command_palette.rs | 6 +-- crates/drag_and_drop/src/drag_and_drop.rs | 40 +++++++++---------- crates/project_panel/src/project_panel.rs | 6 +-- crates/terminal_view/src/terminal_panel.rs | 4 +- .../src/pane/dragged_item_receiver.rs | 12 +++--- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index b3703aa64a4549596caf47b57fc91971c985b8f0..0d398fcce909863991a198018eba6df4c84f929c 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -1,8 +1,8 @@ use collections::CommandPaletteFilter; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, keymap_matcher::Keystroke, Action, AppContext, Element, MouseState, - ViewContext, + actions, elements::*, keymap_matcher::Keystroke, Action, AnyWindowHandle, AppContext, Element, + MouseState, ViewContext, }; use picker::{Picker, PickerDelegate, PickerEvent}; use std::cmp; @@ -28,7 +28,7 @@ pub struct CommandPaletteDelegate { pub enum Event { Dismissed, Confirmed { - window_id: usize, + window: AnyWindowHandle, focused_view_id: usize, action: Box, }, diff --git a/crates/drag_and_drop/src/drag_and_drop.rs b/crates/drag_and_drop/src/drag_and_drop.rs index 828e73040387058ca89adcdb164986513ef96860..ddfed0c858237077bd9ec20b5ace46dfe41e34d9 100644 --- a/crates/drag_and_drop/src/drag_and_drop.rs +++ b/crates/drag_and_drop/src/drag_and_drop.rs @@ -6,7 +6,7 @@ use gpui::{ geometry::{rect::RectF, vector::Vector2F}, platform::{CursorStyle, MouseButton}, scene::{MouseDown, MouseDrag}, - AnyElement, Element, View, ViewContext, WeakViewHandle, WindowContext, + AnyElement, AnyWindowHandle, Element, View, ViewContext, WeakViewHandle, WindowContext, }; const DEAD_ZONE: f32 = 4.; @@ -21,7 +21,7 @@ enum State { region: RectF, }, Dragging { - window_id: usize, + window: AnyWindowHandle, position: Vector2F, region_offset: Vector2F, region: RectF, @@ -49,14 +49,14 @@ impl Clone for State { region, }, State::Dragging { - window_id, + window, position, region_offset, region, payload, render, } => Self::Dragging { - window_id: window_id.clone(), + window: window.clone(), position: position.clone(), region_offset: region_offset.clone(), region: region.clone(), @@ -87,16 +87,16 @@ impl DragAndDrop { self.containers.insert(handle); } - pub fn currently_dragged(&self, window_id: usize) -> Option<(Vector2F, Rc)> { + pub fn currently_dragged(&self, window: AnyWindowHandle) -> Option<(Vector2F, Rc)> { self.currently_dragged.as_ref().and_then(|state| { if let State::Dragging { position, payload, - window_id: window_dragged_from, + window: window_dragged_from, .. } = state { - if &window_id != window_dragged_from { + if &window != window_dragged_from { return None; } @@ -126,9 +126,9 @@ impl DragAndDrop { cx: &mut WindowContext, render: Rc) -> AnyElement>, ) { - let window_id = cx.window_id(); + let window = cx.window(); cx.update_global(|this: &mut Self, cx| { - this.notify_containers_for_window(window_id, cx); + this.notify_containers_for_window(window, cx); match this.currently_dragged.as_ref() { Some(&State::Down { @@ -141,7 +141,7 @@ impl DragAndDrop { }) => { if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE { this.currently_dragged = Some(State::Dragging { - window_id, + window, region_offset, region, position: event.position, @@ -163,7 +163,7 @@ impl DragAndDrop { .. }) => { this.currently_dragged = Some(State::Dragging { - window_id, + window, region_offset, region, position: event.position, @@ -188,14 +188,14 @@ impl DragAndDrop { State::Down { .. } => None, State::DeadZone { .. } => None, State::Dragging { - window_id, + window, region_offset, position, region, payload, render, } => { - if cx.window_id() != window_id { + if cx.window() != window { return None; } @@ -260,27 +260,27 @@ impl DragAndDrop { pub fn cancel_dragging(&mut self, cx: &mut WindowContext) { if let Some(State::Dragging { - payload, window_id, .. + payload, window, .. }) = &self.currently_dragged { if payload.is::

() { - let window_id = *window_id; + let window = *window; self.currently_dragged = Some(State::Canceled); - self.notify_containers_for_window(window_id, cx); + self.notify_containers_for_window(window, cx); } } } fn finish_dragging(&mut self, cx: &mut WindowContext) { - if let Some(State::Dragging { window_id, .. }) = self.currently_dragged.take() { - self.notify_containers_for_window(window_id, cx); + if let Some(State::Dragging { window, .. }) = self.currently_dragged.take() { + self.notify_containers_for_window(window, cx); } } - fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut WindowContext) { + fn notify_containers_for_window(&mut self, window: AnyWindowHandle, cx: &mut WindowContext) { self.containers.retain(|container| { if let Some(container) = container.upgrade(cx) { - if container.window_id() == window_id { + if container.window() == window { container.update(cx, |_, cx| cx.notify()); } true diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index df4334a19fc7daf862504c6ef7ccfc6f3f9e3da0..f7582f1764c573ee5e72ba66c09472e7643aa075 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1415,7 +1415,7 @@ impl ProjectPanel { if cx .global::>() - .currently_dragged::(cx.window_id()) + .currently_dragged::(cx.window()) .is_some() && dragged_entry_destination .as_ref() @@ -1459,7 +1459,7 @@ impl ProjectPanel { .on_up(MouseButton::Left, move |_, this, cx| { if let Some((_, dragged_entry)) = cx .global::>() - .currently_dragged::(cx.window_id()) + .currently_dragged::(cx.window()) { this.move_entry( *dragged_entry, @@ -1472,7 +1472,7 @@ impl ProjectPanel { .on_move(move |_, this, cx| { if cx .global::>() - .currently_dragged::(cx.window_id()) + .currently_dragged::(cx.window()) .is_some() { this.dragged_entry_destination = if matches!(kind, EntryKind::File(_)) { diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 6ad321c735deb00bb557058db5082761da9f7bbb..d00324cf166e8be68f355c0f4ad018a6d3d01fc1 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -48,7 +48,7 @@ impl TerminalPanel { fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { let weak_self = cx.weak_handle(); let pane = cx.add_view(|cx| { - let window_id = cx.window_id(); + let window = cx.window(); let mut pane = Pane::new( workspace.weak_handle(), workspace.project().clone(), @@ -60,7 +60,7 @@ impl TerminalPanel { pane.set_can_navigate(false, cx); pane.on_can_drop(move |drag_and_drop, cx| { drag_and_drop - .currently_dragged::(window_id) + .currently_dragged::(window) .map_or(false, |(_, item)| { item.handle.act_as::(cx).is_some() }) diff --git a/crates/workspace/src/pane/dragged_item_receiver.rs b/crates/workspace/src/pane/dragged_item_receiver.rs index 7146ab7b85b5dd4d91ceb4895fce060e34422ae3..2d3fe8ea8324411a7de5c2a70c1254648994652e 100644 --- a/crates/workspace/src/pane/dragged_item_receiver.rs +++ b/crates/workspace/src/pane/dragged_item_receiver.rs @@ -28,11 +28,11 @@ where let drag_and_drop = cx.global::>(); let drag_position = if (pane.can_drop)(drag_and_drop, cx) { drag_and_drop - .currently_dragged::(cx.window_id()) + .currently_dragged::(cx.window()) .map(|(drag_position, _)| drag_position) .or_else(|| { drag_and_drop - .currently_dragged::(cx.window_id()) + .currently_dragged::(cx.window()) .map(|(drag_position, _)| drag_position) }) } else { @@ -91,10 +91,10 @@ where let drag_and_drop = cx.global::>(); if drag_and_drop - .currently_dragged::(cx.window_id()) + .currently_dragged::(cx.window()) .is_some() || drag_and_drop - .currently_dragged::(cx.window_id()) + .currently_dragged::(cx.window()) .is_some() { cx.notify(); @@ -122,11 +122,11 @@ pub fn handle_dropped_item( } let drag_and_drop = cx.global::>(); let action = if let Some((_, dragged_item)) = - drag_and_drop.currently_dragged::(cx.window_id()) + drag_and_drop.currently_dragged::(cx.window()) { Action::Move(dragged_item.pane.clone(), dragged_item.handle.id()) } else if let Some((_, project_entry)) = - drag_and_drop.currently_dragged::(cx.window_id()) + drag_and_drop.currently_dragged::(cx.window()) { Action::Open(*project_entry) } else { From 0a4633f88f2166818ab57859783181a0972c0e77 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:20:09 -0600 Subject: [PATCH 42/53] Remove more window id usage --- crates/gpui/src/app.rs | 14 +++----------- crates/gpui/src/app/menu.rs | 2 +- crates/gpui/src/app/window.rs | 8 ++------ crates/project/src/terminals.rs | 6 +++--- crates/terminal/src/terminal.rs | 6 +++--- crates/terminal_view/src/terminal_panel.rs | 4 ++-- crates/terminal_view/src/terminal_view.rs | 8 ++++---- crates/workspace/src/item.rs | 7 ++++--- crates/workspace/src/searchable.rs | 2 +- crates/zed/src/main.rs | 4 ++-- 10 files changed, 25 insertions(+), 36 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index e0f46b036a07322844daf0fd7ca30c4d546e185f..e44ac93818fb768a8ae6ea12825ecb3a0e8d39ae 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -833,7 +833,7 @@ impl AppContext { &mut self, callback: F, ) -> Option { - self.main_window() + self.active_window() .and_then(|window| window.update(self, callback)) } @@ -1093,7 +1093,7 @@ impl AppContext { pub fn is_action_available(&self, action: &dyn Action) -> bool { let mut available_in_window = false; let action_id = action.id(); - if let Some(window) = self.main_window() { + if let Some(window) = self.active_window() { available_in_window = self .read_window(window, |cx| { if let Some(focused_view_id) = cx.focused_view_id() { @@ -1436,7 +1436,7 @@ impl AppContext { window } - pub fn main_window(&self) -> Option { + pub fn active_window(&self) -> Option { self.platform.main_window_id().and_then(|main_window_id| { self.windows .get(&main_window_id) @@ -2997,10 +2997,6 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { WeakViewHandle::new(self.window_handle, self.view_id) } - pub fn window_id(&self) -> usize { - self.window_handle.id() - } - pub fn window(&self) -> AnyWindowHandle { self.window_handle } @@ -4138,10 +4134,6 @@ impl ViewHandle { self.any_handle } - pub fn window_id(&self) -> usize { - self.window.id() - } - pub fn window(&self) -> AnyWindowHandle { self.window } diff --git a/crates/gpui/src/app/menu.rs b/crates/gpui/src/app/menu.rs index 1d8908b8fdda9627d2eb7df433a7c5b819f024ed..67531a82975481c6a0bfa42511e6612175c94945 100644 --- a/crates/gpui/src/app/menu.rs +++ b/crates/gpui/src/app/menu.rs @@ -77,7 +77,7 @@ pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform, let cx = app.0.clone(); move |action| { let mut cx = cx.borrow_mut(); - if let Some(main_window) = cx.main_window() { + if let Some(main_window) = cx.active_window() { let dispatched = main_window .update(&mut *cx, |cx| { if let Some(view_id) = cx.focused_view_id() { diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 44a3c5cfdc8ded2e004df2da56ee6ce510396871..df3f8207556ced0bc1cff4521711a1468084f915 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -152,11 +152,11 @@ impl BorrowWindowContext for WindowContext<'_> { } } - fn read_window_optional(&self, window_id: AnyWindowHandle, f: F) -> Option + fn read_window_optional(&self, window: AnyWindowHandle, f: F) -> Option where F: FnOnce(&WindowContext) -> Option, { - BorrowWindowContext::read_window(self, window_id, f) + BorrowWindowContext::read_window(self, window, f) } fn update_window T>( @@ -210,10 +210,6 @@ impl<'a> WindowContext<'a> { self.removed = true; } - pub fn window_id(&self) -> usize { - self.window_handle.id() - } - pub fn window(&self) -> AnyWindowHandle { self.window_handle } diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 7bd9ce8aecb011e287005803e5e186573b35899c..db5996829fa278db04e793d751d02ace086594e3 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -1,5 +1,5 @@ use crate::Project; -use gpui::{ModelContext, ModelHandle, WeakModelHandle}; +use gpui::{AnyWindowHandle, ModelContext, ModelHandle, WeakModelHandle}; use std::path::PathBuf; use terminal::{Terminal, TerminalBuilder, TerminalSettings}; @@ -11,7 +11,7 @@ impl Project { pub fn create_terminal( &mut self, working_directory: Option, - window_id: usize, + window: AnyWindowHandle, cx: &mut ModelContext, ) -> anyhow::Result> { if self.is_remote() { @@ -27,7 +27,7 @@ impl Project { settings.env.clone(), Some(settings.blinking.clone()), settings.alternate_scroll, - window_id, + window, ) .map(|builder| { let terminal_handle = cx.add_model(|cx| builder.subscribe(cx)); diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 06befd5f4ef5b76a4cedccad0fdd526c7d6c0edc..f6cfe5ae309093ddffb7cca1b00666f22448b08d 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -53,7 +53,7 @@ use gpui::{ keymap_matcher::Keystroke, platform::{Modifiers, MouseButton, MouseMovedEvent, TouchPhase}, scene::{MouseDown, MouseDrag, MouseScrollWheel, MouseUp}, - AppContext, ClipboardItem, Entity, ModelContext, Task, + AnyWindowHandle, AppContext, ClipboardItem, Entity, ModelContext, Task, }; use crate::mappings::{ @@ -404,7 +404,7 @@ impl TerminalBuilder { mut env: HashMap, blink_settings: Option, alternate_scroll: AlternateScroll, - window_id: usize, + window: AnyWindowHandle, ) -> Result { let pty_config = { let alac_shell = match shell.clone() { @@ -462,7 +462,7 @@ impl TerminalBuilder { let pty = match tty::new( &pty_config, TerminalSize::default().into(), - window_id as u64, + window.id() as u64, ) { Ok(pty) => pty, Err(error) => { diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index d00324cf166e8be68f355c0f4ad018a6d3d01fc1..6be8a547cd37c8dc8c5fad225b06dd9fcbc6dafd 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -255,10 +255,10 @@ impl TerminalPanel { .clone(); let working_directory = crate::get_working_directory(workspace, cx, working_directory_strategy); - let window_id = cx.window_id(); + let window = cx.window(); if let Some(terminal) = workspace.project().update(cx, |project, cx| { project - .create_terminal(working_directory, window_id, cx) + .create_terminal(working_directory, window, cx) .log_err() }) { let terminal = Box::new(cx.add_view(|cx| { diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index a600046ac2861fb7b0b1250c65d4b31c1af3166c..970e0115df2545ce9f8979fc5e2efc876d08242f 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -112,11 +112,11 @@ impl TerminalView { let working_directory = get_working_directory(workspace, cx, strategy.working_directory.clone()); - let window_id = cx.window_id(); + let window = cx.window(); let terminal = workspace .project() .update(cx, |project, cx| { - project.create_terminal(working_directory, window_id, cx) + project.create_terminal(working_directory, window, cx) }) .notify_err(workspace, cx); @@ -741,7 +741,7 @@ impl Item for TerminalView { item_id: workspace::ItemId, cx: &mut ViewContext, ) -> Task>> { - let window_id = cx.window_id(); + let window = cx.window(); cx.spawn(|pane, mut cx| async move { let cwd = TERMINAL_DB .get_working_directory(item_id, workspace_id) @@ -762,7 +762,7 @@ impl Item for TerminalView { }); let terminal = project.update(&mut cx, |project, cx| { - project.create_terminal(cwd, window_id, cx) + project.create_terminal(cwd, window, cx) })?; Ok(pane.update(&mut cx, |_, cx| { cx.add_view(|cx| TerminalView::new(terminal, workspace, workspace_id, cx)) diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index f0af080d4a6cd9719db897a6320c869695092fe9..67827b78038b048ad619209b9a26bc5c0ba031b6 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -6,6 +6,7 @@ use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings}; use anyhow::Result; use client::{proto, Client}; use gpui::geometry::vector::Vector2F; +use gpui::AnyWindowHandle; use gpui::{ fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, @@ -250,7 +251,7 @@ pub trait ItemHandle: 'static + fmt::Debug { fn workspace_deactivated(&self, cx: &mut WindowContext); fn navigate(&self, data: Box, cx: &mut WindowContext) -> bool; fn id(&self) -> usize; - fn window_id(&self) -> usize; + fn window(&self) -> AnyWindowHandle; fn as_any(&self) -> &AnyViewHandle; fn is_dirty(&self, cx: &AppContext) -> bool; fn has_conflict(&self, cx: &AppContext) -> bool; @@ -542,8 +543,8 @@ impl ItemHandle for ViewHandle { self.id() } - fn window_id(&self) -> usize { - self.window_id() + fn window(&self) -> AnyWindowHandle { + AnyViewHandle::window(self) } fn as_any(&self) -> &AnyViewHandle { diff --git a/crates/workspace/src/searchable.rs b/crates/workspace/src/searchable.rs index ae95838a742bb623a9376947e905d6054a82fff4..72117e1cabbefec98ba3af35ba5bd00571851647 100644 --- a/crates/workspace/src/searchable.rs +++ b/crates/workspace/src/searchable.rs @@ -235,7 +235,7 @@ impl From<&Box> for AnyViewHandle { impl PartialEq for Box { fn eq(&self, other: &Self) -> bool { - self.id() == other.id() && self.window_id() == other.window_id() + self.id() == other.id() && self.window() == other.window() } } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 2a1fef6a56efc5bf1c5496c935c134a752767066..bb30cac6d308b8df2a9a8d1db83084390b6adb61 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -931,11 +931,11 @@ pub fn dock_default_item_factory( .clone(); let working_directory = get_working_directory(workspace, cx, strategy); - let window_id = cx.window_id(); + let window = cx.window(); let terminal = workspace .project() .update(cx, |project, cx| { - project.create_terminal(working_directory, window_id, cx) + project.create_terminal(working_directory, window, cx) }) .notify_err(workspace, cx)?; From fe6a1886c1e26e821bc05cfc106ff3bffe322299 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:20:42 -0600 Subject: [PATCH 43/53] Remove unused dock code --- crates/zed/src/main.rs | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index bb30cac6d308b8df2a9a8d1db83084390b6adb61..795c83aba6583e71a9da1eecb77c2d4d41e0780b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -14,7 +14,7 @@ use futures::{ channel::{mpsc, oneshot}, FutureExt, SinkExt, StreamExt, }; -use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task, ViewContext}; +use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task}; use isahc::{config::Configurable, Request}; use language::{LanguageRegistry, Point}; use log::LevelFilter; @@ -43,7 +43,6 @@ use std::{ time::{Duration, SystemTime, UNIX_EPOCH}, }; use sum_tree::Bias; -use terminal_view::{get_working_directory, TerminalSettings, TerminalView}; use util::{ channel::ReleaseChannel, http::{self, HttpClient}, @@ -56,7 +55,7 @@ use fs::RealFs; #[cfg(debug_assertions)] use staff_mode::StaffMode; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; -use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace}; +use workspace::AppState; use zed::{ assets::Assets, build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus, @@ -922,35 +921,6 @@ async fn handle_cli_connection( } } -pub fn dock_default_item_factory( - workspace: &mut Workspace, - cx: &mut ViewContext, -) -> Option> { - let strategy = settings::get::(cx) - .working_directory - .clone(); - let working_directory = get_working_directory(workspace, cx, strategy); - - let window = cx.window(); - let terminal = workspace - .project() - .update(cx, |project, cx| { - project.create_terminal(working_directory, window, cx) - }) - .notify_err(workspace, cx)?; - - let terminal_view = cx.add_view(|cx| { - TerminalView::new( - terminal, - workspace.weak_handle(), - workspace.database_id(), - cx, - ) - }); - - Some(Box::new(terminal_view)) -} - pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] { &[ ("Go to file", &file_finder::Toggle), From 1fd80ba8bd12e6dcfb26da3090ab876563422dee Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:22:43 -0600 Subject: [PATCH 44/53] Remove AsyncAppContext::remove_window --- crates/gpui/src/app.rs | 4 ---- crates/workspace/src/workspace.rs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index e44ac93818fb768a8ae6ea12825ecb3a0e8d39ae..2b3a3f3bf4a012f6e19b910fb4d0775faae2d9a8 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -411,10 +411,6 @@ impl AsyncAppContext { self.update(|cx| cx.add_window(window_options, build_root_view)) } - pub fn remove_window(&mut self, window: AnyWindowHandle) { - self.update_window(window, |cx| cx.remove_window()); - } - pub fn activate_window(&mut self, window: AnyWindowHandle) { self.update_window(window, |cx| cx.activate_window()); } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f0990322653757344d459e2bc2ef1f8c569cbdcf..f2a7a442f446a34ca265592dfdba46bcbbee855f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1254,7 +1254,7 @@ impl Workspace { let prepare = self.prepare_to_close(false, cx); Some(cx.spawn(|_, mut cx| async move { if prepare.await? { - cx.remove_window(window); + window.remove(&mut cx); } Ok(()) })) From 4f10f0ee865b92f6853cef9f9cd046d0c01eb8c0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:23:49 -0600 Subject: [PATCH 45/53] Remove window methods from AsyncAppContext --- crates/gpui/src/app.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 2b3a3f3bf4a012f6e19b910fb4d0775faae2d9a8..ddec0e10a3ccdbcdbe43b8b564fbba0501a913b0 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -344,22 +344,6 @@ impl AsyncAppContext { self.0.borrow().windows().collect() } - pub fn read_window T>( - &self, - window: AnyWindowHandle, - callback: F, - ) -> Option { - self.0.borrow_mut().read_window(window, callback) - } - - pub fn update_window T>( - &mut self, - window: AnyWindowHandle, - callback: F, - ) -> Option { - self.0.borrow_mut().update_window(window, callback) - } - pub fn debug_elements(&self, window: AnyWindowHandle) -> Option { self.0.borrow().read_window(window, |cx| { let root_view = cx.window.root_view(); From 95cd96e4becbf8e1404effb90893e5a1f6f0b3ce Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:27:19 -0600 Subject: [PATCH 46/53] Move debug_elements to AnyWindowHandle --- crates/gpui/src/app.rs | 16 ++++++++-------- crates/zed/src/zed.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index ddec0e10a3ccdbcdbe43b8b564fbba0501a913b0..5eb4ed744cbcfb54d83e0f72eab6a7e9dcf6bf97 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -344,14 +344,6 @@ impl AsyncAppContext { self.0.borrow().windows().collect() } - pub fn debug_elements(&self, window: AnyWindowHandle) -> Option { - self.0.borrow().read_window(window, |cx| { - let root_view = cx.window.root_view(); - let root_element = cx.window.rendered_views.get(&root_view.id())?; - root_element.debug(cx).log_err() - })? - } - pub fn dispatch_action( &mut self, window: AnyWindowHandle, @@ -4060,6 +4052,14 @@ impl AnyWindowHandle { self.update(cx, |cx| cx.remove_window()) } + pub fn debug_elements(&self, cx: &C) -> Option { + self.read_optional_with(cx, |cx| { + let root_view = cx.window.root_view(); + let root_element = cx.window.rendered_views.get(&root_view.id())?; + root_element.debug(cx).log_err() + }) + } + #[cfg(any(test, feature = "test-support"))] pub fn simulate_activation(&self, cx: &mut TestAppContext) { self.update(cx, |cx| { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 6397a1e6b2a818525b9e29879f0bd012dbc35e22..1cde8d5b9a52b7970ae116f62752182eace1427d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -182,7 +182,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { let window = cx.window(); cx.spawn(|workspace, mut cx| async move { let markdown = markdown.await.log_err(); - let content = to_string_pretty(&cx.debug_elements(window).ok_or_else(|| { + let content = to_string_pretty(&window.debug_elements(&cx).ok_or_else(|| { anyhow!("could not debug elements for window {}", window.id()) })?) .unwrap(); From b2d9ccc0a2f97a8ad4cde4fa15aa9bd500bfc63e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:38:07 -0600 Subject: [PATCH 47/53] Move more window methods off AsyncAppContext --- crates/collab_ui/src/contact_list.rs | 4 +- crates/command_palette/src/command_palette.rs | 13 ++-- crates/context_menu/src/context_menu.rs | 14 +++- crates/gpui/src/app.rs | 71 +++++++++---------- crates/search/src/project_search.rs | 12 ++-- crates/workspace/src/dock.rs | 7 +- crates/workspace/src/workspace.rs | 12 ++-- crates/zed/src/zed.rs | 4 +- 8 files changed, 73 insertions(+), 64 deletions(-) diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index a2b281856d923754e715e0e112eca2f5c682e605..49784523845918b601e2d856fc4fcec6ebe60550 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -312,11 +312,11 @@ impl ContactList { .update(&mut cx, |store, cx| store.remove_contact(user_id, cx)) .await { - cx.prompt( - window, + window.prompt( PromptLevel::Info, &format!("Failed to remove contact: {}", e), &["Ok"], + &mut cx, ); } } diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 0d398fcce909863991a198018eba6df4c84f929c..101d4dc545cc2ecc2130a19989b8d57fea62fe97 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -1,8 +1,8 @@ use collections::CommandPaletteFilter; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, keymap_matcher::Keystroke, Action, AnyWindowHandle, AppContext, Element, - MouseState, ViewContext, + actions, anyhow::anyhow, elements::*, keymap_matcher::Keystroke, Action, AnyWindowHandle, + AppContext, Element, MouseState, ViewContext, }; use picker::{Picker, PickerDelegate, PickerEvent}; use std::cmp; @@ -83,9 +83,10 @@ impl PickerDelegate for CommandPaletteDelegate { let view_id = self.focused_view_id; let window = cx.window(); cx.spawn(move |picker, mut cx| async move { - let actions = cx - .available_actions(window, view_id) + let actions = window + .available_actions(view_id, &cx) .into_iter() + .flatten() .filter_map(|(name, action, bindings)| { let filtered = cx.read(|cx| { if cx.has_global::() { @@ -168,7 +169,9 @@ impl PickerDelegate for CommandPaletteDelegate { let action = self.actions.remove(action_ix).action; cx.app_context() .spawn(move |mut cx| async move { - cx.dispatch_action(window, focused_view_id, action.as_ref()) + window + .dispatch_action(focused_view_id, action.as_ref(), &mut cx) + .ok_or_else(|| anyhow!("window was closed")) }) .detach_and_log_err(cx); } diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index 75cfd872b29aba9b03b1235faeaba24d8ac95ae7..a5534b6262c03166d462fe001ae130a891d4cfa3 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -1,5 +1,5 @@ use gpui::{ - anyhow, + anyhow::{self, anyhow}, elements::*, geometry::vector::Vector2F, keymap_matcher::KeymapContext, @@ -223,7 +223,9 @@ impl ContextMenu { let action = action.boxed_clone(); cx.app_context() .spawn(|mut cx| async move { - cx.dispatch_action(window, view_id, action.as_ref()) + window + .dispatch_action(view_id, action.as_ref(), &mut cx) + .ok_or_else(|| anyhow!("window was closed")) }) .detach_and_log_err(cx); } @@ -486,7 +488,13 @@ impl ContextMenu { let action = action.boxed_clone(); cx.app_context() .spawn(|mut cx| async move { - cx.dispatch_action(window, view_id, action.as_ref()) + window + .dispatch_action( + view_id, + action.as_ref(), + &mut cx, + ) + .ok_or_else(|| anyhow!("window was closed")) }) .detach_and_log_err(cx); } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 5eb4ed744cbcfb54d83e0f72eab6a7e9dcf6bf97..e6e11ca321bfe73a16a1755ce482878519009842 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -344,29 +344,6 @@ impl AsyncAppContext { self.0.borrow().windows().collect() } - pub fn dispatch_action( - &mut self, - window: AnyWindowHandle, - view_id: usize, - action: &dyn Action, - ) -> Result<()> { - self.0 - .borrow_mut() - .update_window(window, |cx| { - cx.dispatch_action(Some(view_id), action); - }) - .ok_or_else(|| anyhow!("window not found")) - } - - pub fn available_actions( - &self, - window: AnyWindowHandle, - view_id: usize, - ) -> Vec<(&'static str, Box, SmallVec<[Binding; 1]>)> { - self.read_window(window, |cx| cx.available_actions(view_id)) - .unwrap_or_default() - } - pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, @@ -387,21 +364,6 @@ impl AsyncAppContext { self.update(|cx| cx.add_window(window_options, build_root_view)) } - pub fn activate_window(&mut self, window: AnyWindowHandle) { - self.update_window(window, |cx| cx.activate_window()); - } - - // TODO: Can we eliminate this method and move it to WindowContext then call it with update_window?s - pub fn prompt( - &mut self, - window: AnyWindowHandle, - level: PromptLevel, - msg: &str, - answers: &[&str], - ) -> Option> { - self.update_window(window, |cx| cx.prompt(level, msg, answers)) - } - pub fn platform(&self) -> Arc { self.0.borrow().platform().clone() } @@ -4060,6 +4022,39 @@ impl AnyWindowHandle { }) } + pub fn activate(&mut self, cx: &mut C) -> C::Result<()> { + self.update(cx, |cx| cx.activate_window()) + } + + pub fn prompt( + &self, + level: PromptLevel, + msg: &str, + answers: &[&str], + cx: &mut C, + ) -> C::Result> { + self.update(cx, |cx| cx.prompt(level, msg, answers)) + } + + pub fn dispatch_action( + &self, + view_id: usize, + action: &dyn Action, + cx: &mut C, + ) -> C::Result<()> { + self.update(cx, |cx| { + cx.dispatch_action(Some(view_id), action); + }) + } + + pub fn available_actions( + &self, + view_id: usize, + cx: &C, + ) -> C::Result, SmallVec<[Binding; 1]>)>> { + self.read_with(cx, |cx| cx.available_actions(view_id)) + } + #[cfg(any(test, feature = "test-support"))] pub fn simulate_activation(&self, cx: &mut TestAppContext) { self.update(cx, |cx| { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 202d494560e7388b689a4b9f8750b07c62758697..018ab9cb11525a1c3145ec96316095573d6406ef 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1599,7 +1599,7 @@ pub mod tests { let search_view_id = search_view.id(); cx.spawn(|mut cx| async move { - cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) + window.dispatch_action(search_view_id, &ToggleFocus, &mut cx); }) .detach(); deterministic.run_until_parked(); @@ -1650,9 +1650,9 @@ pub mod tests { "Search view should be focused after mismatching query had been used in search", ); }); - cx.spawn(|mut cx| async move { - cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) - }) + cx.spawn( + |mut cx| async move { window.dispatch_action(search_view_id, &ToggleFocus, &mut cx) }, + ) .detach(); deterministic.run_until_parked(); search_view.update(cx, |search_view, cx| { @@ -1683,7 +1683,7 @@ pub mod tests { ); }); cx.spawn(|mut cx| async move { - cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) + window.dispatch_action(search_view_id, &ToggleFocus, &mut cx); }) .detach(); deterministic.run_until_parked(); @@ -1713,7 +1713,7 @@ pub mod tests { }); cx.spawn(|mut cx| async move { - cx.dispatch_action(window.into(), search_view_id, &ToggleFocus) + window.dispatch_action(search_view_id, &ToggleFocus, &mut cx); }) .detach(); deterministic.run_until_parked(); diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 5bb0fd93f8c9290c6f71f0ff94972115c9e3629c..e33c0a5391724ba16012ac87bf210f62da857461 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -534,8 +534,11 @@ impl View for PanelButtons { let view_id = this.workspace.id(); let tooltip_action = tooltip_action.boxed_clone(); cx.spawn(|_, mut cx| async move { - cx.dispatch_action(window, view_id, &*tooltip_action) - .ok(); + window.dispatch_action( + view_id, + &*tooltip_action, + &mut cx, + ); }) .detach(); } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f2a7a442f446a34ca265592dfdba46bcbbee855f..2f884b0ceb33d10e598057a65e1c44a60471227e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1280,11 +1280,11 @@ impl Workspace { && workspace_count == 1 && active_call.read_with(&cx, |call, _| call.room().is_some()) { - let answer = cx.prompt( - window, + let answer = window.prompt( PromptLevel::Warning, "Do you want to leave the current call?", &["Close window and hang up", "Cancel"], + &mut cx, ); if let Some(mut answer) = answer { @@ -4000,7 +4000,7 @@ pub fn join_remote_project( workspace.downgrade() }; - cx.activate_window(workspace.window()); + workspace.window().activate(&mut cx); cx.platform().activate(true); workspace.update(&mut cx, |workspace, cx| { @@ -4049,12 +4049,12 @@ pub fn restart(_: &Restart, cx: &mut AppContext) { // prompt in the active window before switching to a different window. workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false)); - if let (true, Some(window)) = (should_confirm, workspace_windows.first().copied()) { - let answer = cx.prompt( - window.into(), + if let (true, Some(window)) = (should_confirm, workspace_windows.first()) { + let answer = window.prompt( PromptLevel::Info, "Are you sure you want to restart?", &["Restart", "Cancel"], + &mut cx, ); if let Some(mut answer) = answer { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1cde8d5b9a52b7970ae116f62752182eace1427d..8ad8080375ec22e1c60840a0cac0565e95969aaa 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -416,11 +416,11 @@ fn quit(_: &Quit, cx: &mut gpui::AppContext) { workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false)); if let (true, Some(window)) = (should_confirm, workspace_windows.first().copied()) { - let answer = cx.prompt( - window.into(), + let answer = window.prompt( PromptLevel::Info, "Are you sure you want to quit?", &["Quit", "Cancel"], + &mut cx, ); if let Some(mut answer) = answer { From b77c336a3d719cc1a483e58319bcdf310739e774 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 11:39:56 -0600 Subject: [PATCH 48/53] Return window handles from WeakItemHandle --- crates/workspace/src/item.rs | 6 +++--- crates/workspace/src/searchable.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 67827b78038b048ad619209b9a26bc5c0ba031b6..21956be44683f0160c7be0d6387d4d03a60c25d7 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -281,7 +281,7 @@ pub trait ItemHandle: 'static + fmt::Debug { pub trait WeakItemHandle { fn id(&self) -> usize; - fn window_id(&self) -> usize; + fn window(&self) -> AnyWindowHandle; fn upgrade(&self, cx: &AppContext) -> Option>; } @@ -650,8 +650,8 @@ impl WeakItemHandle for WeakViewHandle { self.id() } - fn window_id(&self) -> usize { - self.window_id() + fn window(&self) -> AnyWindowHandle { + self.window() } fn upgrade(&self, cx: &AppContext) -> Option> { diff --git a/crates/workspace/src/searchable.rs b/crates/workspace/src/searchable.rs index 72117e1cabbefec98ba3af35ba5bd00571851647..7a470db7c926dd8d2a2aee171fd5d269b6eebab3 100644 --- a/crates/workspace/src/searchable.rs +++ b/crates/workspace/src/searchable.rs @@ -259,7 +259,7 @@ impl WeakSearchableItemHandle for WeakViewHandle { impl PartialEq for Box { fn eq(&self, other: &Self) -> bool { - self.id() == other.id() && self.window_id() == other.window_id() + self.id() == other.id() && self.window() == other.window() } } @@ -267,6 +267,6 @@ impl Eq for Box {} impl std::hash::Hash for Box { fn hash(&self, state: &mut H) { - (self.id(), self.window_id()).hash(state) + (self.id(), self.window().id()).hash(state) } } From afd89b256abb4e5519446de40c6c52067d1b6587 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 16:06:53 -0600 Subject: [PATCH 49/53] Store AnyWindowHandles instead of usizes --- crates/gpui/src/app.rs | 126 ++++++++------------ crates/gpui/src/app/ref_counts.rs | 14 +-- crates/gpui/src/app/test_app_context.rs | 4 +- crates/gpui/src/app/window.rs | 28 ++--- crates/gpui/src/app/window_input_handler.rs | 2 +- crates/gpui/src/platform.rs | 8 +- crates/gpui/src/platform/mac/platform.rs | 12 +- crates/gpui/src/platform/mac/window.rs | 17 +-- crates/gpui/src/platform/test.rs | 40 ++++--- 9 files changed, 117 insertions(+), 134 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index e6e11ca321bfe73a16a1755ce482878519009842..507cbffad279553dea2612dbb9a898ab4358ae8b 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -444,9 +444,9 @@ type WindowShouldCloseSubscriptionCallback = Box b pub struct AppContext { models: HashMap>, - views: HashMap<(usize, usize), Box>, - views_metadata: HashMap<(usize, usize), ViewMetadata>, - windows: HashMap, + views: HashMap<(AnyWindowHandle, usize), Box>, + views_metadata: HashMap<(AnyWindowHandle, usize), ViewMetadata>, + windows: HashMap, globals: HashMap>, element_states: HashMap>, background: Arc, @@ -727,12 +727,12 @@ impl AppContext { } pub fn view_ui_name(&self, window: AnyWindowHandle, view_id: usize) -> Option<&'static str> { - Some(self.views.get(&(window.id(), view_id))?.ui_name()) + Some(self.views.get(&(window, view_id))?.ui_name()) } pub fn view_type_id(&self, window: AnyWindowHandle, view_id: usize) -> Option { self.views_metadata - .get(&(window.id(), view_id)) + .get(&(window, view_id)) .map(|metadata| metadata.type_id) } @@ -758,7 +758,7 @@ impl AppContext { handle: AnyWindowHandle, callback: F, ) -> Option { - let window = self.windows.get(&handle.id())?; + let window = self.windows.get(&handle)?; let window_context = WindowContext::immutable(self, &window, handle); Some(callback(&window_context)) } @@ -1033,7 +1033,7 @@ impl AppContext { if let Some(focused_view_id) = cx.focused_view_id() { for view_id in cx.ancestors(focused_view_id) { if let Some(view_metadata) = - cx.views_metadata.get(&(cx.window_handle.id(), view_id)) + cx.views_metadata.get(&(cx.window_handle, view_id)) { if let Some(actions) = cx.actions.get(&view_metadata.type_id) { if actions.contains_key(&action_id) { @@ -1259,13 +1259,12 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let window_id = post_inc(&mut this.next_id); + let handle = WindowHandle::::new(post_inc(&mut this.next_id)); let platform_window = this.platform - .open_window(window_id, window_options, this.foreground.clone()); - let handle = WindowHandle::::new(window_id); - let window = this.build_window(handle, platform_window, build_root_view); - this.windows.insert(window_id, window); + .open_window(handle.into(), window_options, this.foreground.clone()); + let window = this.build_window(handle.into(), platform_window, build_root_view); + this.windows.insert(handle.into(), window); handle }) } @@ -1276,29 +1275,25 @@ impl AppContext { F: FnOnce(&mut ViewContext) -> V, { self.update(|this| { - let window_id = post_inc(&mut this.next_id); - let platform_window = this.platform.add_status_item(window_id); - let handle = WindowHandle::::new(window_id); - let window = this.build_window(handle, platform_window, build_root_view); - this.windows.insert(window_id, window); + let handle = WindowHandle::::new(post_inc(&mut this.next_id)); + let platform_window = this.platform.add_status_item(handle.into()); + let window = this.build_window(handle.into(), platform_window, build_root_view); + this.windows.insert(handle.into(), window); handle.update_root(this, |view, cx| view.focus_in(cx.handle().into_any(), cx)); handle }) } - pub fn build_window( + pub fn build_window( &mut self, - handle: H, + handle: AnyWindowHandle, mut platform_window: Box, build_root_view: F, ) -> Window where - H: Into, V: View, F: FnOnce(&mut ViewContext) -> V, { - let handle: AnyWindowHandle = handle.into(); - { let mut app = self.upgrade(); @@ -1371,21 +1366,15 @@ impl AppContext { } pub fn active_window(&self) -> Option { - self.platform.main_window_id().and_then(|main_window_id| { - self.windows - .get(&main_window_id) - .map(|window| AnyWindowHandle::new(main_window_id, window.root_view().type_id())) - }) + self.platform.main_window() } pub fn windows(&self) -> impl '_ + Iterator { - self.windows.iter().map(|(window_id, window)| { - AnyWindowHandle::new(*window_id, window.root_view().type_id()) - }) + self.windows.keys().copied() } pub fn read_view(&self, handle: &ViewHandle) -> &T { - if let Some(view) = self.views.get(&(handle.window.id(), handle.view_id)) { + if let Some(view) = self.views.get(&(handle.window, handle.view_id)) { view.as_any().downcast_ref().expect("downcast is type safe") } else { panic!("circular view reference for type {}", type_name::()); @@ -1437,13 +1426,13 @@ impl AppContext { .push_back(Effect::ModelRelease { model_id, model }); } - for (window_id, view_id) in dropped_views { + for (window, view_id) in dropped_views { self.subscriptions.remove(view_id); self.observations.remove(view_id); - self.views_metadata.remove(&(window_id, view_id)); - let mut view = self.views.remove(&(window_id, view_id)).unwrap(); + self.views_metadata.remove(&(window, view_id)); + let mut view = self.views.remove(&(window, view_id)).unwrap(); view.release(self); - if let Some(window) = self.windows.get_mut(&window_id) { + if let Some(window) = self.windows.get_mut(&window) { window.parents.remove(&view_id); window .invalidation @@ -1571,7 +1560,7 @@ impl AppContext { } Effect::ResizeWindow { window } => { - if let Some(window) = self.windows.get_mut(&window.id()) { + if let Some(window) = self.windows.get_mut(&window) { window .invalidation .get_or_insert(WindowInvalidation::default()); @@ -1696,15 +1685,14 @@ impl AppContext { for old_ancestor in old_ancestors.iter().copied() { if !new_ancestors.contains(&old_ancestor) { if let Some(mut view) = - cx.views.remove(&(window.id(), old_ancestor)) + cx.views.remove(&(window, old_ancestor)) { view.focus_out( focused_view_id, cx, old_ancestor, ); - cx.views - .insert((window.id(), old_ancestor), view); + cx.views.insert((window, old_ancestor), view); } } } @@ -1713,15 +1701,14 @@ impl AppContext { for new_ancestor in new_ancestors.iter().copied() { if !old_ancestors.contains(&new_ancestor) { if let Some(mut view) = - cx.views.remove(&(window.id(), new_ancestor)) + cx.views.remove(&(window, new_ancestor)) { view.focus_in( focused_view_id, cx, new_ancestor, ); - cx.views - .insert((window.id(), new_ancestor), view); + cx.views.insert((window, new_ancestor), view); } } } @@ -1730,9 +1717,7 @@ impl AppContext { // there isn't any pending focus, focus the root view. let root_view_id = cx.window.root_view().id(); if focused_view_id != root_view_id - && !cx - .views - .contains_key(&(window.id(), focused_view_id)) + && !cx.views.contains_key(&(window, focused_view_id)) && !focus_effects.contains_key(&window.id()) { focus_effects.insert( @@ -1837,13 +1822,13 @@ impl AppContext { observed_window: AnyWindowHandle, observed_view_id: usize, ) { - let view_key = (observed_window.id(), observed_view_id); + let view_key = (observed_window, observed_view_id); if let Some((view, mut view_metadata)) = self .views .remove(&view_key) .zip(self.views_metadata.remove(&view_key)) { - if let Some(window) = self.windows.get_mut(&observed_window.id()) { + if let Some(window) = self.windows.get_mut(&observed_window) { window .invalidation .get_or_insert_with(Default::default) @@ -1924,7 +1909,7 @@ impl AppContext { let focused_id = match effect { FocusEffect::View { view_id, .. } => { if let Some(view_id) = view_id { - if cx.views.contains_key(&(window.id(), view_id)) { + if cx.views.contains_key(&(window, view_id)) { Some(view_id) } else { Some(cx.root_view().id()) @@ -1949,9 +1934,9 @@ impl AppContext { if focus_changed { if let Some(blurred_id) = blurred_id { for view_id in cx.ancestors(blurred_id).collect::>() { - if let Some(mut view) = cx.views.remove(&(window.id(), view_id)) { + if let Some(mut view) = cx.views.remove(&(window, view_id)) { view.focus_out(blurred_id, cx, view_id); - cx.views.insert((window.id(), view_id), view); + cx.views.insert((window, view_id), view); } } @@ -1963,9 +1948,9 @@ impl AppContext { if focus_changed || effect.is_forced() { if let Some(focused_id) = focused_id { for view_id in cx.ancestors(focused_id).collect::>() { - if let Some(mut view) = cx.views.remove(&(window.id(), view_id)) { + if let Some(mut view) = cx.views.remove(&(window, view_id)) { view.focus_in(focused_id, cx, view_id); - cx.views.insert((window.id(), view_id), view); + cx.views.insert((window, view_id), view); } } @@ -1991,7 +1976,7 @@ impl AppContext { mut callback: WindowShouldCloseSubscriptionCallback, ) { let mut app = self.upgrade(); - if let Some(window) = self.windows.get_mut(&window.id()) { + if let Some(window) = self.windows.get_mut(&window) { window .platform_window .on_should_close(Box::new(move || app.update(|cx| callback(cx)))) @@ -2127,15 +2112,13 @@ impl BorrowWindowContext for AppContext { where F: FnOnce(&mut WindowContext) -> T, { - self.update(|app_context| { - let mut window = app_context.windows.remove(&handle.id())?; - let mut window_context = WindowContext::mutable(app_context, &mut window, handle); - let result = f(&mut window_context); - if !window_context.removed { - app_context.windows.insert(handle.id(), window); - } - Some(result) - }) + let mut window = self.windows.remove(&handle)?; + let mut window_context = WindowContext::mutable(self, &mut window, handle); + let result = f(&mut window_context); + if !window_context.removed { + self.windows.insert(handle, window); + } + Some(result) } fn update_window_optional(&mut self, handle: AnyWindowHandle, f: F) -> Option @@ -2590,7 +2573,7 @@ where } else { let focused_type = cx .views_metadata - .get(&(cx.window_handle.id(), focused_id)) + .get(&(cx.window_handle, focused_id)) .unwrap() .type_id; AnyViewHandle::new( @@ -2610,7 +2593,7 @@ where } else { let blurred_type = cx .views_metadata - .get(&(cx.window_handle.id(), blurred_id)) + .get(&(cx.window_handle, blurred_id)) .unwrap() .type_id; AnyViewHandle::new( @@ -3413,7 +3396,7 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> { let mut contexts = Vec::new(); let mut handler_depth = None; for (i, view_id) in self.ancestors(view_id).enumerate() { - if let Some(view_metadata) = self.views_metadata.get(&(window.id(), view_id)) { + if let Some(view_metadata) = self.views_metadata.get(&(window, view_id)) { if let Some(actions) = self.actions.get(&view_metadata.type_id) { if actions.contains_key(&action.id()) { handler_depth = Some(i); @@ -3873,10 +3856,7 @@ impl Copy for WindowHandle {} impl WindowHandle { fn new(window_id: usize) -> Self { WindowHandle { - any_handle: AnyWindowHandle { - window_id, - root_view_type: TypeId::of::(), - }, + any_handle: AnyWindowHandle::new(window_id, TypeId::of::()), root_view_type: PhantomData, } } @@ -4240,7 +4220,7 @@ impl AnyViewHandle { view_type: TypeId, ref_counts: Arc>, ) -> Self { - ref_counts.lock().inc_view(window.id(), view_id); + ref_counts.lock().inc_view(window, view_id); #[cfg(any(test, feature = "test-support"))] let handle_id = ref_counts @@ -4308,7 +4288,7 @@ impl AnyViewHandle { pub fn debug_json<'a, 'b>(&self, cx: &'b WindowContext<'a>) -> serde_json::Value { cx.views - .get(&(self.window.id(), self.view_id)) + .get(&(self.window, self.view_id)) .map_or_else(|| serde_json::Value::Null, |view| view.debug_json(cx)) } } @@ -4338,9 +4318,7 @@ impl PartialEq> for AnyViewHandle { impl Drop for AnyViewHandle { fn drop(&mut self) { - self.ref_counts - .lock() - .dec_view(self.window.id(), self.view_id); + self.ref_counts.lock().dec_view(self.window, self.view_id); #[cfg(any(test, feature = "test-support"))] self.ref_counts .lock() diff --git a/crates/gpui/src/app/ref_counts.rs b/crates/gpui/src/app/ref_counts.rs index f0c1699f165ea8100ccdfe1facbfb8a3ac1a2d8e..63905326fe2483118221e79a57942d525c41701f 100644 --- a/crates/gpui/src/app/ref_counts.rs +++ b/crates/gpui/src/app/ref_counts.rs @@ -9,7 +9,7 @@ use collections::{hash_map::Entry, HashMap, HashSet}; #[cfg(any(test, feature = "test-support"))] use crate::util::post_inc; -use crate::ElementStateId; +use crate::{AnyWindowHandle, ElementStateId}; lazy_static! { static ref LEAK_BACKTRACE: bool = @@ -26,7 +26,7 @@ pub struct RefCounts { entity_counts: HashMap, element_state_counts: HashMap, dropped_models: HashSet, - dropped_views: HashSet<(usize, usize)>, + dropped_views: HashSet<(AnyWindowHandle, usize)>, dropped_element_states: HashSet, #[cfg(any(test, feature = "test-support"))] @@ -55,12 +55,12 @@ impl RefCounts { } } - pub fn inc_view(&mut self, window_id: usize, view_id: usize) { + pub fn inc_view(&mut self, window: AnyWindowHandle, view_id: usize) { match self.entity_counts.entry(view_id) { Entry::Occupied(mut entry) => *entry.get_mut() += 1, Entry::Vacant(entry) => { entry.insert(1); - self.dropped_views.remove(&(window_id, view_id)); + self.dropped_views.remove(&(window, view_id)); } } } @@ -94,12 +94,12 @@ impl RefCounts { } } - pub fn dec_view(&mut self, window_id: usize, view_id: usize) { + pub fn dec_view(&mut self, window: AnyWindowHandle, view_id: usize) { let count = self.entity_counts.get_mut(&view_id).unwrap(); *count -= 1; if *count == 0 { self.entity_counts.remove(&view_id); - self.dropped_views.insert((window_id, view_id)); + self.dropped_views.insert((window, view_id)); } } @@ -120,7 +120,7 @@ impl RefCounts { &mut self, ) -> ( HashSet, - HashSet<(usize, usize)>, + HashSet<(AnyWindowHandle, usize)>, HashSet, ) { ( diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index b1729d89b8591176f1fa9aa27ccc93cebdc65e8d..6d593c2e7247d6d6005b32320b2b4dd235b4cac8 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -159,7 +159,7 @@ impl TestAppContext { .borrow_mut() .add_window(Default::default(), build_root_view); window.simulate_activation(self); - WindowHandle::new(window.id()) + window } pub fn observe_global(&mut self, callback: F) -> Subscription @@ -516,7 +516,7 @@ impl AnyWindowHandle { cx: &'a mut TestAppContext, ) -> std::cell::RefMut<'a, platform::test::Window> { std::cell::RefMut::map(cx.cx.borrow_mut(), |state| { - let window = state.windows.get_mut(&self.window_id).unwrap(); + let window = state.windows.get_mut(&self).unwrap(); let test_window = window .platform_window .as_any_mut() diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index df3f8207556ced0bc1cff4521711a1468084f915..d39219e65d4b0f19e251676b0901b7a3019328d7 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -235,9 +235,9 @@ impl<'a> WindowContext<'a> { F: FnOnce(&mut dyn AnyView, &mut Self) -> T, { let handle = self.window_handle; - let mut view = self.views.remove(&(handle.id(), view_id))?; + let mut view = self.views.remove(&(handle, view_id))?; let result = f(view.as_mut(), self); - self.views.insert((handle.id(), view_id), view); + self.views.insert((handle, view_id), view); Some(result) } @@ -389,7 +389,7 @@ impl<'a> WindowContext<'a> { let mut contexts = Vec::new(); let mut handler_depths_by_action_id = HashMap::::default(); for (depth, view_id) in self.ancestors(view_id).enumerate() { - if let Some(view_metadata) = self.views_metadata.get(&(handle.id(), view_id)) { + if let Some(view_metadata) = self.views_metadata.get(&(handle, view_id)) { contexts.push(view_metadata.keymap_context.clone()); if let Some(actions) = self.actions.get(&view_metadata.type_id) { handler_depths_by_action_id @@ -440,7 +440,7 @@ impl<'a> WindowContext<'a> { .ancestors(focused_view_id) .filter_map(|view_id| { self.views_metadata - .get(&(handle.id(), view_id)) + .get(&(handle, view_id)) .map(|view| (view_id, view.keymap_context.clone())) }) .collect(); @@ -850,9 +850,9 @@ impl<'a> WindowContext<'a> { let handle = self.window_handle; if let Some(focused_view_id) = self.window.focused_view_id { for view_id in self.ancestors(focused_view_id).collect::>() { - if let Some(mut view) = self.views.remove(&(handle.id(), view_id)) { + if let Some(mut view) = self.views.remove(&(handle, view_id)) { let handled = view.key_down(event, self, view_id); - self.views.insert((handle.id(), view_id), view); + self.views.insert((handle, view_id), view); if handled { return true; } @@ -869,9 +869,9 @@ impl<'a> WindowContext<'a> { let handle = self.window_handle; if let Some(focused_view_id) = self.window.focused_view_id { for view_id in self.ancestors(focused_view_id).collect::>() { - if let Some(mut view) = self.views.remove(&(handle.id(), view_id)) { + if let Some(mut view) = self.views.remove(&(handle, view_id)) { let handled = view.key_up(event, self, view_id); - self.views.insert((handle.id(), view_id), view); + self.views.insert((handle, view_id), view); if handled { return true; } @@ -888,9 +888,9 @@ impl<'a> WindowContext<'a> { let handle = self.window_handle; if let Some(focused_view_id) = self.window.focused_view_id { for view_id in self.ancestors(focused_view_id).collect::>() { - if let Some(mut view) = self.views.remove(&(handle.id(), view_id)) { + if let Some(mut view) = self.views.remove(&(handle, view_id)) { let handled = view.modifiers_changed(event, self, view_id); - self.views.insert((handle.id(), view_id), view); + self.views.insert((handle, view_id), view); if handled { return true; } @@ -929,10 +929,10 @@ impl<'a> WindowContext<'a> { let view_id = params.view_id; let mut view = self .views - .remove(&(handle.id(), view_id)) + .remove(&(handle, view_id)) .ok_or_else(|| anyhow!("view not found"))?; let element = view.render(self, view_id); - self.views.insert((handle.id(), view_id), view); + self.views.insert((handle, view_id), view); Ok(element) } @@ -1190,13 +1190,13 @@ impl<'a> WindowContext<'a> { let mut keymap_context = KeymapContext::default(); view.update_keymap_context(&mut keymap_context, cx.app_context()); self.views_metadata.insert( - (handle.id(), view_id), + (handle, view_id), ViewMetadata { type_id: TypeId::of::(), keymap_context, }, ); - self.views.insert((handle.id(), view_id), Box::new(view)); + self.views.insert((handle, view_id), Box::new(view)); self.window .invalidation .get_or_insert_with(Default::default) diff --git a/crates/gpui/src/app/window_input_handler.rs b/crates/gpui/src/app/window_input_handler.rs index d7c65b11fae6f6c66de0c4c0a1c4302400d651ba..bdc871a80278eaf140887fa9e6d3b64077ac3150 100644 --- a/crates/gpui/src/app/window_input_handler.rs +++ b/crates/gpui/src/app/window_input_handler.rs @@ -23,7 +23,7 @@ impl WindowInputHandler { let mut app = self.app.try_borrow_mut().ok()?; self.window.update_optional(&mut *app, |cx| { let view_id = cx.window.focused_view_id?; - let view = cx.views.get(&(self.window.id(), view_id))?; + let view = cx.views.get(&(self.window, view_id))?; let result = f(view.as_ref(), &cx); Some(result) }) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 67f8e52c04f0cdf18aaf340860d0fe9adc44bc88..1d93a45fc7bd0e58ba2fd514e9db33a5c74e590b 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -19,7 +19,7 @@ use crate::{ }, keymap_matcher::KeymapMatcher, text_layout::{LineLayout, RunStyle}, - Action, ClipboardItem, Menu, Scene, + Action, AnyWindowHandle, ClipboardItem, Menu, Scene, }; use anyhow::{anyhow, bail, Result}; use async_task::Runnable; @@ -58,13 +58,13 @@ pub trait Platform: Send + Sync { fn open_window( &self, - id: usize, + handle: AnyWindowHandle, options: WindowOptions, executor: Rc, ) -> Box; - fn main_window_id(&self) -> Option; + fn main_window(&self) -> Option; - fn add_status_item(&self, id: usize) -> Box; + fn add_status_item(&self, handle: AnyWindowHandle) -> Box; fn write_to_clipboard(&self, item: ClipboardItem); fn read_from_clipboard(&self) -> Option; diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 509c979b8536dfde31a9246b34edd7bd0ca88e0e..277ff8403f8df8ce96984c4a6fc896ad818df6f7 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -6,7 +6,7 @@ use crate::{ executor, keymap_matcher::KeymapMatcher, platform::{self, AppVersion, CursorStyle, Event}, - Action, ClipboardItem, Menu, MenuItem, + Action, AnyWindowHandle, ClipboardItem, Menu, MenuItem, }; use anyhow::{anyhow, Result}; use block::ConcreteBlock; @@ -590,18 +590,18 @@ impl platform::Platform for MacPlatform { fn open_window( &self, - id: usize, + handle: AnyWindowHandle, options: platform::WindowOptions, executor: Rc, ) -> Box { - Box::new(Window::open(id, options, executor, self.fonts())) + Box::new(Window::open(handle, options, executor, self.fonts())) } - fn main_window_id(&self) -> Option { - Window::main_window_id() + fn main_window(&self) -> Option { + Window::main_window() } - fn add_status_item(&self, _id: usize) -> Box { + fn add_status_item(&self, _handle: AnyWindowHandle) -> Box { Box::new(StatusItem::add(self.fonts())) } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index c0f0ade7b926b59dc3a49a714483f576f3a64026..59324f473a97a4682efc277cce5b46e916a37b9a 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -13,6 +13,7 @@ use crate::{ Event, InputHandler, KeyDownEvent, Modifiers, ModifiersChangedEvent, MouseButton, MouseButtonEvent, MouseMovedEvent, Scene, WindowBounds, WindowKind, }, + AnyWindowHandle, }; use block::ConcreteBlock; use cocoa::{ @@ -282,7 +283,7 @@ struct InsertText { } struct WindowState { - id: usize, + handle: AnyWindowHandle, native_window: id, kind: WindowKind, event_callback: Option bool>>, @@ -426,7 +427,7 @@ pub struct Window(Rc>); impl Window { pub fn open( - id: usize, + handle: AnyWindowHandle, options: platform::WindowOptions, executor: Rc, fonts: Arc, @@ -504,7 +505,7 @@ impl Window { assert!(!native_view.is_null()); let window = Self(Rc::new(RefCell::new(WindowState { - id, + handle, native_window, kind: options.kind, event_callback: None, @@ -621,13 +622,13 @@ impl Window { } } - pub fn main_window_id() -> Option { + pub fn main_window() -> Option { unsafe { let app = NSApplication::sharedApplication(nil); let main_window: id = msg_send![app, mainWindow]; if msg_send![main_window, isKindOfClass: WINDOW_CLASS] { - let id = get_window_state(&*main_window).borrow().id; - Some(id) + let handle = get_window_state(&*main_window).borrow().handle; + Some(handle) } else { None } @@ -881,7 +882,7 @@ impl platform::Window for Window { fn is_topmost_for_position(&self, position: Vector2F) -> bool { let self_borrow = self.0.borrow(); - let self_id = self_borrow.id; + let self_id = self_borrow.handle; unsafe { let app = NSApplication::sharedApplication(nil); @@ -898,7 +899,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 == YES || is_window == YES { - let topmost_window_id = get_window_state(&*top_most_window).borrow().id; + let topmost_window_id = get_window_state(&*top_most_window).borrow().handle; topmost_window_id == self_id } else { // Someone else's window is on top diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index f949a6cd78e90c3f4a0025fc5aaae5ad4d251d9a..6c11817b5c14171c6934c68acfd335c9b2790522 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -5,7 +5,7 @@ use crate::{ vector::{vec2f, Vector2F}, }, keymap_matcher::KeymapMatcher, - Action, ClipboardItem, Menu, + Action, AnyWindowHandle, ClipboardItem, Menu, }; use anyhow::{anyhow, Result}; use collections::VecDeque; @@ -102,7 +102,7 @@ pub struct Platform { fonts: Arc, current_clipboard_item: Mutex>, cursor: Mutex, - active_window_id: Arc>>, + active_window: Arc>>, } impl Platform { @@ -112,7 +112,7 @@ impl Platform { fonts: Arc::new(super::current::FontSystem::new()), current_clipboard_item: Default::default(), cursor: Mutex::new(CursorStyle::Arrow), - active_window_id: Default::default(), + active_window: Default::default(), } } } @@ -146,30 +146,30 @@ impl super::Platform for Platform { fn open_window( &self, - id: usize, + handle: AnyWindowHandle, options: super::WindowOptions, _executor: Rc, ) -> Box { - *self.active_window_id.lock() = Some(id); + *self.active_window.lock() = Some(handle); Box::new(Window::new( - id, + handle, match options.bounds { WindowBounds::Maximized | WindowBounds::Fullscreen => vec2f(1024., 768.), WindowBounds::Fixed(rect) => rect.size(), }, - self.active_window_id.clone(), + self.active_window.clone(), )) } - fn main_window_id(&self) -> Option { - self.active_window_id.lock().clone() + fn main_window(&self) -> Option { + self.active_window.lock().clone() } - fn add_status_item(&self, id: usize) -> Box { + fn add_status_item(&self, handle: AnyWindowHandle) -> Box { Box::new(Window::new( - id, + handle, vec2f(24., 24.), - self.active_window_id.clone(), + self.active_window.clone(), )) } @@ -256,7 +256,7 @@ impl super::Screen for Screen { } pub struct Window { - id: usize, + handle: AnyWindowHandle, pub(crate) size: Vector2F, scale_factor: f32, current_scene: Option, @@ -270,13 +270,17 @@ pub struct Window { pub(crate) title: Option, pub(crate) edited: bool, pub(crate) pending_prompts: RefCell>>, - active_window_id: Arc>>, + active_window: Arc>>, } impl Window { - pub fn new(id: usize, size: Vector2F, active_window_id: Arc>>) -> Self { + pub fn new( + handle: AnyWindowHandle, + size: Vector2F, + active_window: Arc>>, + ) -> Self { Self { - id, + handle, size, event_handlers: Default::default(), resize_handlers: Default::default(), @@ -290,7 +294,7 @@ impl Window { title: None, edited: false, pending_prompts: Default::default(), - active_window_id, + active_window, } } @@ -342,7 +346,7 @@ impl super::Window for Window { } fn activate(&self) { - *self.active_window_id.lock() = Some(self.id); + *self.active_window.lock() = Some(self.handle); } fn set_title(&mut self, title: &str) { From 8e49d1419a2ec3e832fc6716f2795305f35320df Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 16:38:46 -0600 Subject: [PATCH 50/53] Minimize window id usage --- crates/gpui/src/app.rs | 58 +++++++++++++------------- crates/gpui/src/app/window.rs | 10 ++--- crates/gpui/src/platform/mac/window.rs | 6 +-- crates/theme/src/ui.rs | 1 - 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 507cbffad279553dea2612dbb9a898ab4358ae8b..8020147844d946ba7bb1ca307a6ee5df7445f4e3 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -477,10 +477,10 @@ pub struct AppContext { focus_observations: CallbackCollection, release_observations: CallbackCollection, action_dispatch_observations: CallbackCollection<(), ActionObservationCallback>, - window_activation_observations: CallbackCollection, - window_fullscreen_observations: CallbackCollection, - window_bounds_observations: CallbackCollection, - keystroke_observations: CallbackCollection, + window_activation_observations: CallbackCollection, + window_fullscreen_observations: CallbackCollection, + window_bounds_observations: CallbackCollection, + keystroke_observations: CallbackCollection, active_labeled_task_observations: CallbackCollection<(), ActiveLabeledTasksCallback>, foreground: Rc, @@ -1460,7 +1460,7 @@ impl AppContext { let mut refreshing = false; let mut updated_windows = HashSet::default(); - let mut focus_effects = HashMap::::default(); + let mut focus_effects = HashMap::::default(); loop { self.remove_dropped_entities(); if let Some(effect) = self.pending_effects.pop_front() { @@ -1538,13 +1538,13 @@ impl AppContext { Effect::Focus(mut effect) => { if focus_effects - .get(&effect.window().id()) + .get(&effect.window()) .map_or(false, |prev_effect| prev_effect.is_forced()) { effect.force(); } - focus_effects.insert(effect.window().id(), effect); + focus_effects.insert(effect.window(), effect); } Effect::FocusObservation { @@ -1577,7 +1577,7 @@ impl AppContext { subscription_id, callback, } => self.window_activation_observations.add_callback( - window.id(), + window, subscription_id, callback, ), @@ -1586,7 +1586,7 @@ impl AppContext { if self.handle_window_activation_effect(window, is_active) && is_active { focus_effects - .entry(window.id()) + .entry(window) .or_insert_with(|| FocusEffect::View { window, view_id: self @@ -1603,7 +1603,7 @@ impl AppContext { subscription_id, callback, } => self.window_fullscreen_observations.add_callback( - window.id(), + window, subscription_id, callback, ), @@ -1618,7 +1618,7 @@ impl AppContext { subscription_id, callback, } => self.window_bounds_observations.add_callback( - window.id(), + window, subscription_id, callback, ), @@ -1718,10 +1718,10 @@ impl AppContext { let root_view_id = cx.window.root_view().id(); if focused_view_id != root_view_id && !cx.views.contains_key(&(window, focused_view_id)) - && !focus_effects.contains_key(&window.id()) + && !focus_effects.contains_key(&window) { focus_effects.insert( - window.id(), + window, FocusEffect::View { window, view_id: Some(root_view_id), @@ -1860,12 +1860,12 @@ impl AppContext { cx.window.is_fullscreen = is_fullscreen; let mut fullscreen_observations = cx.window_fullscreen_observations.clone(); - fullscreen_observations.emit(window.id(), |callback| callback(is_fullscreen, cx)); + fullscreen_observations.emit(window, |callback| callback(is_fullscreen, cx)); if let Some(uuid) = cx.window_display_uuid() { let bounds = cx.window_bounds(); let mut bounds_observations = cx.window_bounds_observations.clone(); - bounds_observations.emit(window.id(), |callback| callback(bounds, uuid, cx)); + bounds_observations.emit(window, |callback| callback(bounds, uuid, cx)); } Some(()) @@ -1881,7 +1881,7 @@ impl AppContext { ) { self.update_window(window, |cx| { let mut observations = cx.keystroke_observations.clone(); - observations.emit(window.id(), move |callback| { + observations.emit(window, move |callback| { callback(&keystroke, &result, handled_by.as_ref(), cx) }); }); @@ -1895,7 +1895,7 @@ impl AppContext { cx.window.is_active = active; let mut observations = cx.window_activation_observations.clone(); - observations.emit(window.id(), |callback| callback(active, cx)); + observations.emit(window, |callback| callback(active, cx)); true }) .unwrap_or(false) @@ -1989,7 +1989,7 @@ impl AppContext { let bounds = cx.window_bounds(); cx.window_bounds_observations .clone() - .emit(window.id(), move |callback| { + .emit(window, move |callback| { callback(bounds, display, cx); true }); @@ -4038,12 +4038,12 @@ impl AnyWindowHandle { #[cfg(any(test, feature = "test-support"))] pub fn simulate_activation(&self, cx: &mut TestAppContext) { self.update(cx, |cx| { - let other_window_ids = cx + let other_windows = cx .windows() .filter(|window| *window != *self) .collect::>(); - for window in other_window_ids { + for window in other_windows { cx.window_changed_active_status(window, false) } @@ -4243,10 +4243,6 @@ impl AnyViewHandle { self.window } - pub fn window_id(&self) -> usize { - self.window.id() - } - pub fn id(&self) -> usize { self.view_id } @@ -4660,10 +4656,16 @@ pub enum Subscription { GlobalSubscription(callback_collection::Subscription), GlobalObservation(callback_collection::Subscription), FocusObservation(callback_collection::Subscription), - WindowActivationObservation(callback_collection::Subscription), - WindowFullscreenObservation(callback_collection::Subscription), - WindowBoundsObservation(callback_collection::Subscription), - KeystrokeObservation(callback_collection::Subscription), + WindowActivationObservation( + callback_collection::Subscription, + ), + WindowFullscreenObservation( + callback_collection::Subscription, + ), + WindowBoundsObservation( + callback_collection::Subscription, + ), + KeystrokeObservation(callback_collection::Subscription), ReleaseObservation(callback_collection::Subscription), ActionObservation(callback_collection::Subscription<(), ActionObservationCallback>), ActiveLabeledTasksObservation( diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index d39219e65d4b0f19e251676b0901b7a3019328d7..1012d7e77ecda14e9b1fcd98cfe6434a89da249c 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -326,7 +326,7 @@ impl<'a> WindowContext<'a> { }); Subscription::WindowActivationObservation( self.window_activation_observations - .subscribe(handle.id(), subscription_id), + .subscribe(handle, subscription_id), ) } @@ -344,7 +344,7 @@ impl<'a> WindowContext<'a> { }); Subscription::WindowActivationObservation( self.window_activation_observations - .subscribe(window.id(), subscription_id), + .subscribe(window, subscription_id), ) } @@ -362,7 +362,7 @@ impl<'a> WindowContext<'a> { }); Subscription::WindowBoundsObservation( self.window_bounds_observations - .subscribe(window.id(), subscription_id), + .subscribe(window, subscription_id), ) } @@ -374,10 +374,10 @@ impl<'a> WindowContext<'a> { let window = self.window_handle; let subscription_id = post_inc(&mut self.next_subscription_id); self.keystroke_observations - .add_callback(window.id(), subscription_id, Box::new(callback)); + .add_callback(window, subscription_id, Box::new(callback)); Subscription::KeystrokeObservation( self.keystroke_observations - .subscribe(window.id(), subscription_id), + .subscribe(window, subscription_id), ) } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 59324f473a97a4682efc277cce5b46e916a37b9a..2a722cad8293797a8dd54d6854729d4f0d2b44a3 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -882,7 +882,7 @@ impl platform::Window for Window { fn is_topmost_for_position(&self, position: Vector2F) -> bool { let self_borrow = self.0.borrow(); - let self_id = self_borrow.handle; + let self_handle = self_borrow.handle; unsafe { let app = NSApplication::sharedApplication(nil); @@ -899,8 +899,8 @@ 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 == YES || is_window == YES { - let topmost_window_id = get_window_state(&*top_most_window).borrow().handle; - topmost_window_id == self_id + let topmost_window = get_window_state(&*top_most_window).borrow().handle; + topmost_window == self_handle } else { // Someone else's window is on top false diff --git a/crates/theme/src/ui.rs b/crates/theme/src/ui.rs index 308ea6f2d7b5e02c222a4e9f049ce58fa866e2bd..a16c3cb21e7631feab176dd33ce11fc48f126fec 100644 --- a/crates/theme/src/ui.rs +++ b/crates/theme/src/ui.rs @@ -192,7 +192,6 @@ where F: FnOnce(&mut gpui::ViewContext) -> D, { const TITLEBAR_HEIGHT: f32 = 28.; - // let active = cx.window_is_active(cx.window_id()); Flex::column() .with_child( From fc96676662f1dc9a06d08cdcf19993fc40c6c08e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 17:20:46 -0600 Subject: [PATCH 51/53] Use AppContext::update when updating windows so we handle effects --- crates/gpui/src/app.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 8020147844d946ba7bb1ca307a6ee5df7445f4e3..8682e6c9dd6050b6586e64b9751d50d11bf924c4 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -2112,13 +2112,15 @@ impl BorrowWindowContext for AppContext { where F: FnOnce(&mut WindowContext) -> T, { - let mut window = self.windows.remove(&handle)?; - let mut window_context = WindowContext::mutable(self, &mut window, handle); - let result = f(&mut window_context); - if !window_context.removed { - self.windows.insert(handle, window); - } - Some(result) + self.update(|cx| { + let mut window = cx.windows.remove(&handle)?; + let mut window_context = WindowContext::mutable(cx, &mut window, handle); + let result = f(&mut window_context); + if !window_context.removed { + cx.windows.insert(handle, window); + } + Some(result) + }) } fn update_window_optional(&mut self, handle: AnyWindowHandle, f: F) -> Option From 0dc70e6cbf07e70c00c99aeab775410626c6a05a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Aug 2023 17:21:06 -0600 Subject: [PATCH 52/53] Rename mac platform Window to MacWindow for clarity --- crates/gpui/src/platform/mac.rs | 2 +- crates/gpui/src/platform/mac/platform.rs | 6 +++--- crates/gpui/src/platform/mac/window.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/gpui/src/platform/mac.rs b/crates/gpui/src/platform/mac.rs index 342c1c66d0cf424f95728db7065ea39fa8b79b64..92ab53f15e34dae90db9635d95eb6721c3a53ee6 100644 --- a/crates/gpui/src/platform/mac.rs +++ b/crates/gpui/src/platform/mac.rs @@ -21,7 +21,7 @@ pub use fonts::FontSystem; use platform::{MacForegroundPlatform, MacPlatform}; pub use renderer::Surface; use std::{ops::Range, rc::Rc, sync::Arc}; -use window::Window; +use window::MacWindow; use crate::executor; diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 277ff8403f8df8ce96984c4a6fc896ad818df6f7..9a799c3a3a17767bb1be4fcdd9d7a7a217f52e11 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -1,6 +1,6 @@ use super::{ event::key_to_native, screen::Screen, status_item::StatusItem, BoolExt as _, Dispatcher, - FontSystem, Window, + FontSystem, MacWindow, }; use crate::{ executor, @@ -594,11 +594,11 @@ impl platform::Platform for MacPlatform { options: platform::WindowOptions, executor: Rc, ) -> Box { - Box::new(Window::open(handle, options, executor, self.fonts())) + Box::new(MacWindow::open(handle, options, executor, self.fonts())) } fn main_window(&self) -> Option { - Window::main_window() + MacWindow::main_window() } fn add_status_item(&self, _handle: AnyWindowHandle) -> Box { diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 2a722cad8293797a8dd54d6854729d4f0d2b44a3..022346ea67b24e99a9644b9dce77b843d05a6fd0 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -423,9 +423,9 @@ impl WindowState { } } -pub struct Window(Rc>); +pub struct MacWindow(Rc>); -impl Window { +impl MacWindow { pub fn open( handle: AnyWindowHandle, options: platform::WindowOptions, @@ -636,7 +636,7 @@ impl Window { } } -impl Drop for Window { +impl Drop for MacWindow { fn drop(&mut self) { let this = self.0.borrow(); let window = this.native_window; @@ -650,7 +650,7 @@ impl Drop for Window { } } -impl platform::Window for Window { +impl platform::Window for MacWindow { fn bounds(&self) -> WindowBounds { self.0.as_ref().borrow().bounds() } From c523ccc4c7cd5739e465a194a4b621840a1cd021 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Tue, 8 Aug 2023 18:42:38 -0400 Subject: [PATCH 53/53] Fix code that identifies language via extension --- crates/language/src/language.rs | 4 +-- crates/util/src/paths.rs | 37 +++++++++++++++++++++-- crates/zed/src/languages/bash/config.toml | 2 +- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 100ab275717066251973c3f6d547e32f72ff0b4c..223f5679aed05c8a5fee158f3a14959133c9151e 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -45,7 +45,7 @@ use syntax_map::SyntaxSnapshot; use theme::{SyntaxTheme, Theme}; use tree_sitter::{self, Query}; use unicase::UniCase; -use util::http::HttpClient; +use util::{http::HttpClient, paths::PathExt}; use util::{merge_json_value_into, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture}; #[cfg(any(test, feature = "test-support"))] @@ -777,7 +777,7 @@ impl LanguageRegistry { ) -> UnwrapFuture>>> { 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 extension = path.extension_or_hidden_file_name(); let path_suffixes = [extension, filename]; self.get_or_load_language(|config| { let path_matches = config diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index f231669197fcfdd560011132e406772da64e3050..e7e6e0ac727da64de4f21306ddf88f3ee26134f9 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -33,6 +33,7 @@ pub mod legacy { pub trait PathExt { fn compact(&self) -> PathBuf; fn icon_suffix(&self) -> Option<&str>; + fn extension_or_hidden_file_name(&self) -> Option<&str>; } impl> PathExt for T { @@ -60,6 +61,7 @@ impl> PathExt for T { } } + /// Returns a suffix of the path that is used to determine which file icon to use fn icon_suffix(&self) -> Option<&str> { let file_name = self.as_ref().file_name()?.to_str()?; @@ -69,8 +71,16 @@ impl> PathExt for T { self.as_ref() .extension() - .map(|extension| extension.to_str()) - .flatten() + .and_then(|extension| extension.to_str()) + } + + /// Returns a file's extension or, if the file is hidden, its name without the leading dot + fn extension_or_hidden_file_name(&self) -> Option<&str> { + if let Some(extension) = self.as_ref().extension() { + return extension.to_str(); + } + + self.as_ref().file_name()?.to_str()?.split('.').last() } } @@ -315,4 +325,27 @@ mod tests { let path = Path::new("/a/b/c/.eslintrc.js"); assert_eq!(path.icon_suffix(), Some("eslintrc.js")); } + + #[test] + fn test_extension_or_hidden_file_name() { + // No dots in name + let path = Path::new("/a/b/c/file_name.rs"); + assert_eq!(path.extension_or_hidden_file_name(), Some("rs")); + + // Single dot in name + let path = Path::new("/a/b/c/file.name.rs"); + assert_eq!(path.extension_or_hidden_file_name(), Some("rs")); + + // Multiple dots in name + let path = Path::new("/a/b/c/long.file.name.rs"); + assert_eq!(path.extension_or_hidden_file_name(), Some("rs")); + + // Hidden file, no extension + let path = Path::new("/a/b/c/.gitignore"); + assert_eq!(path.extension_or_hidden_file_name(), Some("gitignore")); + + // Hidden file, with extension + let path = Path::new("/a/b/c/.eslintrc.js"); + assert_eq!(path.extension_or_hidden_file_name(), Some("js")); + } } diff --git a/crates/zed/src/languages/bash/config.toml b/crates/zed/src/languages/bash/config.toml index d03897a071d78c55c48ac97c4e829c7d2e05db1f..8c4513b2509fced29f2680186993343b4d0ac414 100644 --- a/crates/zed/src/languages/bash/config.toml +++ b/crates/zed/src/languages/bash/config.toml @@ -1,5 +1,5 @@ name = "Shell Script" -path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin"] +path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin", "zprofile"] line_comment = "# " first_line_pattern = "^#!.*\\b(?:ba|z)?sh\\b" brackets = [