gpui_markup/lib.rs
1//! gpui-markup - A declarative markup DSL for building GPUI applications.
2//!
3//! This crate provides a Rust-native syntax for building GPUI UIs:
4//!
5//! ```ignore
6//! ui! {
7//! div @[flex, flex_col, w: px(200.0), bg: theme.secondary] {
8//! div @[text_size: px(16.0)] {
9//! "Hello World",
10//! },
11//! }
12//! }
13//! ```
14//!
15//! Which expands to:
16//!
17//! ```ignore
18//! gpui::ParentElement::child(
19//! div()
20//! .flex()
21//! .flex_col()
22//! .w(px(200.0))
23//! .bg(theme.secondary),
24//! gpui::ParentElement::child(
25//! div().text_size(px(16.0)),
26//! "Hello World"
27//! )
28//! )
29//! ```
30
31mod ast;
32mod codegen;
33mod parser;
34
35use proc_macro::TokenStream;
36use proc_macro_error2::proc_macro_error;
37use quote::quote;
38use syn::parse_macro_input;
39
40use crate::ast::Markup;
41
42/// A declarative markup macro for building GPUI UIs.
43///
44/// # Syntax
45///
46/// ## Basic Elements
47///
48/// ```ignore
49/// ui! { div {} } // -> div()
50/// ui! { div @[flex] {} } // -> div().flex()
51/// ui! { div @[w: px(200.0)] {} } // -> div().w(px(200.0))
52/// ```
53///
54/// ## Children
55///
56/// ```ignore
57/// // Comma-separated children
58/// ui! {
59/// div {
60/// "First",
61/// "Second",
62/// }
63/// }
64/// // -> gpui::ParentElement::child(gpui::ParentElement::child(div(), "First"), "Second")
65/// ```
66///
67/// ## Spread Children
68///
69/// Use `..expr` to spread an iterable as children:
70///
71/// ```ignore
72/// let items: Vec<Div> = vec![div(), div()];
73///
74/// ui! {
75/// div {
76/// ..items,
77/// }
78/// }
79/// // -> gpui::ParentElement::children(div(), items)
80///
81/// // Can be mixed with regular children
82/// ui! {
83/// div {
84/// "Header",
85/// ..items,
86/// "Footer",
87/// }
88/// }
89/// // -> gpui::ParentElement::child(
90/// // gpui::ParentElement::children(
91/// // gpui::ParentElement::child(div(), "Header"),
92/// // items
93/// // ),
94/// // "Footer"
95/// // )
96/// ```
97///
98/// ## Method Chains
99///
100/// Use `.method(args)` to insert method calls at any position.
101/// Supports method chains and generics:
102///
103/// ```ignore
104/// ui! {
105/// div {
106/// "static child",
107/// .when(condition, |d| d.child("dynamic")),
108/// .flex().gap_2(),
109/// .map::<Div, _>(|d| d),
110/// }
111/// }
112/// ```
113///
114/// ## Comments
115///
116/// Use standard Rust comments (`//` or `/* */`) inside `ui!`.
117///
118/// ## Expression Elements
119///
120/// Any expression can be used as an element (braces required at top level):
121///
122/// ```ignore
123/// ui! { Button::new("Click") {} } // -> Button::new("Click")
124/// ui! { Button::new("Click") @[style: Primary] {} }
125/// // -> Button::new("Click").style(Primary)
126/// ui! {
127/// div().flex() @[flex_col] {
128/// "Content",
129/// }
130/// }
131/// // -> gpui::ParentElement::child(div().flex().flex_col(), "Content")
132///
133/// // Parentheses for complex expressions (braces optional)
134/// ui! { (a + b) } // -> a + b
135/// ```
136///
137/// **Why braces at top level?** The `ui!` macro builds a component tree.
138/// Braces declare "this is a UI element" - they mark it as a tree node,
139/// trigger implicit `::new()` for components, and provide a place for
140/// attributes and children.
141///
142/// ## Multi-value Attributes
143///
144/// Use tuples for attributes with multiple arguments:
145///
146/// ```ignore
147/// ui! { div @[when: (condition, |d| d.flex())] {} }
148/// // -> div().when(condition, |d| d.flex())
149/// ```
150#[proc_macro]
151#[proc_macro_error]
152pub fn ui(input: TokenStream) -> TokenStream {
153 let markup = parse_macro_input!(input as Markup);
154 let output = quote! { #markup };
155 output.into()
156}