Remove 2 suffix from gpui_macros, fix compile errors in tests

Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-01-03 13:12:21 -08:00
parent f5ba22659b
commit 83f4c61657
29 changed files with 71 additions and 1670 deletions

View File

@ -1,6 +1,3 @@
[alias]
xtask = "run --package xtask --"
[build]
# v0 mangling scheme provides more detailed backtraces around closures
rustflags = ["-C", "symbol-mangling-version=v0"]

56
Cargo.lock generated
View File

@ -1618,19 +1618,6 @@ dependencies = [
"zed_actions",
]
[[package]]
name = "component_test"
version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
"project",
"settings",
"theme",
"util",
"workspace",
]
[[package]]
name = "concurrent-queue"
version = "2.2.0"
@ -1665,17 +1652,6 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "context_menu"
version = "0.1.0"
dependencies = [
"gpui",
"menu",
"settings",
"smallvec",
"theme",
]
[[package]]
name = "convert_case"
version = "0.4.0"
@ -2354,14 +2330,6 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "drag_and_drop"
version = "0.1.0"
dependencies = [
"collections",
"gpui",
]
[[package]]
name = "dwrote"
version = "0.11.0"
@ -3185,7 +3153,7 @@ dependencies = [
"font-kit",
"foreign-types",
"futures 0.3.28",
"gpui2_macros",
"gpui_macros",
"image",
"itertools 0.10.5",
"lazy_static",
@ -3225,20 +3193,10 @@ dependencies = [
"waker-fn",
]
[[package]]
name = "gpui2_macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "gpui_macros"
version = "0.1.0"
dependencies = [
"lazy_static",
"proc-macro2",
"quote",
"syn 1.0.109",
@ -7677,6 +7635,7 @@ dependencies = [
"story",
"strum",
"theme",
"ui",
"util",
]
@ -9936,17 +9895,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "xtask"
version = "0.1.0"
dependencies = [
"anyhow",
"clap 4.4.4",
"schemars",
"serde_json",
"theme",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"

View File

@ -15,15 +15,12 @@ members = [
"crates/collab_ui",
"crates/collections",
"crates/command_palette",
"crates/component_test",
"crates/context_menu",
"crates/copilot",
"crates/copilot_button",
"crates/db",
"crates/refineable",
"crates/refineable/derive_refineable",
"crates/diagnostics",
"crates/drag_and_drop",
"crates/editor",
"crates/feature_flags",
"crates/feedback",
@ -36,7 +33,7 @@ members = [
"crates/gpui",
"crates/gpui_macros",
"crates/gpui",
"crates/gpui2_macros",
"crates/gpui_macros",
"crates/install_cli",
"crates/journal",
"crates/journal",
@ -85,7 +82,6 @@ members = [
"crates/vcs_menu",
"crates/workspace",
"crates/welcome",
"crates/xtask",
"crates/zed",
"crates/zed_actions",
]

View File

@ -1,5 +1,5 @@
use gpui::{
div, AnyElement, ElementId, IntoElement, ParentElement, RenderOnce, Styled, WindowContext,
div, AnyElement, IntoElement, ParentElement, RenderOnce, Styled, WindowContext,
};
use smallvec::SmallVec;

View File

@ -1,18 +0,0 @@
[package]
name = "component_test"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/component_test.rs"
doctest = false
[dependencies]
anyhow.workspace = true
gpui = { path = "../gpui" }
settings = { path = "../settings" }
util = { path = "../util" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
project = { path = "../project" }

View File

@ -1,121 +0,0 @@
use gpui::{
actions,
elements::{Component, Flex, ParentElement, SafeStylable},
AppContext, Element, Entity, ModelHandle, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use project::Project;
use theme::components::{action_button::Button, label::Label, ComponentExt};
use workspace::{
item::Item, register_deserializable_item, ItemId, Pane, PaneBackdrop, Workspace, WorkspaceId,
};
pub fn init(cx: &mut AppContext) {
cx.add_action(ComponentTest::toggle_disclosure);
cx.add_action(ComponentTest::toggle_toggle);
cx.add_action(ComponentTest::deploy);
register_deserializable_item::<ComponentTest>(cx);
}
actions!(
test,
[NoAction, ToggleDisclosure, ToggleToggle, NewComponentTest]
);
struct ComponentTest {
disclosed: bool,
toggled: bool,
}
impl ComponentTest {
fn new() -> Self {
Self {
disclosed: false,
toggled: false,
}
}
fn deploy(workspace: &mut Workspace, _: &NewComponentTest, cx: &mut ViewContext<Workspace>) {
workspace.add_item(Box::new(cx.add_view(|_| ComponentTest::new())), cx);
}
fn toggle_disclosure(&mut self, _: &ToggleDisclosure, cx: &mut ViewContext<Self>) {
self.disclosed = !self.disclosed;
cx.notify();
}
fn toggle_toggle(&mut self, _: &ToggleToggle, cx: &mut ViewContext<Self>) {
self.toggled = !self.toggled;
cx.notify();
}
}
impl Entity for ComponentTest {
type Event = ();
}
impl View for ComponentTest {
fn ui_name() -> &'static str {
"Component Test"
}
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> gpui::AnyElement<Self> {
let theme = theme::current(cx);
PaneBackdrop::new(
cx.view_id(),
Flex::column()
.with_spacing(10.)
.with_child(
Button::action(NoAction)
.with_tooltip("Here's what a tooltip looks like", theme.tooltip.clone())
.with_contents(Label::new("Click me!"))
.with_style(theme.component_test.button.clone())
.element(),
)
.with_child(
Button::action(ToggleToggle)
.with_tooltip("Here's what a tooltip looks like", theme.tooltip.clone())
.with_contents(Label::new("Toggle me!"))
.toggleable(self.toggled)
.with_style(theme.component_test.toggle.clone())
.element(),
)
.with_child(
Label::new("A disclosure")
.disclosable(Some(self.disclosed), Box::new(ToggleDisclosure))
.with_style(theme.component_test.disclosure.clone())
.element(),
)
.constrained()
.with_width(200.)
.aligned()
.into_any(),
)
.into_any()
}
}
impl Item for ComponentTest {
fn tab_content<V: 'static>(
&self,
_: Option<usize>,
style: &theme::Tab,
_: &AppContext,
) -> gpui::AnyElement<V> {
gpui::elements::Label::new("Component test", style.label.clone()).into_any()
}
fn serialized_item_kind() -> Option<&'static str> {
Some("ComponentTest")
}
fn deserialize(
_project: ModelHandle<Project>,
_workspace: WeakViewHandle<Workspace>,
_workspace_id: WorkspaceId,
_item_id: ItemId,
cx: &mut ViewContext<Pane>,
) -> Task<anyhow::Result<ViewHandle<Self>>> {
Task::ready(Ok(cx.add_view(|_| Self::new())))
}
}

View File

@ -1,16 +0,0 @@
[package]
name = "context_menu"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/context_menu.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
menu = { path = "../menu" }
settings = { path = "../settings" }
theme = { path = "../theme" }
smallvec.workspace = true

View File

@ -1,528 +0,0 @@
use gpui::{
anyhow::{self, anyhow},
elements::*,
geometry::vector::Vector2F,
keymap_matcher::KeymapContext,
platform::{CursorStyle, MouseButton},
Action, AnyViewHandle, AppContext, Axis, Entity, MouseState, SizeConstraint, Subscription,
View, ViewContext,
};
use menu::*;
use std::{any::TypeId, borrow::Cow, sync::Arc, time::Duration};
pub fn init(cx: &mut AppContext) {
cx.add_action(ContextMenu::select_first);
cx.add_action(ContextMenu::select_last);
cx.add_action(ContextMenu::select_next);
cx.add_action(ContextMenu::select_prev);
cx.add_action(ContextMenu::confirm);
cx.add_action(ContextMenu::cancel);
}
pub type StaticItem = Box<dyn Fn(&mut AppContext) -> AnyElement<ContextMenu>>;
type ContextMenuItemBuilder =
Box<dyn Fn(&mut MouseState, &theme::ContextMenuItem) -> AnyElement<ContextMenu>>;
pub enum ContextMenuItemLabel {
String(Cow<'static, str>),
Element(ContextMenuItemBuilder),
}
impl From<Cow<'static, str>> for ContextMenuItemLabel {
fn from(s: Cow<'static, str>) -> Self {
Self::String(s)
}
}
impl From<&'static str> for ContextMenuItemLabel {
fn from(s: &'static str) -> Self {
Self::String(s.into())
}
}
impl From<String> for ContextMenuItemLabel {
fn from(s: String) -> Self {
Self::String(s.into())
}
}
impl<T> From<T> for ContextMenuItemLabel
where
T: 'static + Fn(&mut MouseState, &theme::ContextMenuItem) -> AnyElement<ContextMenu>,
{
fn from(f: T) -> Self {
Self::Element(Box::new(f))
}
}
pub enum ContextMenuItemAction {
Action(Box<dyn Action>),
Handler(Arc<dyn Fn(&mut ViewContext<ContextMenu>)>),
}
impl Clone for ContextMenuItemAction {
fn clone(&self) -> Self {
match self {
Self::Action(action) => Self::Action(action.boxed_clone()),
Self::Handler(handler) => Self::Handler(handler.clone()),
}
}
}
pub enum ContextMenuItem {
Item {
label: ContextMenuItemLabel,
action: ContextMenuItemAction,
},
Static(StaticItem),
Separator,
}
impl ContextMenuItem {
pub fn action(label: impl Into<ContextMenuItemLabel>, action: impl 'static + Action) -> Self {
Self::Item {
label: label.into(),
action: ContextMenuItemAction::Action(Box::new(action)),
}
}
pub fn handler(
label: impl Into<ContextMenuItemLabel>,
handler: impl 'static + Fn(&mut ViewContext<ContextMenu>),
) -> Self {
Self::Item {
label: label.into(),
action: ContextMenuItemAction::Handler(Arc::new(handler)),
}
}
pub fn separator() -> Self {
Self::Separator
}
fn is_action(&self) -> bool {
matches!(self, Self::Item { .. })
}
fn action_id(&self) -> Option<TypeId> {
match self {
ContextMenuItem::Item { action, .. } => match action {
ContextMenuItemAction::Action(action) => Some(action.id()),
ContextMenuItemAction::Handler(_) => None,
},
ContextMenuItem::Static(..) | ContextMenuItem::Separator => None,
}
}
}
pub struct ContextMenu {
show_count: usize,
anchor_position: Vector2F,
anchor_corner: AnchorCorner,
position_mode: OverlayPositionMode,
items: Vec<ContextMenuItem>,
selected_index: Option<usize>,
visible: bool,
delay_cancel: bool,
previously_focused_view_id: Option<usize>,
parent_view_id: usize,
_actions_observation: Subscription,
}
impl Entity for ContextMenu {
type Event = ();
}
impl View for ContextMenu {
fn ui_name() -> &'static str {
"ContextMenu"
}
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
Self::reset_to_default_keymap_context(keymap);
keymap.add_identifier("menu");
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
if !self.visible {
return Empty::new().into_any();
}
// Render the menu once at minimum width.
let mut collapsed_menu = self.render_menu_for_measurement(cx);
let expanded_menu =
self.render_menu(cx)
.constrained()
.dynamically(move |constraint, view, cx| {
SizeConstraint::strict_along(
Axis::Horizontal,
collapsed_menu.layout(constraint, view, cx).0.x(),
)
});
Overlay::new(expanded_menu)
.with_hoverable(true)
.with_fit_mode(OverlayFitMode::SnapToWindow)
.with_anchor_position(self.anchor_position)
.with_anchor_corner(self.anchor_corner)
.with_position_mode(self.position_mode)
.into_any()
}
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
self.reset(cx);
}
}
impl ContextMenu {
pub fn new(parent_view_id: usize, cx: &mut ViewContext<Self>) -> Self {
Self {
show_count: 0,
delay_cancel: false,
anchor_position: Default::default(),
anchor_corner: AnchorCorner::TopLeft,
position_mode: OverlayPositionMode::Window,
items: Default::default(),
selected_index: Default::default(),
visible: Default::default(),
previously_focused_view_id: Default::default(),
parent_view_id,
_actions_observation: cx.observe_actions(Self::action_dispatched),
}
}
pub fn visible(&self) -> bool {
self.visible
}
fn action_dispatched(&mut self, action_id: TypeId, cx: &mut ViewContext<Self>) {
if let Some(ix) = self
.items
.iter()
.position(|item| item.action_id() == Some(action_id))
{
self.selected_index = Some(ix);
cx.notify();
cx.spawn(|this, mut cx| async move {
cx.background().timer(Duration::from_millis(50)).await;
this.update(&mut cx, |this, cx| this.cancel(&Default::default(), cx))?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
}
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) {
match action {
ContextMenuItemAction::Action(action) => {
let window = cx.window();
let view_id = self.parent_view_id;
let action = action.boxed_clone();
cx.app_context()
.spawn(|mut cx| async move {
window
.dispatch_action(view_id, action.as_ref(), &mut cx)
.ok_or_else(|| anyhow!("window was closed"))
})
.detach_and_log_err(cx);
}
ContextMenuItemAction::Handler(handler) => handler(cx),
}
self.reset(cx);
}
}
}
pub fn delay_cancel(&mut self) {
self.delay_cancel = true;
}
fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
if !self.delay_cancel {
self.reset(cx);
let show_count = self.show_count;
cx.defer(move |this, cx| {
if cx.handle().is_focused(cx) && this.show_count == show_count {
(**cx).focus(this.previously_focused_view_id.take());
}
});
} else {
self.delay_cancel = false;
}
}
fn reset(&mut self, cx: &mut ViewContext<Self>) {
self.items.clear();
self.visible = false;
self.selected_index.take();
cx.notify();
}
fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
self.selected_index = self.items.iter().position(|item| item.is_action());
cx.notify();
}
fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
for (ix, item) in self.items.iter().enumerate().rev() {
if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
}
}
}
fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
for (ix, item) in self.items.iter().enumerate().skip(ix + 1) {
if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
}
}
} else {
self.select_first(&Default::default(), cx);
}
}
fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
for (ix, item) in self.items.iter().enumerate().take(ix).rev() {
if item.is_action() {
self.selected_index = Some(ix);
cx.notify();
break;
}
}
} else {
self.select_last(&Default::default(), cx);
}
}
pub fn toggle(
&mut self,
anchor_position: Vector2F,
anchor_corner: AnchorCorner,
items: Vec<ContextMenuItem>,
cx: &mut ViewContext<Self>,
) {
if self.visible() {
self.cancel(&Cancel, cx);
} else {
let mut items = items.into_iter().peekable();
if items.peek().is_some() {
self.items = items.collect();
self.anchor_position = anchor_position;
self.anchor_corner = anchor_corner;
self.visible = true;
self.show_count += 1;
if !cx.is_self_focused() {
self.previously_focused_view_id = cx.focused_view_id();
}
cx.focus_self();
} else {
self.visible = false;
}
}
cx.notify();
}
pub fn show(
&mut self,
anchor_position: Vector2F,
anchor_corner: AnchorCorner,
items: Vec<ContextMenuItem>,
cx: &mut ViewContext<Self>,
) {
let mut items = items.into_iter().peekable();
if items.peek().is_some() {
self.items = items.collect();
self.anchor_position = anchor_position;
self.anchor_corner = anchor_corner;
self.visible = true;
self.show_count += 1;
if !cx.is_self_focused() {
self.previously_focused_view_id = cx.focused_view_id();
}
cx.focus_self();
} else {
self.visible = false;
}
cx.notify();
}
pub fn set_position_mode(&mut self, mode: OverlayPositionMode) {
self.position_mode = mode;
}
fn render_menu_for_measurement(&self, cx: &mut ViewContext<Self>) -> impl Element<ContextMenu> {
let style = theme::current(cx).context_menu.clone();
Flex::row()
.with_child(
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { label, .. } => {
let style = style.item.in_state(self.selected_index == Some(ix));
let style = style.style_for(&mut Default::default());
match label {
ContextMenuItemLabel::String(label) => {
Label::new(label.to_string(), style.label.clone())
.contained()
.with_style(style.container)
.into_any()
}
ContextMenuItemLabel::Element(element) => {
element(&mut Default::default(), style)
}
}
}
ContextMenuItem::Static(f) => f(cx),
ContextMenuItem::Separator => Empty::new()
.collapsed()
.contained()
.with_style(style.separator)
.constrained()
.with_height(1.)
.into_any(),
}
})),
)
.with_child(
Flex::column()
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { action, .. } => {
let style = style.item.in_state(self.selected_index == Some(ix));
let style = style.style_for(&mut Default::default());
match action {
ContextMenuItemAction::Action(action) => KeystrokeLabel::new(
self.parent_view_id,
action.boxed_clone(),
style.keystroke.container,
style.keystroke.text.clone(),
)
.into_any(),
ContextMenuItemAction::Handler(_) => Empty::new().into_any(),
}
}
ContextMenuItem::Static(_) => Empty::new().into_any(),
ContextMenuItem::Separator => Empty::new()
.collapsed()
.constrained()
.with_height(1.)
.contained()
.with_style(style.separator)
.into_any(),
}
}))
.contained()
.with_margin_left(style.keystroke_margin),
)
.contained()
.with_style(style.container)
}
fn render_menu(&self, cx: &mut ViewContext<Self>) -> impl Element<ContextMenu> {
enum Menu {}
enum MenuItem {}
let style = theme::current(cx).context_menu.clone();
MouseEventHandler::new::<Menu, _>(0, cx, |_, cx| {
Flex::column()
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
match item {
ContextMenuItem::Item { label, action } => {
let action = action.clone();
let view_id = self.parent_view_id;
MouseEventHandler::new::<MenuItem, _>(ix, cx, |state, _| {
let style = style.item.in_state(self.selected_index == Some(ix));
let style = style.style_for(state);
let keystroke = match &action {
ContextMenuItemAction::Action(action) => Some(
KeystrokeLabel::new(
view_id,
action.boxed_clone(),
style.keystroke.container,
style.keystroke.text.clone(),
)
.flex_float(),
),
ContextMenuItemAction::Handler(_) => None,
};
Flex::row()
.with_child(match label {
ContextMenuItemLabel::String(label) => {
Label::new(label.clone(), style.label.clone())
.contained()
.into_any()
}
ContextMenuItemLabel::Element(element) => {
element(state, style)
}
})
.with_children(keystroke)
.contained()
.with_style(style.container)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_up(MouseButton::Left, |_, _, _| {}) // Capture these events
.on_down(MouseButton::Left, |_, _, _| {}) // Capture these events
.on_click(MouseButton::Left, move |_, menu, cx| {
menu.cancel(&Default::default(), cx);
let window = cx.window();
match &action {
ContextMenuItemAction::Action(action) => {
let action = action.boxed_clone();
cx.app_context()
.spawn(|mut cx| async move {
window
.dispatch_action(
view_id,
action.as_ref(),
&mut cx,
)
.ok_or_else(|| anyhow!("window was closed"))
})
.detach_and_log_err(cx);
}
ContextMenuItemAction::Handler(handler) => handler(cx),
}
})
.on_drag(MouseButton::Left, |_, _, _| {})
.into_any()
}
ContextMenuItem::Static(f) => f(cx),
ContextMenuItem::Separator => Empty::new()
.constrained()
.with_height(1.)
.contained()
.with_style(style.separator)
.into_any(),
}
}))
.contained()
.with_style(style.container)
})
.on_down_out(MouseButton::Left, |_, this, cx| {
this.cancel(&Default::default(), cx);
})
.on_down_out(MouseButton::Right, |_, this, cx| {
this.cancel(&Default::default(), cx);
})
}
}

