Merge branch 'main' into nate/storybook-components

This commit is contained in:
Nate Butler 2023-09-11 11:49:49 -04:00
commit c7ad89d7fa
71 changed files with 966 additions and 534 deletions

10
Cargo.lock generated
View file

@ -8425,6 +8425,15 @@ dependencies = [
"tree-sitter", "tree-sitter",
] ]
[[package]]
name = "tree-sitter-nu"
version = "0.0.1"
source = "git+https://github.com/nushell/tree-sitter-nu?rev=786689b0562b9799ce53e824cb45a1a2a04dc673#786689b0562b9799ce53e824cb45a1a2a04dc673"
dependencies = [
"cc",
"tree-sitter",
]
[[package]] [[package]]
name = "tree-sitter-php" name = "tree-sitter-php"
version = "0.19.1" version = "0.19.1"
@ -9887,6 +9896,7 @@ dependencies = [
"tree-sitter-lua", "tree-sitter-lua",
"tree-sitter-markdown", "tree-sitter-markdown",
"tree-sitter-nix", "tree-sitter-nix",
"tree-sitter-nu",
"tree-sitter-php", "tree-sitter-php",
"tree-sitter-python", "tree-sitter-python",
"tree-sitter-racket", "tree-sitter-racket",

View file

@ -142,6 +142,7 @@ tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-rack
tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930"} tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930"}
tree-sitter-lua = "0.0.14" tree-sitter-lua = "0.0.14"
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" } tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "786689b0562b9799ce53e824cb45a1a2a04dc673"}
[patch.crates-io] [patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" } tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" }

View file

@ -8,7 +8,31 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea
### Dependencies ### Dependencies
* Install [Postgres.app](https://postgresapp.com) and start it. * Install Xcode from https://apps.apple.com/us/app/xcode/id497799835?mt=12, and accept the license:
```
sudo xcodebuild -license
```
* Install homebrew, rust and node
```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install rust
brew install node
```
* Ensure rust executables are in your $PATH
```
echo $HOME/.cargo/bin | sudo tee /etc/paths.d/10-rust
```
* Install postgres and configure the database
```
brew install postgresql@15
brew services start postgresql@15
psql -c "CREATE ROLE postgres SUPERUSER LOGIN" postgres
psql -U postgres -c "CREATE DATABASE zed"
```
* Install the `LiveKit` server and the `foreman` process supervisor: * Install the `LiveKit` server and the `foreman` process supervisor:
``` ```
@ -41,6 +65,17 @@ Welcome to Zed, a lightning-fast, collaborative code editor that makes your drea
GITHUB_TOKEN=<$token> script/bootstrap GITHUB_TOKEN=<$token> script/bootstrap
``` ```
* Now try running zed with collaboration disabled:
```
cargo run
```
### Common errors
* `xcrun: error: unable to find utility "metal", not a developer tool or in PATH`
* You need to install Xcode and then run: `xcode-select --switch /Applications/Xcode.app/Contents/Developer`
* (see https://github.com/gfx-rs/gfx/issues/2309)
### Testing against locally-running servers ### Testing against locally-running servers
Start the web and collab servers: Start the web and collab servers:

View file

@ -198,6 +198,18 @@
"z c": "editor::Fold", "z c": "editor::Fold",
"z o": "editor::UnfoldLines", "z o": "editor::UnfoldLines",
"z f": "editor::FoldSelectedRanges", "z f": "editor::FoldSelectedRanges",
"shift-z shift-q": [
"pane::CloseActiveItem",
{
"saveBehavior": "dontSave"
}
],
"shift-z shift-z": [
"pane::CloseActiveItem",
{
"saveBehavior": "promptOnConflict"
}
],
// Count support // Count support
"1": [ "1": [
"vim::Number", "vim::Number",

View file

@ -13,8 +13,8 @@ use gpui::{
geometry::{rect::RectF, vector::vec2f, PathBuilder}, geometry::{rect::RectF, vector::vec2f, PathBuilder},
json::{self, ToJson}, json::{self, ToJson},
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, MouseButton},
AppContext, Entity, ImageData, LayoutContext, ModelHandle, PaintContext, Subscription, View, AppContext, Entity, ImageData, ModelHandle, Subscription, View, ViewContext, ViewHandle,
ViewContext, ViewHandle, WeakViewHandle, WeakViewHandle,
}; };
use picker::PickerEvent; use picker::PickerEvent;
use project::{Project, RepositoryEntry}; use project::{Project, RepositoryEntry};
@ -1165,7 +1165,7 @@ impl Element<CollabTitlebarItem> for AvatarRibbon {
&mut self, &mut self,
constraint: gpui::SizeConstraint, constraint: gpui::SizeConstraint,
_: &mut CollabTitlebarItem, _: &mut CollabTitlebarItem,
_: &mut LayoutContext<CollabTitlebarItem>, _: &mut ViewContext<CollabTitlebarItem>,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
(constraint.max, ()) (constraint.max, ())
} }
@ -1176,7 +1176,7 @@ impl Element<CollabTitlebarItem> for AvatarRibbon {
_: RectF, _: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
_: &mut CollabTitlebarItem, _: &mut CollabTitlebarItem,
cx: &mut PaintContext<CollabTitlebarItem>, cx: &mut ViewContext<CollabTitlebarItem>,
) -> Self::PaintState { ) -> Self::PaintState {
let mut path = PathBuilder::new(); let mut path = PathBuilder::new();
path.reset(bounds.lower_left()); path.reset(bounds.lower_left());

View file

@ -7,7 +7,7 @@ use gpui::{
}, },
json::ToJson, json::ToJson,
serde_json::{self, json}, serde_json::{self, json},
AnyElement, Axis, Element, LayoutContext, PaintContext, View, ViewContext, AnyElement, Axis, Element, View, ViewContext,
}; };
pub(crate) struct FacePile<V: View> { pub(crate) struct FacePile<V: View> {
@ -32,7 +32,7 @@ impl<V: View> Element<V> for FacePile<V> {
&mut self, &mut self,
constraint: gpui::SizeConstraint, constraint: gpui::SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY); debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY);
@ -57,7 +57,7 @@ impl<V: View> Element<V> for FacePile<V> {
visible_bounds: RectF, visible_bounds: RectF,
_layout: &mut Self::LayoutState, _layout: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();

View file

@ -32,8 +32,8 @@ use gpui::{
json::{self, ToJson}, json::{self, ToJson},
platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent}, platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
text_layout::{self, Line, RunStyle, TextLayoutCache}, text_layout::{self, Line, RunStyle, TextLayoutCache},
AnyElement, Axis, CursorRegion, Element, EventContext, FontCache, LayoutContext, MouseRegion, AnyElement, Axis, CursorRegion, Element, EventContext, FontCache, MouseRegion, Quad,
PaintContext, Quad, SizeConstraint, ViewContext, WindowContext, SizeConstraint, ViewContext, WindowContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use json::json; use json::json;
@ -635,7 +635,7 @@ impl EditorElement {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut LayoutState, layout: &mut LayoutState,
editor: &mut Editor, editor: &mut Editor,
cx: &mut PaintContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let line_height = layout.position_map.line_height; let line_height = layout.position_map.line_height;
@ -778,7 +778,7 @@ impl EditorElement {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut LayoutState, layout: &mut LayoutState,
editor: &mut Editor, editor: &mut Editor,
cx: &mut PaintContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let style = &self.style; let style = &self.style;
let scroll_position = layout.position_map.snapshot.scroll_position(); let scroll_position = layout.position_map.snapshot.scroll_position();
@ -1351,7 +1351,7 @@ impl EditorElement {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut LayoutState, layout: &mut LayoutState,
editor: &mut Editor, editor: &mut Editor,
cx: &mut PaintContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let scroll_position = layout.position_map.snapshot.scroll_position(); let scroll_position = layout.position_map.snapshot.scroll_position();
let scroll_left = scroll_position.x() * layout.position_map.em_width; let scroll_left = scroll_position.x() * layout.position_map.em_width;
@ -1670,7 +1670,7 @@ impl EditorElement {
style: &EditorStyle, style: &EditorStyle,
line_layouts: &[LineWithInvisibles], line_layouts: &[LineWithInvisibles],
editor: &mut Editor, editor: &mut Editor,
cx: &mut LayoutContext<Editor>, cx: &mut ViewContext<Editor>,
) -> (f32, Vec<BlockLayout>) { ) -> (f32, Vec<BlockLayout>) {
let mut block_id = 0; let mut block_id = 0;
let scroll_x = snapshot.scroll_anchor.offset.x(); let scroll_x = snapshot.scroll_anchor.offset.x();
@ -2092,7 +2092,7 @@ impl Element<Editor> for EditorElement {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
editor: &mut Editor, editor: &mut Editor,
cx: &mut LayoutContext<Editor>, cx: &mut ViewContext<Editor>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut size = constraint.max; let mut size = constraint.max;
if size.x().is_infinite() { if size.x().is_infinite() {
@ -2570,7 +2570,7 @@ impl Element<Editor> for EditorElement {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
editor: &mut Editor, editor: &mut Editor,
cx: &mut PaintContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
cx.scene().push_layer(Some(visible_bounds)); cx.scene().push_layer(Some(visible_bounds));
@ -3177,11 +3177,10 @@ mod tests {
Point::new(5, 6)..Point::new(6, 0), Point::new(5, 6)..Point::new(6, 0),
]); ]);
}); });
let mut layout_cx = LayoutContext::new(cx);
element.layout( element.layout(
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
editor, editor,
&mut layout_cx, cx,
) )
}); });
assert_eq!(state.selections.len(), 1); assert_eq!(state.selections.len(), 1);
@ -3262,11 +3261,10 @@ mod tests {
DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0), DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0),
]); ]);
}); });
let mut layout_cx = LayoutContext::new(cx);
element.layout( element.layout(
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
editor, editor,
&mut layout_cx, cx,
) )
}); });
@ -3322,11 +3320,10 @@ mod tests {
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
let (size, mut state) = editor.update(cx, |editor, cx| { let (size, mut state) = editor.update(cx, |editor, cx| {
let mut layout_cx = LayoutContext::new(cx);
element.layout( element.layout(
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
editor, editor,
&mut layout_cx, cx,
) )
}); });
@ -3343,13 +3340,7 @@ mod tests {
// Don't panic. // Don't panic.
let bounds = RectF::new(Default::default(), size); let bounds = RectF::new(Default::default(), size);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
element.paint( element.paint(bounds, bounds, &mut state, editor, cx);
bounds,
bounds,
&mut state,
editor,
&mut PaintContext::new(cx),
);
}); });
} }
@ -3517,11 +3508,10 @@ mod tests {
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
editor.set_wrap_width(Some(editor_width), cx); editor.set_wrap_width(Some(editor_width), cx);
let mut layout_cx = LayoutContext::new(cx);
element.layout( element.layout(
SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)), SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)),
editor, editor,
&mut layout_cx, cx,
) )
}); });

View file

