Merge branch 'main' into collab_ui-zed2

This commit is contained in:
Piotr Osiewicz 2023-11-23 15:02:55 +01:00
commit 390ecb5b0c
51 changed files with 339 additions and 323 deletions

View file

@ -469,7 +469,7 @@ impl ActiveCall {
&self.pending_invites &self.pending_invites
} }
pub fn report_call_event(&self, operation: &'static str, cx: &AppContext) { pub fn report_call_event(&self, operation: &'static str, cx: &mut AppContext) {
if let Some(room) = self.room() { if let Some(room) = self.room() {
let room = room.read(cx); let room = room.read(cx);
report_call_event_for_room(operation, room.id(), room.channel_id(), &self.client, cx); report_call_event_for_room(operation, room.id(), room.channel_id(), &self.client, cx);
@ -482,7 +482,7 @@ pub fn report_call_event_for_room(
room_id: u64, room_id: u64,
channel_id: Option<u64>, channel_id: Option<u64>,
client: &Arc<Client>, client: &Arc<Client>,
cx: &AppContext, cx: &mut AppContext,
) { ) {
let telemetry = client.telemetry(); let telemetry = client.telemetry();
let telemetry_settings = *TelemetrySettings::get_global(cx); let telemetry_settings = *TelemetrySettings::get_global(cx);

View file

@ -109,6 +109,10 @@ pub enum ClickhouseEvent {
virtual_memory_in_bytes: u64, virtual_memory_in_bytes: u64,
milliseconds_since_first_event: i64, milliseconds_since_first_event: i64,
}, },
App {
operation: &'static str,
milliseconds_since_first_event: i64,
},
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -168,13 +172,8 @@ impl Telemetry {
let mut state = self.state.lock(); let mut state = self.state.lock();
state.installation_id = installation_id.map(|id| id.into()); state.installation_id = installation_id.map(|id| id.into());
state.session_id = Some(session_id.into()); state.session_id = Some(session_id.into());
let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
drop(state); drop(state);
if has_clickhouse_events {
self.flush_clickhouse_events();
}
let this = self.clone(); let this = self.clone();
cx.spawn(|mut cx| async move { cx.spawn(|mut cx| async move {
// Avoiding calling `System::new_all()`, as there have been crashes related to it // Avoiding calling `System::new_all()`, as there have been crashes related to it
@ -256,7 +255,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_copilot_event( pub fn report_copilot_event(
@ -273,7 +272,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_assistant_event( pub fn report_assistant_event(
@ -290,7 +289,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_call_event( pub fn report_call_event(
@ -307,7 +306,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_cpu_event( pub fn report_cpu_event(
@ -322,7 +321,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_memory_event( pub fn report_memory_event(
@ -337,7 +336,21 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
}
// app_events are called at app open and app close, so flush is set to immediately send
pub fn report_app_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
operation: &'static str,
) {
let event = ClickhouseEvent::App {
operation,
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, true)
} }
fn milliseconds_since_first_event(&self) -> i64 { fn milliseconds_since_first_event(&self) -> i64 {
@ -358,6 +371,7 @@ impl Telemetry {
self: &Arc<Self>, self: &Arc<Self>,
event: ClickhouseEvent, event: ClickhouseEvent,
telemetry_settings: TelemetrySettings, telemetry_settings: TelemetrySettings,
immediate_flush: bool,
) { ) {
if !telemetry_settings.metrics { if !telemetry_settings.metrics {
return; return;
@ -370,7 +384,7 @@ impl Telemetry {
.push(ClickhouseEventWrapper { signed_in, event }); .push(ClickhouseEventWrapper { signed_in, event });
if state.installation_id.is_some() { if state.installation_id.is_some() {
if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN { if immediate_flush || state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN {
drop(state); drop(state);
self.flush_clickhouse_events(); self.flush_clickhouse_events();
} else { } else {

View file

@ -382,7 +382,7 @@ impl settings::Settings for TelemetrySettings {
} }
impl Client { impl Client {
pub fn new(http: Arc<dyn HttpClient>, cx: &AppContext) -> Arc<Self> { pub fn new(http: Arc<dyn HttpClient>, cx: &mut AppContext) -> Arc<Self> {
Arc::new(Self { Arc::new(Self {
id: AtomicU64::new(0), id: AtomicU64::new(0),
peer: Peer::new(0), peer: Peer::new(0),

View file

@ -107,6 +107,10 @@ pub enum ClickhouseEvent {
virtual_memory_in_bytes: u64, virtual_memory_in_bytes: u64,
milliseconds_since_first_event: i64, milliseconds_since_first_event: i64,
}, },
App {
operation: &'static str,
milliseconds_since_first_event: i64,
},
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -122,12 +126,13 @@ const DEBOUNCE_INTERVAL: Duration = Duration::from_secs(1);
const DEBOUNCE_INTERVAL: Duration = Duration::from_secs(30); const DEBOUNCE_INTERVAL: Duration = Duration::from_secs(30);
impl Telemetry { impl Telemetry {
pub fn new(client: Arc<dyn HttpClient>, cx: &AppContext) -> Arc<Self> { pub fn new(client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Arc<Self> {
let release_channel = if cx.has_global::<ReleaseChannel>() { let release_channel = if cx.has_global::<ReleaseChannel>() {
Some(cx.global::<ReleaseChannel>().display_name()) Some(cx.global::<ReleaseChannel>().display_name())
} else { } else {
None None
}; };
// TODO: Replace all hardware stuff with nested SystemSpecs json // TODO: Replace all hardware stuff with nested SystemSpecs json
let this = Arc::new(Self { let this = Arc::new(Self {
http_client: client, http_client: client,
@ -147,9 +152,22 @@ impl Telemetry {
}), }),
}); });
// We should only ever have one instance of Telemetry, leak the subscription to keep it alive
// rather than store in TelemetryState, complicating spawn as subscriptions are not Send
// std::mem::forget(cx.on_app_quit({
// let this = this.clone();
// move |cx| this.shutdown_telemetry(cx)
// }));
this this
} }
// fn shutdown_telemetry(self: &Arc<Self>, cx: &mut AppContext) -> impl Future<Output = ()> {
// let telemetry_settings = TelemetrySettings::get_global(cx).clone();
// self.report_app_event(telemetry_settings, "close");
// Task::ready(())
// }
pub fn log_file_path(&self) -> Option<PathBuf> { pub fn log_file_path(&self) -> Option<PathBuf> {
Some(self.state.lock().log_file.as_ref()?.path().to_path_buf()) Some(self.state.lock().log_file.as_ref()?.path().to_path_buf())
} }
@ -163,13 +181,8 @@ impl Telemetry {
let mut state = self.state.lock(); let mut state = self.state.lock();
state.installation_id = installation_id.map(|id| id.into()); state.installation_id = installation_id.map(|id| id.into());
state.session_id = Some(session_id.into()); state.session_id = Some(session_id.into());
let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
drop(state); drop(state);
if has_clickhouse_events {
self.flush_clickhouse_events();
}
let this = self.clone(); let this = self.clone();
cx.spawn(|cx| async move { cx.spawn(|cx| async move {
// Avoiding calling `System::new_all()`, as there have been crashes related to it // Avoiding calling `System::new_all()`, as there have been crashes related to it
@ -257,7 +270,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_copilot_event( pub fn report_copilot_event(
@ -274,7 +287,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_assistant_event( pub fn report_assistant_event(
@ -291,7 +304,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_call_event( pub fn report_call_event(
@ -308,7 +321,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_cpu_event( pub fn report_cpu_event(
@ -323,7 +336,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
} }
pub fn report_memory_event( pub fn report_memory_event(
@ -338,7 +351,21 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(), milliseconds_since_first_event: self.milliseconds_since_first_event(),
}; };
self.report_clickhouse_event(event, telemetry_settings) self.report_clickhouse_event(event, telemetry_settings, false)
}
// app_events are called at app open and app close, so flush is set to immediately send
pub fn report_app_event(
self: &Arc<Self>,
telemetry_settings: TelemetrySettings,
operation: &'static str,
) {
let event = ClickhouseEvent::App {
operation,
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
self.report_clickhouse_event(event, telemetry_settings, true)
} }
fn milliseconds_since_first_event(&self) -> i64 { fn milliseconds_since_first_event(&self) -> i64 {
@ -359,6 +386,7 @@ impl Telemetry {
self: &Arc<Self>, self: &Arc<Self>,
event: ClickhouseEvent, event: ClickhouseEvent,
telemetry_settings: TelemetrySettings, telemetry_settings: TelemetrySettings,
immediate_flush: bool,
) { ) {
if !telemetry_settings.metrics { if !telemetry_settings.metrics {
return; return;
@ -371,7 +399,7 @@ impl Telemetry {
.push(ClickhouseEventWrapper { signed_in, event }); .push(ClickhouseEventWrapper { signed_in, event });
if state.installation_id.is_some() { if state.installation_id.is_some() {
if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN { if immediate_flush || state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN {
drop(state); drop(state);
self.flush_clickhouse_events(); self.flush_clickhouse_events();
} else { } else {

View file

@ -149,7 +149,7 @@ impl TestServer {
.user_id .user_id
}; };
let client_name = name.to_string(); let client_name = name.to_string();
let mut client = cx.read(|cx| Client::new(http.clone(), cx)); let mut client = cx.update(|cx| Client::new(http.clone(), cx));
let server = self.server.clone(); let server = self.server.clone();
let db = self.app_state.db.clone(); let db = self.app_state.db.clone();
let connection_killers = self.connection_killers.clone(); let connection_killers = self.connection_killers.clone();

View file

@ -31,7 +31,7 @@ use std::sync::Arc;
use call::ActiveCall; use call::ActiveCall;
use client::{Client, UserStore}; use client::{Client, UserStore};
use gpui::{ use gpui::{
div, px, rems, AppContext, Div, InteractiveElement, Model, ParentElement, Render, RenderOnce, div, px, rems, AppContext, Div, InteractiveElement, IntoElement, Model, ParentElement, Render,
Stateful, StatefulInteractiveElement, Styled, Subscription, ViewContext, VisualContext, Stateful, StatefulInteractiveElement, Styled, Subscription, ViewContext, VisualContext,
WeakView, WindowBounds, WeakView, WindowBounds,
}; };

View file

@ -14,8 +14,8 @@ use editor::{
use futures::future::try_join_all; use futures::future::try_join_all;
use gpui::{ use gpui::{
actions, div, AnyElement, AnyView, AppContext, Context, Div, EventEmitter, FocusEvent, actions, div, AnyElement, AnyView, AppContext, Context, Div, EventEmitter, FocusEvent,
FocusHandle, Focusable, FocusableElement, FocusableView, InteractiveElement, Model, FocusHandle, Focusable, FocusableElement, FocusableView, InteractiveElement, IntoElement,
ParentElement, Render, RenderOnce, SharedString, Styled, Subscription, Task, View, ViewContext, Model, ParentElement, Render, SharedString, Styled, Subscription, Task, View, ViewContext,
VisualContext, WeakView, WindowContext, VisualContext, WeakView, WindowContext,
}; };
use language::{ use language::{
@ -792,14 +792,14 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
.when_some(diagnostic.code.as_ref(), |stack, code| { .when_some(diagnostic.code.as_ref(), |stack, code| {
stack.child(Label::new(code.clone())) stack.child(Label::new(code.clone()))
}) })
.render_into_any() .into_any_element()
}) })
} }
pub(crate) fn render_summary(summary: &DiagnosticSummary) -> AnyElement { pub(crate) fn render_summary(summary: &DiagnosticSummary) -> AnyElement {
if summary.error_count == 0 && summary.warning_count == 0 { if summary.error_count == 0 && summary.warning_count == 0 {
let label = Label::new("No problems"); let label = Label::new("No problems");
label.render_into_any() label.into_any_element()
} else { } else {
h_stack() h_stack()
.bg(gpui::red()) .bg(gpui::red())
@ -807,7 +807,7 @@ pub(crate) fn render_summary(summary: &DiagnosticSummary) -> AnyElement {
.child(Label::new(summary.error_count.to_string())) .child(Label::new(summary.error_count.to_string()))
.child(IconElement::new(Icon::ExclamationTriangle)) .child(IconElement::new(Icon::ExclamationTriangle))
.child(Label::new(summary.warning_count.to_string())) .child(Label::new(summary.warning_count.to_string()))
.render_into_any() .into_any_element()
} }
} }

View file

@ -40,9 +40,9 @@ use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display; use git::diff_hunk_to_display;
use gpui::{ use gpui::{
actions, div, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement, actions, div, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement,
AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
EventEmitter, FocusHandle, FocusableView, FontFeatures, FontStyle, FontWeight, HighlightStyle, EventEmitter, FocusHandle, FocusableView, FontFeatures, FontStyle, FontWeight, HighlightStyle,
Hsla, InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render, Hsla, InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render, RenderOnce,
SharedString, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, SharedString, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View,
ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
}; };
@ -966,20 +966,22 @@ impl CompletionsMenu {
fn pre_resolve_completion_documentation( fn pre_resolve_completion_documentation(
&self, &self,
project: Option<Model<Project>>, _editor: &Editor,
cx: &mut ViewContext<Editor>, _cx: &mut ViewContext<Editor>,
) { ) -> Option<Task<()>> {
// todo!("implementation below "); // todo!("implementation below ");
None
} }
// ) { // {
// let settings = EditorSettings::get_global(cx); // let settings = EditorSettings::get_global(cx);
// if !settings.show_completion_documentation { // if !settings.show_completion_documentation {
// return; // return None;
// } // }
// let Some(project) = project else { // let Some(project) = editor.project.clone() else {
// return; // return None;
// }; // };
// let client = project.read(cx).client(); // let client = project.read(cx).client();
// let language_registry = project.read(cx).languages().clone(); // let language_registry = project.read(cx).languages().clone();
@ -989,7 +991,7 @@ impl CompletionsMenu {
// let completions = self.completions.clone(); // let completions = self.completions.clone();
// let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect(); // let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect();
// cx.spawn(move |this, mut cx| async move { // Some(cx.spawn(move |this, mut cx| async move {
// if is_remote { // if is_remote {
// let Some(project_id) = project_id else { // let Some(project_id) = project_id else {
// log::error!("Remote project without remote_id"); // log::error!("Remote project without remote_id");
@ -1051,8 +1053,7 @@ impl CompletionsMenu {
// _ = this.update(&mut cx, |_, cx| cx.notify()); // _ = this.update(&mut cx, |_, cx| cx.notify());
// } // }
// } // }
// }) // }))
// .detach();
// } // }
fn attempt_resolve_selected_completion_documentation( fn attempt_resolve_selected_completion_documentation(
@ -1373,7 +1374,7 @@ impl CompletionsMenu {
.track_scroll(self.scroll_handle.clone()) .track_scroll(self.scroll_handle.clone())
.with_width_from_item(widest_completion_ix); .with_width_from_item(widest_completion_ix);
list.render_into_any() list.into_any_element()
// todo!("multiline documentation") // todo!("multiline documentation")
// enum MultiLineDocumentation {} // enum MultiLineDocumentation {}
@ -1596,7 +1597,7 @@ impl CodeActionsMenu {
.max_by_key(|(_, action)| action.lsp_action.title.chars().count()) .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
.map(|(ix, _)| ix), .map(|(ix, _)| ix),
) )
.render_into_any(); .into_any_element();
if self.deployed_from_indicator { if self.deployed_from_indicator {
*cursor_position.column_mut() = 0; *cursor_position.column_mut() = 0;
@ -3596,7 +3597,8 @@ impl Editor {
let id = post_inc(&mut self.next_completion_id); let id = post_inc(&mut self.next_completion_id);
let task = cx.spawn(|this, mut cx| { let task = cx.spawn(|this, mut cx| {
async move { async move {
let menu = if let Some(completions) = completions.await.log_err() { let completions = completions.await.log_err();
let (menu, pre_resolve_task) = if let Some(completions) = completions {
let mut menu = CompletionsMenu { let mut menu = CompletionsMenu {
id, id,
initial_position: position, initial_position: position,
@ -3619,20 +3621,24 @@ impl Editor {
}; };
menu.filter(query.as_deref(), cx.background_executor().clone()) menu.filter(query.as_deref(), cx.background_executor().clone())
.await; .await;
if menu.matches.is_empty() { if menu.matches.is_empty() {
None (None, None)
} else { } else {
_ = this.update(&mut cx, |editor, cx| { let pre_resolve_task = this
menu.pre_resolve_completion_documentation(editor.project.clone(), cx); .update(&mut cx, |editor, cx| {
}); menu.pre_resolve_completion_documentation(editor, cx)
Some(menu) })
.ok()
.flatten();
(Some(menu), pre_resolve_task)
} }
} else { } else {
None (None, None)
}; };
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.completion_tasks.retain(|(task_id, _)| *task_id > id); this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
let mut context_menu = this.context_menu.write(); let mut context_menu = this.context_menu.write();
match context_menu.as_ref() { match context_menu.as_ref() {
@ -3664,10 +3670,15 @@ impl Editor {
} }
})?; })?;
if let Some(pre_resolve_task) = pre_resolve_task {
pre_resolve_task.await;
}
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())
} }
.log_err() .log_err()
}); });
self.completion_tasks.push((id, task)); self.completion_tasks.push((id, task));
} }
@ -7796,7 +7807,7 @@ impl Editor {
.clone(), .clone(),
}, },
)) ))
.render_into_any() .into_any_element()
} }
}), }),
disposition: BlockDisposition::Below, disposition: BlockDisposition::Below,
@ -10005,7 +10016,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
cx.write_to_clipboard(ClipboardItem::new(message.clone())); cx.write_to_clipboard(ClipboardItem::new(message.clone()));
})) }))
.tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)) .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx))
.render_into_any() .into_any_element()
}) })
} }

View file

@ -19,8 +19,8 @@ use anyhow::Result;
use collections::{BTreeMap, HashMap}; use collections::{BTreeMap, HashMap};
use gpui::{ use gpui::{
div, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, div, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace,
BorrowWindow, Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase, Edges, Element, ElementId,
ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveElement, LineLayout, ElementInputHandler, Entity, EntityId, Hsla, InteractiveElement, IntoElement, LineLayout,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, RenderOnce, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, RenderOnce,
ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled, ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
TextRun, TextStyle, View, ViewContext, WeakView, WindowContext, WrappedLine, TextRun, TextStyle, View, ViewContext, WeakView, WindowContext, WrappedLine,
@ -663,7 +663,7 @@ impl EditorElement {
for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() { for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
if let Some(mut fold_indicator) = fold_indicator { if let Some(mut fold_indicator) = fold_indicator {
let mut fold_indicator = fold_indicator.render_into_any(); let mut fold_indicator = fold_indicator.into_any_element();
let available_space = size( let available_space = size(
AvailableSpace::MinContent, AvailableSpace::MinContent,
AvailableSpace::Definite(line_height * 0.55), AvailableSpace::Definite(line_height * 0.55),
@ -684,7 +684,7 @@ impl EditorElement {
} }
if let Some(indicator) = layout.code_actions_indicator.take() { if let Some(indicator) = layout.code_actions_indicator.take() {
let mut button = indicator.button.render_into_any(); let mut button = indicator.button.into_any_element();
let available_space = size( let available_space = size(
AvailableSpace::MinContent, AvailableSpace::MinContent,
AvailableSpace::Definite(line_height), AvailableSpace::Definite(line_height),
@ -2653,14 +2653,14 @@ impl Element for EditorElement {
} }
} }
impl RenderOnce for EditorElement { impl IntoElement for EditorElement {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> { fn element_id(&self) -> Option<gpui::ElementId> {
self.editor.element_id() self.editor.element_id()
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -3,7 +3,7 @@ use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
use fuzzy::{CharBag, PathMatch, PathMatchCandidate}; use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{ use gpui::{
actions, div, AppContext, Div, EventEmitter, FocusHandle, FocusableView, InteractiveElement, actions, div, AppContext, Div, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
Manager, Model, ParentElement, Render, RenderOnce, Styled, Task, View, ViewContext, IntoElement, Manager, Model, ParentElement, Render, Styled, Task, View, ViewContext,
VisualContext, WeakView, VisualContext, WeakView,
}; };
use picker::{Picker, PickerDelegate}; use picker::{Picker, PickerDelegate};

View file

@ -10,6 +10,7 @@ pub use entity_map::*;
pub use model_context::*; pub use model_context::*;
use refineable::Refineable; use refineable::Refineable;
use smallvec::SmallVec; use smallvec::SmallVec;
use smol::future::FutureExt;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub use test_context::*; pub use test_context::*;
@ -983,6 +984,22 @@ impl AppContext {
pub fn all_action_names(&self) -> &[SharedString] { pub fn all_action_names(&self) -> &[SharedString] {
self.actions.all_action_names() self.actions.all_action_names()
} }
pub fn on_app_quit<Fut>(
&mut self,
mut on_quit: impl FnMut(&mut AppContext) -> Fut + 'static,
) -> Subscription
where
Fut: 'static + Future<Output = ()>,
{
self.quit_observers.insert(
(),
Box::new(move |cx| {
let future = on_quit(cx);
async move { future.await }.boxed_local()
}),
)
}
} }
impl Context for AppContext { impl Context for AppContext {

View file

@ -12,15 +12,15 @@ pub trait Render: 'static + Sized {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element; fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element;
} }
pub trait RenderOnce: Sized { pub trait IntoElement: Sized {
type Element: Element + 'static; type Element: Element + 'static;
fn element_id(&self) -> Option<ElementId>; fn element_id(&self) -> Option<ElementId>;
fn render_once(self) -> Self::Element; fn into_element(self) -> Self::Element;
fn render_into_any(self) -> AnyElement { fn into_any_element(self) -> AnyElement {
self.render_once().into_any() self.into_element().into_any()
} }
fn draw<T, R>( fn draw<T, R>(
@ -33,7 +33,7 @@ pub trait RenderOnce: Sized {
where where
T: Clone + Default + Debug + Into<AvailableSpace>, T: Clone + Default + Debug + Into<AvailableSpace>,
{ {
let element = self.render_once(); let element = self.into_element();
let element_id = element.element_id(); let element_id = element.element_id();
let element = DrawableElement { let element = DrawableElement {
element: Some(element), element: Some(element),
@ -57,7 +57,7 @@ pub trait RenderOnce: Sized {
fn map<U>(self, f: impl FnOnce(Self) -> U) -> U fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
where where
Self: Sized, Self: Sized,
U: RenderOnce, U: IntoElement,
{ {
f(self) f(self)
} }
@ -83,7 +83,7 @@ pub trait RenderOnce: Sized {
} }
} }
pub trait Element: 'static + RenderOnce { pub trait Element: 'static + IntoElement {
type State: 'static; type State: 'static;
fn layout( fn layout(
@ -99,30 +99,30 @@ pub trait Element: 'static + RenderOnce {
} }
} }
pub trait Component: 'static { pub trait RenderOnce: 'static {
type Rendered: RenderOnce; type Rendered: IntoElement;
fn render(self, cx: &mut WindowContext) -> Self::Rendered; fn render(self, cx: &mut WindowContext) -> Self::Rendered;
} }
pub struct CompositeElement<C> { pub struct Component<C> {
component: Option<C>, component: Option<C>,
} }
pub struct CompositeElementState<C: Component> { pub struct CompositeElementState<C: RenderOnce> {
rendered_element: Option<<C::Rendered as RenderOnce>::Element>, rendered_element: Option<<C::Rendered as IntoElement>::Element>,
rendered_element_state: <<C::Rendered as RenderOnce>::Element as Element>::State, rendered_element_state: <<C::Rendered as IntoElement>::Element as Element>::State,
} }
impl<C> CompositeElement<C> { impl<C> Component<C> {
pub fn new(component: C) -> Self { pub fn new(component: C) -> Self {
CompositeElement { Component {
component: Some(component), component: Some(component),
} }
} }
} }
impl<C: Component> Element for CompositeElement<C> { impl<C: RenderOnce> Element for Component<C> {
type State = CompositeElementState<C>; type State = CompositeElementState<C>;
fn layout( fn layout(
@ -130,7 +130,7 @@ impl<C: Component> Element for CompositeElement<C> {
state: Option<Self::State>, state: Option<Self::State>,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> (LayoutId, Self::State) { ) -> (LayoutId, Self::State) {
let mut element = self.component.take().unwrap().render(cx).render_once(); let mut element = self.component.take().unwrap().render(cx).into_element();
let (layout_id, state) = element.layout(state.map(|s| s.rendered_element_state), cx); let (layout_id, state) = element.layout(state.map(|s| s.rendered_element_state), cx);
let state = CompositeElementState { let state = CompositeElementState {
rendered_element: Some(element), rendered_element: Some(element),
@ -148,14 +148,14 @@ impl<C: Component> Element for CompositeElement<C> {
} }
} }
impl<C: Component> RenderOnce for CompositeElement<C> { impl<C: RenderOnce> IntoElement for Component<C> {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
None None
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
@ -166,23 +166,20 @@ pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
pub trait ParentElement { pub trait ParentElement {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>; fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
fn child(mut self, child: impl RenderOnce) -> Self fn child(mut self, child: impl IntoElement) -> Self
where where
Self: Sized, Self: Sized,
{ {
self.children_mut().push(child.render_once().into_any()); self.children_mut().push(child.into_element().into_any());
self self
} }
fn children(mut self, children: impl IntoIterator<Item = impl RenderOnce>) -> Self fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
where where
Self: Sized, Self: Sized,
{ {
self.children_mut().extend( self.children_mut()
children .extend(children.into_iter().map(|child| child.into_any_element()));
.into_iter()
.map(|child| child.render_once().into_any()),
);
self self
} }
} }
@ -486,14 +483,14 @@ impl Element for AnyElement {
} }
} }
impl RenderOnce for AnyElement { impl IntoElement for AnyElement {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
None None
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -1,9 +1,9 @@
use crate::{ use crate::{
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext, point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusEvent, FocusHandle, BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusEvent, FocusHandle,
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent, IntoElement, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
MouseUpEvent, ParentElement, Pixels, Point, Render, RenderOnce, ScrollWheelEvent, SharedString, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
Size, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext, SharedString, Size, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
}; };
use collections::HashMap; use collections::HashMap;
use refineable::Refineable; use refineable::Refineable;
@ -666,14 +666,14 @@ impl Element for Div {
} }
} }
impl RenderOnce for Div { impl IntoElement for Div {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone() self.interactivity.element_id.clone()
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
@ -1278,7 +1278,7 @@ where
} }
} }
impl<E> RenderOnce for Focusable<E> impl<E> IntoElement for Focusable<E>
where where
E: Element, E: Element,
{ {
@ -1288,7 +1288,7 @@ where
self.element.element_id() self.element.element_id()
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self.element self.element
} }
} }
@ -1352,7 +1352,7 @@ where
} }
} }
impl<E> RenderOnce for Stateful<E> impl<E> IntoElement for Stateful<E>
where where
E: Element, E: Element,
{ {
@ -1362,7 +1362,7 @@ where
self.element.element_id() self.element.element_id()
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -2,7 +2,7 @@ use std::sync::Arc;
use crate::{ use crate::{
Bounds, Element, ImageData, InteractiveElement, InteractiveElementState, Interactivity, Bounds, Element, ImageData, InteractiveElement, InteractiveElementState, Interactivity,
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, WindowContext, IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
}; };
use futures::FutureExt; use futures::FutureExt;
use util::ResultExt; use util::ResultExt;
@ -120,14 +120,14 @@ impl Element for Img {
} }
} }
impl RenderOnce for Img { impl IntoElement for Img {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> { fn element_id(&self) -> Option<crate::ElementId> {
self.interactivity.element_id.clone() self.interactivity.element_id.clone()
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -2,8 +2,8 @@ use smallvec::SmallVec;
use taffy::style::{Display, Position}; use taffy::style::{Display, Position};
use crate::{ use crate::{
point, AnyElement, BorrowWindow, Bounds, Element, LayoutId, ParentElement, Pixels, Point, point, AnyElement, BorrowWindow, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels,
RenderOnce, Size, Style, WindowContext, Point, Size, Style, WindowContext,
}; };
pub struct OverlayState { pub struct OverlayState {
@ -151,14 +151,14 @@ impl Element for Overlay {
} }
} }
impl RenderOnce for Overlay { impl IntoElement for Overlay {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> { fn element_id(&self) -> Option<crate::ElementId> {
None None
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity, Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, WindowContext, IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
}; };
use util::ResultExt; use util::ResultExt;
@ -49,14 +49,14 @@ impl Element for Svg {
} }
} }
impl RenderOnce for Svg { impl IntoElement for Svg {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone() self.interactivity.element_id.clone()
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
Bounds, Element, ElementId, LayoutId, Pixels, RenderOnce, SharedString, Size, TextRun, Bounds, Element, ElementId, IntoElement, LayoutId, Pixels, SharedString, Size, TextRun,
WhiteSpace, WindowContext, WrappedLine, WhiteSpace, WindowContext, WrappedLine,
}; };
use anyhow::anyhow; use anyhow::anyhow;
@ -26,14 +26,14 @@ impl Element for &'static str {
} }
} }
impl RenderOnce for &'static str { impl IntoElement for &'static str {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
None None
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
@ -57,14 +57,14 @@ impl Element for SharedString {
} }
} }
impl RenderOnce for SharedString { impl IntoElement for SharedString {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
None None
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
@ -106,14 +106,14 @@ impl Element for StyledText {
} }
} }
impl RenderOnce for StyledText { impl IntoElement for StyledText {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> { fn element_id(&self) -> Option<crate::ElementId> {
None None
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
@ -272,14 +272,14 @@ impl Element for InteractiveText {
} }
} }
impl RenderOnce for InteractiveText { impl IntoElement for InteractiveText {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
Some(self.element_id.clone()) Some(self.element_id.clone())
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element, point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
ElementId, InteractiveElement, InteractiveElementState, Interactivity, LayoutId, Pixels, Point, ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
Render, RenderOnce, Size, StyleRefinement, Styled, View, ViewContext, WindowContext, Pixels, Point, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@ -18,7 +18,7 @@ pub fn uniform_list<I, R, V>(
) -> UniformList ) -> UniformList
where where
I: Into<ElementId>, I: Into<ElementId>,
R: RenderOnce, R: IntoElement,
V: Render, V: Render,
{ {
let id = id.into(); let id = id.into();
@ -29,7 +29,7 @@ where
view.update(cx, |this, cx| { view.update(cx, |this, cx| {
f(this, range, cx) f(this, range, cx)
.into_iter() .into_iter()
.map(|component| component.render_into_any()) .map(|component| component.into_any_element())
.collect() .collect()
}) })
}; };
@ -243,14 +243,14 @@ impl Element for UniformList {
} }
} }
impl RenderOnce for UniformList { impl IntoElement for UniformList {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> { fn element_id(&self) -> Option<crate::ElementId> {
Some(self.id.clone()) Some(self.id.clone())
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
div, point, Div, Element, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render, RenderOnce, div, point, Div, Element, FocusHandle, IntoElement, Keystroke, Modifiers, Pixels, Point,
ViewContext, Render, ViewContext,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf}; use std::{any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf};
@ -64,7 +64,7 @@ pub struct Drag<S, R, V, E>
where where
R: Fn(&mut V, &mut ViewContext<V>) -> E, R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static, V: 'static,
E: RenderOnce, E: IntoElement,
{ {
pub state: S, pub state: S,
pub render_drag_handle: R, pub render_drag_handle: R,
@ -286,8 +286,8 @@ pub struct FocusEvent {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::{
self as gpui, div, Div, FocusHandle, InteractiveElement, KeyBinding, Keystroke, self as gpui, div, Div, FocusHandle, InteractiveElement, IntoElement, KeyBinding,
ParentElement, Render, RenderOnce, Stateful, TestAppContext, VisualContext, Keystroke, ParentElement, Render, Stateful, TestAppContext, VisualContext,
}; };
struct TestView { struct TestView {
@ -315,7 +315,7 @@ mod test {
div() div()
.key_context("nested") .key_context("nested")
.track_focus(&self.focus_handle) .track_focus(&self.focus_handle)
.render_once(), .into_element(),
), ),
) )
} }

View file

@ -1,5 +1,5 @@
pub use crate::{ pub use crate::{
BorrowAppContext, BorrowWindow, Component, Context, Element, FocusableElement, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement, InteractiveElement,
InteractiveElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement, Styled,
Styled, VisualContext, VisualContext,
}; };

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
private::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow, private::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, LayoutId, Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement,
Model, Pixels, Point, Render, RenderOnce, Size, ViewContext, VisualContext, WeakModel, LayoutId, Model, Pixels, Point, Render, Size, ViewContext, VisualContext, WeakModel,
WindowContext, WindowContext,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -244,26 +244,26 @@ impl Element for AnyView {
} }
} }
impl<V: 'static + Render> RenderOnce for View<V> { impl<V: 'static + Render> IntoElement for View<V> {
type Element = View<V>; type Element = View<V>;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
Some(ElementId::from_entity_id(self.model.entity_id)) Some(ElementId::from_entity_id(self.model.entity_id))
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
impl RenderOnce for AnyView { impl IntoElement for AnyView {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
Some(ElementId::from_entity_id(self.model.entity_id)) Some(ElementId::from_entity_id(self.model.entity_id))
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -1,66 +0,0 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, parse_quote, DeriveInput};
pub fn derive_component(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
let mut trait_generics = ast.generics.clone();
let view_type = if let Some(view_type) = specified_view_type(&ast) {
quote! { #view_type }
} else {
if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
if let syn::GenericParam::Type(type_param) = param {
Some(type_param.ident.clone())
} else {
None
}
}) {
quote! { #first_type_param }
} else {
trait_generics.params.push(parse_quote! { V: 'static });
quote! { V }
}
};
let (impl_generics, _, where_clause) = trait_generics.split_for_impl();
let (_, ty_generics, _) = ast.generics.split_for_impl();
let expanded = quote! {
impl #impl_generics gpui::Component<#view_type> for #name #ty_generics #where_clause {
fn render(self) -> gpui::AnyElement<#view_type> {
(move |view_state: &mut #view_type, cx: &mut gpui::ViewContext<'_, #view_type>| self.render(view_state, cx))
.render()
}
}
};
TokenStream::from(expanded)
}
fn specified_view_type(ast: &DeriveInput) -> Option<proc_macro2::Ident> {
let component_attr = ast
.attrs
.iter()
.find(|attr| attr.path.is_ident("component"))?;
if let Ok(syn::Meta::List(meta_list)) = component_attr.parse_meta() {
meta_list.nested.iter().find_map(|nested| {
if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = nested {
if nv.path.is_ident("view_type") {
if let syn::Lit::Str(lit_str) = &nv.lit {
return Some(
lit_str
.parse::<syn::Ident>()
.expect("Failed to parse view_type"),
);
}
}
}
None
})
} else {
None
}
}

View file

@ -2,23 +2,23 @@ use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput}; use syn::{parse_macro_input, DeriveInput};
pub fn derive_render_once(input: TokenStream) -> TokenStream { pub fn derive_into_element(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput); let ast = parse_macro_input!(input as DeriveInput);
let type_name = &ast.ident; let type_name = &ast.ident;
let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl(); let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
let gen = quote! { let gen = quote! {
impl #impl_generics gpui::RenderOnce for #type_name #type_generics impl #impl_generics gpui::IntoElement for #type_name #type_generics
#where_clause #where_clause
{ {
type Element = gpui::CompositeElement<Self>; type Element = gpui::Component<Self>;
fn element_id(&self) -> Option<ElementId> { fn element_id(&self) -> Option<ElementId> {
None None
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
gpui::CompositeElement::new(self) gpui::Component::new(self)
} }
} }
}; };

View file

@ -1,6 +1,5 @@
mod action; mod action;
mod derive_component; mod derive_into_element;
mod derive_render_once;
mod register_action; mod register_action;
mod style_helpers; mod style_helpers;
mod test; mod test;
@ -17,14 +16,9 @@ pub fn register_action(attr: TokenStream, item: TokenStream) -> TokenStream {
register_action::register_action_macro(attr, item) register_action::register_action_macro(attr, item)
} }
#[proc_macro_derive(Component, attributes(component))] #[proc_macro_derive(IntoElement)]
pub fn derive_component(input: TokenStream) -> TokenStream { pub fn derive_into_element(input: TokenStream) -> TokenStream {
derive_component::derive_component(input) derive_into_element::derive_into_element(input)
}
#[proc_macro_derive(RenderOnce, attributes(view))]
pub fn derive_render_once(input: TokenStream) -> TokenStream {
derive_render_once::derive_render_once(input)
} }
#[proc_macro] #[proc_macro]

View file

@ -15,7 +15,7 @@ pub struct Picker<D: PickerDelegate> {
} }
pub trait PickerDelegate: Sized + 'static { pub trait PickerDelegate: Sized + 'static {
type ListItem: RenderOnce; type ListItem: IntoElement;
fn match_count(&self) -> usize; fn match_count(&self) -> usize;
fn selected_index(&self) -> usize; fn selected_index(&self) -> usize;

View file

@ -1056,7 +1056,7 @@ async fn test_create_directory_during_initial_scan(cx: &mut TestAppContext) {
async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) { async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
init_test(cx); init_test(cx);
cx.executor().allow_parking(); cx.executor().allow_parking();
let client_fake = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); let client_fake = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
let fs_fake = FakeFs::new(cx.background_executor.clone()); let fs_fake = FakeFs::new(cx.background_executor.clone());
fs_fake fs_fake
@ -1096,7 +1096,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
assert!(tree.entry_for_path("a/b/").unwrap().is_dir()); assert!(tree.entry_for_path("a/b/").unwrap().is_dir());
}); });
let client_real = cx.read(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); let client_real = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx));
let fs_real = Arc::new(RealFs); let fs_real = Arc::new(RealFs);
let temp_root = temp_tree(json!({ let temp_root = temp_tree(json!({
@ -2181,7 +2181,7 @@ async fn test_propagate_git_statuses(cx: &mut TestAppContext) {
fn build_client(cx: &mut TestAppContext) -> Arc<Client> { fn build_client(cx: &mut TestAppContext) -> Arc<Client> {
let http_client = FakeHttpClient::with_404_response(); let http_client = FakeHttpClient::with_404_response();
cx.read(|cx| Client::new(http_client, cx)) cx.update(|cx| Client::new(http_client, cx))
} }
#[track_caller] #[track_caller]

View file

@ -10,8 +10,8 @@ use anyhow::{anyhow, Result};
use gpui::{ use gpui::{
actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext, actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement, ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, IntoElement, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel,
RenderOnce, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, Render, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View,
ViewContext, VisualContext as _, WeakView, WindowContext, ViewContext, VisualContext as _, WeakView, WindowContext,
}; };
use menu::{Confirm, SelectNext, SelectPrev}; use menu::{Confirm, SelectNext, SelectPrev};

View file

@ -10,9 +10,9 @@ use collections::HashMap;
use editor::Editor; use editor::Editor;
use futures::channel::oneshot; use futures::channel::oneshot;
use gpui::{ use gpui::{
actions, div, red, Action, AppContext, Div, EventEmitter, InteractiveElement as _, actions, div, red, Action, AppContext, Div, EventEmitter, InteractiveElement as _, IntoElement,
ParentElement as _, Render, RenderOnce, Styled, Subscription, Task, View, ViewContext, ParentElement as _, Render, Styled, Subscription, Task, View, ViewContext, VisualContext as _,
VisualContext as _, WindowContext, WindowContext,
}; };
use project::search::SearchQuery; use project::search::SearchQuery;
use serde::Deserialize; use serde::Deserialize;
@ -538,7 +538,7 @@ impl BufferSearchBar {
self.update_matches(cx) self.update_matches(cx)
} }
fn render_action_button(&self) -> impl RenderOnce { fn render_action_button(&self) -> impl IntoElement {
// let tooltip_style = theme.tooltip.clone(); // let tooltip_style = theme.tooltip.clone();
// let style = theme.search.action_button.clone(); // let style = theme.search.action_button.clone();

View file

@ -1,6 +1,6 @@
use bitflags::bitflags; use bitflags::bitflags;
pub use buffer_search::BufferSearchBar; pub use buffer_search::BufferSearchBar;
use gpui::{actions, Action, AppContext, RenderOnce}; use gpui::{actions, Action, AppContext, IntoElement};
pub use mode::SearchMode; pub use mode::SearchMode;
use project::search::SearchQuery; use project::search::SearchQuery;
use ui::ButtonVariant; use ui::ButtonVariant;
@ -82,7 +82,7 @@ impl SearchOptions {
options options
} }
pub fn as_button(&self, active: bool) -> impl RenderOnce { pub fn as_button(&self, active: bool) -> impl IntoElement {
ui::IconButton::new(0, self.icon()) ui::IconButton::new(0, self.icon())
.on_click({ .on_click({
let action = self.to_toggle_action(); let action = self.to_toggle_action();
@ -95,7 +95,7 @@ impl SearchOptions {
} }
} }
fn toggle_replace_button(active: bool) -> impl RenderOnce { fn toggle_replace_button(active: bool) -> impl IntoElement {
// todo: add toggle_replace button // todo: add toggle_replace button
ui::IconButton::new(0, ui::Icon::Replace) ui::IconButton::new(0, ui::Icon::Replace)
.on_click(|_, cx| { .on_click(|_, cx| {
@ -109,7 +109,7 @@ fn toggle_replace_button(active: bool) -> impl RenderOnce {
fn render_replace_button( fn render_replace_button(
action: impl Action + 'static + Send + Sync, action: impl Action + 'static + Send + Sync,
icon: ui::Icon, icon: ui::Icon,
) -> impl RenderOnce { ) -> impl IntoElement {
// todo: add tooltip // todo: add tooltip
ui::IconButton::new(0, icon).on_click(move |_, cx| { ui::IconButton::new(0, icon).on_click(move |_, cx| {
cx.dispatch_action(action.boxed_clone()); cx.dispatch_action(action.boxed_clone());

View file

@ -1,4 +1,4 @@
use gpui::{MouseDownEvent, RenderOnce, WindowContext}; use gpui::{IntoElement, MouseDownEvent, WindowContext};
use ui::{Button, ButtonVariant, IconButton}; use ui::{Button, ButtonVariant, IconButton};
use crate::mode::SearchMode; use crate::mode::SearchMode;
@ -7,7 +7,7 @@ pub(super) fn render_nav_button(
icon: ui::Icon, icon: ui::Icon,
_active: bool, _active: bool,
on_click: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static, on_click: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> impl RenderOnce { ) -> impl IntoElement {
// let tooltip_style = cx.theme().tooltip.clone(); // let tooltip_style = cx.theme().tooltip.clone();
// let cursor_style = if active { // let cursor_style = if active {
// CursorStyle::PointingHand // CursorStyle::PointingHand

View file

@ -1,4 +1,4 @@
use gpui::{px, rgb, Div, Hsla, Render, RenderOnce}; use gpui::{px, rgb, Div, Hsla, IntoElement, Render, RenderOnce};
use story::Story; use story::Story;
use ui::prelude::*; use ui::prelude::*;
@ -76,12 +76,12 @@ trait Styles: Styled + Sized {
impl Styles for Div {} impl Styles for Div {}
#[derive(RenderOnce)] #[derive(IntoElement)]
struct ZIndexExample { struct ZIndexExample {
z_index: u32, z_index: u32,
} }
impl Component for ZIndexExample { impl RenderOnce for ZIndexExample {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,5 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
use gpui::{img, ImageSource, Img, RenderOnce}; use gpui::{img, ImageSource, Img, IntoElement};
#[derive(Debug, Default, PartialEq, Clone)] #[derive(Debug, Default, PartialEq, Clone)]
pub enum Shape { pub enum Shape {
@ -8,13 +8,13 @@ pub enum Shape {
RoundedRectangle, RoundedRectangle,
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct Avatar { pub struct Avatar {
src: ImageSource, src: ImageSource,
shape: Shape, shape: Shape,
} }
impl Component for Avatar { impl RenderOnce for Avatar {
type Rendered = Img; type Rendered = Img;
fn render(self, _: &mut WindowContext) -> Self::Rendered { fn render(self, _: &mut WindowContext) -> Self::Rendered {

View file

@ -1,8 +1,8 @@
use std::rc::Rc; use std::rc::Rc;
use gpui::{ use gpui::{
DefiniteLength, Div, Hsla, MouseButton, MouseDownEvent, RenderOnce, StatefulInteractiveElement, DefiniteLength, Div, Hsla, IntoElement, MouseButton, MouseDownEvent,
WindowContext, StatefulInteractiveElement, WindowContext,
}; };
use crate::prelude::*; use crate::prelude::*;
@ -64,7 +64,7 @@ impl ButtonVariant {
} }
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct Button { pub struct Button {
disabled: bool, disabled: bool,
click_handler: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext)>>, click_handler: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext)>>,
@ -76,7 +76,7 @@ pub struct Button {
color: Option<Color>, color: Option<Color>,
} }
impl Component for Button { impl RenderOnce for Button {
type Rendered = gpui::Stateful<Div>; type Rendered = gpui::Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -207,12 +207,12 @@ impl Button {
} }
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct ButtonGroup { pub struct ButtonGroup {
buttons: Vec<Button>, buttons: Vec<Button>,
} }
impl Component for ButtonGroup { impl RenderOnce for ButtonGroup {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,4 +1,4 @@
use gpui::{div, prelude::*, Div, Element, ElementId, RenderOnce, Styled, WindowContext}; use gpui::{div, prelude::*, Div, Element, ElementId, IntoElement, Styled, WindowContext};
use theme2::ActiveTheme; use theme2::ActiveTheme;
@ -11,7 +11,7 @@ pub type CheckHandler = Box<dyn Fn(&Selection, &mut WindowContext) + 'static>;
/// Checkboxes are used for multiple choices, not for mutually exclusive choices. /// Checkboxes are used for multiple choices, not for mutually exclusive choices.
/// Each checkbox works independently from other checkboxes in the list, /// Each checkbox works independently from other checkboxes in the list,
/// therefore checking an additional box does not affect any other selections. /// therefore checking an additional box does not affect any other selections.
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct Checkbox { pub struct Checkbox {
id: ElementId, id: ElementId,
checked: Selection, checked: Selection,
@ -19,7 +19,7 @@ pub struct Checkbox {
on_click: Option<CheckHandler>, on_click: Option<CheckHandler>,
} }
impl Component for Checkbox { impl RenderOnce for Checkbox {
type Rendered = gpui::Stateful<Div>; type Rendered = gpui::Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -5,8 +5,8 @@ use crate::{prelude::*, v_stack, Label, List};
use crate::{ListItem, ListSeparator, ListSubHeader}; use crate::{ListItem, ListSeparator, ListSubHeader};
use gpui::{ use gpui::{
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DispatchPhase, overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DispatchPhase,
Div, EventEmitter, FocusHandle, FocusableView, LayoutId, ManagedView, Manager, MouseButton, Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId, ManagedView, Manager,
MouseDownEvent, Pixels, Point, Render, RenderOnce, View, VisualContext, MouseButton, MouseDownEvent, Pixels, Point, Render, View, VisualContext,
}; };
pub enum ContextMenuItem { pub enum ContextMenuItem {
@ -105,9 +105,9 @@ impl Render for ContextMenu {
// .border_color(cx.theme().colors().border) // .border_color(cx.theme().colors().border)
.child( .child(
List::new().children(self.items.iter().map(|item| match item { List::new().children(self.items.iter().map(|item| match item {
ContextMenuItem::Separator => ListSeparator::new().render_into_any(), ContextMenuItem::Separator => ListSeparator::new().into_any_element(),
ContextMenuItem::Header(header) => { ContextMenuItem::Header(header) => {
ListSubHeader::new(header.clone()).render_into_any() ListSubHeader::new(header.clone()).into_any_element()
} }
ContextMenuItem::Entry(entry, callback) => { ContextMenuItem::Entry(entry, callback) => {
let callback = callback.clone(); let callback = callback.clone();
@ -119,7 +119,7 @@ impl Render for ContextMenu {
callback(event, cx); callback(event, cx);
dismiss(event, cx) dismiss(event, cx)
}) })
.render_into_any() .into_any_element()
} }
})), })),
), ),
@ -141,8 +141,8 @@ impl<M: ManagedView> MenuHandle<M> {
self self
} }
pub fn child<R: RenderOnce>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self { pub fn child<R: IntoElement>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
self.child_builder = Some(Box::new(|b| f(b).render_once().into_any())); self.child_builder = Some(Box::new(|b| f(b).into_element().into_any()));
self self
} }
@ -287,14 +287,14 @@ impl<M: ManagedView> Element for MenuHandle<M> {
} }
} }
impl<M: ManagedView> RenderOnce for MenuHandle<M> { impl<M: ManagedView> IntoElement for MenuHandle<M> {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> { fn element_id(&self) -> Option<gpui::ElementId> {
Some(self.id.clone()) Some(self.id.clone())
} }
fn render_once(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }

View file

@ -1,4 +1,4 @@
use gpui::{Div, RenderOnce}; use gpui::{Div, IntoElement};
use crate::prelude::*; use crate::prelude::*;
@ -7,13 +7,13 @@ enum DividerDirection {
Vertical, Vertical,
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct Divider { pub struct Divider {
direction: DividerDirection, direction: DividerDirection,
inset: bool, inset: bool,
} }
impl Component for Divider { impl RenderOnce for Divider {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,4 +1,4 @@
use gpui::{rems, svg, RenderOnce, Svg}; use gpui::{rems, svg, IntoElement, Svg};
use strum::EnumIter; use strum::EnumIter;
use crate::prelude::*; use crate::prelude::*;
@ -133,14 +133,14 @@ impl Icon {
} }
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct IconElement { pub struct IconElement {
path: SharedString, path: SharedString,
color: Color, color: Color,
size: IconSize, size: IconSize,
} }
impl Component for IconElement { impl RenderOnce for IconElement {
type Rendered = Svg; type Rendered = Svg;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,7 +1,7 @@
use crate::{h_stack, prelude::*, Icon, IconElement}; use crate::{h_stack, prelude::*, Icon, IconElement};
use gpui::{prelude::*, Action, AnyView, Div, MouseButton, MouseDownEvent, Stateful}; use gpui::{prelude::*, Action, AnyView, Div, MouseButton, MouseDownEvent, Stateful};
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct IconButton { pub struct IconButton {
id: ElementId, id: ElementId,
icon: Icon, icon: Icon,
@ -13,7 +13,7 @@ pub struct IconButton {
on_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>, on_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
} }
impl Component for IconButton { impl RenderOnce for IconButton {
type Rendered = Stateful<Div>; type Rendered = Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,5 +1,5 @@
use crate::{prelude::*, Label}; use crate::{prelude::*, Label};
use gpui::{prelude::*, Div, RenderOnce, Stateful}; use gpui::{prelude::*, Div, IntoElement, Stateful};
#[derive(Default, PartialEq)] #[derive(Default, PartialEq)]
pub enum InputVariant { pub enum InputVariant {
@ -8,7 +8,7 @@ pub enum InputVariant {
Filled, Filled,
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct Input { pub struct Input {
placeholder: SharedString, placeholder: SharedString,
value: String, value: String,
@ -18,7 +18,7 @@ pub struct Input {
is_active: bool, is_active: bool,
} }
impl Component for Input { impl RenderOnce for Input {
type Rendered = Stateful<Div>; type Rendered = Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use gpui::{Action, Div, RenderOnce}; use gpui::{Action, Div, IntoElement};
#[derive(RenderOnce, Clone)] #[derive(IntoElement, Clone)]
pub struct KeyBinding { pub struct KeyBinding {
/// A keybinding consists of a key and a set of modifier keys. /// A keybinding consists of a key and a set of modifier keys.
/// More then one keybinding produces a chord. /// More then one keybinding produces a chord.
@ -10,7 +10,7 @@ pub struct KeyBinding {
key_binding: gpui::KeyBinding, key_binding: gpui::KeyBinding,
} }
impl Component for KeyBinding { impl RenderOnce for KeyBinding {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -44,12 +44,12 @@ impl KeyBinding {
} }
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct Key { pub struct Key {
key: SharedString, key: SharedString,
} }
impl Component for Key { impl RenderOnce for Key {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use crate::styled_ext::StyledExt; use crate::styled_ext::StyledExt;
use gpui::{relative, Div, Hsla, RenderOnce, StyledText, TextRun, WindowContext}; use gpui::{relative, Div, Hsla, IntoElement, StyledText, TextRun, WindowContext};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum LabelSize { pub enum LabelSize {
@ -17,7 +17,7 @@ pub enum LineHeightStyle {
UILabel, UILabel,
} }
#[derive(Clone, RenderOnce)] #[derive(IntoElement, Clone)]
pub struct Label { pub struct Label {
label: SharedString, label: SharedString,
size: LabelSize, size: LabelSize,
@ -26,7 +26,7 @@ pub struct Label {
strikethrough: bool, strikethrough: bool,
} }
impl Component for Label { impl RenderOnce for Label {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -85,7 +85,7 @@ impl Label {
} }
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct HighlightedLabel { pub struct HighlightedLabel {
label: SharedString, label: SharedString,
size: LabelSize, size: LabelSize,
@ -94,7 +94,7 @@ pub struct HighlightedLabel {
strikethrough: bool, strikethrough: bool,
} }
impl Component for HighlightedLabel { impl RenderOnce for HighlightedLabel {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,5 +1,5 @@
use gpui::{ use gpui::{
div, px, AnyElement, ClickEvent, Div, RenderOnce, Stateful, StatefulInteractiveElement, div, px, AnyElement, ClickEvent, Div, IntoElement, Stateful, StatefulInteractiveElement,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::rc::Rc; use std::rc::Rc;
@ -25,7 +25,7 @@ pub enum ListHeaderMeta {
Text(Label), Text(Label),
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct ListHeader { pub struct ListHeader {
label: SharedString, label: SharedString,
left_icon: Option<Icon>, left_icon: Option<Icon>,
@ -34,7 +34,7 @@ pub struct ListHeader {
toggle: Toggle, toggle: Toggle,
} }
impl Component for ListHeader { impl RenderOnce for ListHeader {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -179,7 +179,7 @@ impl ListHeader {
// } // }
} }
#[derive(RenderOnce, Clone)] #[derive(IntoElement, Clone)]
pub struct ListSubHeader { pub struct ListSubHeader {
label: SharedString, label: SharedString,
left_icon: Option<Icon>, left_icon: Option<Icon>,
@ -201,7 +201,7 @@ impl ListSubHeader {
} }
} }
impl Component for ListSubHeader { impl RenderOnce for ListSubHeader {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -238,7 +238,7 @@ pub enum ListEntrySize {
Medium, Medium,
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct ListItem { pub struct ListItem {
id: ElementId, id: ElementId,
disabled: bool, disabled: bool,
@ -311,7 +311,7 @@ impl ListItem {
} }
} }
impl Component for ListItem { impl RenderOnce for ListItem {
type Rendered = Stateful<Div>; type Rendered = Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -388,7 +388,7 @@ impl ParentElement for ListItem {
} }
} }
#[derive(RenderOnce, Clone)] #[derive(IntoElement, Clone)]
pub struct ListSeparator; pub struct ListSeparator;
impl ListSeparator { impl ListSeparator {
@ -397,7 +397,7 @@ impl ListSeparator {
} }
} }
impl Component for ListSeparator { impl RenderOnce for ListSeparator {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -405,7 +405,7 @@ impl Component for ListSeparator {
} }
} }
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct List { pub struct List {
/// Message to display when the list is empty /// Message to display when the list is empty
/// Defaults to "No items" /// Defaults to "No items"
@ -415,7 +415,7 @@ pub struct List {
children: SmallVec<[AnyElement; 2]>, children: SmallVec<[AnyElement; 2]>,
} }
impl Component for List { impl RenderOnce for List {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {

View file

@ -1,5 +1,5 @@
use gpui::{ use gpui::{
AnyElement, Component, Div, Element, ElementId, ParentElement, RenderOnce, Styled, AnyElement, Div, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled,
WindowContext, WindowContext,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
@ -33,13 +33,13 @@ use crate::{v_stack, StyledExt};
/// ///
/// Example: A theme select control. Displays "One Dark", clicking it opens a list of themes. /// Example: A theme select control. Displays "One Dark", clicking it opens a list of themes.
/// When one is selected, the theme select control displays the selected theme. /// When one is selected, the theme select control displays the selected theme.
#[derive(RenderOnce)] #[derive(IntoElement)]
pub struct Popover { pub struct Popover {
children: SmallVec<[AnyElement; 2]>, children: SmallVec<[AnyElement; 2]>,
aside: Option<AnyElement>, aside: Option<AnyElement>,
} }
impl Component for Popover { impl RenderOnce for Popover {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
@ -73,11 +73,11 @@ impl Popover {
} }
} }
pub fn aside(mut self, aside: impl RenderOnce) -> Self pub fn aside(mut self, aside: impl IntoElement) -> Self
where where
Self: Sized, Self: Sized,
{ {
self.aside = Some(aside.render_once().into_any()); self.aside = Some(aside.into_element().into_any());
self self
} }
} }

View file

@ -1,4 +1,4 @@
use gpui::{overlay, Action, AnyView, Overlay, Render, RenderOnce, VisualContext}; use gpui::{overlay, Action, AnyView, IntoElement, Overlay, Render, VisualContext};
use settings2::Settings; use settings2::Settings;
use theme2::{ActiveTheme, ThemeSettings}; use theme2::{ActiveTheme, ThemeSettings};

View file

@ -1,5 +1,5 @@
pub use gpui::{ pub use gpui::{
div, Component, Element, ElementId, InteractiveElement, ParentElement, SharedString, Styled, div, Element, ElementId, InteractiveElement, ParentElement, RenderOnce, SharedString, Styled,
ViewContext, WindowContext, ViewContext, WindowContext,
}; };

View file

@ -1,7 +1,7 @@
use crate::{status_bar::StatusItemView, Axis, Workspace}; use crate::{status_bar::StatusItemView, Axis, Workspace};
use gpui::{ use gpui::{
div, px, Action, AnchorCorner, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, div, px, Action, AnchorCorner, AnyView, AppContext, Div, Entity, EntityId, EventEmitter,
FocusHandle, FocusableView, ParentElement, Render, RenderOnce, SharedString, Styled, FocusHandle, FocusableView, IntoElement, ParentElement, Render, SharedString, Styled,
Subscription, View, ViewContext, VisualContext, WeakView, WindowContext, Subscription, View, ViewContext, VisualContext, WeakView, WindowContext,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;

View file

@ -1344,7 +1344,7 @@ impl Pane {
item: &Box<dyn ItemHandle>, item: &Box<dyn ItemHandle>,
detail: usize, detail: usize,
cx: &mut ViewContext<'_, Pane>, cx: &mut ViewContext<'_, Pane>,
) -> impl RenderOnce { ) -> impl IntoElement {
let label = item.tab_content(Some(detail), cx); let label = item.tab_content(Some(detail), cx);
let close_icon = || { let close_icon = || {
let id = item.item_id(); let id = item.item_id();
@ -1439,7 +1439,7 @@ impl Pane {
) )
} }
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl RenderOnce { fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
div() div()
.group("tab_bar") .group("tab_bar")
.id("tab_bar") .id("tab_bar")

View file

@ -6,7 +6,7 @@ use db2::sqlez::{
statement::Statement, statement::Statement,
}; };
use gpui::{ use gpui::{
point, size, AnyWeakView, Bounds, Div, Model, Pixels, Point, RenderOnce, View, ViewContext, point, size, AnyWeakView, Bounds, Div, IntoElement, Model, Pixels, Point, View, ViewContext,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use project2::Project; use project2::Project;
@ -130,7 +130,7 @@ impl PaneGroup {
zoomed: Option<&AnyWeakView>, zoomed: Option<&AnyWeakView>,
app_state: &Arc<AppState>, app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> impl RenderOnce { ) -> impl IntoElement {
self.root.render( self.root.render(
project, project,
0, 0,
@ -200,7 +200,7 @@ impl Member {
zoomed: Option<&AnyWeakView>, zoomed: Option<&AnyWeakView>,
app_state: &Arc<AppState>, app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> impl RenderOnce { ) -> impl IntoElement {
match self { match self {
Member::Pane(pane) => { Member::Pane(pane) => {
// todo!() // todo!()
@ -579,8 +579,8 @@ impl PaneAxis {
app_state, app_state,
cx, cx,
) )
.render_into_any(), .into_any_element(),
Member::Pane(pane) => pane.clone().render_into_any(), Member::Pane(pane) => pane.clone().into_any_element(),
} }
})) }))

View file

@ -2,7 +2,7 @@ use std::any::TypeId;
use crate::{ItemHandle, Pane}; use crate::{ItemHandle, Pane};
use gpui::{ use gpui::{
div, AnyView, Div, ParentElement, Render, RenderOnce, Styled, Subscription, View, ViewContext, div, AnyView, Div, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
WindowContext, WindowContext,
}; };
use theme2::ActiveTheme; use theme2::ActiveTheme;
@ -53,14 +53,14 @@ impl Render for StatusBar {
} }
impl StatusBar { impl StatusBar {
fn render_left_tools(&self, cx: &mut ViewContext<Self>) -> impl RenderOnce { fn render_left_tools(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
h_stack() h_stack()
.items_center() .items_center()
.gap_2() .gap_2()
.children(self.left_items.iter().map(|item| item.to_any())) .children(self.left_items.iter().map(|item| item.to_any()))
} }
fn render_right_tools(&self, cx: &mut ViewContext<Self>) -> impl RenderOnce { fn render_right_tools(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
h_stack() h_stack()
.items_center() .items_center()
.gap_2() .gap_2()

View file

@ -65,7 +65,8 @@ fn main() {
log::info!("========== starting zed =========="); log::info!("========== starting zed ==========");
let mut app = gpui::App::new(Assets).unwrap(); let mut app = gpui::App::new(Assets).unwrap();
let installation_id = app.background().block(installation_id()).ok(); let (installation_id, existing_installation_id_found) =
app.background().block(installation_id()).ok().unzip();
let session_id = Uuid::new_v4().to_string(); let session_id = Uuid::new_v4().to_string();
init_panic_hook(&app, installation_id.clone(), session_id.clone()); init_panic_hook(&app, installation_id.clone(), session_id.clone());
@ -166,6 +167,14 @@ fn main() {
.detach(); .detach();
client.telemetry().start(installation_id, session_id, cx); client.telemetry().start(installation_id, session_id, cx);
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
let event_operation = match existing_installation_id_found {
Some(false) => "first open",
_ => "open",
};
client
.telemetry()
.report_app_event(telemetry_settings, event_operation);
let app_state = Arc::new(AppState { let app_state = Arc::new(AppState {
languages, languages,
@ -317,11 +326,11 @@ async fn authenticate(client: Arc<Client>, cx: &AsyncAppContext) -> Result<()> {
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())
} }
async fn installation_id() -> Result<String> { async fn installation_id() -> Result<(String, bool)> {
let legacy_key_name = "device_id"; let legacy_key_name = "device_id";
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) { if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
Ok(installation_id) Ok((installation_id, true))
} else { } else {
let installation_id = Uuid::new_v4().to_string(); let installation_id = Uuid::new_v4().to_string();
@ -329,7 +338,7 @@ async fn installation_id() -> Result<String> {
.write_kvp(legacy_key_name.to_string(), installation_id.clone()) .write_kvp(legacy_key_name.to_string(), installation_id.clone())
.await?; .await?;
Ok(installation_id) Ok((installation_id, false))
} }
} }

View file

@ -71,7 +71,11 @@ fn main() {
log::info!("========== starting zed =========="); log::info!("========== starting zed ==========");
let app = App::production(Arc::new(Assets)); let app = App::production(Arc::new(Assets));
let installation_id = app.background_executor().block(installation_id()).ok(); let (installation_id, existing_installation_id_found) = app
.background_executor()
.block(installation_id())
.ok()
.unzip();
let session_id = Uuid::new_v4().to_string(); let session_id = Uuid::new_v4().to_string();
init_panic_hook(&app, installation_id.clone(), session_id.clone()); init_panic_hook(&app, installation_id.clone(), session_id.clone());
@ -173,6 +177,14 @@ fn main() {
// .detach(); // .detach();
client.telemetry().start(installation_id, session_id, cx); client.telemetry().start(installation_id, session_id, cx);
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
let event_operation = match existing_installation_id_found {
Some(false) => "first open",
_ => "open",
};
client
.telemetry()
.report_app_event(telemetry_settings, event_operation);
let app_state = Arc::new(AppState { let app_state = Arc::new(AppState {
languages, languages,
@ -334,11 +346,11 @@ fn main() {
// Ok::<_, anyhow::Error>(()) // Ok::<_, anyhow::Error>(())
// } // }
async fn installation_id() -> Result<String> { async fn installation_id() -> Result<(String, bool)> {
let legacy_key_name = "device_id"; let legacy_key_name = "device_id";
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) { if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
Ok(installation_id) Ok((installation_id, true))
} else { } else {
let installation_id = Uuid::new_v4().to_string(); let installation_id = Uuid::new_v4().to_string();
@ -346,7 +358,7 @@ async fn installation_id() -> Result<String> {
.write_kvp(legacy_key_name.to_string(), installation_id.clone()) .write_kvp(legacy_key_name.to_string(), installation_id.clone())
.await?; .await?;
Ok(installation_id) Ok((installation_id, false))
} }
} }