View File

@ -1,16 +0,0 @@
[package]
name = "drag_and_drop"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/drag_and_drop.rs"
doctest = false
[dependencies]
collections = { path = "../collections" }
gpui = { path = "../gpui" }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }

View File

@ -1,378 +0,0 @@
use std::{any::Any, rc::Rc};
use collections::HashSet;
use gpui::{
elements::{Empty, MouseEventHandler, Overlay},
geometry::{rect::RectF, vector::Vector2F},
platform::{CursorStyle, Modifiers, MouseButton},
scene::{MouseDown, MouseDrag},
AnyElement, AnyWindowHandle, Element, View, ViewContext, WeakViewHandle, WindowContext,
};
const DEAD_ZONE: f32 = 4.;
enum State<V> {
Down {
region_offset: Vector2F,
region: RectF,
},
DeadZone {
region_offset: Vector2F,
region: RectF,
},
Dragging {
modifiers: Modifiers,
window: AnyWindowHandle,
position: Vector2F,
region_offset: Vector2F,
region: RectF,
payload: Rc<dyn Any + 'static>,
render: Rc<dyn Fn(&Modifiers, Rc<dyn Any>, &mut ViewContext<V>) -> AnyElement<V>>,
},
Canceled,
}
impl<V> Clone for State<V> {
fn clone(&self) -> Self {
match self {
&State::Down {
region_offset,
region,
} => State::Down {
region_offset,
region,
},
&State::DeadZone {
region_offset,
region,
} => State::DeadZone {
region_offset,
region,
},
State::Dragging {
modifiers,
window,
position,
region_offset,
region,
payload,
render,
} => Self::Dragging {
window: window.clone(),
position: position.clone(),
region_offset: region_offset.clone(),
region: region.clone(),
payload: payload.clone(),
render: render.clone(),
modifiers: modifiers.clone(),
},
State::Canceled => State::Canceled,
}
}
}
pub struct DragAndDrop<V> {
containers: HashSet<WeakViewHandle<V>>,
currently_dragged: Option<State<V>>,
}
impl<V> Default for DragAndDrop<V> {
fn default() -> Self {
Self {
containers: Default::default(),
currently_dragged: Default::default(),
}
}
}
impl<V: 'static> DragAndDrop<V> {
pub fn register_container(&mut self, handle: WeakViewHandle<V>) {
self.containers.insert(handle);
}
pub fn currently_dragged<T: Any>(&self, window: AnyWindowHandle) -> Option<(Vector2F, Rc<T>)> {
self.currently_dragged.as_ref().and_then(|state| {
if let State::Dragging {
position,
payload,
window: window_dragged_from,
..
} = state
{
if &window != window_dragged_from {
return None;
}
payload
.is::<T>()
.then(|| payload.clone().downcast::<T>().ok())
.flatten()
.map(|payload| (position.clone(), payload))
} else {
None
}
})
}
pub fn any_currently_dragged(&self, window: AnyWindowHandle) -> bool {
self.currently_dragged
.as_ref()
.map(|state| {
if let State::Dragging {
window: window_dragged_from,
..
} = state
{
if &window != window_dragged_from {
return false;
}
true
} else {
false
}
})
.unwrap_or(false)
}
pub fn drag_started(event: MouseDown, cx: &mut WindowContext) {
cx.update_global(|this: &mut Self, _| {
this.currently_dragged = Some(State::Down {
region_offset: event.position - event.region.origin(),
region: event.region,
});
})
}
pub fn dragging<T: Any>(
event: MouseDrag,
payload: Rc<T>,
cx: &mut WindowContext,
render: Rc<impl 'static + Fn(&Modifiers, &T, &mut ViewContext<V>) -> AnyElement<V>>,
) {
let window = cx.window();
cx.update_global(|this: &mut Self, cx| {
this.notify_containers_for_window(window, cx);
match this.currently_dragged.as_ref() {
Some(&State::Down {
region_offset,
region,
})
| Some(&State::DeadZone {
region_offset,
region,
}) => {
if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE {
this.currently_dragged = Some(State::Dragging {
modifiers: event.modifiers,
window,
region_offset,
region,
position: event.position,
payload,
render: Rc::new(move |modifiers, payload, cx| {
render(modifiers, payload.downcast_ref::<T>().unwrap(), cx)
}),
});
} else {
this.currently_dragged = Some(State::DeadZone {
region_offset,
region,
})
}
}
Some(&State::Dragging {
region_offset,
region,
modifiers,
..
}) => {
this.currently_dragged = Some(State::Dragging {
modifiers,
window,
region_offset,
region,
position: event.position,
payload,
render: Rc::new(move |modifiers, payload, cx| {
render(modifiers, payload.downcast_ref::<T>().unwrap(), cx)
}),
});
}
_ => {}
}
});
}
pub fn update_modifiers(new_modifiers: Modifiers, cx: &mut ViewContext<V>) -> bool {
let result = cx.update_global(|this: &mut Self, _| match &mut this.currently_dragged {
Some(state) => match state {
State::Dragging { modifiers, .. } => {
*modifiers = new_modifiers;
true
}
_ => false,
},
None => false,
});
if result {
cx.notify();
}
result
}
pub fn render(cx: &mut ViewContext<V>) -> Option<AnyElement<V>> {
enum DraggedElementHandler {}
cx.global::<Self>()
.currently_dragged
.clone()
.and_then(|state| {
match state {
State::Down { .. } => None,
State::DeadZone { .. } => None,
State::Dragging {
modifiers,
window,
region_offset,
position,
region,
payload,
render,
} => {
if cx.window() != window {
return None;
}
let position = (position - region_offset).round();
Some(
Overlay::new(
MouseEventHandler::new::<DraggedElementHandler, _>(
0,
cx,
|_, cx| render(&modifiers, payload, cx),
)
.with_cursor_style(CursorStyle::Arrow)
.on_up(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, cx| {
this.finish_dragging(cx)
});
});
cx.propagate_event();
})
.on_up_out(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, cx| {
this.finish_dragging(cx)
});
});
})
// Don't block hover events or invalidations
.with_hoverable(false)
.constrained()
.with_width(region.width())
.with_height(region.height()),
)
.with_anchor_position(position)
.into_any(),
)
}
State::Canceled => Some(
MouseEventHandler::new::<DraggedElementHandler, _>(0, cx, |_, _| {
Empty::new().constrained().with_width(0.).with_height(0.)
})
.on_up(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, _| {
this.currently_dragged = None;
});
});
})
.on_up_out(MouseButton::Left, |_, _, cx| {
cx.window_context().defer(|cx| {
cx.update_global::<Self, _, _>(|this, _| {
this.currently_dragged = None;
});
});
})
.into_any(),
),
}
})
}
pub fn cancel_dragging<P: Any>(&mut self, cx: &mut WindowContext) {
if let Some(State::Dragging {
payload, window, ..
}) = &self.currently_dragged
{
if payload.is::<P>() {
let window = *window;
self.currently_dragged = Some(State::Canceled);
self.notify_containers_for_window(window, cx);
}
}
}
fn finish_dragging(&mut self, cx: &mut WindowContext) {
if let Some(State::Dragging { window, .. }) = self.currently_dragged.take() {
self.notify_containers_for_window(window, cx);
}
}
fn notify_containers_for_window(&mut self, window: AnyWindowHandle, cx: &mut WindowContext) {
self.containers.retain(|container| {
if let Some(container) = container.upgrade(cx) {
if container.window() == window {
container.update(cx, |_, cx| cx.notify());
}
true
} else {
false
}
});
}
}
pub trait Draggable<V> {
fn as_draggable<D: View, P: Any>(
self,
payload: P,
render: impl 'static + Fn(&Modifiers, &P, &mut ViewContext<D>) -> AnyElement<D>,
) -> Self
where
Self: Sized;
}
impl<V: 'static> Draggable<V> for MouseEventHandler<V> {
fn as_draggable<D: View, P: Any>(
self,
payload: P,
render: impl 'static + Fn(&Modifiers, &P, &mut ViewContext<D>) -> AnyElement<D>,
) -> Self
where
Self: Sized,
{
let payload = Rc::new(payload);
let render = Rc::new(render);
self.on_down(MouseButton::Left, move |e, _, cx| {
cx.propagate_event();
DragAndDrop::<D>::drag_started(e, cx);
})
.on_drag(MouseButton::Left, move |e, _, cx| {
if e.end {
cx.update_global::<DragAndDrop<D>, _, _>(|drag_and_drop, cx| {
drag_and_drop.finish_dragging(cx)
})
} else {
let payload = payload.clone();
let render = render.clone();
DragAndDrop::<D>::dragging(e, payload, cx, render)
}
})
}
}