@ -1528,8 +1528,13 @@ mod tests {
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
active_pane active_pane
.update(cx, |pane, cx| { .update(cx, |pane, cx| {
pane.close_active_item(&workspace::CloseActiveItem, cx) pane.close_active_item(
.unwrap() &workspace::CloseActiveItem {
save_behavior: None,
},
cx,
)
.unwrap()
}) })
.await .await
.unwrap(); .unwrap();

View file

@ -42,7 +42,7 @@ impl<V: View> gpui::Element<V> for CornersElement {
&mut self, &mut self,
constraint: gpui::SizeConstraint, constraint: gpui::SizeConstraint,
_: &mut V, _: &mut V,
_: &mut gpui::LayoutContext<V>, _: &mut gpui::ViewContext<V>,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
(constraint.max, ()) (constraint.max, ())
} }
@ -53,7 +53,7 @@ impl<V: View> gpui::Element<V> for CornersElement {
_: pathfinder_geometry::rect::RectF, _: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
_: &mut V, _: &mut V,
cx: &mut gpui::PaintContext<V>, cx: &mut gpui::ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
cx.scene().push_quad(Quad { cx.scene().push_quad(Quad {
bounds, bounds,

View file

@ -3404,6 +3404,16 @@ impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
.or_default() .or_default()
.push(self_view_id); .push(self_view_id);
} }
pub fn paint_layer<F, R>(&mut self, clip_bounds: Option<RectF>, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
self.scene().push_layer(clip_bounds);
let result = f(self);
self.scene().pop_layer();
result
}
} }
impl<V: View> ViewContext<'_, '_, V> { impl<V: View> ViewContext<'_, '_, V> {
@ -3495,151 +3505,6 @@ impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
} }
} }
pub struct LayoutContext<'a, 'b, 'c, V> {
// Nathan: Making this is public while I work on gpui2.
pub view_context: &'c mut ViewContext<'a, 'b, V>,
}
impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> {
pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
Self { view_context }
}
pub fn view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
self.view_context
}
}
impl<'a, 'b, 'c, V> Deref for LayoutContext<'a, 'b, 'c, V> {
type Target = ViewContext<'a, 'b, V>;
fn deref(&self) -> &Self::Target {
&self.view_context
}
}
impl<V> DerefMut for LayoutContext<'_, '_, '_, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.view_context
}
}
impl<V> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
BorrowAppContext::read_with(&*self.view_context, f)
}
fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
BorrowAppContext::update(&mut *self.view_context, f)
}
}
impl<V> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
type Result<T> = T;
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
BorrowWindowContext::read_window(&*self.view_context, window, f)
}
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
where
F: FnOnce(&WindowContext) -> Option<T>,
{
BorrowWindowContext::read_window_optional(&*self.view_context, window, f)
}
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window: AnyWindowHandle,
f: F,
) -> T {
BorrowWindowContext::update_window(&mut *self.view_context, window, f)
}
fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
where
F: FnOnce(&mut WindowContext) -> Option<T>,
{
BorrowWindowContext::update_window_optional(&mut *self.view_context, window, f)
}
}
pub struct PaintContext<'a, 'b, 'c, V> {
pub view_context: &'c mut ViewContext<'a, 'b, V>,
}
impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> {
pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
Self { view_context }
}
pub fn paint_layer<F, R>(&mut self, clip_bounds: Option<RectF>, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
self.scene().push_layer(clip_bounds);
let result = f(self);
self.scene().pop_layer();
result
}
}
impl<'a, 'b, 'c, V> Deref for PaintContext<'a, 'b, 'c, V> {
type Target = ViewContext<'a, 'b, V>;
fn deref(&self) -> &Self::Target {
&self.view_context
}
}
impl<V> DerefMut for PaintContext<'_, '_, '_, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.view_context
}
}
impl<V> BorrowAppContext for PaintContext<'_, '_, '_, V> {
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
BorrowAppContext::read_with(&*self.view_context, f)
}
fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
BorrowAppContext::update(&mut *self.view_context, f)
}
}
impl<V> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
type Result<T> = T;
fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
where
F: FnOnce(&WindowContext) -> T,
{
BorrowWindowContext::read_window(self.view_context, window, f)
}
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
where
F: FnOnce(&WindowContext) -> Option<T>,
{
BorrowWindowContext::read_window_optional(self.view_context, window, f)
}
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Self::Result<T>
where
F: FnOnce(&mut WindowContext) -> T,
{
BorrowWindowContext::update_window(self.view_context, window, f)
}
fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
where
F: FnOnce(&mut WindowContext) -> Option<T>,
{
BorrowWindowContext::update_window_optional(self.view_context, window, f)
}
}
pub struct EventContext<'a, 'b, 'c, V> { pub struct EventContext<'a, 'b, 'c, V> {
view_context: &'c mut ViewContext<'a, 'b, V>, view_context: &'c mut ViewContext<'a, 'b, V>,
pub(crate) handled: bool, pub(crate) handled: bool,
@ -6489,25 +6354,21 @@ mod tests {
view_1.update(cx, |_, cx| { view_1.update(cx, |_, cx| {
view_2.update(cx, |_, cx| { view_2.update(cx, |_, cx| {
// Sanity check // Sanity check
let mut layout_cx = LayoutContext::new(cx);
assert_eq!( assert_eq!(
layout_cx cx.keystrokes_for_action(view_1_id, &Action1)
.keystrokes_for_action(view_1_id, &Action1)
.unwrap() .unwrap()
.as_slice(), .as_slice(),
&[Keystroke::parse("a").unwrap()] &[Keystroke::parse("a").unwrap()]
); );
assert_eq!( assert_eq!(
layout_cx cx.keystrokes_for_action(view_2.id(), &Action2)
.keystrokes_for_action(view_2.id(), &Action2)
.unwrap() .unwrap()
.as_slice(), .as_slice(),
&[Keystroke::parse("b").unwrap()] &[Keystroke::parse("b").unwrap()]
); );
assert_eq!(layout_cx.keystrokes_for_action(view_1.id(), &Action3), None); assert_eq!(cx.keystrokes_for_action(view_1.id(), &Action3), None);
assert_eq!( assert_eq!(
layout_cx cx.keystrokes_for_action(view_2.id(), &Action3)
.keystrokes_for_action(view_2.id(), &Action3)
.unwrap() .unwrap()
.as_slice(), .as_slice(),
&[Keystroke::parse("c").unwrap()] &[Keystroke::parse("c").unwrap()]
@ -6516,21 +6377,17 @@ mod tests {
// The 'a' keystroke propagates up the view tree from view_2 // The 'a' keystroke propagates up the view tree from view_2
// to view_1. The action, Action1, is handled by view_1. // to view_1. The action, Action1, is handled by view_1.
assert_eq!( assert_eq!(
layout_cx cx.keystrokes_for_action(view_2.id(), &Action1)
.keystrokes_for_action(view_2.id(), &Action1)
.unwrap() .unwrap()
.as_slice(), .as_slice(),
&[Keystroke::parse("a").unwrap()] &[Keystroke::parse("a").unwrap()]
); );
// Actions that are handled below the current view don't have bindings // Actions that are handled below the current view don't have bindings
assert_eq!(layout_cx.keystrokes_for_action(view_1_id, &Action2), None); assert_eq!(cx.keystrokes_for_action(view_1_id, &Action2), None);
// Actions that are handled in other branches of the tree should not have a binding // Actions that are handled in other branches of the tree should not have a binding
assert_eq!( assert_eq!(cx.keystrokes_for_action(view_2.id(), &GlobalAction), None);
layout_cx.keystrokes_for_action(view_2.id(), &GlobalAction),
None
);
}); });
}); });

View file

@ -16,9 +16,8 @@ use crate::{
text_layout::TextLayoutCache, text_layout::TextLayoutCache,
util::post_inc, util::post_inc,
Action, AnyView, AnyViewHandle, AnyWindowHandle, AppContext, BorrowAppContext, Action, AnyView, AnyViewHandle, AnyWindowHandle, AppContext, BorrowAppContext,
BorrowWindowContext, Effect, Element, Entity, Handle, LayoutContext, MouseRegion, BorrowWindowContext, Effect, Element, Entity, Handle, MouseRegion, MouseRegionId, SceneBuilder,
MouseRegionId, PaintContext, SceneBuilder, Subscription, View, ViewContext, ViewHandle, Subscription, View, ViewContext, ViewHandle, WindowInvalidation,
WindowInvalidation,
}; };
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
@ -1677,13 +1676,13 @@ impl<V: 'static> Element<V> for ChildView {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut V, _: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) { if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
let parent_id = cx.view_id(); let parent_id = cx.view_id();
cx.window.new_parents.insert(self.view_id, parent_id); cx.window.new_parents.insert(self.view_id, parent_id);
let size = rendered_view let size = rendered_view
.layout(constraint, cx.view_context) .layout(constraint, cx)
.log_err() .log_err()
.unwrap_or(Vector2F::zero()); .unwrap_or(Vector2F::zero());
cx.window.rendered_views.insert(self.view_id, rendered_view); cx.window.rendered_views.insert(self.view_id, rendered_view);
@ -1704,7 +1703,7 @@ impl<V: 'static> Element<V> for ChildView {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
_: &mut V, _: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) { if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
rendered_view rendered_view

View file

@ -34,8 +34,8 @@ use crate::{
rect::RectF, rect::RectF,
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json, Action, Entity, LayoutContext, PaintContext, SizeConstraint, TypeTag, View, ViewContext, json, Action, Entity, SizeConstraint, TypeTag, View, ViewContext, WeakViewHandle,
WeakViewHandle, WindowContext, WindowContext,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use core::panic; use core::panic;
@ -59,7 +59,7 @@ pub trait Element<V: 'static>: 'static {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState); ) -> (Vector2F, Self::LayoutState);
fn paint( fn paint(
@ -68,7 +68,7 @@ pub trait Element<V: 'static>: 'static {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState; ) -> Self::PaintState;
fn rect_for_text_range( fn rect_for_text_range(
@ -259,7 +259,7 @@ trait AnyElementState<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> Vector2F; ) -> Vector2F;
fn paint( fn paint(
@ -267,7 +267,7 @@ trait AnyElementState<V> {
origin: Vector2F, origin: Vector2F,
visible_bounds: RectF, visible_bounds: RectF,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
); );
fn rect_for_text_range( fn rect_for_text_range(
@ -310,7 +310,7 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> Vector2F { ) -> Vector2F {
let result; let result;
*self = match mem::take(self) { *self = match mem::take(self) {
@ -347,7 +347,7 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
origin: Vector2F, origin: Vector2F,
visible_bounds: RectF, visible_bounds: RectF,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
*self = match mem::take(self) { *self = match mem::take(self) {
ElementState::PostLayout { ElementState::PostLayout {
@ -357,13 +357,7 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
mut layout, mut layout,
} => { } => {
let bounds = RectF::new(origin, size); let bounds = RectF::new(origin, size);
let paint = element.paint( let paint = element.paint(bounds, visible_bounds, &mut layout, view, cx);
bounds,
visible_bounds,
&mut layout,
view,
&mut PaintContext::new(cx),
);
ElementState::PostPaint { ElementState::PostPaint {
element, element,
constraint, constraint,
@ -381,13 +375,7 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
.. ..
} => { } => {
let bounds = RectF::new(origin, bounds.size()); let bounds = RectF::new(origin, bounds.size());
let paint = element.paint( let paint = element.paint(bounds, visible_bounds, &mut layout, view, cx);
bounds,
visible_bounds,
&mut layout,
view,
&mut PaintContext::new(cx),
);
ElementState::PostPaint { ElementState::PostPaint {
element, element,
constraint, constraint,
@ -510,7 +498,7 @@ impl<V> AnyElement<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> Vector2F { ) -> Vector2F {
self.state.layout(constraint, view, cx) self.state.layout(constraint, view, cx)
} }
@ -520,7 +508,7 @@ impl<V> AnyElement<V> {
origin: Vector2F, origin: Vector2F,
visible_bounds: RectF, visible_bounds: RectF,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
self.state.paint(origin, visible_bounds, view, cx); self.state.paint(origin, visible_bounds, view, cx);
} }
@ -570,7 +558,7 @@ impl<V: 'static> Element<V> for AnyElement<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let size = self.layout(constraint, view, cx); let size = self.layout(constraint, view, cx);
(size, ()) (size, ())
@ -582,7 +570,7 @@ impl<V: 'static> Element<V> for AnyElement<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.paint(bounds.origin(), visible_bounds, view, cx); self.paint(bounds.origin(), visible_bounds, view, cx);
} }
@ -659,10 +647,7 @@ impl<V: View> AnyRootElement for RootElement<V> {
.view .view
.upgrade(cx) .upgrade(cx)
.ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?; .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
view.update(cx, |view, cx| { view.update(cx, |view, cx| Ok(self.element.layout(constraint, view, cx)))
let mut cx = LayoutContext::new(cx);
Ok(self.element.layout(constraint, view, &mut cx))
})
} }
fn paint( fn paint(
@ -677,8 +662,7 @@ impl<V: View> AnyRootElement for RootElement<V> {
.ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?; .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
let mut cx = PaintContext::new(cx); self.element.paint(origin, visible_bounds, view, cx);
self.element.paint(origin, visible_bounds, view, &mut cx);
Ok(()) Ok(())
}) })
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json, AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext, json, AnyElement, Element, SizeConstraint, ViewContext,
}; };
use json::ToJson; use json::ToJson;
@ -48,7 +48,7 @@ impl<V: 'static> Element<V> for Align<V> {
&mut self, &mut self,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut size = constraint.max; let mut size = constraint.max;
constraint.min = Vector2F::zero(); constraint.min = Vector2F::zero();
@ -68,7 +68,7 @@ impl<V: 'static> Element<V> for Align<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let my_center = bounds.size() / 2.; let my_center = bounds.size() / 2.;
let my_target = my_center + my_center * self.alignment; let my_target = my_center + my_center * self.alignment;

View file

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use super::Element; use super::Element;
use crate::{ use crate::{
json::{self, json}, json::{self, json},
PaintContext, ViewContext, ViewContext,
}; };
use json::ToJson; use json::ToJson;
use pathfinder_geometry::{ use pathfinder_geometry::{
@ -15,7 +15,7 @@ pub struct Canvas<V, F>(F, PhantomData<V>);
impl<V, F> Canvas<V, F> impl<V, F> Canvas<V, F>
where where
F: FnMut(RectF, RectF, &mut V, &mut PaintContext<V>), F: FnMut(RectF, RectF, &mut V, &mut ViewContext<V>),
{ {
pub fn new(f: F) -> Self { pub fn new(f: F) -> Self {
Self(f, PhantomData) Self(f, PhantomData)
@ -24,7 +24,7 @@ where
impl<V: 'static, F> Element<V> for Canvas<V, F> impl<V: 'static, F> Element<V> for Canvas<V, F>
where where
F: 'static + FnMut(RectF, RectF, &mut V, &mut PaintContext<V>), F: 'static + FnMut(RectF, RectF, &mut V, &mut ViewContext<V>),
{ {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
@ -33,7 +33,7 @@ where
&mut self, &mut self,
constraint: crate::SizeConstraint, constraint: crate::SizeConstraint,
_: &mut V, _: &mut V,
_: &mut crate::LayoutContext<V>, _: &mut crate::ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let x = if constraint.max.x().is_finite() { let x = if constraint.max.x().is_finite() {
constraint.max.x() constraint.max.x()
@ -54,7 +54,7 @@ where
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.0(bounds, visible_bounds, view, cx) self.0(bounds, visible_bounds, view, cx)
} }

View file

@ -3,7 +3,7 @@ use std::ops::Range;
use pathfinder_geometry::{rect::RectF, vector::Vector2F}; use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use serde_json::json; use serde_json::json;
use crate::{json, AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext}; use crate::{json, AnyElement, Element, SizeConstraint, ViewContext};
pub struct Clipped<V> { pub struct Clipped<V> {
child: AnyElement<V>, child: AnyElement<V>,
@ -23,7 +23,7 @@ impl<V: 'static> Element<V> for Clipped<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
(self.child.layout(constraint, view, cx), ()) (self.child.layout(constraint, view, cx), ())
} }
@ -34,7 +34,7 @@ impl<V: 'static> Element<V> for Clipped<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
cx.scene().push_layer(Some(bounds)); cx.scene().push_layer(Some(bounds));
let state = self.child.paint(bounds.origin(), visible_bounds, view, cx); let state = self.child.paint(bounds.origin(), visible_bounds, view, cx);

View file

@ -2,7 +2,7 @@ use std::{any::Any, marker::PhantomData};
use pathfinder_geometry::{rect::RectF, vector::Vector2F}; use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use crate::{AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext}; use crate::{AnyElement, Element, SizeConstraint, ViewContext};
use super::Empty; use super::Empty;
@ -282,14 +282,14 @@ impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdap
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
if self.element.is_none() { if self.element.is_none() {
let element = self let element = self
.component .component
.take() .take()
.expect("Component can only be rendered once") .expect("Component can only be rendered once")
.render(view, cx.view_context()); .render(view, cx);
self.element = Some(element); self.element = Some(element);
} }
let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx); let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
@ -302,7 +302,7 @@ impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdap
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.element self.element
.as_mut() .as_mut()

View file

@ -5,7 +5,7 @@ use serde_json::json;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json, AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext, json, AnyElement, Element, SizeConstraint, ViewContext,
}; };
pub struct ConstrainedBox<V> { pub struct ConstrainedBox<V> {
@ -15,7 +15,7 @@ pub struct ConstrainedBox<V> {
pub enum Constraint<V> { pub enum Constraint<V> {
Static(SizeConstraint), Static(SizeConstraint),
Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint>), Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint>),
} }
impl<V> ToJson for Constraint<V> { impl<V> ToJson for Constraint<V> {
@ -37,8 +37,7 @@ impl<V: 'static> ConstrainedBox<V> {
pub fn dynamically( pub fn dynamically(
mut self, mut self,
constraint: impl 'static constraint: impl 'static + FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint,
+ FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint,
) -> Self { ) -> Self {
self.constraint = Constraint::Dynamic(Box::new(constraint)); self.constraint = Constraint::Dynamic(Box::new(constraint));
self self
@ -120,7 +119,7 @@ impl<V: 'static> ConstrainedBox<V> {
&mut self, &mut self,
input_constraint: SizeConstraint, input_constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> SizeConstraint { ) -> SizeConstraint {
match &mut self.constraint { match &mut self.constraint {
Constraint::Static(constraint) => *constraint, Constraint::Static(constraint) => *constraint,
@ -139,7 +138,7 @@ impl<V: 'static> Element<V> for ConstrainedBox<V> {
&mut self, &mut self,
mut parent_constraint: SizeConstraint, mut parent_constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let constraint = self.constraint(parent_constraint, view, cx); let constraint = self.constraint(parent_constraint, view, cx);
parent_constraint.min = parent_constraint.min.max(constraint.min); parent_constraint.min = parent_constraint.min.max(constraint.min);
@ -155,7 +154,7 @@ impl<V: 'static> Element<V> for ConstrainedBox<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
cx.scene().push_layer(Some(visible_bounds)); cx.scene().push_layer(Some(visible_bounds));
self.child.paint(bounds.origin(), visible_bounds, view, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);

View file

@ -10,7 +10,7 @@ use crate::{
json::ToJson, json::ToJson,
platform::CursorStyle, platform::CursorStyle,
scene::{self, CornerRadii, CursorRegion, Quad}, scene::{self, CornerRadii, CursorRegion, Quad},
AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext, AnyElement, Element, SizeConstraint, ViewContext,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
@ -371,7 +371,7 @@ impl<V: 'static> Element<V> for Container<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut size_buffer = self.margin_size() + self.padding_size(); let mut size_buffer = self.margin_size() + self.padding_size();
if !self.style.border.overlay { if !self.style.border.overlay {
@ -391,7 +391,7 @@ impl<V: 'static> Element<V> for Container<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let quad_bounds = RectF::from_points( let quad_bounds = RectF::from_points(
bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top), bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),

View file

@ -6,7 +6,7 @@ use crate::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::{json, ToJson}, json::{json, ToJson},
LayoutContext, PaintContext, ViewContext, ViewContext,
}; };
use crate::{Element, SizeConstraint}; use crate::{Element, SizeConstraint};
@ -34,7 +34,7 @@ impl<V: 'static> Element<V> for Empty {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut V, _: &mut V,
_: &mut LayoutContext<V>, _: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let x = if constraint.max.x().is_finite() && !self.collapsed { let x = if constraint.max.x().is_finite() && !self.collapsed {
constraint.max.x() constraint.max.x()
@ -56,7 +56,7 @@ impl<V: 'static> Element<V> for Empty {
_: RectF, _: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
_: &mut V, _: &mut V,
_: &mut PaintContext<V>, _: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
} }

View file

@ -2,7 +2,7 @@ use std::ops::Range;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json, AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext, json, AnyElement, Element, SizeConstraint, ViewContext,
}; };
use serde_json::json; use serde_json::json;
@ -42,7 +42,7 @@ impl<V: 'static> Element<V> for Expanded<V> {
&mut self, &mut self,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
if self.full_width { if self.full_width {
constraint.min.set_x(constraint.max.x()); constraint.min.set_x(constraint.max.x());
@ -60,7 +60,7 @@ impl<V: 'static> Element<V> for Expanded<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.child.paint(bounds.origin(), visible_bounds, view, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);
} }

View file

@ -2,8 +2,7 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
use crate::{ use crate::{
json::{self, ToJson, Value}, json::{self, ToJson, Value},
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SizeConstraint, AnyElement, Axis, Element, ElementStateHandle, SizeConstraint, Vector2FExt, ViewContext,
Vector2FExt, ViewContext,
}; };
use pathfinder_geometry::{ use pathfinder_geometry::{
rect::RectF, rect::RectF,
@ -85,7 +84,7 @@ impl<V: 'static> Flex<V> {
remaining_flex: &mut f32, remaining_flex: &mut f32,
cross_axis_max: &mut f32, cross_axis_max: &mut f32,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) { ) {
let cross_axis = self.axis.invert(); let cross_axis = self.axis.invert();
for child in self.children.iter_mut() { for child in self.children.iter_mut() {
@ -136,7 +135,7 @@ impl<V: 'static> Element<V> for Flex<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut total_flex = None; let mut total_flex = None;
let mut fixed_space = self.children.len().saturating_sub(1) as f32 * self.spacing; let mut fixed_space = self.children.len().saturating_sub(1) as f32 * self.spacing;
@ -225,7 +224,7 @@ impl<V: 'static> Element<V> for Flex<V> {
} }
if let Some(scroll_state) = self.scroll_state.as_ref() { if let Some(scroll_state) = self.scroll_state.as_ref() {
scroll_state.0.update(cx.view_context(), |scroll_state, _| { scroll_state.0.update(cx, |scroll_state, _| {
if let Some(scroll_to) = scroll_state.scroll_to.take() { if let Some(scroll_to) = scroll_state.scroll_to.take() {
let visible_start = scroll_state.scroll_position.get(); let visible_start = scroll_state.scroll_position.get();
let visible_end = visible_start + size.along(self.axis); let visible_end = visible_start + size.along(self.axis);
@ -264,7 +263,7 @@ impl<V: 'static> Element<V> for Flex<V> {
visible_bounds: RectF, visible_bounds: RectF,
remaining_space: &mut Self::LayoutState, remaining_space: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
@ -442,7 +441,7 @@ impl<V: 'static> Element<V> for FlexItem<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, view, cx); let size = self.child.layout(constraint, view, cx);
(size, ()) (size, ())
@ -454,7 +453,7 @@ impl<V: 'static> Element<V> for FlexItem<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.child.paint(bounds.origin(), visible_bounds, view, cx) self.child.paint(bounds.origin(), visible_bounds, view, cx)
} }

View file

@ -3,7 +3,7 @@ use std::ops::Range;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json::json, json::json,
AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext, AnyElement, Element, SizeConstraint, ViewContext,
}; };
pub struct Hook<V> { pub struct Hook<V> {
@ -36,7 +36,7 @@ impl<V: 'static> Element<V> for Hook<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, view, cx); let size = self.child.layout(constraint, view, cx);
if let Some(handler) = self.after_layout.as_mut() { if let Some(handler) = self.after_layout.as_mut() {
@ -51,7 +51,7 @@ impl<V: 'static> Element<V> for Hook<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
self.child.paint(bounds.origin(), visible_bounds, view, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);
} }

View file

@ -5,7 +5,7 @@ use crate::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::{json, ToJson}, json::{json, ToJson},
scene, Element, ImageData, LayoutContext, PaintContext, SizeConstraint, ViewContext, scene, Element, ImageData, SizeConstraint, ViewContext,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
@ -64,7 +64,7 @@ impl<V: 'static> Element<V> for Image {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut V, _: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let data = match &self.source { let data = match &self.source {
ImageSource::Path(path) => match cx.asset_cache.png(path) { ImageSource::Path(path) => match cx.asset_cache.png(path) {
@ -95,7 +95,7 @@ impl<V: 'static> Element<V> for Image {
_: RectF, _: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
_: &mut V, _: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
if let Some(data) = layout { if let Some(data) = layout {
cx.scene().push_image(scene::Image { cx.scene().push_image(scene::Image {

View file

@ -39,7 +39,7 @@ impl<V: 'static> Element<V> for KeystrokeLabel {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, AnyElement<V>) { ) -> (Vector2F, AnyElement<V>) {
let mut element = if let Some(keystrokes) = let mut element = if let Some(keystrokes) =
cx.keystrokes_for_action(self.view_id, self.action.as_ref()) cx.keystrokes_for_action(self.view_id, self.action.as_ref())
@ -65,7 +65,7 @@ impl<V: 'static> Element<V> for KeystrokeLabel {
visible_bounds: RectF, visible_bounds: RectF,
element: &mut AnyElement<V>, element: &mut AnyElement<V>,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
element.paint(bounds.origin(), visible_bounds, view, cx); element.paint(bounds.origin(), visible_bounds, view, cx);
} }

View file

@ -8,7 +8,7 @@ use crate::{
}, },
json::{ToJson, Value}, json::{ToJson, Value},
text_layout::{Line, RunStyle}, text_layout::{Line, RunStyle},
Element, LayoutContext, PaintContext, SizeConstraint, ViewContext, Element, SizeConstraint, ViewContext,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
@ -136,7 +136,7 @@ impl<V: 'static> Element<V> for Label {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut V, _: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let runs = self.compute_runs(); let runs = self.compute_runs();
let line = cx.text_layout_cache().layout_str( let line = cx.text_layout_cache().layout_str(
@ -162,7 +162,7 @@ impl<V: 'static> Element<V> for Label {
visible_bounds: RectF, visible_bounds: RectF,
line: &mut Self::LayoutState, line: &mut Self::LayoutState,
_: &mut V, _: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
line.paint(bounds.origin(), visible_bounds, bounds.size().y(), cx) line.paint(bounds.origin(), visible_bounds, bounds.size().y(), cx)

View file

@ -4,7 +4,7 @@ use crate::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::json, json::json,
AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SizeConstraint, ViewContext, AnyElement, Element, MouseRegion, SizeConstraint, ViewContext,
}; };
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc}; use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
use sum_tree::{Bias, SumTree}; use sum_tree::{Bias, SumTree};
@ -99,7 +99,7 @@ impl<V: 'static> Element<V> for List<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let state = &mut *self.state.0.borrow_mut(); let state = &mut *self.state.0.borrow_mut();
let size = constraint.max; let size = constraint.max;
@ -253,7 +253,7 @@ impl<V: 'static> Element<V> for List<V> {
visible_bounds: RectF, visible_bounds: RectF,
scroll_top: &mut ListOffset, scroll_top: &mut ListOffset,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
cx.scene().push_layer(Some(visible_bounds)); cx.scene().push_layer(Some(visible_bounds));
@ -449,7 +449,7 @@ impl<V: 'static> StateInner<V> {
existing_element: Option<&ListItem<V>>, existing_element: Option<&ListItem<V>>,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> Option<Rc<RefCell<AnyElement<V>>>> { ) -> Option<Rc<RefCell<AnyElement<V>>>> {
if let Some(ListItem::Rendered(element)) = existing_element { if let Some(ListItem::Rendered(element)) = existing_element {
Some(element.clone()) Some(element.clone())
@ -643,7 +643,7 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{elements::Empty, geometry::vector::vec2f, Entity, PaintContext}; use crate::{elements::Empty, geometry::vector::vec2f, Entity};
use rand::prelude::*; use rand::prelude::*;
use std::env; use std::env;
@ -662,8 +662,7 @@ mod tests {
}); });
let mut list = List::new(state.clone()); let mut list = List::new(state.clone());
let mut layout_cx = LayoutContext::new(cx); let (size, _) = list.layout(constraint, &mut view, cx);
let (size, _) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(size, vec2f(100., 40.)); assert_eq!(size, vec2f(100., 40.));
assert_eq!( assert_eq!(
state.0.borrow().items.summary().clone(), state.0.borrow().items.summary().clone(),
@ -687,8 +686,7 @@ mod tests {
cx, cx,
); );
let mut layout_cx = LayoutContext::new(cx); let (_, logical_scroll_top) = list.layout(constraint, &mut view, cx);
let (_, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!( assert_eq!(
logical_scroll_top, logical_scroll_top,
ListOffset { ListOffset {
@ -712,8 +710,7 @@ mod tests {
} }
); );
let mut layout_cx = LayoutContext::new(cx); let (size, logical_scroll_top) = list.layout(constraint, &mut view, cx);
let (size, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(size, vec2f(100., 40.)); assert_eq!(size, vec2f(100., 40.));
assert_eq!( assert_eq!(
state.0.borrow().items.summary().clone(), state.0.borrow().items.summary().clone(),
@ -831,11 +828,10 @@ mod tests {
let mut list = List::new(state.clone()); let mut list = List::new(state.clone());
let window_size = vec2f(width, height); let window_size = vec2f(width, height);
let mut layout_cx = LayoutContext::new(cx);
let (size, logical_scroll_top) = list.layout( let (size, logical_scroll_top) = list.layout(
SizeConstraint::new(vec2f(0., 0.), window_size), SizeConstraint::new(vec2f(0., 0.), window_size),
&mut view, &mut view,
&mut layout_cx, cx,
); );
assert_eq!(size, window_size); assert_eq!(size, window_size);
last_logical_scroll_top = Some(logical_scroll_top); last_logical_scroll_top = Some(logical_scroll_top);
@ -948,12 +944,12 @@ mod tests {
&mut self, &mut self,
_: SizeConstraint, _: SizeConstraint,
_: &mut V, _: &mut V,
_: &mut LayoutContext<V>, _: &mut ViewContext<V>,
) -> (Vector2F, ()) { ) -> (Vector2F, ()) {
(self.size, ()) (self.size, ())
} }
fn paint(&mut self, _: RectF, _: RectF, _: &mut (), _: &mut V, _: &mut PaintContext<V>) { fn paint(&mut self, _: RectF, _: RectF, _: &mut (), _: &mut V, _: &mut ViewContext<V>) {
unimplemented!() unimplemented!()
} }

View file

@ -10,8 +10,8 @@ use crate::{
CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag, CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
}, },
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext, AnyElement, Element, EventContext, MouseRegion, MouseState, SizeConstraint, TypeTag,
SizeConstraint, TypeTag, ViewContext, ViewContext,
}; };
use serde_json::json; use serde_json::json;
use std::ops::Range; use std::ops::Range;
@ -270,7 +270,7 @@ impl<V: 'static> Element<V> for MouseEventHandler<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
(self.child.layout(constraint, view, cx), ()) (self.child.layout(constraint, view, cx), ())
} }
@ -281,13 +281,13 @@ impl<V: 'static> Element<V> for MouseEventHandler<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
if self.above { if self.above {
self.child.paint(bounds.origin(), visible_bounds, view, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);
cx.scene().push_layer(None); cx.paint_layer(None, |cx| {
self.paint_regions(bounds, visible_bounds, cx); self.paint_regions(bounds, visible_bounds, cx);
cx.scene().pop_layer(); });
} else { } else {
self.paint_regions(bounds, visible_bounds, cx); self.paint_regions(bounds, visible_bounds, cx);
self.child.paint(bounds.origin(), visible_bounds, view, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);

View file

@ -3,8 +3,7 @@ use std::ops::Range;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json::ToJson, json::ToJson,
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SizeConstraint, AnyElement, Axis, Element, MouseRegion, SizeConstraint, ViewContext,
ViewContext,
}; };
use serde_json::json; use serde_json::json;
@ -125,7 +124,7 @@ impl<V: 'static> Element<V> for Overlay<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let constraint = if self.anchor_position.is_some() { let constraint = if self.anchor_position.is_some() {
SizeConstraint::new(Vector2F::zero(), cx.window_size()) SizeConstraint::new(Vector2F::zero(), cx.window_size())
@ -142,7 +141,7 @@ impl<V: 'static> Element<V> for Overlay<V> {
_: RectF, _: RectF,
size: &mut Self::LayoutState, size: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
let (anchor_position, mut bounds) = match self.position_mode { let (anchor_position, mut bounds) = match self.position_mode {
OverlayPositionMode::Window => { OverlayPositionMode::Window => {

View file

@ -7,8 +7,7 @@ use serde_json::json;
use crate::{ use crate::{
geometry::rect::RectF, geometry::rect::RectF,
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, MouseButton},
AnyElement, AppContext, Axis, Element, LayoutContext, MouseRegion, PaintContext, AnyElement, AppContext, Axis, Element, MouseRegion, SizeConstraint, TypeTag, View, ViewContext,
SizeConstraint, TypeTag, View, ViewContext,
}; };
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -105,7 +104,7 @@ impl<V: 'static> Element<V> for Resizable<V> {
&mut self, &mut self,
constraint: crate::SizeConstraint, constraint: crate::SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
(self.child.layout(constraint, view, cx), constraint) (self.child.layout(constraint, view, cx), constraint)
} }
@ -116,7 +115,7 @@ impl<V: 'static> Element<V> for Resizable<V> {
visible_bounds: pathfinder_geometry::rect::RectF, visible_bounds: pathfinder_geometry::rect::RectF,
constraint: &mut SizeConstraint, constraint: &mut SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
cx.scene().push_stacking_context(None, None); cx.scene().push_stacking_context(None, None);
@ -241,7 +240,7 @@ impl<V: View, P: 'static> Element<V> for BoundsProvider<V, P> {
&mut self, &mut self,
constraint: crate::SizeConstraint, constraint: crate::SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut crate::LayoutContext<V>, cx: &mut crate::ViewContext<V>,
) -> (pathfinder_geometry::vector::Vector2F, Self::LayoutState) { ) -> (pathfinder_geometry::vector::Vector2F, Self::LayoutState) {
(self.child.layout(constraint, view, cx), ()) (self.child.layout(constraint, view, cx), ())
} }
@ -252,7 +251,7 @@ impl<V: View, P: 'static> Element<V> for BoundsProvider<V, P> {
visible_bounds: pathfinder_geometry::rect::RectF, visible_bounds: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut crate::PaintContext<V>, cx: &mut crate::ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
cx.update_default_global::<ProviderMap, _, _>(|map, _| { cx.update_default_global::<ProviderMap, _, _>(|map, _| {
map.0.insert(TypeTag::new::<P>(), (bounds, visible_bounds)); map.0.insert(TypeTag::new::<P>(), (bounds, visible_bounds));

View file

@ -3,7 +3,7 @@ use std::ops::Range;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json::{self, json, ToJson}, json::{self, json, ToJson},
AnyElement, Element, LayoutContext, PaintContext, SizeConstraint, ViewContext, AnyElement, Element, SizeConstraint, ViewContext,
}; };
/// Element which renders it's children in a stack on top of each other. /// Element which renders it's children in a stack on top of each other.
@ -34,7 +34,7 @@ impl<V: 'static> Element<V> for Stack<V> {
&mut self, &mut self,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut size = constraint.min; let mut size = constraint.min;
let mut children = self.children.iter_mut(); let mut children = self.children.iter_mut();
@ -56,7 +56,7 @@ impl<V: 'static> Element<V> for Stack<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
for child in &mut self.children { for child in &mut self.children {
cx.scene().push_layer(None); cx.scene().push_layer(None);

View file

@ -1,13 +1,12 @@
use super::constrain_size_preserving_aspect_ratio; use super::constrain_size_preserving_aspect_ratio;
use crate::json::ToJson; use crate::json::ToJson;
use crate::PaintContext;
use crate::{ use crate::{
color::Color, color::Color,
geometry::{ geometry::{
rect::RectF, rect::RectF,
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
scene, Element, LayoutContext, SizeConstraint, ViewContext, scene, Element, SizeConstraint, ViewContext,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde_derive::Deserialize; use serde_derive::Deserialize;
@ -49,7 +48,7 @@ impl<V: 'static> Element<V> for Svg {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut V, _: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
match cx.asset_cache.svg(&self.path) { match cx.asset_cache.svg(&self.path) {
Ok(tree) => { Ok(tree) => {
@ -73,7 +72,7 @@ impl<V: 'static> Element<V> for Svg {
_visible_bounds: RectF, _visible_bounds: RectF,
svg: &mut Self::LayoutState, svg: &mut Self::LayoutState,
_: &mut V, _: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
if let Some(svg) = svg.clone() { if let Some(svg) = svg.clone() {
cx.scene().push_icon(scene::Icon { cx.scene().push_icon(scene::Icon {

View file

@ -7,8 +7,7 @@ use crate::{
}, },
json::{ToJson, Value}, json::{ToJson, Value},
text_layout::{Line, RunStyle, ShapedBoundary}, text_layout::{Line, RunStyle, ShapedBoundary},
Element, FontCache, LayoutContext, PaintContext, SizeConstraint, TextLayoutCache, ViewContext, Element, FontCache, SizeConstraint, TextLayoutCache, ViewContext, WindowContext,
WindowContext,
}; };
use log::warn; use log::warn;
use serde_json::json; use serde_json::json;
@ -78,7 +77,7 @@ impl<V: 'static> Element<V> for Text {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut V, _: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
// Convert the string and highlight ranges into an iterator of highlighted chunks. // Convert the string and highlight ranges into an iterator of highlighted chunks.
@ -170,7 +169,7 @@ impl<V: 'static> Element<V> for Text {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
_: &mut V, _: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let mut origin = bounds.origin(); let mut origin = bounds.origin();
let empty = Vec::new(); let empty = Vec::new();
@ -409,11 +408,10 @@ mod tests {
let mut view = TestView; let mut view = TestView;
fonts::with_font_cache(cx.font_cache().clone(), || { fonts::with_font_cache(cx.font_cache().clone(), || {
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true); let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
let mut layout_cx = LayoutContext::new(cx);
let (_, state) = text.layout( let (_, state) = text.layout(
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)), SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
&mut view, &mut view,
&mut layout_cx, cx,
); );
assert_eq!(state.shaped_lines.len(), 2); assert_eq!(state.shaped_lines.len(), 2);
assert_eq!(state.wrap_boundaries.len(), 2); assert_eq!(state.wrap_boundaries.len(), 2);

View file

@ -6,8 +6,7 @@ use crate::{
fonts::TextStyle, fonts::TextStyle,
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json::json, json::json,
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SizeConstraint, Task, TypeTag, Action, Axis, ElementStateHandle, SizeConstraint, Task, TypeTag, ViewContext,
ViewContext,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
@ -189,7 +188,7 @@ impl<V: 'static> Element<V> for Tooltip<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, view, cx); let size = self.child.layout(constraint, view, cx);
if let Some(tooltip) = self.tooltip.as_mut() { if let Some(tooltip) = self.tooltip.as_mut() {
@ -208,7 +207,7 @@ impl<V: 'static> Element<V> for Tooltip<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
self.child.paint(bounds.origin(), visible_bounds, view, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);
if let Some(tooltip) = self.tooltip.as_mut() { if let Some(tooltip) = self.tooltip.as_mut() {

View file

@ -6,7 +6,7 @@ use crate::{
}, },
json::{self, json}, json::{self, json},
platform::ScrollWheelEvent, platform::ScrollWheelEvent,
AnyElement, LayoutContext, MouseRegion, PaintContext, ViewContext, AnyElement, MouseRegion, ViewContext,
}; };
use json::ToJson; use json::ToJson;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@ -158,7 +158,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
if constraint.max.y().is_infinite() { if constraint.max.y().is_infinite() {
unimplemented!( unimplemented!(
@ -276,7 +276,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();

View file

@ -261,6 +261,8 @@ impl SceneBuilder {
self.stacking_contexts.push(StackingContext::new(None, 0)); self.stacking_contexts.push(StackingContext::new(None, 0));
self.active_stacking_context_stack.clear(); self.active_stacking_context_stack.clear();
self.active_stacking_context_stack.push(0); self.active_stacking_context_stack.push(0);
#[cfg(debug_assertions)]
self.mouse_region_ids.clear();
} }
pub fn build(&mut self, scale_factor: f32) -> Scene { pub fn build(&mut self, scale_factor: f32) -> Scene {

View file

@ -1,4 +1,4 @@
use crate::{paint_context::PaintContext, ViewContext}; use crate::ViewContext;
use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId}; use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
use util::ResultExt; use util::ResultExt;
@ -13,7 +13,7 @@ impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
&mut self, &mut self,
constraint: gpui::SizeConstraint, constraint: gpui::SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut gpui::LayoutContext<V>, cx: &mut gpui::ViewContext<V>,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
cx.push_layout_engine(LayoutEngine::new()); cx.push_layout_engine(LayoutEngine::new());
@ -40,13 +40,13 @@ impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
_visible_bounds: RectF, _visible_bounds: RectF,
layout_data: &mut Option<(LayoutEngine, LayoutId)>, layout_data: &mut Option<(LayoutEngine, LayoutId)>,
view: &mut V, view: &mut V,
legacy_cx: &mut gpui::PaintContext<V>, cx: &mut gpui::ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let (layout_engine, layout_id) = layout_data.take().unwrap(); let (layout_engine, layout_id) = layout_data.take().unwrap();
legacy_cx.push_layout_engine(layout_engine); cx.push_layout_engine(layout_engine);
let mut cx = PaintContext::new(legacy_cx); self.0
self.0.paint(view, bounds.origin(), &mut cx); .paint(view, bounds.origin(), &mut ViewContext::new(cx));
*layout_data = legacy_cx.pop_layout_engine().zip(Some(layout_id)); *layout_data = cx.pop_layout_engine().zip(Some(layout_id));
debug_assert!(layout_data.is_some()); debug_assert!(layout_data.is_some());
} }

View file

@ -1,4 +1,3 @@
pub use crate::paint_context::PaintContext;
pub use crate::ViewContext; pub use crate::ViewContext;
use anyhow::Result; use anyhow::Result;
use gpui::geometry::vector::Vector2F; use gpui::geometry::vector::Vector2F;
@ -22,7 +21,7 @@ pub trait Element<V: 'static>: 'static + IntoElement<V> {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &Layout, layout: &Layout,
state: &mut Self::PaintState, state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) where ) where
Self: Sized; Self: Sized;
@ -40,7 +39,7 @@ pub trait Element<V: 'static>: 'static + IntoElement<V> {
/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>. /// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
trait AnyStatefulElement<V> { trait AnyStatefulElement<V> {
fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId>; fn layout(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> Result<LayoutId>;
fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>); fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>);
} }
/// A wrapper around an element that stores its layout state. /// A wrapper around an element that stores its layout state.
@ -105,7 +104,7 @@ impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
result result
} }
fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) { fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>) {
self.phase = match std::mem::take(&mut self.phase) { self.phase = match std::mem::take(&mut self.phase) {
ElementPhase::PostLayout { ElementPhase::PostLayout {
layout_id, layout_id,
@ -149,7 +148,7 @@ impl<V> AnyElement<V> {
self.0.layout(view, cx) self.0.layout(view, cx)
} }
pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) { pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut ViewContext<V>) {
self.0.paint(view, parent_origin, cx) self.0.paint(view, parent_origin, cx)
} }
} }

View file

@ -3,7 +3,6 @@ use std::{cell::Cell, rc::Rc};
use crate::{ use crate::{
element::{AnyElement, Element, IntoElement, Layout, ParentElement}, element::{AnyElement, Element, IntoElement, Layout, ParentElement},
hsla, hsla,
paint_context::PaintContext,
style::{CornerRadii, Overflow, Style, StyleHelpers, Styleable}, style::{CornerRadii, Overflow, Style, StyleHelpers, Styleable},
InteractionHandlers, Interactive, ViewContext, InteractionHandlers, Interactive, ViewContext,
}; };
@ -69,7 +68,7 @@ impl<V: 'static> Element<V> for Div<V> {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &Layout, layout: &Layout,
child_layouts: &mut Vec<LayoutId>, child_layouts: &mut Vec<LayoutId>,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) where ) where
Self: Sized, Self: Sized,
{ {
@ -167,7 +166,7 @@ impl<V: 'static> Div<V> {
bounds: RectF, bounds: RectF,
overflow: Point<Overflow>, overflow: Point<Overflow>,
child_layout_ids: &[LayoutId], child_layout_ids: &[LayoutId],
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
if overflow.y == Overflow::Scroll || overflow.x == Overflow::Scroll { if overflow.y == Overflow::Scroll || overflow.x == Overflow::Scroll {
let mut scroll_max = Vector2F::zero(); let mut scroll_max = Vector2F::zero();
@ -214,7 +213,7 @@ impl<V: 'static> Div<V> {
} }
} }
fn paint_inspector(&self, parent_origin: Vector2F, layout: &Layout, cx: &mut PaintContext<V>) { fn paint_inspector(&self, parent_origin: Vector2F, layout: &Layout, cx: &mut ViewContext<V>) {
let style = self.styles.merged(); let style = self.styles.merged();
let bounds = layout.bounds + parent_origin; let bounds = layout.bounds + parent_origin;

View file

@ -1,7 +1,6 @@
use crate::{ use crate::{
element::{AnyElement, Element, IntoElement, Layout, ParentElement}, element::{AnyElement, Element, IntoElement, Layout, ParentElement},
interactive::{InteractionHandlers, Interactive}, interactive::{InteractionHandlers, Interactive},
paint_context::PaintContext,
style::{Style, StyleHelpers, Styleable}, style::{Style, StyleHelpers, Styleable},
ViewContext, ViewContext,
}; };
@ -59,7 +58,7 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &Layout, layout: &Layout,
paint_state: &mut Self::PaintState, paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) where ) where
Self: Sized, Self: Sized,
{ {

View file

@ -53,7 +53,7 @@ impl<V: 'static> Element<V> for Img {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &gpui::Layout, layout: &gpui::Layout,
_: &mut Self::PaintState, _: &mut Self::PaintState,
cx: &mut crate::paint_context::PaintContext<V>, cx: &mut crate::ViewContext<V>,
) where ) where
Self: Sized, Self: Sized,
{ {

View file

@ -1,7 +1,6 @@
use crate::{ use crate::{
element::{AnyElement, Element, IntoElement, Layout, ParentElement}, element::{AnyElement, Element, IntoElement, Layout, ParentElement},
interactive::{InteractionHandlers, Interactive}, interactive::{InteractionHandlers, Interactive},
paint_context::PaintContext,
style::{Style, StyleHelpers, Styleable}, style::{Style, StyleHelpers, Styleable},
ViewContext, ViewContext,
}; };
@ -59,7 +58,7 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &Layout, layout: &Layout,
paint_state: &mut Self::PaintState, paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) where ) where
Self: Sized, Self: Sized,
{ {

View file

@ -49,7 +49,7 @@ impl<V: 'static> Element<V> for Svg {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &Layout, layout: &Layout,
_: &mut Self::PaintState, _: &mut Self::PaintState,
cx: &mut crate::paint_context::PaintContext<V>, cx: &mut crate::ViewContext<V>,
) where ) where
Self: Sized, Self: Sized,
{ {

View file

@ -1,6 +1,5 @@
use crate::{ use crate::{
element::{Element, IntoElement, Layout}, element::{Element, IntoElement, Layout},
paint_context::PaintContext,
ViewContext, ViewContext,
}; };
use anyhow::Result; use anyhow::Result;
@ -71,7 +70,7 @@ impl<V: 'static> Element<V> for Text {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &Layout, layout: &Layout,
paint_state: &mut Self::PaintState, paint_state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) { ) {
let bounds = layout.bounds + parent_origin; let bounds = layout.bounds + parent_origin;

View file

@ -3,7 +3,6 @@ pub mod color;
pub mod element; pub mod element;
pub mod elements; pub mod elements;
pub mod interactive; pub mod interactive;
pub mod paint_context;
pub mod style; pub mod style;
pub mod view; pub mod view;
pub mod view_context; pub mod view_context;

View file

@ -6,7 +6,7 @@ use gpui::{
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::Cell, rc::Rc}; use std::{cell::Cell, rc::Rc};
use crate::element::PaintContext; use crate::ViewContext;
pub trait Interactive<V: 'static> { pub trait Interactive<V: 'static> {
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>; fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
@ -121,7 +121,7 @@ pub struct InteractionHandlers<V: 'static> {
} }
impl<V: 'static> InteractionHandlers<V> { impl<V: 'static> InteractionHandlers<V> {
pub fn paint(&self, order: u32, bounds: RectF, cx: &mut PaintContext<V>) { pub fn paint(&self, order: u32, bounds: RectF, cx: &mut ViewContext<V>) {
for handler in self.mouse_down.iter().cloned() { for handler in self.mouse_down.iter().cloned() {
cx.on_event(order, move |view, event: &MouseButtonEvent, cx| { cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
if event.is_down && bounds.contains_point(event.position) { if event.is_down && bounds.contains_point(event.position) {

View file

@ -1,50 +0,0 @@
use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut};
pub use gpui::taffy::tree::NodeId;
use gpui::{
scene::EventHandler, EventContext, Layout, LayoutId, PaintContext as LegacyPaintContext,
};
use std::{any::TypeId, rc::Rc};
#[derive(Deref, DerefMut)]
pub struct PaintContext<'a, 'b, 'c, 'd, V> {
#[deref]
#[deref_mut]
pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
}
impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
pub fn new(legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>) -> Self {
Self { legacy_cx }
}
pub fn on_event<E: 'static>(
&mut self,
order: u32,
handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
) {
let view = self.weak_handle();
self.scene().event_handlers.push(EventHandler {
order,
handler: Rc::new(move |event, window_cx| {
if let Some(view) = view.upgrade(window_cx) {
view.update(window_cx, |view, view_cx| {
let mut event_cx = EventContext::new(view_cx);
handler(view, event.downcast_ref().unwrap(), &mut event_cx);
event_cx.bubble
})
} else {
true
}
}),
event_type: TypeId::of::<E>(),
})
}
pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
self.layout_engine()
.ok_or_else(|| anyhow!("no layout engine present"))?
.computed_layout(layout_id)
}
}

View file

@ -2,7 +2,7 @@ use crate::{
color::Hsla, color::Hsla,
elements::hoverable::{hoverable, Hoverable}, elements::hoverable::{hoverable, Hoverable},
elements::pressable::{pressable, Pressable}, elements::pressable::{pressable, Pressable},
paint_context::PaintContext, ViewContext,
}; };
pub use fonts::Style as FontStyle; pub use fonts::Style as FontStyle;
pub use fonts::Weight as FontWeight; pub use fonts::Weight as FontWeight;
@ -164,7 +164,7 @@ impl Style {
} }
/// Paints the background of an element styled with this style. /// Paints the background of an element styled with this style.
pub fn paint_background<V: 'static>(&self, bounds: RectF, cx: &mut PaintContext<V>) { pub fn paint_background<V: 'static>(&self, bounds: RectF, cx: &mut ViewContext<V>) {
let rem_size = cx.rem_size(); let rem_size = cx.rem_size();
if let Some(color) = self.fill.as_ref().and_then(Fill::color) { if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
cx.scene().push_quad(gpui::Quad { cx.scene().push_quad(gpui::Quad {
@ -177,7 +177,7 @@ impl Style {
} }
/// Paints the foreground of an element styled with this style. /// Paints the foreground of an element styled with this style.
pub fn paint_foreground<V: 'static>(&self, bounds: RectF, cx: &mut PaintContext<V>) { pub fn paint_foreground<V: 'static>(&self, bounds: RectF, cx: &mut ViewContext<V>) {
let rem_size = cx.rem_size(); let rem_size = cx.rem_size();
if let Some(color) = self.border_color { if let Some(color) = self.border_color {

View file

@ -1,7 +1,9 @@
use std::{any::TypeId, rc::Rc};
use crate::{element::LayoutId, style::Style}; use crate::{element::LayoutId, style::Style};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use gpui::{geometry::Size, MeasureParams}; use gpui::{geometry::Size, scene::EventHandler, EventContext, Layout, MeasureParams};
pub use gpui::{taffy::tree::NodeId, ViewContext as LegacyViewContext}; pub use gpui::{taffy::tree::NodeId, ViewContext as LegacyViewContext};
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
@ -44,4 +46,34 @@ impl<'a, 'b, 'c, V: 'static> ViewContext<'a, 'b, 'c, V> {
Ok(layout_id) Ok(layout_id)
} }
pub fn on_event<E: 'static>(
&mut self,
order: u32,
handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
) {
let view = self.weak_handle();
self.scene().event_handlers.push(EventHandler {
order,
handler: Rc::new(move |event, window_cx| {
if let Some(view) = view.upgrade(window_cx) {
view.update(window_cx, |view, view_cx| {
let mut event_cx = EventContext::new(view_cx);
handler(view, event.downcast_ref().unwrap(), &mut event_cx);
event_cx.bubble
})
} else {
true
}
}),
event_type: TypeId::of::<E>(),
})
}
pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
self.layout_engine()
.ok_or_else(|| anyhow!("no layout engine present"))?
.computed_layout(layout_id)
}
} }

View file

@ -80,7 +80,7 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
parent_origin: gpui2::Vector2F, parent_origin: gpui2::Vector2F,
_: &gpui2::element::Layout, _: &gpui2::element::Layout,
rendered_element: &mut Self::PaintState, rendered_element: &mut Self::PaintState,
cx: &mut gpui2::element::PaintContext<V>, cx: &mut gpui2::ViewContext<V>,
) { ) {
rendered_element.paint(view, parent_origin, cx); rendered_element.paint(view, parent_origin, cx);
} }

View file

@ -329,7 +329,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
&mut self, &mut self,
constraint: gpui::SizeConstraint, constraint: gpui::SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut gpui::LayoutContext<V>, cx: &mut gpui::ViewContext<V>,
) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) { ) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) {
let mut element = self.render(view, cx).into_any(); let mut element = self.render(view, cx).into_any();
let size = element.layout(constraint, view, cx); let size = element.layout(constraint, view, cx);
@ -342,7 +342,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
visible_bounds: gpui::geometry::rect::RectF, visible_bounds: gpui::geometry::rect::RectF,
element: &mut gpui::elements::AnyElement<V>, element: &mut gpui::elements::AnyElement<V>,
view: &mut V, view: &mut V,
cx: &mut gpui::PaintContext<V>, cx: &mut gpui::ViewContext<V>,
) { ) {
element.paint(bounds.origin(), visible_bounds, view, cx); element.paint(bounds.origin(), visible_bounds, view, cx);
} }

View file

@ -42,8 +42,8 @@
"repositoryURL": "https://github.com/apple/swift-protobuf.git", "repositoryURL": "https://github.com/apple/swift-protobuf.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "0af9125c4eae12a4973fb66574c53a54962a9e1e", "revision": "ce20dc083ee485524b802669890291c0d8090170",
"version": "1.21.0" "version": "1.22.1"
} }
} }
] ]

View file

@ -34,6 +34,7 @@ use std::{
ops::{Not, Range}, ops::{Not, Range},
path::PathBuf, path::PathBuf,
sync::Arc, sync::Arc,
time::{Duration, Instant},
}; };
use util::ResultExt as _; use util::ResultExt as _;
use workspace::{ use workspace::{
@ -130,6 +131,7 @@ pub struct ProjectSearchView {
struct SemanticState { struct SemanticState {
index_status: SemanticIndexStatus, index_status: SemanticIndexStatus,
maintain_rate_limit: Option<Task<()>>,
_subscription: Subscription, _subscription: Subscription,
} }
@ -319,11 +321,28 @@ impl View for ProjectSearchView {
let status = semantic.index_status; let status = semantic.index_status;
match status { match status {
SemanticIndexStatus::Indexed => Some("Indexing complete".to_string()), SemanticIndexStatus::Indexed => Some("Indexing complete".to_string()),
SemanticIndexStatus::Indexing { remaining_files } => { SemanticIndexStatus::Indexing {
remaining_files,
rate_limit_expiry,
} => {
if remaining_files == 0 { if remaining_files == 0 {
Some(format!("Indexing...")) Some(format!("Indexing..."))
} else { } else {
Some(format!("Remaining files to index: {}", remaining_files)) if let Some(rate_limit_expiry) = rate_limit_expiry {
let remaining_seconds =
rate_limit_expiry.duration_since(Instant::now());
if remaining_seconds > Duration::from_secs(0) {
Some(format!(
"Remaining files to index (rate limit resets in {}s): {}",
remaining_seconds.as_secs(),
remaining_files
))
} else {
Some(format!("Remaining files to index: {}", remaining_files))
}
} else {
Some(format!("Remaining files to index: {}", remaining_files))
}
} }
} }
SemanticIndexStatus::NotIndexed => None, SemanticIndexStatus::NotIndexed => None,
@ -651,9 +670,10 @@ impl ProjectSearchView {
self.semantic_state = Some(SemanticState { self.semantic_state = Some(SemanticState {
index_status: semantic_index.read(cx).status(&project), index_status: semantic_index.read(cx).status(&project),
maintain_rate_limit: None,
_subscription: cx.observe(&semantic_index, Self::semantic_index_changed), _subscription: cx.observe(&semantic_index, Self::semantic_index_changed),
}); });
cx.notify(); self.semantic_index_changed(semantic_index, cx);
} }
} }
@ -664,8 +684,25 @@ impl ProjectSearchView {
) { ) {
let project = self.model.read(cx).project.clone(); let project = self.model.read(cx).project.clone();
if let Some(semantic_state) = self.semantic_state.as_mut() { if let Some(semantic_state) = self.semantic_state.as_mut() {
semantic_state.index_status = semantic_index.read(cx).status(&project);
cx.notify(); cx.notify();
semantic_state.index_status = semantic_index.read(cx).status(&project);
if let SemanticIndexStatus::Indexing {
rate_limit_expiry: Some(_),
..
} = &semantic_state.index_status
{
if semantic_state.maintain_rate_limit.is_none() {
semantic_state.maintain_rate_limit =
Some(cx.spawn(|this, mut cx| async move {
loop {
cx.background().timer(Duration::from_secs(1)).await;
this.update(&mut cx, |_, cx| cx.notify()).log_err();
}
}));
return;
}
}
semantic_state.maintain_rate_limit = None;
} }
} }

View file

@ -7,13 +7,16 @@ use isahc::http::StatusCode;
use isahc::prelude::Configurable; use isahc::prelude::Configurable;
use isahc::{AsyncBody, Response}; use isahc::{AsyncBody, Response};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use parking_lot::Mutex;
use parse_duration::parse; use parse_duration::parse;
use postage::watch;
use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef}; use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef};
use rusqlite::ToSql; use rusqlite::ToSql;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::env; use std::env;
use std::ops::Add;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::{Duration, Instant};
use tiktoken_rs::{cl100k_base, CoreBPE}; use tiktoken_rs::{cl100k_base, CoreBPE};
use util::http::{HttpClient, Request}; use util::http::{HttpClient, Request};
@ -82,6 +85,8 @@ impl ToSql for Embedding {
pub struct OpenAIEmbeddings { pub struct OpenAIEmbeddings {
pub client: Arc<dyn HttpClient>, pub client: Arc<dyn HttpClient>,
pub executor: Arc<Background>, pub executor: Arc<Background>,
rate_limit_count_rx: watch::Receiver<Option<Instant>>,
rate_limit_count_tx: Arc<Mutex<watch::Sender<Option<Instant>>>>,
} }
#[derive(Serialize)] #[derive(Serialize)]
@ -114,12 +119,16 @@ pub trait EmbeddingProvider: Sync + Send {
async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>>; async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>>;
fn max_tokens_per_batch(&self) -> usize; fn max_tokens_per_batch(&self) -> usize;
fn truncate(&self, span: &str) -> (String, usize); fn truncate(&self, span: &str) -> (String, usize);
fn rate_limit_expiration(&self) -> Option<Instant>;
} }
pub struct DummyEmbeddings {} pub struct DummyEmbeddings {}
#[async_trait] #[async_trait]
impl EmbeddingProvider for DummyEmbeddings { impl EmbeddingProvider for DummyEmbeddings {
fn rate_limit_expiration(&self) -> Option<Instant> {
None
}
async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>> { async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>> {
// 1024 is the OpenAI Embeddings size for ada models. // 1024 is the OpenAI Embeddings size for ada models.
// the model we will likely be starting with. // the model we will likely be starting with.
@ -149,6 +158,50 @@ impl EmbeddingProvider for DummyEmbeddings {
const OPENAI_INPUT_LIMIT: usize = 8190; const OPENAI_INPUT_LIMIT: usize = 8190;
impl OpenAIEmbeddings { impl OpenAIEmbeddings {
pub fn new(client: Arc<dyn HttpClient>, executor: Arc<Background>) -> Self {
let (rate_limit_count_tx, rate_limit_count_rx) = watch::channel_with(None);
let rate_limit_count_tx = Arc::new(Mutex::new(rate_limit_count_tx));
OpenAIEmbeddings {
client,
executor,
rate_limit_count_rx,
rate_limit_count_tx,
}
}
fn resolve_rate_limit(&self) {
let reset_time = *self.rate_limit_count_tx.lock().borrow();
if let Some(reset_time) = reset_time {
if Instant::now() >= reset_time {
*self.rate_limit_count_tx.lock().borrow_mut() = None
}
}
log::trace!(
"resolving reset time: {:?}",
*self.rate_limit_count_tx.lock().borrow()
);
}
fn update_reset_time(&self, reset_time: Instant) {
let original_time = *self.rate_limit_count_tx.lock().borrow();
let updated_time = if let Some(original_time) = original_time {
if reset_time < original_time {
Some(reset_time)
} else {
Some(original_time)
}
} else {
Some(reset_time)
};
log::trace!("updating rate limit time: {:?}", updated_time);
*self.rate_limit_count_tx.lock().borrow_mut() = updated_time;
}
async fn send_request( async fn send_request(
&self, &self,
api_key: &str, api_key: &str,
@ -179,6 +232,9 @@ impl EmbeddingProvider for OpenAIEmbeddings {
50000 50000
} }
fn rate_limit_expiration(&self) -> Option<Instant> {
*self.rate_limit_count_rx.borrow()
}
fn truncate(&self, span: &str) -> (String, usize) { fn truncate(&self, span: &str) -> (String, usize) {
let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span); let mut tokens = OPENAI_BPE_TOKENIZER.encode_with_special_tokens(span);
let output = if tokens.len() > OPENAI_INPUT_LIMIT { let output = if tokens.len() > OPENAI_INPUT_LIMIT {
@ -203,6 +259,7 @@ impl EmbeddingProvider for OpenAIEmbeddings {
.ok_or_else(|| anyhow!("no api key"))?; .ok_or_else(|| anyhow!("no api key"))?;
let mut request_number = 0; let mut request_number = 0;
let mut rate_limiting = false;
let mut request_timeout: u64 = 15; let mut request_timeout: u64 = 15;
let mut response: Response<AsyncBody>; let mut response: Response<AsyncBody>;
while request_number < MAX_RETRIES { while request_number < MAX_RETRIES {
@ -229,6 +286,12 @@ impl EmbeddingProvider for OpenAIEmbeddings {
response.usage.total_tokens response.usage.total_tokens
); );
// If we complete a request successfully that was previously rate_limited
// resolve the rate limit
if rate_limiting {
self.resolve_rate_limit()
}
return Ok(response return Ok(response
.data .data
.into_iter() .into_iter()
@ -236,6 +299,7 @@ impl EmbeddingProvider for OpenAIEmbeddings {
.collect()); .collect());
} }
StatusCode::TOO_MANY_REQUESTS => { StatusCode::TOO_MANY_REQUESTS => {
rate_limiting = true;
let mut body = String::new(); let mut body = String::new();
response.body_mut().read_to_string(&mut body).await?; response.body_mut().read_to_string(&mut body).await?;
@ -254,6 +318,10 @@ impl EmbeddingProvider for OpenAIEmbeddings {
} }
}; };
// If we've previously rate limited, increment the duration but not the count
let reset_time = Instant::now().add(delay_duration);
self.update_reset_time(reset_time);
log::trace!( log::trace!(
"openai rate limiting: waiting {:?} until lifted", "openai rate limiting: waiting {:?} until lifted",
&delay_duration &delay_duration

View file

@ -91,10 +91,7 @@ pub fn init(
let semantic_index = SemanticIndex::new( let semantic_index = SemanticIndex::new(
fs, fs,
db_file_path, db_file_path,
Arc::new(OpenAIEmbeddings { Arc::new(OpenAIEmbeddings::new(http_client, cx.background())),
client: http_client,
executor: cx.background(),
}),
language_registry, language_registry,
cx.clone(), cx.clone(),
) )
@ -113,7 +110,10 @@ pub fn init(
pub enum SemanticIndexStatus { pub enum SemanticIndexStatus {
NotIndexed, NotIndexed,
Indexed, Indexed,
Indexing { remaining_files: usize }, Indexing {
remaining_files: usize,
rate_limit_expiry: Option<Instant>,
},
} }
pub struct SemanticIndex { pub struct SemanticIndex {
@ -293,6 +293,7 @@ impl SemanticIndex {
} else { } else {
SemanticIndexStatus::Indexing { SemanticIndexStatus::Indexing {
remaining_files: project_state.pending_file_count_rx.borrow().clone(), remaining_files: project_state.pending_file_count_rx.borrow().clone(),
rate_limit_expiry: self.embedding_provider.rate_limit_expiration(),
} }
} }
} else { } else {

View file

@ -21,7 +21,7 @@ use std::{
atomic::{self, AtomicUsize}, atomic::{self, AtomicUsize},
Arc, Arc,
}, },
time::SystemTime, time::{Instant, SystemTime},
}; };
use unindent::Unindent; use unindent::Unindent;
use util::RandomCharIter; use util::RandomCharIter;
@ -1275,6 +1275,10 @@ impl EmbeddingProvider for FakeEmbeddingProvider {
200 200
} }
fn rate_limit_expiration(&self) -> Option<Instant> {
None
}
async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>> { async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>> {
self.embedding_count self.embedding_count
.fetch_add(spans.len(), atomic::Ordering::SeqCst); .fetch_add(spans.len(), atomic::Ordering::SeqCst);

View file

@ -1,7 +1,6 @@
use gpui2::{ use gpui2::{
color::Hsla, color::Hsla, element::Element, serde_json, AppContext, IntoElement, Vector2F, ViewContext,
element::{Element, PaintContext}, WindowContext,
serde_json, AppContext, IntoElement, Vector2F, ViewContext, WindowContext,
}; };
use serde::{de::Visitor, Deserialize, Deserializer}; use serde::{de::Visitor, Deserialize, Deserializer};
use std::{collections::HashMap, fmt, marker::PhantomData}; use std::{collections::HashMap, fmt, marker::PhantomData};
@ -162,7 +161,7 @@ impl<V: 'static, E: Element<V>> Element<V> for Themed<V, E> {
parent_origin: Vector2F, parent_origin: Vector2F,
layout: &gpui2::Layout, layout: &gpui2::Layout,
state: &mut Self::PaintState, state: &mut Self::PaintState,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) where ) where
Self: Sized, Self: Sized,
{ {

View file

@ -10,9 +10,8 @@ use gpui::{
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, MouseButton},
serde_json::json, serde_json::json,
text_layout::{Line, RunStyle}, text_layout::{Line, RunStyle},
AnyElement, Element, EventContext, FontCache, LayoutContext, ModelContext, MouseRegion, AnyElement, Element, EventContext, FontCache, ModelContext, MouseRegion, Quad, SizeConstraint,
PaintContext, Quad, SizeConstraint, TextLayoutCache, ViewContext, WeakModelHandle, TextLayoutCache, ViewContext, WeakModelHandle, WindowContext,
WindowContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use language::CursorShape; use language::CursorShape;
@ -527,7 +526,7 @@ impl Element<TerminalView> for TerminalElement {
&mut self, &mut self,
constraint: gpui::SizeConstraint, constraint: gpui::SizeConstraint,
view: &mut TerminalView, view: &mut TerminalView,
cx: &mut LayoutContext<TerminalView>, cx: &mut ViewContext<TerminalView>,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
let settings = settings::get::<ThemeSettings>(cx); let settings = settings::get::<ThemeSettings>(cx);
let terminal_settings = settings::get::<TerminalSettings>(cx); let terminal_settings = settings::get::<TerminalSettings>(cx);
@ -734,7 +733,7 @@ impl Element<TerminalView> for TerminalElement {
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
view: &mut TerminalView, view: &mut TerminalView,
cx: &mut PaintContext<TerminalView>, cx: &mut ViewContext<TerminalView>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();

View file

@ -283,7 +283,12 @@ impl TerminalView {
pub fn deploy_context_menu(&mut self, position: Vector2F, cx: &mut ViewContext<Self>) { pub fn deploy_context_menu(&mut self, position: Vector2F, cx: &mut ViewContext<Self>) {
let menu_entries = vec![ let menu_entries = vec![
ContextMenuItem::action("Clear", Clear), ContextMenuItem::action("Clear", Clear),
ContextMenuItem::action("Close", pane::CloseActiveItem), ContextMenuItem::action(
"Close",
pane::CloseActiveItem {
save_behavior: None,
},
),
]; ];
self.context_menu.update(cx, |menu, cx| { self.context_menu.update(cx, |menu, cx| {

View file

@ -474,8 +474,14 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
for item_event in T::to_item_events(event).into_iter() { for item_event in T::to_item_events(event).into_iter() {
match item_event { match item_event {
ItemEvent::CloseItem => { ItemEvent::CloseItem => {
pane.update(cx, |pane, cx| pane.close_item_by_id(item.id(), cx)) pane.update(cx, |pane, cx| {
.detach_and_log_err(cx); pane.close_item_by_id(
item.id(),
crate::SaveBehavior::PromptOnWrite,
cx,
)
})
.detach_and_log_err(cx);
return; return;
} }

View file

@ -25,8 +25,8 @@ use gpui::{
keymap_matcher::KeymapContext, keymap_matcher::KeymapContext,
platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel}, platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext, Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
LayoutContext, ModelHandle, MouseRegion, PaintContext, Quad, Task, View, ViewContext, ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
ViewHandle, WeakViewHandle, WindowContext, WindowContext,
}; };
use project::{Project, ProjectEntryId, ProjectPath}; use project::{Project, ProjectEntryId, ProjectPath};
use serde::Deserialize; use serde::Deserialize;
@ -43,6 +43,19 @@ use std::{
}; };
use theme::{Theme, ThemeSettings}; use theme::{Theme, ThemeSettings};
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub enum SaveBehavior {
/// ask before overwriting conflicting files (used by default with %s)
PromptOnConflict,
/// ask before writing any file that wouldn't be auto-saved (used by default with %w)
PromptOnWrite,
/// never prompt, write on conflict (used with vim's :w!)
SilentlyOverwrite,
/// skip all save-related behaviour (used with vim's :cq)
DontSave,
}
#[derive(Clone, Deserialize, PartialEq)] #[derive(Clone, Deserialize, PartialEq)]
pub struct ActivateItem(pub usize); pub struct ActivateItem(pub usize);
@ -64,13 +77,17 @@ pub struct CloseItemsToTheRightById {
pub pane: WeakViewHandle<Pane>, pub pane: WeakViewHandle<Pane>,
} }
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
pub struct CloseActiveItem {
pub save_behavior: Option<SaveBehavior>,
}
actions!( actions!(
pane, pane,
[ [
ActivatePrevItem, ActivatePrevItem,
ActivateNextItem, ActivateNextItem,
ActivateLastItem, ActivateLastItem,
CloseActiveItem,
CloseInactiveItems, CloseInactiveItems,
CloseCleanItems, CloseCleanItems,
CloseItemsToTheLeft, CloseItemsToTheLeft,
@ -86,7 +103,7 @@ actions!(
] ]
); );
impl_actions!(pane, [ActivateItem]); impl_actions!(pane, [ActivateItem, CloseActiveItem]);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@ -696,22 +713,29 @@ impl Pane {
pub fn close_active_item( pub fn close_active_item(
&mut self, &mut self,
_: &CloseActiveItem, action: &CloseActiveItem,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<Task<Result<()>>> { ) -> Option<Task<Result<()>>> {
if self.items.is_empty() { if self.items.is_empty() {
return None; return None;
} }
let active_item_id = self.items[self.active_item_index].id(); let active_item_id = self.items[self.active_item_index].id();
Some(self.close_item_by_id(active_item_id, cx)) Some(self.close_item_by_id(
active_item_id,
action.save_behavior.unwrap_or(SaveBehavior::PromptOnWrite),
cx,
))
} }
pub fn close_item_by_id( pub fn close_item_by_id(
&mut self, &mut self,
item_id_to_close: usize, item_id_to_close: usize,
save_behavior: SaveBehavior,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
self.close_items(cx, move |view_id| view_id == item_id_to_close) self.close_items(cx, save_behavior, move |view_id| {
view_id == item_id_to_close
})
} }
pub fn close_inactive_items( pub fn close_inactive_items(
@ -724,7 +748,11 @@ impl Pane {
} }
let active_item_id = self.items[self.active_item_index].id(); let active_item_id = self.items[self.active_item_index].id();
Some(self.close_items(cx, move |item_id| item_id != active_item_id)) Some(
self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
item_id != active_item_id
}),
)
} }
pub fn close_clean_items( pub fn close_clean_items(
@ -737,7 +765,11 @@ impl Pane {
.filter(|item| !item.is_dirty(cx)) .filter(|item| !item.is_dirty(cx))
.map(|item| item.id()) .map(|item| item.id())
.collect(); .collect();
Some(self.close_items(cx, move |item_id| item_ids.contains(&item_id))) Some(
self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
item_ids.contains(&item_id)
}),
)
} }
pub fn close_items_to_the_left( pub fn close_items_to_the_left(
@ -762,7 +794,9 @@ impl Pane {
.take_while(|item| item.id() != item_id) .take_while(|item| item.id() != item_id)
.map(|item| item.id()) .map(|item| item.id())
.collect(); .collect();
self.close_items(cx, move |item_id| item_ids.contains(&item_id)) self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
item_ids.contains(&item_id)
})
} }
pub fn close_items_to_the_right( pub fn close_items_to_the_right(
@ -788,7 +822,9 @@ impl Pane {
.take_while(|item| item.id() != item_id) .take_while(|item| item.id() != item_id)
.map(|item| item.id()) .map(|item| item.id())
.collect(); .collect();
self.close_items(cx, move |item_id| item_ids.contains(&item_id)) self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
item_ids.contains(&item_id)
})
} }
pub fn close_all_items( pub fn close_all_items(
@ -800,12 +836,13 @@ impl Pane {
return None; return None;
} }
Some(self.close_items(cx, move |_| true)) Some(self.close_items(cx, SaveBehavior::PromptOnWrite, |_| true))
} }
pub fn close_items( pub fn close_items(
&mut self, &mut self,
cx: &mut ViewContext<Pane>, cx: &mut ViewContext<Pane>,
save_behavior: SaveBehavior,
should_close: impl 'static + Fn(usize) -> bool, should_close: impl 'static + Fn(usize) -> bool,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
// Find the items to close. // Find the items to close.
@ -858,8 +895,15 @@ impl Pane {
.any(|id| saved_project_items_ids.insert(*id)); .any(|id| saved_project_items_ids.insert(*id));
if should_save if should_save
&& !Self::save_item(project.clone(), &pane, item_ix, &*item, true, &mut cx) && !Self::save_item(
.await? project.clone(),
&pane,
item_ix,
&*item,
save_behavior,
&mut cx,
)
.await?
{ {
break; break;
} }
@ -954,13 +998,17 @@ impl Pane {
pane: &WeakViewHandle<Pane>, pane: &WeakViewHandle<Pane>,
item_ix: usize, item_ix: usize,
item: &dyn ItemHandle, item: &dyn ItemHandle,
should_prompt_for_save: bool, save_behavior: SaveBehavior,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,
) -> Result<bool> { ) -> Result<bool> {
const CONFLICT_MESSAGE: &str = const CONFLICT_MESSAGE: &str =
"This file has changed on disk since you started editing it. Do you want to overwrite it?"; "This file has changed on disk since you started editing it. Do you want to overwrite it?";
const DIRTY_MESSAGE: &str = "This file contains unsaved edits. Do you want to save it?"; const DIRTY_MESSAGE: &str = "This file contains unsaved edits. Do you want to save it?";
if save_behavior == SaveBehavior::DontSave {
return Ok(true);
}
let (has_conflict, is_dirty, can_save, is_singleton) = cx.read(|cx| { let (has_conflict, is_dirty, can_save, is_singleton) = cx.read(|cx| {
( (
item.has_conflict(cx), item.has_conflict(cx),
@ -971,18 +1019,22 @@ impl Pane {
}); });
if has_conflict && can_save { if has_conflict && can_save {
let mut answer = pane.update(cx, |pane, cx| { if save_behavior == SaveBehavior::SilentlyOverwrite {
pane.activate_item(item_ix, true, true, cx); pane.update(cx, |_, cx| item.save(project, cx))?.await?;
cx.prompt( } else {
PromptLevel::Warning, let mut answer = pane.update(cx, |pane, cx| {
CONFLICT_MESSAGE, pane.activate_item(item_ix, true, true, cx);
&["Overwrite", "Discard", "Cancel"], cx.prompt(
) PromptLevel::Warning,
})?; CONFLICT_MESSAGE,
match answer.next().await { &["Overwrite", "Discard", "Cancel"],
Some(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?, )
Some(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?, })?;
_ => return Ok(false), match answer.next().await {
Some(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?,
Some(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?,
_ => return Ok(false),
}
} }
} else if is_dirty && (can_save || is_singleton) { } else if is_dirty && (can_save || is_singleton) {
let will_autosave = cx.read(|cx| { let will_autosave = cx.read(|cx| {
@ -991,7 +1043,7 @@ impl Pane {
AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
) && Self::can_autosave_item(&*item, cx) ) && Self::can_autosave_item(&*item, cx)
}); });
let should_save = if should_prompt_for_save && !will_autosave { let should_save = if save_behavior == SaveBehavior::PromptOnWrite && !will_autosave {
let mut answer = pane.update(cx, |pane, cx| { let mut answer = pane.update(cx, |pane, cx| {
pane.activate_item(item_ix, true, true, cx); pane.activate_item(item_ix, true, true, cx);
cx.prompt( cx.prompt(
@ -1113,7 +1165,12 @@ impl Pane {
AnchorCorner::TopLeft, AnchorCorner::TopLeft,
if is_active_item { if is_active_item {
vec![ vec![
ContextMenuItem::action("Close Active Item", CloseActiveItem), ContextMenuItem::action(
"Close Active Item",
CloseActiveItem {
save_behavior: None,
},
),
ContextMenuItem::action("Close Inactive Items", CloseInactiveItems), ContextMenuItem::action("Close Inactive Items", CloseInactiveItems),
ContextMenuItem::action("Close Clean Items", CloseCleanItems), ContextMenuItem::action("Close Clean Items", CloseCleanItems),
ContextMenuItem::action("Close Items To The Left", CloseItemsToTheLeft), ContextMenuItem::action("Close Items To The Left", CloseItemsToTheLeft),
@ -1128,8 +1185,12 @@ impl Pane {
move |cx| { move |cx| {
if let Some(pane) = pane.upgrade(cx) { if let Some(pane) = pane.upgrade(cx) {
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
pane.close_item_by_id(target_item_id, cx) pane.close_item_by_id(
.detach_and_log_err(cx); target_item_id,
SaveBehavior::PromptOnWrite,
cx,
)
.detach_and_log_err(cx);
}) })
} }
} }
@ -1278,7 +1339,12 @@ impl Pane {
.on_click(MouseButton::Middle, { .on_click(MouseButton::Middle, {
let item_id = item.id(); let item_id = item.id();
move |_, pane, cx| { move |_, pane, cx| {
pane.close_item_by_id(item_id, cx).detach_and_log_err(cx); pane.close_item_by_id(
item_id,
SaveBehavior::PromptOnWrite,
cx,
)
.detach_and_log_err(cx);
} }
}) })
.on_down( .on_down(
@ -1486,7 +1552,8 @@ impl Pane {
cx.window_context().defer(move |cx| { cx.window_context().defer(move |cx| {
if let Some(pane) = pane.upgrade(cx) { if let Some(pane) = pane.upgrade(cx) {
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
pane.close_item_by_id(item_id, cx).detach_and_log_err(cx); pane.close_item_by_id(item_id, SaveBehavior::PromptOnWrite, cx)
.detach_and_log_err(cx);
}); });
} }
}); });
@ -1999,7 +2066,7 @@ impl<V: 'static> Element<V> for PaneBackdrop<V> {
&mut self, &mut self,
constraint: gpui::SizeConstraint, constraint: gpui::SizeConstraint,
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, view, cx); let size = self.child.layout(constraint, view, cx);
(size, ()) (size, ())
@ -2011,7 +2078,7 @@ impl<V: 'static> Element<V> for PaneBackdrop<V> {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let background = theme::current(cx).editor.background; let background = theme::current(cx).editor.background;
@ -2087,7 +2154,14 @@ mod tests {
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
assert!(pane.close_active_item(&CloseActiveItem, cx).is_none()) assert!(pane
.close_active_item(
&CloseActiveItem {
save_behavior: None
},
cx
)
.is_none())
}); });
} }
@ -2337,31 +2411,59 @@ mod tests {
add_labeled_item(&pane, "1", false, cx); add_labeled_item(&pane, "1", false, cx);
assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx); assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx);
pane.update(cx, |pane, cx| pane.close_active_item(&CloseActiveItem, cx)) pane.update(cx, |pane, cx| {
.unwrap() pane.close_active_item(
.await &CloseActiveItem {
.unwrap(); save_behavior: None,
},
cx,
)
})
.unwrap()
.await
.unwrap();
assert_item_labels(&pane, ["A", "B*", "C", "D"], cx); assert_item_labels(&pane, ["A", "B*", "C", "D"], cx);
pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx)); pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx));
assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
pane.update(cx, |pane, cx| pane.close_active_item(&CloseActiveItem, cx)) pane.update(cx, |pane, cx| {
.unwrap() pane.close_active_item(
.await &CloseActiveItem {
.unwrap(); save_behavior: None,
},
cx,
)
})
.unwrap()
.await
.unwrap();
assert_item_labels(&pane, ["A", "B*", "C"], cx); assert_item_labels(&pane, ["A", "B*", "C"], cx);
pane.update(cx, |pane, cx| pane.close_active_item(&CloseActiveItem, cx)) pane.update(cx, |pane, cx| {
.unwrap() pane.close_active_item(
.await &CloseActiveItem {
.unwrap(); save_behavior: None,
},
cx,
)
})
.unwrap()
.await
.unwrap();
assert_item_labels(&pane, ["A", "C*"], cx); assert_item_labels(&pane, ["A", "C*"], cx);
pane.update(cx, |pane, cx| pane.close_active_item(&CloseActiveItem, cx)) pane.update(cx, |pane, cx| {
.unwrap() pane.close_active_item(
.await &CloseActiveItem {
.unwrap(); save_behavior: None,
},
cx,
)
})
.unwrap()
.await
.unwrap();
assert_item_labels(&pane, ["A*"], cx); assert_item_labels(&pane, ["A*"], cx);
} }

View file

@ -594,8 +594,8 @@ mod element {
json::{self, ToJson}, json::{self, ToJson},
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, MouseButton},
scene::MouseDrag, scene::MouseDrag,
AnyElement, Axis, CursorRegion, Element, EventContext, LayoutContext, MouseRegion, AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, RectFExt,
PaintContext, RectFExt, SizeConstraint, Vector2FExt, ViewContext, SizeConstraint, Vector2FExt, ViewContext,
}; };
use crate::{ use crate::{
@ -641,7 +641,7 @@ mod element {
remaining_flex: &mut f32, remaining_flex: &mut f32,
cross_axis_max: &mut f32, cross_axis_max: &mut f32,
view: &mut Workspace, view: &mut Workspace,
cx: &mut LayoutContext<Workspace>, cx: &mut ViewContext<Workspace>,
) { ) {
let flexes = self.flexes.borrow(); let flexes = self.flexes.borrow();
let cross_axis = self.axis.invert(); let cross_axis = self.axis.invert();
@ -789,7 +789,7 @@ mod element {
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
view: &mut Workspace, view: &mut Workspace,
cx: &mut LayoutContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
debug_assert!(self.children.len() == self.flexes.borrow().len()); debug_assert!(self.children.len() == self.flexes.borrow().len());
@ -855,7 +855,7 @@ mod element {
visible_bounds: RectF, visible_bounds: RectF,
remaining_space: &mut Self::LayoutState, remaining_space: &mut Self::LayoutState,
view: &mut Workspace, view: &mut Workspace,
cx: &mut PaintContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> Self::PaintState { ) -> Self::PaintState {
let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.; let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();

View file

@ -8,8 +8,8 @@ use gpui::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::{json, ToJson}, json::{json, ToJson},
AnyElement, AnyViewHandle, Entity, LayoutContext, PaintContext, SizeConstraint, Subscription, AnyElement, AnyViewHandle, Entity, SizeConstraint, Subscription, View, ViewContext, ViewHandle,
View, ViewContext, ViewHandle, WindowContext, WindowContext,
}; };
pub trait StatusItemView: View { pub trait StatusItemView: View {
@ -208,7 +208,7 @@ impl Element<StatusBar> for StatusBarElement {
&mut self, &mut self,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
view: &mut StatusBar, view: &mut StatusBar,
cx: &mut LayoutContext<StatusBar>, cx: &mut ViewContext<StatusBar>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let max_width = constraint.max.x(); let max_width = constraint.max.x();
constraint.min = vec2f(0., constraint.min.y()); constraint.min = vec2f(0., constraint.min.y());
@ -230,7 +230,7 @@ impl Element<StatusBar> for StatusBarElement {
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &mut StatusBar, view: &mut StatusBar,
cx: &mut PaintContext<StatusBar>, cx: &mut ViewContext<StatusBar>,
) -> Self::PaintState { ) -> Self::PaintState {
let origin_y = bounds.upper_right().y(); let origin_y = bounds.upper_right().y();
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();

View file

@ -1308,13 +1308,15 @@ impl Workspace {
} }
Ok(this Ok(this
.update(&mut cx, |this, cx| this.save_all_internal(true, cx))? .update(&mut cx, |this, cx| {
this.save_all_internal(SaveBehavior::PromptOnWrite, cx)
})?
.await?) .await?)
}) })
} }
fn save_all(&mut self, _: &SaveAll, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> { fn save_all(&mut self, _: &SaveAll, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
let save_all = self.save_all_internal(false, cx); let save_all = self.save_all_internal(SaveBehavior::PromptOnConflict, cx);
Some(cx.foreground().spawn(async move { Some(cx.foreground().spawn(async move {
save_all.await?; save_all.await?;
Ok(()) Ok(())
@ -1323,7 +1325,7 @@ impl Workspace {
fn save_all_internal( fn save_all_internal(
&mut self, &mut self,
should_prompt_to_save: bool, save_behaviour: SaveBehavior,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Task<Result<bool>> { ) -> Task<Result<bool>> {
if self.project.read(cx).is_read_only() { if self.project.read(cx).is_read_only() {
@ -1358,7 +1360,7 @@ impl Workspace {
&pane, &pane,
ix, ix,
&*item, &*item,
should_prompt_to_save, save_behaviour,
&mut cx, &mut cx,
) )
.await? .await?
@ -4358,7 +4360,9 @@ mod tests {
let item1_id = item1.id(); let item1_id = item1.id();
let item3_id = item3.id(); let item3_id = item3.id();
let item4_id = item4.id(); let item4_id = item4.id();
pane.close_items(cx, move |id| [item1_id, item3_id, item4_id].contains(&id)) pane.close_items(cx, SaveBehavior::PromptOnWrite, move |id| {
[item1_id, item3_id, item4_id].contains(&id)
})
}); });
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -4493,7 +4497,9 @@ mod tests {
// once for project entry 0, and once for project entry 2. After those two // once for project entry 0, and once for project entry 2. After those two
// prompts, the task should complete. // prompts, the task should complete.
let close = left_pane.update(cx, |pane, cx| pane.close_items(cx, |_| true)); let close = left_pane.update(cx, |pane, cx| {
pane.close_items(cx, SaveBehavior::PromptOnWrite, move |_| true)
});
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
left_pane.read_with(cx, |pane, cx| { left_pane.read_with(cx, |pane, cx| {
assert_eq!( assert_eq!(
@ -4609,9 +4615,11 @@ mod tests {
item.is_dirty = true; item.is_dirty = true;
}); });
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)) pane.update(cx, |pane, cx| {
.await pane.close_items(cx, SaveBehavior::PromptOnWrite, move |id| id == item_id)
.unwrap(); })
.await
.unwrap();
assert!(!window.has_pending_prompt(cx)); assert!(!window.has_pending_prompt(cx));
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
@ -4630,8 +4638,9 @@ mod tests {
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
// Ensure autosave is prevented for deleted files also when closing the buffer. // Ensure autosave is prevented for deleted files also when closing the buffer.
let _close_items = let _close_items = pane.update(cx, |pane, cx| {
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)); pane.close_items(cx, SaveBehavior::PromptOnWrite, move |id| id == item_id)
});
deterministic.run_until_parked(); deterministic.run_until_parked();
assert!(window.has_pending_prompt(cx)); assert!(window.has_pending_prompt(cx));
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));

View file

@ -132,6 +132,7 @@ tree-sitter-racket.workspace = true
tree-sitter-yaml.workspace = true tree-sitter-yaml.workspace = true
tree-sitter-lua.workspace = true tree-sitter-lua.workspace = true
tree-sitter-nix.workspace = true tree-sitter-nix.workspace = true
tree-sitter-nu.workspace = true
url = "2.2" url = "2.2"
urlencoding = "2.1.2" urlencoding = "2.1.2"

View file

@ -170,6 +170,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<dyn NodeRuntime>
language("elm", tree_sitter_elm::language(), vec![]); language("elm", tree_sitter_elm::language(), vec![]);
language("glsl", tree_sitter_glsl::language(), vec![]); language("glsl", tree_sitter_glsl::language(), vec![]);
language("nix", tree_sitter_nix::language(), vec![]); language("nix", tree_sitter_nix::language(), vec![]);
language("nu", tree_sitter_nu::language(), vec![]);
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]

View file

@ -0,0 +1,4 @@
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
(parameter_pipes "|" @open "|" @close)

View file

@ -0,0 +1,9 @@
name = "Nu"
path_suffixes = ["nu"]
line_comment = "# "
autoclose_before = ";:.,=}])>` \n\t\""
brackets = [
{ start = "{", end = "}", close = true, newline = true },
{ start = "[", end = "]", close = true, newline = true },
{ start = "(", end = ")", close = true, newline = true },
]

View file

@ -0,0 +1,302 @@
;;; ---
;;; keywords
[
"def"
"def-env"
"alias"
"export-env"
"export"
"extern"
"module"
"let"
"let-env"
"mut"
"const"
"hide-env"
"source"
"source-env"
"overlay"
"register"
"loop"
"while"
"error"
"do"
"if"
"else"
"try"
"catch"
"match"
"break"
"continue"
"return"
] @keyword
(hide_mod "hide" @keyword)
(decl_use "use" @keyword)
(ctrl_for
"for" @keyword
"in" @keyword
)
(overlay_list "list" @keyword)
(overlay_hide "hide" @keyword)
(overlay_new "new" @keyword)
(overlay_use
"use" @keyword
"as" @keyword
)
(ctrl_error "make" @keyword)
;;; ---
;;; literals
(val_number) @constant
(val_duration
unit: [
"ns" "µs" "us" "ms" "sec" "min" "hr" "day" "wk"
] @variable
)
(val_filesize
unit: [
"b" "B"
"kb" "kB" "Kb" "KB"
"mb" "mB" "Mb" "MB"
"gb" "gB" "Gb" "GB"
"tb" "tB" "Tb" "TB"
"pb" "pB" "Pb" "PB"
"eb" "eB" "Eb" "EB"
"zb" "zB" "Zb" "ZB"
"kib" "kiB" "kIB" "kIb" "Kib" "KIb" "KIB"
"mib" "miB" "mIB" "mIb" "Mib" "MIb" "MIB"
"gib" "giB" "gIB" "gIb" "Gib" "GIb" "GIB"
"tib" "tiB" "tIB" "tIb" "Tib" "TIb" "TIB"
"pib" "piB" "pIB" "pIb" "Pib" "PIb" "PIB"
"eib" "eiB" "eIB" "eIb" "Eib" "EIb" "EIB"
"zib" "ziB" "zIB" "zIb" "Zib" "ZIb" "ZIB"
] @variable
)
(val_binary
[
"0b"
"0o"
"0x"
] @constant
"[" @punctuation.bracket
digit: [
"," @punctuation.delimiter
(hex_digit) @constant
]
"]" @punctuation.bracket
) @constant
(val_bool) @constant.builtin
(val_nothing) @constant.builtin
(val_string) @string
(val_date) @constant
(inter_escape_sequence) @constant
(escape_sequence) @constant
(val_interpolated [
"$\""
"$\'"
"\""
"\'"
] @string)
(unescaped_interpolated_content) @string
(escaped_interpolated_content) @string
(expr_interpolated ["(" ")"] @variable)
;;; ---
;;; operators
(expr_binary [
"+"
"-"
"*"
"/"
"mod"
"//"
"++"
"**"
"=="
"!="
"<"
"<="
">"
">="
"=~"
"!~"
"and"
"or"
"xor"
"bit-or"
"bit-xor"
"bit-and"
"bit-shl"
"bit-shr"
"in"
"not-in"
"starts-with"
"ends-with"
] @operator)
(expr_binary opr: ([
"and"
"or"
"xor"
"bit-or"
"bit-xor"
"bit-and"
"bit-shl"
"bit-shr"
"in"
"not-in"
"starts-with"
"ends-with"
]) @keyword)
(where_command [
"+"
"-"
"*"
"/"
"mod"
"//"
"++"
"**"
"=="
"!="
"<"
"<="
">"
">="
"=~"
"!~"
"and"
"or"
"xor"
"bit-or"
"bit-xor"
"bit-and"
"bit-shl"
"bit-shr"
"in"
"not-in"
"starts-with"
"ends-with"
] @operator)
(assignment [
"="
"+="
"-="
"*="
"/="
"++="
] @operator)
(expr_unary ["not" "-"] @operator)
(val_range [
".."
"..="
"..<"
] @operator)
["=>" "=" "|"] @operator
[
"o>" "out>"
"e>" "err>"
"e+o>" "err+out>"
"o+e>" "out+err>"
] @special
;;; ---
;;; punctuation
[
","
";"
] @punctuation.delimiter
(param_short_flag "-" @punctuation.delimiter)
(param_long_flag ["--"] @punctuation.delimiter)
(long_flag ["--"] @punctuation.delimiter)
(param_rest "..." @punctuation.delimiter)
(param_type [":"] @punctuation.special)
(param_value ["="] @punctuation.special)
(param_cmd ["@"] @punctuation.special)
(param_opt ["?"] @punctuation.special)
[
"(" ")"
"{" "}"
"[" "]"
] @punctuation.bracket
(val_record
(record_entry ":" @punctuation.delimiter))
;;; ---
;;; identifiers
(param_rest
name: (_) @variable)
(param_opt
name: (_) @variable)
(parameter
param_name: (_) @variable)
(param_cmd
(cmd_identifier) @string)
(param_long_flag) @variable
(param_short_flag) @variable
(short_flag) @variable
(long_flag) @variable
(scope_pattern [(wild_card) @function])
(cmd_identifier) @function
(command
"^" @punctuation.delimiter
head: (_) @function
)
"where" @function
(path
["." "?"] @punctuation.delimiter
) @variable
(val_variable
"$" @operator
[
(identifier) @variable
"in" @type.builtin
"nu" @type.builtin
"env" @type.builtin
"nothing" @type.builtin
] ; If we have a special styling, use it here
)
;;; ---
;;; types
(flat_type) @type.builtin
(list_type
"list" @type
["<" ">"] @punctuation.bracket
)
(collection_type
["record" "table"] @type
"<" @punctuation.bracket
key: (_) @variable
["," ":"] @punctuation.delimiter
">" @punctuation.bracket
)
(shebang) @comment
(comment) @comment

View file

@ -0,0 +1,3 @@
(_ "[" "]" @end) @indent
(_ "{" "}" @end) @indent
(_ "(" ")" @end) @indent

View file

@ -41,7 +41,12 @@ pub fn menus() -> Vec<Menu<'static>> {
MenuItem::action("Save", workspace::Save), MenuItem::action("Save", workspace::Save),
MenuItem::action("Save As…", workspace::SaveAs), MenuItem::action("Save As…", workspace::SaveAs),
MenuItem::action("Save All", workspace::SaveAll), MenuItem::action("Save All", workspace::SaveAll),
MenuItem::action("Close Editor", workspace::CloseActiveItem), MenuItem::action(
"Close Editor",
workspace::CloseActiveItem {
save_behavior: None,
},
),
MenuItem::action("Close Window", workspace::CloseWindow), MenuItem::action("Close Window", workspace::CloseWindow),
], ],
}, },

View file

@ -733,7 +733,7 @@ mod tests {
use theme::{ThemeRegistry, ThemeSettings}; use theme::{ThemeRegistry, ThemeSettings};
use workspace::{ use workspace::{
item::{Item, ItemHandle}, item::{Item, ItemHandle},
open_new, open_paths, pane, NewFile, SplitDirection, WorkspaceHandle, open_new, open_paths, pane, NewFile, SaveBehavior, SplitDirection, WorkspaceHandle,
}; };
#[gpui::test] #[gpui::test]
@ -1495,7 +1495,12 @@ mod tests {
pane2_item.downcast::<Editor>().unwrap().downgrade() pane2_item.downcast::<Editor>().unwrap().downgrade()
}); });
cx.dispatch_action(window.into(), workspace::CloseActiveItem); cx.dispatch_action(
window.into(),
workspace::CloseActiveItem {
save_behavior: None,
},
);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
workspace.read_with(cx, |workspace, _| { workspace.read_with(cx, |workspace, _| {
@ -1503,7 +1508,12 @@ mod tests {
assert_eq!(workspace.active_pane(), &pane_1); assert_eq!(workspace.active_pane(), &pane_1);
}); });
cx.dispatch_action(window.into(), workspace::CloseActiveItem); cx.dispatch_action(
window.into(),
workspace::CloseActiveItem {
save_behavior: None,
},
);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
window.simulate_prompt_answer(1, cx); window.simulate_prompt_answer(1, cx);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -1661,7 +1671,7 @@ mod tests {
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
let editor3_id = editor3.id(); let editor3_id = editor3.id();
drop(editor3); drop(editor3);
pane.close_item_by_id(editor3_id, cx) pane.close_item_by_id(editor3_id, SaveBehavior::PromptOnWrite, cx)
}) })
.await .await
.unwrap(); .unwrap();
@ -1696,7 +1706,7 @@ mod tests {
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
let editor2_id = editor2.id(); let editor2_id = editor2.id();
drop(editor2); drop(editor2);
pane.close_item_by_id(editor2_id, cx) pane.close_item_by_id(editor2_id, SaveBehavior::PromptOnWrite, cx)
}) })
.await .await
.unwrap(); .unwrap();
@ -1852,24 +1862,32 @@ mod tests {
assert_eq!(active_path(&workspace, cx), Some(file4.clone())); assert_eq!(active_path(&workspace, cx), Some(file4.clone()));
// Close all the pane items in some arbitrary order. // Close all the pane items in some arbitrary order.
pane.update(cx, |pane, cx| pane.close_item_by_id(file1_item_id, cx)) pane.update(cx, |pane, cx| {
.await pane.close_item_by_id(file1_item_id, SaveBehavior::PromptOnWrite, cx)
.unwrap(); })
.await
.unwrap();
assert_eq!(active_path(&workspace, cx), Some(file4.clone())); assert_eq!(active_path(&workspace, cx), Some(file4.clone()));
pane.update(cx, |pane, cx| pane.close_item_by_id(file4_item_id, cx)) pane.update(cx, |pane, cx| {
.await pane.close_item_by_id(file4_item_id, SaveBehavior::PromptOnWrite, cx)
.unwrap(); })
.await
.unwrap();
assert_eq!(active_path(&workspace, cx), Some(file3.clone())); assert_eq!(active_path(&workspace, cx), Some(file3.clone()));
pane.update(cx, |pane, cx| pane.close_item_by_id(file2_item_id, cx)) pane.update(cx, |pane, cx| {
.await pane.close_item_by_id(file2_item_id, SaveBehavior::PromptOnWrite, cx)
.unwrap(); })
.await
.unwrap();
assert_eq!(active_path(&workspace, cx), Some(file3.clone())); assert_eq!(active_path(&workspace, cx), Some(file3.clone()));
pane.update(cx, |pane, cx| pane.close_item_by_id(file3_item_id, cx)) pane.update(cx, |pane, cx| {
.await pane.close_item_by_id(file3_item_id, SaveBehavior::PromptOnWrite, cx)
.unwrap(); })
.await
.unwrap();
assert_eq!(active_path(&workspace, cx), None); assert_eq!(active_path(&workspace, cx), None);
// Reopen all the closed items, ensuring they are reopened in the same order // Reopen all the closed items, ensuring they are reopened in the same order