View File

@ -15,7 +15,7 @@ doctest = false
[dependencies]
collections = { path = "../collections" }
gpui2_macros = { path = "../gpui2_macros" }
gpui_macros = { path = "../gpui_macros" }
util = { path = "../util" }
sum_tree = { path = "../sum_tree" }
sqlez = { path = "../sqlez" }

View File

@ -47,7 +47,7 @@ pub use element::*;
pub use elements::*;
pub use executor::*;
pub use geometry::*;
pub use gpui2_macros::*;
pub use gpui_macros::*;
pub use image_cache::*;
pub use input::*;
pub use interactive::*;

View File

@ -10,7 +10,7 @@ use taffy::style::Overflow;
pub trait Styled: Sized {
fn style(&mut self) -> &mut StyleRefinement;
gpui2_macros::style_helpers!();
gpui_macros::style_helpers!();
fn z_index(mut self, z_index: u8) -> Self {
self.style().z_index = Some(z_index);

View File

@ -1,11 +1,9 @@
use gpui2::{actions, impl_actions};
use gpui2_macros::register_action;
use gpui::{actions, impl_actions};
use gpui_macros::register_action;
use serde_derive::Deserialize;
#[test]
fn test_action_macros() {
use gpui2 as gpui;
actions!(test, [TestAction]);
#[derive(PartialEq, Clone, Deserialize)]

View File

@ -1,14 +0,0 @@
[package]
name = "gpui2_macros"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/gpui2_macros.rs"
proc-macro = true
[dependencies]
syn = { version = "1.0.72", features = ["full"] }
quote = "1.0.9"
proc-macro2 = "1.0.66"

View File

@ -1,32 +0,0 @@
mod derive_into_element;
mod derive_render;
mod register_action;
mod style_helpers;
mod test;
use proc_macro::TokenStream;
#[proc_macro]
pub fn register_action(ident: TokenStream) -> TokenStream {
register_action::register_action_macro(ident)
}
#[proc_macro_derive(IntoElement)]
pub fn derive_into_element(input: TokenStream) -> TokenStream {
derive_into_element::derive_into_element(input)
}
#[proc_macro_derive(Render)]
pub fn derive_render(input: TokenStream) -> TokenStream {
derive_render::derive_render(input)
}
#[proc_macro]
pub fn style_helpers(input: TokenStream) -> TokenStream {
style_helpers::style_helpers(input)
}
#[proc_macro_attribute]
pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
test::test(args, function)
}

View File

@ -7,10 +7,8 @@ publish = false
[lib]
path = "src/gpui_macros.rs"
proc-macro = true
doctest = false
[dependencies]
lazy_static.workspace = true
proc-macro2 = "1.0"
syn = "1.0"
quote = "1.0"
syn = { version = "1.0.72", features = ["full"] }
quote = "1.0.9"
proc-macro2 = "1.0.66"

View File

@ -13,7 +13,7 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
{
type Element = gpui::Component<Self>;
fn element_id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<gpui::ElementId> {
None
}

View File

@ -11,7 +11,7 @@ pub fn derive_render(input: TokenStream) -> TokenStream {
impl #impl_generics gpui::Render for #type_name #type_generics
#where_clause
{
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> impl gpui::Element {
()
}
}

View File

@ -1,381 +1,32 @@
mod derive_into_element;
mod derive_render;
mod register_action;
mod style_helpers;
mod test;
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{format_ident, quote};
use std::mem;
use syn::{
parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, DeriveInput, FnArg,
GenericParam, Generics, ItemFn, Lit, Meta, NestedMeta, Type, WhereClause,
};
#[proc_macro]
pub fn register_action(ident: TokenStream) -> TokenStream {
register_action::register_action_macro(ident)
}
#[proc_macro_derive(IntoElement)]
pub fn derive_into_element(input: TokenStream) -> TokenStream {
derive_into_element::derive_into_element(input)
}
#[proc_macro_derive(Render)]
pub fn derive_render(input: TokenStream) -> TokenStream {
derive_render::derive_render(input)
}
#[proc_macro]
pub fn style_helpers(input: TokenStream) -> TokenStream {
style_helpers::style_helpers(input)
}
#[proc_macro_attribute]
pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
let mut namespace = format_ident!("gpui");
let args = syn::parse_macro_input!(args as AttributeArgs);
let mut max_retries = 0;
let mut num_iterations = 1;
let mut starting_seed = 0;
let mut detect_nondeterminism = false;
let mut on_failure_fn_name = quote!(None);
for arg in args {
match arg {
NestedMeta::Meta(Meta::Path(name))
if name.get_ident().map_or(false, |n| n == "self") =>
{
namespace = format_ident!("crate");
}
NestedMeta::Meta(Meta::NameValue(meta)) => {
let key_name = meta.path.get_ident().map(|i| i.to_string());
let result = (|| {
match key_name.as_deref() {
Some("detect_nondeterminism") => {
detect_nondeterminism = parse_bool(&meta.lit)?
}
Some("retries") => max_retries = parse_int(&meta.lit)?,
Some("iterations") => num_iterations = parse_int(&meta.lit)?,
Some("seed") => starting_seed = parse_int(&meta.lit)?,
Some("on_failure") => {
if let Lit::Str(name) = meta.lit {
let mut path = syn::Path {
leading_colon: None,
segments: Default::default(),
};
for part in name.value().split("::") {
path.segments.push(Ident::new(part, name.span()).into());
}
on_failure_fn_name = quote!(Some(#path));
} else {
return Err(TokenStream::from(
syn::Error::new(
meta.lit.span(),
"on_failure argument must be a string",
)
.into_compile_error(),
));
}
}
_ => {
return Err(TokenStream::from(
syn::Error::new(meta.path.span(), "invalid argument")
.into_compile_error(),
))
}
}
Ok(())
})();
if let Err(tokens) = result {
return tokens;
}
}
other => {
return TokenStream::from(
syn::Error::new_spanned(other, "invalid argument").into_compile_error(),
)
}
}
}
let mut inner_fn = parse_macro_input!(function as ItemFn);
if max_retries > 0 && num_iterations > 1 {
return TokenStream::from(
syn::Error::new_spanned(inner_fn, "retries and randomized iterations can't be mixed")
.into_compile_error(),
);
}
let inner_fn_attributes = mem::take(&mut inner_fn.attrs);
let inner_fn_name = format_ident!("_{}", inner_fn.sig.ident);
let outer_fn_name = mem::replace(&mut inner_fn.sig.ident, inner_fn_name.clone());
let mut outer_fn: ItemFn = if inner_fn.sig.asyncness.is_some() {
// Pass to the test function the number of app contexts that it needs,
// based on its parameter list.
let mut cx_vars = proc_macro2::TokenStream::new();
let mut cx_teardowns = proc_macro2::TokenStream::new();
let mut inner_fn_args = proc_macro2::TokenStream::new();
for (ix, arg) in inner_fn.sig.inputs.iter().enumerate() {
if let FnArg::Typed(arg) = arg {
if let Type::Path(ty) = &*arg.ty {
let last_segment = ty.path.segments.last();
match last_segment.map(|s| s.ident.to_string()).as_deref() {
Some("StdRng") => {
inner_fn_args.extend(quote!(rand::SeedableRng::seed_from_u64(seed),));
continue;
}
Some("Arc") => {
if let syn::PathArguments::AngleBracketed(args) =
&last_segment.unwrap().arguments
{
if let Some(syn::GenericArgument::Type(syn::Type::Path(ty))) =
args.args.last()
{
let last_segment = ty.path.segments.last();
if let Some("Deterministic") =
last_segment.map(|s| s.ident.to_string()).as_deref()
{
inner_fn_args.extend(quote!(deterministic.clone(),));
continue;
}
}
}
}
_ => {}
}
} else if let Type::Reference(ty) = &*arg.ty {
if let Type::Path(ty) = &*ty.elem {
let last_segment = ty.path.segments.last();
if let Some("TestAppContext") =
last_segment.map(|s| s.ident.to_string()).as_deref()
{
let first_entity_id = ix * 100_000;
let cx_varname = format_ident!("cx_{}", ix);
cx_vars.extend(quote!(
let mut #cx_varname = #namespace::TestAppContext::new(
foreground_platform.clone(),
cx.platform().clone(),
deterministic.build_foreground(#ix),
deterministic.build_background(),
cx.font_cache().clone(),
cx.leak_detector(),
#first_entity_id,
stringify!(#outer_fn_name).to_string(),
);
));
cx_teardowns.extend(quote!(
#cx_varname.remove_all_windows();
deterministic.run_until_parked();
#cx_varname.update(|cx| cx.clear_globals());
));
inner_fn_args.extend(quote!(&mut #cx_varname,));
continue;
}
}
}
}
return TokenStream::from(
syn::Error::new_spanned(arg, "invalid argument").into_compile_error(),
);
}
parse_quote! {
#[test]
fn #outer_fn_name() {
#inner_fn
#namespace::test::run_test(
#num_iterations as u64,
#starting_seed as u64,
#max_retries,
#detect_nondeterminism,
&mut |cx, foreground_platform, deterministic, seed| {
// some of the macro contents do not use all variables, silence the warnings
let _ = (&cx, &foreground_platform, &deterministic, &seed);
#cx_vars
cx.foreground().run(#inner_fn_name(#inner_fn_args));
#cx_teardowns
},
#on_failure_fn_name,
stringify!(#outer_fn_name).to_string(),
);
}
}
} else {
// Pass to the test function the number of app contexts that it needs,
// based on its parameter list.
let mut cx_vars = proc_macro2::TokenStream::new();
let mut cx_teardowns = proc_macro2::TokenStream::new();
let mut inner_fn_args = proc_macro2::TokenStream::new();
for (ix, arg) in inner_fn.sig.inputs.iter().enumerate() {
if let FnArg::Typed(arg) = arg {
if let Type::Path(ty) = &*arg.ty {
let last_segment = ty.path.segments.last();
if let Some("StdRng") = last_segment.map(|s| s.ident.to_string()).as_deref() {
inner_fn_args.extend(quote!(rand::SeedableRng::seed_from_u64(seed),));
continue;
}
} else if let Type::Reference(ty) = &*arg.ty {
if let Type::Path(ty) = &*ty.elem {
let last_segment = ty.path.segments.last();
match last_segment.map(|s| s.ident.to_string()).as_deref() {
Some("AppContext") => {
inner_fn_args.extend(quote!(cx,));
continue;
}
Some("TestAppContext") => {
let first_entity_id = ix * 100_000;
let cx_varname = format_ident!("cx_{}", ix);
cx_vars.extend(quote!(
let mut #cx_varname = #namespace::TestAppContext::new(
foreground_platform.clone(),
cx.platform().clone(),
deterministic.build_foreground(#ix),
deterministic.build_background(),
cx.font_cache().clone(),
cx.leak_detector(),
#first_entity_id,
stringify!(#outer_fn_name).to_string(),
);
));
cx_teardowns.extend(quote!(
#cx_varname.remove_all_windows();
deterministic.run_until_parked();
#cx_varname.update(|cx| cx.clear_globals());
));
inner_fn_args.extend(quote!(&mut #cx_varname,));
continue;
}
_ => {}
}
}
}
}
return TokenStream::from(
syn::Error::new_spanned(arg, "invalid argument").into_compile_error(),
);
}
parse_quote! {
#[test]
fn #outer_fn_name() {
#inner_fn
#namespace::test::run_test(
#num_iterations as u64,
#starting_seed as u64,
#max_retries,
#detect_nondeterminism,
&mut |cx, foreground_platform, deterministic, seed| {
// some of the macro contents do not use all variables, silence the warnings
let _ = (&cx, &foreground_platform, &deterministic, &seed);
#cx_vars
#inner_fn_name(#inner_fn_args);
#cx_teardowns
},
#on_failure_fn_name,
stringify!(#outer_fn_name).to_string(),
);
}
}
};
outer_fn.attrs.extend(inner_fn_attributes);
TokenStream::from(quote!(#outer_fn))
}
fn parse_int(literal: &Lit) -> Result<usize, TokenStream> {
let result = if let Lit::Int(int) = &literal {
int.base10_parse()
} else {
Err(syn::Error::new(literal.span(), "must be an integer"))
};
result.map_err(|err| TokenStream::from(err.into_compile_error()))
}
fn parse_bool(literal: &Lit) -> Result<bool, TokenStream> {
let result = if let Lit::Bool(result) = &literal {
Ok(result.value)
} else {
Err(syn::Error::new(literal.span(), "must be a boolean"))
};
result.map_err(|err| TokenStream::from(err.into_compile_error()))
}
#[proc_macro_derive(Element)]
pub fn element_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let type_name = ast.ident;
let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
let placeholder_view_type_name: Ident = parse_quote! { V };
let view_type_name: Ident;
let impl_generics: syn::ImplGenerics<'_>;
let type_generics: Option<syn::TypeGenerics<'_>>;
let where_clause: Option<&'_ WhereClause>;
match ast.generics.params.iter().find_map(|param| {
if let GenericParam::Type(type_param) = param {
Some(type_param.ident.clone())
} else {
None
}
}) {
Some(type_name) => {
view_type_name = type_name;
let generics = ast.generics.split_for_impl();
impl_generics = generics.0;
type_generics = Some(generics.1);
where_clause = generics.2;
}
_ => {
view_type_name = placeholder_view_type_name;
let generics = placeholder_view_generics.split_for_impl();
impl_generics = generics.0;
type_generics = None;
where_clause = generics.2;
}
}
let gen = quote! {
impl #impl_generics Element<#view_type_name> for #type_name #type_generics
#where_clause
{
type LayoutState = gpui::elements::AnyElement<V>;
type PaintState = ();
fn layout(
&mut self,
constraint: gpui::SizeConstraint,
view: &mut V,
cx: &mut gpui::ViewContext<V>,
) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) {
let mut element = self.render(view, cx).into_any();
let size = element.layout(constraint, view, cx);
(size, element)
}
fn paint(
&mut self,
bounds: gpui::geometry::rect::RectF,
visible_bounds: gpui::geometry::rect::RectF,
element: &mut gpui::elements::AnyElement<V>,
view: &mut V,
cx: &mut gpui::ViewContext<V>,
) {
element.paint(bounds.origin(), visible_bounds, view, cx);
}
fn rect_for_text_range(
&self,
range_utf16: std::ops::Range<usize>,
_: gpui::geometry::rect::RectF,
_: gpui::geometry::rect::RectF,
element: &gpui::elements::AnyElement<V>,
_: &(),
view: &V,
cx: &gpui::ViewContext<V>,
) -> Option<gpui::geometry::rect::RectF> {
element.rect_for_text_range(range_utf16, view, cx)
}
fn debug(
&self,
_: gpui::geometry::rect::RectF,
element: &gpui::elements::AnyElement<V>,
_: &(),
view: &V,
cx: &gpui::ViewContext<V>,
) -> gpui::json::Value {
element.debug(view, cx)
}
}
};
gen.into()
test::test(args, function)
}

View File

@ -342,7 +342,7 @@ impl Peer {
pub fn add_test_connection(
self: &Arc<Self>,
connection: Connection,
executor: Arc<gpui::executor::Background>,
executor: gpui::BackgroundExecutor,
) -> (
ConnectionId,
impl Future<Output = anyhow::Result<()>> + Send,
@ -559,7 +559,6 @@ mod tests {
use async_tungstenite::tungstenite::Message as WebSocketMessage;
use gpui::TestAppContext;
#[ctor::ctor]
fn init_logger() {
if std::env::var("RUST_LOG").is_ok() {
env_logger::init();
@ -568,7 +567,9 @@ mod tests {
#[gpui::test(iterations = 50)]
async fn test_request_response(cx: &mut TestAppContext) {
let executor = cx.foreground();
init_logger();
let executor = cx.executor();
// create 2 clients connected to 1 server
let server = Peer::new(0);
@ -576,18 +577,18 @@ mod tests {
let client2 = Peer::new(0);
let (client1_to_server_conn, server_to_client_1_conn, _kill) =
Connection::in_memory(cx.background());
Connection::in_memory(cx.executor());
let (client1_conn_id, io_task1, client1_incoming) =
client1.add_test_connection(client1_to_server_conn, cx.background());
client1.add_test_connection(client1_to_server_conn, cx.executor());
let (_, io_task2, server_incoming1) =
server.add_test_connection(server_to_client_1_conn, cx.background());
server.add_test_connection(server_to_client_1_conn, cx.executor());
let (client2_to_server_conn, server_to_client_2_conn, _kill) =
Connection::in_memory(cx.background());
Connection::in_memory(cx.executor());
let (client2_conn_id, io_task3, client2_incoming) =
client2.add_test_connection(client2_to_server_conn, cx.background());
client2.add_test_connection(client2_to_server_conn, cx.executor());
let (_, io_task4, server_incoming2) =
server.add_test_connection(server_to_client_2_conn, cx.background());
server.add_test_connection(server_to_client_2_conn, cx.executor());
executor.spawn(io_task1).detach();
executor.spawn(io_task2).detach();
@ -664,25 +665,25 @@ mod tests {
#[gpui::test(iterations = 50)]
async fn test_order_of_response_and_incoming(cx: &mut TestAppContext) {
let executor = cx.foreground();
let executor = cx.executor();
let server = Peer::new(0);
let client = Peer::new(0);
let (client_to_server_conn, server_to_client_conn, _kill) =
Connection::in_memory(cx.background());
Connection::in_memory(executor.clone());
let (client_to_server_conn_id, io_task1, mut client_incoming) =
client.add_test_connection(client_to_server_conn, cx.background());
client.add_test_connection(client_to_server_conn, executor.clone());
let (server_to_client_conn_id, io_task2, mut server_incoming) =
server.add_test_connection(server_to_client_conn, cx.background());
server.add_test_connection(server_to_client_conn, executor.clone());
executor.spawn(io_task1).detach();
executor.spawn(io_task2).detach();
executor
.spawn(async move {
let request = server_incoming
.next()
.await
let future = server_incoming.next().await;
let request = future
.unwrap()
.into_any()
.downcast::<TypedEnvelope<proto::Ping>>()
@ -762,16 +763,16 @@ mod tests {
#[gpui::test(iterations = 50)]
async fn test_dropping_request_before_completion(cx: &mut TestAppContext) {
let executor = cx.foreground();
let executor = cx.executor();
let server = Peer::new(0);
let client = Peer::new(0);
let (client_to_server_conn, server_to_client_conn, _kill) =
Connection::in_memory(cx.background());
Connection::in_memory(cx.executor());
let (client_to_server_conn_id, io_task1, mut client_incoming) =
client.add_test_connection(client_to_server_conn, cx.background());
client.add_test_connection(client_to_server_conn, cx.executor());
let (server_to_client_conn_id, io_task2, mut server_incoming) =
server.add_test_connection(server_to_client_conn, cx.background());
server.add_test_connection(server_to_client_conn, cx.executor());
executor.spawn(io_task1).detach();
executor.spawn(io_task2).detach();
@ -858,7 +859,7 @@ mod tests {
.detach();
// Allow the request to make some progress before dropping it.
cx.background().simulate_random_delay().await;
cx.executor().simulate_random_delay().await;
drop(request1_task);
request2_task.await;
@ -874,13 +875,13 @@ mod tests {
#[gpui::test(iterations = 50)]
async fn test_disconnect(cx: &mut TestAppContext) {
let executor = cx.foreground();
let executor = cx.executor();
let (client_conn, mut server_conn, _kill) = Connection::in_memory(cx.background());
let (client_conn, mut server_conn, _kill) = Connection::in_memory(executor.clone());
let client = Peer::new(0);
let (connection_id, io_handler, mut incoming) =
client.add_test_connection(client_conn, cx.background());
client.add_test_connection(client_conn, executor.clone());
let (io_ended_tx, io_ended_rx) = oneshot::channel();
executor
@ -910,12 +911,12 @@ mod tests {
#[gpui::test(iterations = 50)]
async fn test_io_error(cx: &mut TestAppContext) {
let executor = cx.foreground();
let (client_conn, mut server_conn, _kill) = Connection::in_memory(cx.background());
let executor = cx.executor();
let (client_conn, mut server_conn, _kill) = Connection::in_memory(executor.clone());
let client = Peer::new(0);
let (connection_id, io_handler, mut incoming) =
client.add_test_connection(client_conn, cx.background());
client.add_test_connection(client_conn, executor.clone());
executor.spawn(io_handler).detach();
executor
.spawn(async move { incoming.next().await })

View File

@ -31,6 +31,7 @@ smallvec.workspace = true
story = { path = "../story" }
theme = { path = "../theme" }
menu = { path = "../menu" }
ui = { path = "../ui", features = ["stories"] }
util = { path = "../util" }
picker = { path = "../picker" }

View File

@ -1,9 +1,8 @@
use gpui::{
div, AnyElement, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled,
div, AnyElement, Element, IntoElement, ParentElement, RenderOnce, Styled,
WindowContext,
};
use smallvec::SmallVec;
use crate::prelude::*;
use crate::v_stack;

View File

@ -1,13 +0,0 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
clap = {version = "4.0", features = ["derive"]}
theme = {path = "../theme"}
serde_json.workspace = true
schemars.workspace = true

View File

@ -1,23 +0,0 @@
use clap::{Parser, Subcommand};
use std::path::PathBuf;
/// Common utilities for Zed developers.
// For more information, see [matklad's repository README](https://github.com/matklad/cargo-xtask/)
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
/// Command to run.
#[derive(Subcommand)]
pub enum Commands {
/// Builds theme types for interop with Typescript.
BuildThemeTypes {
#[clap(short, long, default_value = "schemas")]
out_dir: PathBuf,
#[clap(short, long, default_value = "theme.json")]
file_name: PathBuf,
},
}

View File

@ -1,29 +0,0 @@
mod cli;
use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use schemars::schema_for;
use theme::Theme;
fn build_themes(out_dir: PathBuf, file_name: PathBuf) -> Result<()> {
let theme = schema_for!(Theme);
let output = serde_json::to_string_pretty(&theme)?;
std::fs::create_dir(&out_dir)?;
let mut file_path = out_dir;
file_path.push(file_name);
std::fs::write(file_path, output)?;
Ok(())
}
fn main() -> Result<()> {
let args = cli::Cli::parse();
match args.command {
cli::Commands::BuildThemeTypes { out_dir, file_name } => build_themes(out_dir, file_name),
}
}