catchup with main

KCaverly created

Change summary

.github/workflows/publish_collab_image.yml                   |    7 
Cargo.lock                                                   |  181 
Cargo.toml                                                   |    5 
assets/keymaps/default.json                                  |    2 
assets/keymaps/textmate.json                                 |   19 
assets/keymaps/vim.json                                      |   16 
assets/settings/default.json                                 |    8 
crates/ai/src/ai.rs                                          |    3 
crates/ai/src/assistant.rs                                   |   45 
crates/ai/src/assistant_settings.rs                          |   33 
crates/call/src/call.rs                                      |   18 
crates/client/src/telemetry.rs                               |    1 
crates/collab/Cargo.toml                                     |    2 
crates/collab_ui/src/collab_titlebar_item.rs                 |    2 
crates/collab_ui/src/collab_ui.rs                            |   11 
crates/collab_ui/src/notifications.rs                        |    5 
crates/diagnostics/src/diagnostics.rs                        |    4 
crates/drag_and_drop/src/drag_and_drop.rs                    |   14 
crates/editor/src/editor.rs                                  |   57 
crates/editor/src/element.rs                                 |    6 
crates/editor/src/items.rs                                   |   31 
crates/editor/src/movement.rs                                |   30 
crates/editor/src/multi_buffer.rs                            |   20 
crates/editor/src/test/editor_lsp_test_context.rs            |   31 
crates/feedback/src/feedback_editor.rs                       |    2 
crates/gpui/Cargo.toml                                       |    2 
crates/gpui/examples/text.rs                                 |    1 
crates/gpui/playground/Cargo.lock                            | 2919 +++
crates/gpui/playground/Cargo.toml                            |   26 
crates/gpui/playground/docs/thoughts.md                      |   72 
crates/gpui/playground/src/adapter.rs                        |   78 
crates/gpui/playground/src/color.rs                          |  276 
crates/gpui/playground/src/components.rs                     |  100 
crates/gpui/playground/src/div.rs                            |  108 
crates/gpui/playground/src/element.rs                        |  158 
crates/gpui/playground/src/hoverable.rs                      |   76 
crates/gpui/playground/src/interactive.rs                    |   34 
crates/gpui/playground/src/layout_context.rs                 |   54 
crates/gpui/playground/src/paint_context.rs                  |   71 
crates/gpui/playground/src/playground.rs                     |   83 
crates/gpui/playground/src/style.rs                          |  286 
crates/gpui/playground/src/text.rs                           |  151 
crates/gpui/playground/src/themes.rs                         |   84 
crates/gpui/playground/src/themes/rose_pine.rs               |  133 
crates/gpui/playground/src/view.rs                           |   26 
crates/gpui/playground_macros/Cargo.toml                     |   14 
crates/gpui/playground_macros/src/derive_element.rs          |   91 
crates/gpui/playground_macros/src/derive_into_element.rs     |   69 
crates/gpui/playground_macros/src/playground_macros.rs       |   26 
crates/gpui/playground_macros/src/styleable_helpers.rs       |  147 
crates/gpui/playground_macros/src/tailwind_lengths.rs        |   99 
crates/gpui/src/app.rs                                       |  361 
crates/gpui/src/app/window.rs                                |  219 
crates/gpui/src/color.rs                                     |   62 
crates/gpui/src/elements.rs                                  |   60 
crates/gpui/src/elements/align.rs                            |    8 
crates/gpui/src/elements/canvas.rs                           |    5 
crates/gpui/src/elements/clipped.rs                          |    8 
crates/gpui/src/elements/constrained_box.rs                  |   12 
crates/gpui/src/elements/container.rs                        |   11 
crates/gpui/src/elements/empty.rs                            |    4 
crates/gpui/src/elements/expanded.rs                         |    8 
crates/gpui/src/elements/flex.rs                             |   16 
crates/gpui/src/elements/hook.rs                             |    9 
crates/gpui/src/elements/image.rs                            |    4 
crates/gpui/src/elements/keystroke_label.rs                  |    2 
crates/gpui/src/elements/label.rs                            |    4 
crates/gpui/src/elements/list.rs                             |   32 
crates/gpui/src/elements/mouse_event_handler.rs              |    8 
crates/gpui/src/elements/overlay.rs                          |    8 
crates/gpui/src/elements/resizable.rs                        |   10 
crates/gpui/src/elements/stack.rs                            |   13 
crates/gpui/src/elements/svg.rs                              |    6 
crates/gpui/src/elements/text.rs                             |    6 
crates/gpui/src/elements/tooltip.rs                          |    8 
crates/gpui/src/elements/uniform_list.rs                     |   11 
crates/gpui/src/fonts.rs                                     |   56 
crates/gpui/src/geometry.rs                                  |  256 
crates/gpui/src/gpui.rs                                      |    5 
crates/gpui/src/platform.rs                                  |    2 
crates/gpui/src/platform/event.rs                            |   20 
crates/gpui/src/platform/mac/event.rs                        |    2 
crates/gpui/src/scene.rs                                     |   28 
crates/gpui/src/scene/mouse_region.rs                        |   48 
crates/gpui/src/scene/region.rs                              |    7 
crates/gpui/src/text_layout.rs                               |    2 
crates/gpui/tests/test.rs                                    |   10 
crates/gpui_macros/Cargo.toml                                |    3 
crates/gpui_macros/src/gpui_macros.rs                        |   54 
crates/language/src/buffer.rs                                |   25 
crates/language/src/language.rs                              |    5 
crates/language_tools/src/lsp_log.rs                         |    2 
crates/language_tools/src/syntax_tree_view.rs                |    3 
crates/project/src/project.rs                                |    2 
crates/project/src/search.rs                                 |   27 
crates/project_panel/src/project_panel.rs                    |    2 
crates/recent_projects/src/highlighted_workspace_location.rs |    4 
crates/refineable/Cargo.toml                                 |   15 
crates/refineable/derive_refineable/Cargo.toml               |   15 
crates/refineable/derive_refineable/src/derive_refineable.rs |  188 
crates/refineable/src/refineable.rs                          |   14 
crates/search/src/project_search.rs                          |    2 
crates/settings/src/settings_store.rs                        |    3 
crates/terminal_view/src/terminal_element.rs                 |    1 
crates/terminal_view/src/terminal_view.rs                    |    2 
crates/theme/src/ui.rs                                       |   16 
crates/vim/src/motion.rs                                     |   18 
crates/vim/src/normal.rs                                     |  185 
crates/vim/src/normal/change.rs                              |    9 
crates/vim/src/normal/paste.rs                               |  468 
crates/vim/src/object.rs                                     |   21 
crates/vim/src/test.rs                                       |   26 
crates/vim/src/test/neovim_backed_test_context.rs            |   84 
crates/vim/src/test/neovim_connection.rs                     |   31 
crates/vim/src/test/vim_test_context.rs                      |   12 
crates/vim/src/utils.rs                                      |    8 
crates/vim/src/visual.rs                                     |  171 
crates/vim/test_data/test_p.json                             |   13 
crates/vim/test_data/test_paste.json                         |   31 
crates/vim/test_data/test_paste_visual.json                  |   42 
crates/vim/test_data/test_paste_visual_block.json            |   31 
crates/vim/test_data/test_visual_paste.json                  |   26 
crates/welcome/src/welcome.rs                                |    2 
crates/workspace/src/item.rs                                 |    4 
crates/workspace/src/pane.rs                                 |    6 
crates/workspace/src/pane/dragged_item_receiver.rs           |    4 
crates/workspace/src/shared_screen.rs                        |    2 
crates/zed/Cargo.toml                                        |    4 
crates/zed/src/languages/javascript/config.toml              |    1 
crates/zed/src/languages/php/config.toml                     |    1 
crates/zed/src/languages/tsx/config.toml                     |    1 
crates/zed/src/languages/typescript/config.toml              |    1 
test.rs                                                      | 5670 ++++++
133 files changed, 13,369 insertions(+), 954 deletions(-)

Detailed changes

.github/workflows/publish_collab_image.yml 🔗

@@ -11,7 +11,7 @@ env:
 
 jobs:
   publish:
-    name: Publish collab server image 
+    name: Publish collab server image
     runs-on:
       - self-hosted
       - deploy
@@ -22,6 +22,9 @@ jobs:
       - name: Sign into DigitalOcean docker registry
         run: doctl registry login
 
+      - name: Prune Docker system
+        run: docker system prune
+
       - name: Checkout repo
         uses: actions/checkout@v3
         with:
@@ -41,6 +44,6 @@ jobs:
 
       - name: Build docker image
         run: docker build . --tag registry.digitalocean.com/zed/collab:v${COLLAB_VERSION}
-    
+
       - name: Publish docker image
         run: docker push registry.digitalocean.com/zed/collab:v${COLLAB_VERSION}

Cargo.lock 🔗

@@ -36,11 +36,11 @@ dependencies = [
 
 [[package]]
 name = "addr2line"
-version = "0.20.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
 dependencies = [
- "gimli 0.27.3",
+ "gimli 0.28.0",
 ]
 
 [[package]]
@@ -244,9 +244,9 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
 
 [[package]]
 name = "anstyle-parse"
@@ -739,16 +739,16 @@ dependencies = [
 
 [[package]]
 name = "backtrace"
-version = "0.3.68"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
 dependencies = [
- "addr2line 0.20.0",
+ "addr2line 0.21.0",
  "cc",
  "cfg-if 1.0.0",
  "libc",
  "miniz_oxide 0.7.1",
- "object 0.31.1",
+ "object 0.32.0",
  "rustc-demangle",
 ]
 
@@ -1252,9 +1252,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.3.23"
+version = "4.3.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3"
+checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487"
 dependencies = [
  "clap_builder",
  "clap_derive 4.3.12",
@@ -1263,9 +1263,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.3.23"
+version = "4.3.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98"
+checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e"
 dependencies = [
  "anstream",
  "anstyle",
@@ -1356,7 +1356,7 @@ dependencies = [
  "sum_tree",
  "tempfile",
  "thiserror",
- "time 0.3.25",
+ "time 0.3.27",
  "tiny_http",
  "url",
  "util",
@@ -1410,7 +1410,7 @@ dependencies = [
 
 [[package]]
 name = "collab"
-version = "0.17.0"
+version = "0.18.0"
 dependencies = [
  "anyhow",
  "async-tungstenite",
@@ -1458,7 +1458,7 @@ dependencies = [
  "sha-1 0.9.8",
  "sqlx",
  "theme",
- "time 0.3.25",
+ "time 0.3.27",
  "tokio",
  "tokio-tungstenite",
  "toml 0.5.11",
@@ -2006,9 +2006,9 @@ dependencies = [
 
 [[package]]
 name = "dashmap"
-version = "5.5.0"
+version = "5.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d"
+checksum = "edd72493923899c6f10c641bdbdeddc7183d6396641d99c1a0d1597f37f92e28"
 dependencies = [
  "cfg-if 1.0.0",
  "hashbrown 0.14.0",
@@ -2086,6 +2086,15 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "derive_refineable"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "dhat"
 version = "0.3.2"
@@ -2312,9 +2321,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.32"
+version = "0.8.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
+checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
 dependencies = [
  "cfg-if 1.0.0",
 ]
@@ -2688,7 +2697,7 @@ dependencies = [
  "smol",
  "sum_tree",
  "tempfile",
- "time 0.3.25",
+ "time 0.3.27",
  "util",
 ]
 
@@ -2945,9 +2954,9 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.27.3"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
 
 [[package]]
 name = "git"
@@ -3068,6 +3077,7 @@ dependencies = [
  "png",
  "postage",
  "rand 0.8.5",
+ "refineable",
  "resvg",
  "schemars",
  "seahash",
@@ -3079,7 +3089,8 @@ dependencies = [
  "smol",
  "sqlez",
  "sum_tree",
- "time 0.3.25",
+ "taffy",
+ "time 0.3.27",
  "tiny-skia",
  "usvg",
  "util",
@@ -3091,16 +3102,23 @@ dependencies = [
 name = "gpui_macros"
 version = "0.1.0"
 dependencies = [
+ "lazy_static",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "grid"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c"
+
 [[package]]
 name = "h2"
-version = "0.3.20"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
+checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
 dependencies = [
  "bytes 1.4.0",
  "fnv",
@@ -4544,9 +4562,9 @@ dependencies = [
 
 [[package]]
 name = "num-bigint"
-version = "0.4.3"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
 dependencies = [
  "autocfg",
  "num-integer",
@@ -4702,9 +4720,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.31.1"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
+checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
 dependencies = [
  "memchr",
 ]
@@ -5082,6 +5100,33 @@ version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 
+[[package]]
+name = "playground"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "derive_more",
+ "gpui",
+ "log",
+ "parking_lot 0.11.2",
+ "playground_macros",
+ "refineable",
+ "serde",
+ "simplelog",
+ "smallvec",
+ "taffy",
+ "util",
+]
+
+[[package]]
+name = "playground_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "plist"
 version = "1.5.0"
@@ -5093,7 +5138,7 @@ dependencies = [
  "line-wrap",
  "quick-xml",
  "serde",
- "time 0.3.25",
+ "time 0.3.27",
 ]
 
 [[package]]
@@ -5759,6 +5804,16 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "refineable"
+version = "0.1.0"
+dependencies = [
+ "derive_refineable",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "regalloc2"
 version = "0.2.3"
@@ -5847,9 +5902,9 @@ dependencies = [
 
 [[package]]
 name = "reqwest"
-version = "0.11.18"
+version = "0.11.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
+checksum = "20b9b67e2ca7dd9e9f9285b759de30ff538aab981abaaf7bc9bd90b84a0126c3"
 dependencies = [
  "base64 0.21.2",
  "bytes 1.4.0",
@@ -6105,13 +6160,12 @@ dependencies = [
 
 [[package]]
 name = "rust_decimal"
-version = "1.31.0"
+version = "1.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a2ab0025103a60ecaaf3abf24db1db240a4e1c15837090d2c32f625ac98abea"
+checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd"
 dependencies = [
  "arrayvec 0.7.4",
  "borsh",
- "byteorder",
  "bytes 1.4.0",
  "num-traits",
  "rand 0.8.5",
@@ -6388,7 +6442,7 @@ dependencies = [
  "serde_json",
  "sqlx",
  "thiserror",
- "time 0.3.25",
+ "time 0.3.27",
  "tracing",
  "url",
  "uuid 1.4.1",
@@ -6416,7 +6470,7 @@ dependencies = [
  "rust_decimal",
  "sea-query-derive",
  "serde_json",
- "time 0.3.25",
+ "time 0.3.27",
  "uuid 1.4.1",
 ]
 
@@ -6431,7 +6485,7 @@ dependencies = [
  "sea-query",
  "serde_json",
  "sqlx",
- "time 0.3.25",
+ "time 0.3.27",
  "uuid 1.4.1",
 ]
 
@@ -6912,9 +6966,9 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
 
 [[package]]
 name = "slab"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
 dependencies = [
  "autocfg",
 ]
@@ -6925,6 +6979,15 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
 
+[[package]]
+name = "slotmap"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "sluice"
 version = "0.5.5"
@@ -7119,7 +7182,7 @@ dependencies = [
  "sqlx-rt",
  "stringprep",
  "thiserror",
- "time 0.3.25",
+ "time 0.3.27",
  "tokio-stream",
  "url",
  "uuid 1.4.1",
@@ -7374,6 +7437,17 @@ dependencies = [
  "winx",
 ]
 
+[[package]]
+name = "taffy"
+version = "0.3.11"
+source = "git+https://github.com/DioxusLabs/taffy?rev=dab541d6104d58e2e10ce90c4a1dad0b703160cd#dab541d6104d58e2e10ce90c4a1dad0b703160cd"
+dependencies = [
+ "arrayvec 0.7.4",
+ "grid",
+ "num-traits",
+ "slotmap",
+]
+
 [[package]]
 name = "take-until"
 version = "0.2.0"
@@ -7646,9 +7720,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.25"
+version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
+checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07"
 dependencies = [
  "deranged",
  "itoa",
@@ -7665,9 +7739,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
 
 [[package]]
 name = "time-macros"
-version = "0.2.11"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd"
+checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9"
 dependencies = [
  "time-core",
 ]
@@ -8370,9 +8444,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
 
 [[package]]
 name = "unicase"
-version = "2.6.0"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
 dependencies = [
  "version_check",
 ]
@@ -9404,11 +9478,12 @@ dependencies = [
 
 [[package]]
 name = "winreg"
-version = "0.10.1"
+version = "0.50.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
 dependencies = [
- "winapi 0.3.9",
+ "cfg-if 1.0.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -9527,7 +9602,7 @@ name = "xtask"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "clap 4.3.23",
+ "clap 4.3.24",
  "schemars",
  "serde_json",
  "theme",
@@ -9562,7 +9637,7 @@ dependencies = [
 
 [[package]]
 name = "zed"
-version = "0.101.0"
+version = "0.102.0"
 dependencies = [
  "activity_indicator",
  "ai",

Cargo.toml 🔗

@@ -17,6 +17,8 @@ members = [
     "crates/copilot",
     "crates/copilot_button",
     "crates/db",
+    "crates/refineable",
+    "crates/refineable/derive_refineable",
     "crates/diagnostics",
     "crates/drag_and_drop",
     "crates/editor",
@@ -28,6 +30,8 @@ members = [
     "crates/git",
     "crates/go_to_line",
     "crates/gpui",
+    "crates/gpui/playground",
+    "crates/gpui/playground_macros",
     "crates/gpui_macros",
     "crates/install_cli",
     "crates/journal",
@@ -92,6 +96,7 @@ ordered-float = { version = "2.1.1" }
 parking_lot = { version = "0.11.1" }
 postage = { version = "0.5", features = ["futures-traits"] }
 rand = { version = "0.8.5" }
+refineable = { path = "./crates/refineable" }
 regex = { version = "1.5" }
 rust-embed = { version = "6.3", features = ["include-exclude"] }
 schemars = { version = "0.8" }

assets/keymaps/default.json 🔗

@@ -543,6 +543,8 @@
     "bindings": {
       "left": "project_panel::CollapseSelectedEntry",
       "right": "project_panel::ExpandSelectedEntry",
+      "cmd-n": "project_panel::NewFile",
+      "alt-cmd-n": "project_panel::NewDirectory",
       "cmd-x": "project_panel::Cut",
       "cmd-c": "project_panel::Copy",
       "cmd-v": "project_panel::Paste",

assets/keymaps/textmate.json 🔗

@@ -2,7 +2,6 @@
   {
     "bindings": {
       "cmd-shift-o": "projects::OpenRecent",
-      "cmd-shift-b": "branches::OpenRecent",
       "cmd-alt-tab": "project_panel::ToggleFocus"
     }
   },
@@ -12,8 +11,9 @@
       "cmd-l": "go_to_line::Toggle",
       "ctrl-shift-d": "editor::DuplicateLine",
       "cmd-b": "editor::GoToDefinition",
-      "alt-cmd-b": "editor::GoToDefinition",
       "cmd-j": "editor::ScrollCursorCenter",
+      "cmd-enter": "editor::NewlineBelow",
+      "cmd-alt-enter": "editor::NewLineAbove",
       "cmd-shift-l": "editor::SelectLine",
       "cmd-shift-t": "outline::Toggle",
       "alt-backspace": "editor::DeleteToPreviousWordStart",
@@ -51,14 +51,17 @@
         }
       ],
       "ctrl-shift-left": "editor::SelectToPreviousSubwordStart",
-      "ctrl-shift-right": "editor::SelectToNextSubwordEnd"
+      "ctrl-shift-right": "editor::SelectToNextSubwordEnd",
+      "ctrl-w": "editor::SelectNext",
+      "ctrl-u": "editor::ConvertToUpperCase",
+      "ctrl-shift-u": "editor::ConvertToLowerCase",
+      "ctrl-alt-u": "editor::ConvertToUpperCamelCase",
+      "ctrl-_": "editor::ConvertToSnakeCase"
     }
   },
   {
     "context": "Editor && mode == full",
-    "bindings": {
-      "cmd-alt-enter": "editor::NewlineAbove"
-    }
+    "bindings": {}
   },
   {
     "context": "BufferSearchBar",
@@ -85,5 +88,9 @@
   {
     "context": "ProjectPanel",
     "bindings": {}
+  },
+  {
+    "context": "Dock",
+    "bindings": {}
   }
 ]

assets/keymaps/vim.json 🔗

@@ -287,6 +287,12 @@
       "shift-o": "vim::InsertLineAbove",
       "~": "vim::ChangeCase",
       "p": "vim::Paste",
+      "shift-p": [
+        "vim::Paste",
+        {
+          "before": true
+        }
+      ],
       "u": "editor::Undo",
       "ctrl-r": "editor::Redo",
       "/": "vim::Search",
@@ -375,7 +381,13 @@
       "d": "vim::VisualDelete",
       "x": "vim::VisualDelete",
       "y": "vim::VisualYank",
-      "p": "vim::VisualPaste",
+      "p": "vim::Paste",
+      "shift-p": [
+        "vim::Paste",
+        {
+          "preserveClipboard": true
+        }
+      ],
       "s": "vim::Substitute",
       "c": "vim::Substitute",
       "~": "vim::ChangeCase",
@@ -421,7 +433,7 @@
     }
   },
   {
-    "context": "Editor && vim_mode == insert",
+    "context": "Editor && vim_mode == insert && !menu",
     "bindings": {
       "escape": "vim::NormalBefore",
       "ctrl-c": "vim::NormalBefore",

assets/settings/default.json 🔗

@@ -138,7 +138,13 @@
     // Default width when the assistant is docked to the left or right.
     "default_width": 640,
     // Default height when the assistant is docked to the bottom.
-    "default_height": 320
+    "default_height": 320,
+    // The default OpenAI model to use when starting new conversations. This
+    // setting can take two values:
+    //
+    // 1. "gpt-3.5-turbo-0613""
+    // 2. "gpt-4-0613""
+    "default_open_ai_model": "gpt-4-0613"
   },
   // Whether the screen sharing icon is shown in the os status bar.
   "show_call_status_icon": true,

crates/ai/src/ai.rs 🔗

@@ -3,6 +3,7 @@ mod assistant_settings;
 
 use anyhow::Result;
 pub use assistant::AssistantPanel;
+use assistant_settings::OpenAIModel;
 use chrono::{DateTime, Local};
 use collections::HashMap;
 use fs::Fs;
@@ -60,7 +61,7 @@ struct SavedConversation {
     messages: Vec<SavedMessage>,
     message_metadata: HashMap<MessageId, MessageMetadata>,
     summary: String,
-    model: String,
+    model: OpenAIModel,
 }
 
 impl SavedConversation {

crates/ai/src/assistant.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    assistant_settings::{AssistantDockPosition, AssistantSettings},
+    assistant_settings::{AssistantDockPosition, AssistantSettings, OpenAIModel},
     MessageId, MessageMetadata, MessageStatus, OpenAIRequest, OpenAIResponseStreamEvent,
     RequestMessage, Role, SavedConversation, SavedConversationMetadata, SavedMessage,
 };
@@ -833,7 +833,7 @@ struct Conversation {
     pending_summary: Task<Option<()>>,
     completion_count: usize,
     pending_completions: Vec<PendingCompletion>,
-    model: String,
+    model: OpenAIModel,
     token_count: Option<usize>,
     max_token_count: usize,
     pending_token_count: Task<Option<()>>,
@@ -853,7 +853,6 @@ impl Conversation {
         language_registry: Arc<LanguageRegistry>,
         cx: &mut ModelContext<Self>,
     ) -> Self {
-        let model = "gpt-3.5-turbo-0613";
         let markdown = language_registry.language_for_name("Markdown");
         let buffer = cx.add_model(|cx| {
             let mut buffer = Buffer::new(0, "", cx);
@@ -872,6 +871,9 @@ impl Conversation {
             buffer
         });
 
+        let settings = settings::get::<AssistantSettings>(cx);
+        let model = settings.default_open_ai_model.clone();
+
         let mut this = Self {
             message_anchors: Default::default(),
             messages_metadata: Default::default(),
@@ -881,9 +883,9 @@ impl Conversation {
             completion_count: Default::default(),
             pending_completions: Default::default(),
             token_count: None,
-            max_token_count: tiktoken_rs::model::get_context_size(model),
+            max_token_count: tiktoken_rs::model::get_context_size(&model.full_name()),
             pending_token_count: Task::ready(None),
-            model: model.into(),
+            model: model.clone(),
             _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
             pending_save: Task::ready(Ok(())),
             path: None,
@@ -977,7 +979,7 @@ impl Conversation {
             completion_count: Default::default(),
             pending_completions: Default::default(),
             token_count: None,
-            max_token_count: tiktoken_rs::model::get_context_size(&model),
+            max_token_count: tiktoken_rs::model::get_context_size(&model.full_name()),
             pending_token_count: Task::ready(None),
             model,
             _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
@@ -1031,13 +1033,16 @@ impl Conversation {
                 cx.background().timer(Duration::from_millis(200)).await;
                 let token_count = cx
                     .background()
-                    .spawn(async move { tiktoken_rs::num_tokens_from_messages(&model, &messages) })
+                    .spawn(async move {
+                        tiktoken_rs::num_tokens_from_messages(&model.full_name(), &messages)
+                    })
                     .await?;
 
                 this.upgrade(&cx)
                     .ok_or_else(|| anyhow!("conversation was dropped"))?
                     .update(&mut cx, |this, cx| {
-                        this.max_token_count = tiktoken_rs::model::get_context_size(&this.model);
+                        this.max_token_count =
+                            tiktoken_rs::model::get_context_size(&this.model.full_name());
                         this.token_count = Some(token_count);
                         cx.notify()
                     });
@@ -1051,7 +1056,7 @@ impl Conversation {
         Some(self.max_token_count as isize - self.token_count? as isize)
     }
 
-    fn set_model(&mut self, model: String, cx: &mut ModelContext<Self>) {
+    fn set_model(&mut self, model: OpenAIModel, cx: &mut ModelContext<Self>) {
         self.model = model;
         self.count_remaining_tokens(cx);
         cx.notify();
@@ -1093,7 +1098,7 @@ impl Conversation {
                 }
             } else {
                 let request = OpenAIRequest {
-                    model: self.model.clone(),
+                    model: self.model.full_name().to_string(),
                     messages: self
                         .messages(cx)
                         .filter(|message| matches!(message.status, MessageStatus::Done))
@@ -1419,7 +1424,7 @@ impl Conversation {
                                 .into(),
                     }));
                 let request = OpenAIRequest {
-                    model: self.model.clone(),
+                    model: self.model.full_name().to_string(),
                     messages: messages.collect(),
                     stream: true,
                 };
@@ -2023,11 +2028,8 @@ impl ConversationEditor {
 
     fn cycle_model(&mut self, cx: &mut ViewContext<Self>) {
         self.conversation.update(cx, |conversation, cx| {
-            let new_model = match conversation.model.as_str() {
-                "gpt-4-0613" => "gpt-3.5-turbo-0613",
-                _ => "gpt-4-0613",
-            };
-            conversation.set_model(new_model.into(), cx);
+            let new_model = conversation.model.cycle();
+            conversation.set_model(new_model, cx);
         });
     }
 
@@ -2049,7 +2051,8 @@ impl ConversationEditor {
 
         MouseEventHandler::new::<Model, _>(0, cx, |state, cx| {
             let style = style.model.style_for(state);
-            Label::new(self.conversation.read(cx).model.clone(), style.text.clone())
+            let model_display_name = self.conversation.read(cx).model.short_name();
+            Label::new(model_display_name, style.text.clone())
                 .contained()
                 .with_style(style.container)
         })
@@ -2238,6 +2241,8 @@ mod tests {
 
     #[gpui::test]
     fn test_inserting_and_removing_messages(cx: &mut AppContext) {
+        cx.set_global(SettingsStore::test(cx));
+        init(cx);
         let registry = Arc::new(LanguageRegistry::test());
         let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
         let buffer = conversation.read(cx).buffer.clone();
@@ -2364,6 +2369,8 @@ mod tests {
 
     #[gpui::test]
     fn test_message_splitting(cx: &mut AppContext) {
+        cx.set_global(SettingsStore::test(cx));
+        init(cx);
         let registry = Arc::new(LanguageRegistry::test());
         let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
         let buffer = conversation.read(cx).buffer.clone();
@@ -2458,6 +2465,8 @@ mod tests {
 
     #[gpui::test]
     fn test_messages_for_offsets(cx: &mut AppContext) {
+        cx.set_global(SettingsStore::test(cx));
+        init(cx);
         let registry = Arc::new(LanguageRegistry::test());
         let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
         let buffer = conversation.read(cx).buffer.clone();
@@ -2538,6 +2547,8 @@ mod tests {
 
     #[gpui::test]
     fn test_serialization(cx: &mut AppContext) {
+        cx.set_global(SettingsStore::test(cx));
+        init(cx);
         let registry = Arc::new(LanguageRegistry::test());
         let conversation =
             cx.add_model(|cx| Conversation::new(Default::default(), registry.clone(), cx));

crates/ai/src/assistant_settings.rs 🔗

@@ -3,6 +3,37 @@ use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::Setting;
 
+#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
+pub enum OpenAIModel {
+    #[serde(rename = "gpt-3.5-turbo-0613")]
+    ThreePointFiveTurbo,
+    #[serde(rename = "gpt-4-0613")]
+    Four,
+}
+
+impl OpenAIModel {
+    pub fn full_name(&self) -> &'static str {
+        match self {
+            OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo-0613",
+            OpenAIModel::Four => "gpt-4-0613",
+        }
+    }
+
+    pub fn short_name(&self) -> &'static str {
+        match self {
+            OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo",
+            OpenAIModel::Four => "gpt-4",
+        }
+    }
+
+    pub fn cycle(&self) -> Self {
+        match self {
+            OpenAIModel::ThreePointFiveTurbo => OpenAIModel::Four,
+            OpenAIModel::Four => OpenAIModel::ThreePointFiveTurbo,
+        }
+    }
+}
+
 #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum AssistantDockPosition {
@@ -17,6 +48,7 @@ pub struct AssistantSettings {
     pub dock: AssistantDockPosition,
     pub default_width: f32,
     pub default_height: f32,
+    pub default_open_ai_model: OpenAIModel,
 }
 
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
@@ -25,6 +57,7 @@ pub struct AssistantSettingsContent {
     pub dock: Option<AssistantDockPosition>,
     pub default_width: Option<f32>,
     pub default_height: Option<f32>,
+    pub default_open_ai_model: Option<OpenAIModel>,
 }
 
 impl Setting for AssistantSettings {

crates/call/src/call.rs 🔗

@@ -274,7 +274,7 @@ impl ActiveCall {
             .borrow_mut()
             .take()
             .ok_or_else(|| anyhow!("no incoming call"))?;
-        Self::report_call_event_for_room("decline incoming", call.room_id, &self.client, cx);
+        Self::report_call_event_for_room("decline incoming", call.room_id, None, &self.client, cx);
         self.client.send(proto::DeclineCall {
             room_id: call.room_id,
         })?;
@@ -406,19 +406,31 @@ impl ActiveCall {
 
     fn report_call_event(&self, operation: &'static str, cx: &AppContext) {
         if let Some(room) = self.room() {
-            Self::report_call_event_for_room(operation, room.read(cx).id(), &self.client, cx)
+            let room = room.read(cx);
+            Self::report_call_event_for_room(
+                operation,
+                room.id(),
+                room.channel_id(),
+                &self.client,
+                cx,
+            )
         }
     }
 
     pub fn report_call_event_for_room(
         operation: &'static str,
         room_id: u64,
+        channel_id: Option<u64>,
         client: &Arc<Client>,
         cx: &AppContext,
     ) {
         let telemetry = client.telemetry();
         let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
-        let event = ClickhouseEvent::Call { operation, room_id };
+        let event = ClickhouseEvent::Call {
+            operation,
+            room_id,
+            channel_id,
+        };
         telemetry.report_clickhouse_event(event, telemetry_settings);
     }
 }

crates/collab/Cargo.toml 🔗

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
 default-run = "collab"
 edition = "2021"
 name = "collab"
-version = "0.17.0"
+version = "0.18.0"
 publish = false
 
 [[bin]]

crates/collab_ui/src/collab_titlebar_item.rs 🔗

@@ -1096,7 +1096,7 @@ impl CollabTitlebarItem {
         style
     }
 
-    fn render_face<V: View>(
+    fn render_face<V: 'static>(
         avatar: Arc<ImageData>,
         avatar_style: AvatarStyle,
         background_color: Color,

crates/collab_ui/src/collab_ui.rs 🔗

@@ -49,6 +49,7 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
                 ActiveCall::report_call_event_for_room(
                     "disable screen share",
                     room.id(),
+                    room.channel_id(),
                     &client,
                     cx,
                 );
@@ -57,6 +58,7 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
                 ActiveCall::report_call_event_for_room(
                     "enable screen share",
                     room.id(),
+                    room.channel_id(),
                     &client,
                     cx,
                 );
@@ -73,11 +75,18 @@ pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
         let client = call.client();
         room.update(cx, |room, cx| {
             if room.is_muted(cx) {
-                ActiveCall::report_call_event_for_room("enable microphone", room.id(), &client, cx);
+                ActiveCall::report_call_event_for_room(
+                    "enable microphone",
+                    room.id(),
+                    room.channel_id(),
+                    &client,
+                    cx,
+                );
             } else {
                 ActiveCall::report_call_event_for_room(
                     "disable microphone",
                     room.id(),
+                    room.channel_id(),
                     &client,
                     cx,
                 );

crates/collab_ui/src/notifications.rs 🔗

@@ -2,14 +2,14 @@ use client::User;
 use gpui::{
     elements::*,
     platform::{CursorStyle, MouseButton},
-    AnyElement, Element, View, ViewContext,
+    AnyElement, Element, ViewContext,
 };
 use std::sync::Arc;
 
 enum Dismiss {}
 enum Button {}
 
-pub fn render_user_notification<F, V>(
+pub fn render_user_notification<F, V: 'static>(
     user: Arc<User>,
     title: &'static str,
     body: Option<&'static str>,
@@ -19,7 +19,6 @@ pub fn render_user_notification<F, V>(
 ) -> AnyElement<V>
 where
     F: 'static + Fn(&mut V, &mut ViewContext<V>),
-    V: View,
 {
     let theme = theme::current(cx).clone();
     let theme = &theme.contact_notification;

crates/diagnostics/src/diagnostics.rs 🔗

@@ -538,7 +538,7 @@ impl ProjectDiagnosticsEditor {
 }
 
 impl Item for ProjectDiagnosticsEditor {
-    fn tab_content<T: View>(
+    fn tab_content<T: 'static>(
         &self,
         _detail: Option<usize>,
         style: &theme::Tab,
@@ -735,7 +735,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
     })
 }
 
-pub(crate) fn render_summary<T: View>(
+pub(crate) fn render_summary<T: 'static>(
     summary: &DiagnosticSummary,
     text_style: &TextStyle,
     theme: &theme::ProjectDiagnostics,

crates/drag_and_drop/src/drag_and_drop.rs 🔗

@@ -11,7 +11,7 @@ use gpui::{
 
 const DEAD_ZONE: f32 = 4.;
 
-enum State<V: View> {
+enum State<V> {
     Down {
         region_offset: Vector2F,
         region: RectF,
@@ -31,7 +31,7 @@ enum State<V: View> {
     Canceled,
 }
 
-impl<V: View> Clone for State<V> {
+impl<V> Clone for State<V> {
     fn clone(&self) -> Self {
         match self {
             &State::Down {
@@ -68,12 +68,12 @@ impl<V: View> Clone for State<V> {
     }
 }
 
-pub struct DragAndDrop<V: View> {
+pub struct DragAndDrop<V> {
     containers: HashSet<WeakViewHandle<V>>,
     currently_dragged: Option<State<V>>,
 }
 
-impl<V: View> Default for DragAndDrop<V> {
+impl<V> Default for DragAndDrop<V> {
     fn default() -> Self {
         Self {
             containers: Default::default(),
@@ -82,7 +82,7 @@ impl<V: View> Default for DragAndDrop<V> {
     }
 }
 
-impl<V: View> DragAndDrop<V> {
+impl<V: 'static> DragAndDrop<V> {
     pub fn register_container(&mut self, handle: WeakViewHandle<V>) {
         self.containers.insert(handle);
     }
@@ -291,7 +291,7 @@ impl<V: View> DragAndDrop<V> {
     }
 }
 
-pub trait Draggable<V: View> {
+pub trait Draggable<V> {
     fn as_draggable<D: View, P: Any>(
         self,
         payload: P,
@@ -301,7 +301,7 @@ pub trait Draggable<V: View> {
         Self: Sized;
 }
 
-impl<V: View> Draggable<V> for MouseEventHandler<V> {
+impl<V: 'static> Draggable<V> for MouseEventHandler<V> {
     fn as_draggable<D: View, P: Any>(
         self,
         payload: P,

crates/editor/src/editor.rs 🔗

@@ -1736,6 +1736,31 @@ impl Editor {
         });
     }
 
+    pub fn edit_with_block_indent<I, S, T>(
+        &mut self,
+        edits: I,
+        original_indent_columns: Vec<u32>,
+        cx: &mut ViewContext<Self>,
+    ) where
+        I: IntoIterator<Item = (Range<S>, T)>,
+        S: ToOffset,
+        T: Into<Arc<str>>,
+    {
+        if self.read_only {
+            return;
+        }
+
+        self.buffer.update(cx, |buffer, cx| {
+            buffer.edit(
+                edits,
+                Some(AutoindentMode::Block {
+                    original_indent_columns,
+                }),
+                cx,
+            )
+        });
+    }
+
     fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
         self.hide_context_menu(cx);
 
@@ -2667,7 +2692,6 @@ impl Editor {
             false
         });
     }
-
     fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
         let offset = position.to_offset(buffer);
         let (word_range, kind) = buffer.surrounding_word(offset);
@@ -4742,6 +4766,7 @@ impl Editor {
         let mut clipboard_selections = Vec::with_capacity(selections.len());
         {
             let max_point = buffer.max_point();
+            let mut is_first = true;
             for selection in &mut selections {
                 let is_entire_line = selection.is_empty() || self.selections.line_mode;
                 if is_entire_line {
@@ -4749,6 +4774,11 @@ impl Editor {
                     selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
                     selection.goal = SelectionGoal::None;
                 }
+                if is_first {
+                    is_first = false;
+                } else {
+                    text += "\n";
+                }
                 let mut len = 0;
                 for chunk in buffer.text_for_range(selection.start..selection.end) {
                     text.push_str(chunk);
@@ -4779,6 +4809,7 @@ impl Editor {
         let mut clipboard_selections = Vec::with_capacity(selections.len());
         {
             let max_point = buffer.max_point();
+            let mut is_first = true;
             for selection in selections.iter() {
                 let mut start = selection.start;
                 let mut end = selection.end;
@@ -4787,6 +4818,11 @@ impl Editor {
                     start = Point::new(start.row, 0);
                     end = cmp::min(max_point, Point::new(end.row + 1, 0));
                 }
+                if is_first {
+                    is_first = false;
+                } else {
+                    text += "\n";
+                }
                 let mut len = 0;
                 for chunk in buffer.text_for_range(start..end) {
                     text.push_str(chunk);
@@ -4806,7 +4842,7 @@ impl Editor {
     pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
         self.transact(cx, |this, cx| {
             if let Some(item) = cx.read_from_clipboard() {
-                let mut clipboard_text = Cow::Borrowed(item.text());
+                let clipboard_text = Cow::Borrowed(item.text());
                 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
                     let old_selections = this.selections.all::<usize>(cx);
                     let all_selections_were_entire_line =
@@ -4814,18 +4850,7 @@ impl Editor {
                     let first_selection_indent_column =
                         clipboard_selections.first().map(|s| s.first_line_indent);
                     if clipboard_selections.len() != old_selections.len() {
-                        let mut newline_separated_text = String::new();
-                        let mut clipboard_selections = clipboard_selections.drain(..).peekable();
-                        let mut ix = 0;
-                        while let Some(clipboard_selection) = clipboard_selections.next() {
-                            newline_separated_text
-                                .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
-                            ix += clipboard_selection.len;
-                            if clipboard_selections.peek().is_some() {
-                                newline_separated_text.push('\n');
-                            }
-                        }
-                        clipboard_text = Cow::Owned(newline_separated_text);
+                        clipboard_selections.drain(..);
                     }
 
                     this.buffer.update(cx, |buffer, cx| {
@@ -4841,8 +4866,9 @@ impl Editor {
                             if let Some(clipboard_selection) = clipboard_selections.get(ix) {
                                 let end_offset = start_offset + clipboard_selection.len;
                                 to_insert = &clipboard_text[start_offset..end_offset];
+                                dbg!(start_offset, end_offset, &clipboard_text, &to_insert);
                                 entire_line = clipboard_selection.is_entire_line;
-                                start_offset = end_offset;
+                                start_offset = end_offset + 1;
                                 original_indent_column =
                                     Some(clipboard_selection.first_line_indent);
                             } else {
@@ -8537,6 +8563,7 @@ fn build_style(
                 font_size,
                 font_properties,
                 underline: Default::default(),
+                soft_wrap: false,
             },
             placeholder_text: None,
             line_height_scalar,

crates/editor/src/element.rs 🔗

@@ -605,7 +605,7 @@ impl EditorElement {
         visible_bounds: RectF,
         layout: &mut LayoutState,
         editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut PaintContext<Editor>,
     ) {
         let line_height = layout.position_map.line_height;
 
@@ -760,7 +760,7 @@ impl EditorElement {
         visible_bounds: RectF,
         layout: &mut LayoutState,
         editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut PaintContext<Editor>,
     ) {
         let style = &self.style;
         let local_replica_id = editor.replica_id(cx);
@@ -1337,7 +1337,7 @@ impl EditorElement {
         visible_bounds: RectF,
         layout: &mut LayoutState,
         editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        cx: &mut PaintContext<Editor>,
     ) {
         let scroll_position = layout.position_map.snapshot.scroll_position();
         let scroll_left = scroll_position.x() * layout.position_map.em_width;

crates/editor/src/items.rs 🔗

@@ -561,7 +561,7 @@ impl Item for Editor {
         }
     }
 
-    fn tab_content<T: View>(
+    fn tab_content<T: 'static>(
         &self,
         detail: Option<usize>,
         style: &theme::Tab,
@@ -1028,7 +1028,7 @@ impl SearchableItem for Editor {
             if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() {
                 ranges.extend(
                     query
-                        .search(excerpt_buffer.as_rope())
+                        .search(excerpt_buffer, None)
                         .await
                         .into_iter()
                         .map(|range| {
@@ -1038,17 +1038,22 @@ impl SearchableItem for Editor {
             } else {
                 for excerpt in buffer.excerpt_boundaries_in_range(0..buffer.len()) {
                     let excerpt_range = excerpt.range.context.to_offset(&excerpt.buffer);
-                    let rope = excerpt.buffer.as_rope().slice(excerpt_range.clone());
-                    ranges.extend(query.search(&rope).await.into_iter().map(|range| {
-                        let start = excerpt
-                            .buffer
-                            .anchor_after(excerpt_range.start + range.start);
-                        let end = excerpt
-                            .buffer
-                            .anchor_before(excerpt_range.start + range.end);
-                        buffer.anchor_in_excerpt(excerpt.id.clone(), start)
-                            ..buffer.anchor_in_excerpt(excerpt.id.clone(), end)
-                    }));
+                    ranges.extend(
+                        query
+                            .search(&excerpt.buffer, Some(excerpt_range.clone()))
+                            .await
+                            .into_iter()
+                            .map(|range| {
+                                let start = excerpt
+                                    .buffer
+                                    .anchor_after(excerpt_range.start + range.start);
+                                let end = excerpt
+                                    .buffer
+                                    .anchor_before(excerpt_range.start + range.end);
+                                buffer.anchor_in_excerpt(excerpt.id.clone(), start)
+                                    ..buffer.anchor_in_excerpt(excerpt.id.clone(), end)
+                            }),
+                    );
                 }
             }
             ranges

crates/editor/src/movement.rs 🔗

@@ -176,14 +176,21 @@ pub fn line_end(
 }
 
 pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
+    let raw_point = point.to_point(map);
+    let language = map.buffer_snapshot.language_at(raw_point);
+
     find_preceding_boundary(map, point, |left, right| {
-        (char_kind(left) != char_kind(right) && !right.is_whitespace()) || left == '\n'
+        (char_kind(language, left) != char_kind(language, right) && !right.is_whitespace())
+            || left == '\n'
     })
 }
 
 pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
+    let raw_point = point.to_point(map);
+    let language = map.buffer_snapshot.language_at(raw_point);
     find_preceding_boundary(map, point, |left, right| {
-        let is_word_start = char_kind(left) != char_kind(right) && !right.is_whitespace();
+        let is_word_start =
+            char_kind(language, left) != char_kind(language, right) && !right.is_whitespace();
         let is_subword_start =
             left == '_' && right != '_' || left.is_lowercase() && right.is_uppercase();
         is_word_start || is_subword_start || left == '\n'
@@ -191,14 +198,20 @@ pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> Dis
 }
 
 pub fn next_word_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
+    let raw_point = point.to_point(map);
+    let language = map.buffer_snapshot.language_at(raw_point);
     find_boundary(map, point, |left, right| {
-        (char_kind(left) != char_kind(right) && !left.is_whitespace()) || right == '\n'
+        (char_kind(language, left) != char_kind(language, right) && !left.is_whitespace())
+            || right == '\n'
     })
 }
 
 pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
+    let raw_point = point.to_point(map);
+    let language = map.buffer_snapshot.language_at(raw_point);
     find_boundary(map, point, |left, right| {
-        let is_word_end = (char_kind(left) != char_kind(right)) && !left.is_whitespace();
+        let is_word_end =
+            (char_kind(language, left) != char_kind(language, right)) && !left.is_whitespace();
         let is_subword_end =
             left != '_' && right == '_' || left.is_lowercase() && right.is_uppercase();
         is_word_end || is_subword_end || right == '\n'
@@ -385,10 +398,15 @@ pub fn find_boundary_in_line(
 }
 
 pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
+    let raw_point = point.to_point(map);
+    let language = map.buffer_snapshot.language_at(raw_point);
     let ix = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left);
     let text = &map.buffer_snapshot;
-    let next_char_kind = text.chars_at(ix).next().map(char_kind);
-    let prev_char_kind = text.reversed_chars_at(ix).next().map(char_kind);
+    let next_char_kind = text.chars_at(ix).next().map(|c| char_kind(language, c));
+    let prev_char_kind = text
+        .reversed_chars_at(ix)
+        .next()
+        .map(|c| char_kind(language, c));
     prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
 }
 

crates/editor/src/multi_buffer.rs 🔗

@@ -1346,10 +1346,7 @@ impl MultiBuffer {
             .map(|state| state.buffer.clone())
     }
 
-    pub fn is_completion_trigger<T>(&self, position: T, text: &str, cx: &AppContext) -> bool
-    where
-        T: ToOffset,
-    {
+    pub fn is_completion_trigger(&self, position: Anchor, text: &str, cx: &AppContext) -> bool {
         let mut chars = text.chars();
         let char = if let Some(char) = chars.next() {
             char
@@ -1360,7 +1357,9 @@ impl MultiBuffer {
             return false;
         }
 
-        if char.is_alphanumeric() || char == '_' {
+        let language = self.language_at(position.clone(), cx);
+
+        if char_kind(language.as_ref(), char) == CharKind::Word {
             return true;
         }
 
@@ -1865,13 +1864,16 @@ impl MultiBufferSnapshot {
         let mut end = start;
         let mut next_chars = self.chars_at(start).peekable();
         let mut prev_chars = self.reversed_chars_at(start).peekable();
+
+        let language = self.language_at(start);
+        let kind = |c| char_kind(language, c);
         let word_kind = cmp::max(
-            prev_chars.peek().copied().map(char_kind),
-            next_chars.peek().copied().map(char_kind),
+            prev_chars.peek().copied().map(kind),
+            next_chars.peek().copied().map(kind),
         );
 
         for ch in prev_chars {
-            if Some(char_kind(ch)) == word_kind && ch != '\n' {
+            if Some(kind(ch)) == word_kind && ch != '\n' {
                 start -= ch.len_utf8();
             } else {
                 break;
@@ -1879,7 +1881,7 @@ impl MultiBufferSnapshot {
         }
 
         for ch in next_chars {
-            if Some(char_kind(ch)) == word_kind && ch != '\n' {
+            if Some(kind(ch)) == word_kind && ch != '\n' {
                 end += ch.len_utf8();
             } else {
                 break;

crates/editor/src/test/editor_lsp_test_context.rs 🔗

@@ -6,6 +6,7 @@ use std::{
 
 use anyhow::Result;
 
+use collections::HashSet;
 use futures::Future;
 use gpui::{json, ViewContext, ViewHandle};
 use indoc::indoc;
@@ -154,10 +155,23 @@ impl<'a> EditorLspTestContext<'a> {
         capabilities: lsp::ServerCapabilities,
         cx: &'a mut gpui::TestAppContext,
     ) -> EditorLspTestContext<'a> {
+        let mut word_characters: HashSet<char> = Default::default();
+        word_characters.insert('$');
+        word_characters.insert('#');
         let language = Language::new(
             LanguageConfig {
                 name: "Typescript".into(),
                 path_suffixes: vec!["ts".to_string()],
+                brackets: language::BracketPairConfig {
+                    pairs: vec![language::BracketPair {
+                        start: "{".to_string(),
+                        end: "}".to_string(),
+                        close: true,
+                        newline: true,
+                    }],
+                    disabled_scopes_by_bracket_ix: Default::default(),
+                },
+                word_characters,
                 ..Default::default()
             },
             Some(tree_sitter_typescript::language_typescript()),
@@ -169,6 +183,23 @@ impl<'a> EditorLspTestContext<'a> {
                 ("{" @open "}" @close)
                 ("<" @open ">" @close)
                 ("\"" @open "\"" @close)"#})),
+            indents: Some(Cow::from(indoc! {r#"
+                [
+                    (call_expression)
+                    (assignment_expression)
+                    (member_expression)
+                    (lexical_declaration)
+                    (variable_declaration)
+                    (assignment_expression)
+                    (if_statement)
+                    (for_statement)
+                ] @indent
+
+                (_ "[" "]" @end) @indent
+                (_ "<" ">" @end) @indent
+                (_ "{" "}" @end) @indent
+                (_ "(" ")" @end) @indent
+                "#})),
             ..Default::default()
         })
         .expect("Could not parse queries");

crates/feedback/src/feedback_editor.rs 🔗

@@ -268,7 +268,7 @@ impl Item for FeedbackEditor {
         Some("Send Feedback".into())
     }
 
-    fn tab_content<T: View>(
+    fn tab_content<T: 'static>(
         &self,
         _: Option<usize>,
         style: &theme::Tab,

crates/gpui/Cargo.toml 🔗

@@ -39,6 +39,7 @@ pathfinder_color = "0.5"
 pathfinder_geometry = "0.5"
 postage.workspace = true
 rand.workspace = true
+refineable.workspace = true
 resvg = "0.14"
 schemars = "0.8"
 seahash = "4.1"
@@ -47,6 +48,7 @@ serde_derive.workspace = true
 serde_json.workspace = true
 smallvec.workspace = true
 smol.workspace = true
+taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
 time.workspace = true
 tiny-skia = "0.5"
 usvg = { version = "0.14", features = [] }

crates/gpui/examples/text.rs 🔗

@@ -58,6 +58,7 @@ impl gpui::View for TextView {
                 font_family_id: family,
                 underline: Default::default(),
                 font_properties: Default::default(),
+                soft_wrap: false,
             },
         )
         .with_highlights(vec![(17..26, underline), (34..40, underline)])

crates/gpui/playground/Cargo.lock 🔗

@@ -0,0 +1,2919 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "adler32"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
+
+[[package]]
+name = "aho-corasick"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+
+[[package]]
+name = "arrayref"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "async-channel"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb"
+dependencies = [
+ "async-lock",
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-fs"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
+dependencies = [
+ "async-lock",
+ "autocfg",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "async-io"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
+dependencies = [
+ "async-lock",
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-lite",
+ "log",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "socket2",
+ "waker-fn",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-net"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f"
+dependencies = [
+ "async-io",
+ "autocfg",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "autocfg",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix",
+ "signal-hook",
+ "windows-sys",
+]
+
+[[package]]
+name = "async-task"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
+
+[[package]]
+name = "atomic"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide 0.7.1",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "bindgen"
+version = "0.65.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "peeking_take_while",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 2.0.25",
+ "which",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "blocking"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65"
+dependencies = [
+ "async-channel",
+ "async-lock",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "log",
+]
+
+[[package]]
+name = "bstr"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "bytemuck"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "castaway"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clang-sys"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading 0.7.4",
+]
+
+[[package]]
+name = "cmake"
+version = "0.1.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "cocoa"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
+dependencies = [
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "cocoa-foundation"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6"
+dependencies = [
+ "bitflags",
+ "block",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "collections"
+version = "0.1.0"
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "concurrent-queue"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "const-cstr"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+ "uuid 0.5.1",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "core-graphics"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "core-text"
+version = "19.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25"
+dependencies = [
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.63+curl-8.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "winapi",
+]
+
+[[package]]
+name = "data-url"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193"
+dependencies = [
+ "matches",
+]
+
+[[package]]
+name = "deflate"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
+dependencies = [
+ "adler32",
+ "byteorder",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading 0.8.0",
+]
+
+[[package]]
+name = "dwrote"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "winapi",
+ "wio",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
+
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "erased-serde"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f94c0e13118e7d7533271f754a168ae8400e6a1cc043f2bfd53cc7290f1a1de3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "etagere"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcf22f748754352918e082e0039335ee92454a5d62bcaf69b5e8daf5907d9644"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "euclid"
+version = "0.22.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide 0.7.1",
+]
+
+[[package]]
+name = "float-cmp"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e"
+
+[[package]]
+name = "float-ord"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "font-kit"
+version = "0.11.0"
+source = "git+https://github.com/zed-industries/font-kit?rev=b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18#b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "core-foundation",
+ "core-graphics",
+ "core-text",
+ "dirs-next",
+ "dwrote",
+ "float-ord",
+ "freetype",
+ "lazy_static",
+ "libc",
+ "log",
+ "pathfinder_geometry",
+ "pathfinder_simd",
+ "walkdir",
+ "winapi",
+ "yeslogic-fontconfig-sys",
+]
+
+[[package]]
+name = "fontdb"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e58903f4f8d5b58c7d300908e4ebe5289c1bfdf5587964330f12023b8ff17fd1"
+dependencies = [
+ "log",
+ "memmap2",
+ "ttf-parser 0.12.3",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "freetype"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6"
+dependencies = [
+ "freetype-sys",
+ "libc",
+]
+
+[[package]]
+name = "freetype-sys"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a"
+dependencies = [
+ "cmake",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-lite"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gif"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gimli"
+version = "0.27.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "globset"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "fnv",
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "gpui"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-task",
+ "bindgen",
+ "block",
+ "cc",
+ "cocoa",
+ "collections",
+ "core-foundation",
+ "core-graphics",
+ "core-text",
+ "ctor",
+ "etagere",
+ "font-kit",
+ "foreign-types",
+ "futures",
+ "gpui_macros",
+ "image",
+ "itertools",
+ "lazy_static",
+ "log",
+ "media",
+ "metal",
+ "num_cpus",
+ "objc",
+ "ordered-float",
+ "parking",
+ "parking_lot 0.11.2",
+ "pathfinder_color",
+ "pathfinder_geometry",
+ "postage",
+ "rand",
+ "resvg",
+ "schemars",
+ "seahash",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "smallvec",
+ "smol",
+ "sqlez",
+ "sum_tree",
+ "time",
+ "tiny-skia",
+ "usvg",
+ "util",
+ "uuid 1.4.0",
+ "waker-fn",
+]
+
+[[package]]
+name = "gpui_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "idna"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "image"
+version = "0.23.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "gif",
+ "jpeg-decoder",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+ "png",
+ "scoped_threadpool",
+ "tiff",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "indoc"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "isahc"
+version = "1.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9"
+dependencies = [
+ "async-channel",
+ "castaway",
+ "crossbeam-utils",
+ "curl",
+ "curl-sys",
+ "encoding_rs",
+ "event-listener",
+ "futures-lite",
+ "http",
+ "log",
+ "mime",
+ "once_cell",
+ "polling",
+ "slab",
+ "sluice",
+ "tracing",
+ "tracing-futures",
+ "url",
+ "waker-fn",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "kurbo"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449"
+dependencies = [
+ "arrayvec 0.7.4",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
+dependencies = [
+ "cfg-if",
+ "windows-sys",
+]
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "lock_api"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+dependencies = [
+ "serde",
+ "value-bag",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
+[[package]]
+name = "media"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bindgen",
+ "block",
+ "bytes",
+ "core-foundation",
+ "foreign-types",
+ "metal",
+ "objc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memmap2"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metal"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c"
+dependencies = [
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "foreign-types",
+ "log",
+ "objc",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
+dependencies = [
+ "adler32",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "object"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "ordered-float"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "parking"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core 0.8.6",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.8",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.3.5",
+ "smallvec",
+ "windows-targets",
+]
+
+[[package]]
+name = "pathfinder_color"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69bdc0d277d559e35e1b374de56df9262a6b71e091ca04a8831a239f8c7f0c62"
+dependencies = [
+ "pathfinder_simd",
+]
+
+[[package]]
+name = "pathfinder_geometry"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3"
+dependencies = [
+ "log",
+ "pathfinder_simd",
+]
+
+[[package]]
+name = "pathfinder_simd"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "peeking_take_while"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "pest"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9"
+dependencies = [
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pico-args"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
+
+[[package]]
+name = "pin-project"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "playground"
+version = "0.1.0"
+dependencies = [
+ "gpui",
+]
+
+[[package]]
+name = "png"
+version = "0.16.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "deflate",
+ "miniz_oxide 0.3.7",
+]
+
+[[package]]
+name = "polling"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
+dependencies = [
+ "autocfg",
+ "bitflags",
+ "cfg-if",
+ "concurrent-queue",
+ "libc",
+ "log",
+ "pin-project-lite",
+ "windows-sys",
+]
+
+[[package]]
+name = "pollster"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
+
+[[package]]
+name = "postage"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1"
+dependencies = [
+ "atomic",
+ "crossbeam-queue",
+ "futures",
+ "log",
+ "parking_lot 0.12.1",
+ "pin-project",
+ "pollster",
+ "static_assertions",
+ "thiserror",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "rctree"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be9e29cb19c8fe84169fcb07f8f11e66bc9e6e0280efd4715c54818296f8a4a8"
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall 0.2.16",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
+
+[[package]]
+name = "resvg"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09697862c5c3f940cbaffef91969c62188b5c8ed385b0aef43a5ff01ddc8000f"
+dependencies = [
+ "jpeg-decoder",
+ "log",
+ "pico-args",
+ "png",
+ "rgb",
+ "svgfilters",
+ "tiny-skia",
+ "usvg",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "roxmltree"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b"
+dependencies = [
+ "xmlparser",
+]
+
+[[package]]
+name = "rust-embed"
+version = "6.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661"
+dependencies = [
+ "rust-embed-impl",
+ "rust-embed-utils",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-impl"
+version = "6.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rust-embed-utils",
+ "syn 2.0.25",
+ "walkdir",
+]
+
+[[package]]
+name = "rust-embed-utils"
+version = "7.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74"
+dependencies = [
+ "globset",
+ "sha2",
+ "walkdir",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc_version"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustybuzz"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "smallvec",
+ "ttf-parser 0.9.0",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-general-category",
+ "unicode-script",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
+
+[[package]]
+name = "safe_arch"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "schemars"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "scoped_threadpool"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "seahash"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
+
+[[package]]
+name = "semver"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
+dependencies = [
+ "pest",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "serde_fmt"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed"
+dependencies = [
+ "indexmap",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "shlex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
+
+[[package]]
+name = "signal-hook"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "simplecss"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
+
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "sluice"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5"
+dependencies = [
+ "async-channel",
+ "futures-core",
+ "futures-io",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
+
+[[package]]
+name = "smol"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-fs",
+ "async-io",
+ "async-lock",
+ "async-net",
+ "async-process",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "sqlez"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "futures",
+ "indoc",
+ "lazy_static",
+ "libsqlite3-sys",
+ "parking_lot 0.11.2",
+ "smol",
+ "thread_local",
+ "uuid 1.4.0",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "sum_tree"
+version = "0.1.0"
+dependencies = [
+ "arrayvec 0.7.4",
+ "log",
+]
+
+[[package]]
+name = "sval"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b031320a434d3e9477ccf9b5756d57d4272937b8d22cb88af80b7633a1b78b1"
+
+[[package]]
+name = "sval_buffer"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bf7e9412af26b342f3f2cc5cc4122b0105e9d16eb76046cd14ed10106cf6028"
+dependencies = [
+ "sval",
+ "sval_ref",
+]
+
+[[package]]
+name = "sval_dynamic"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0ef628e8a77a46ed3338db8d1b08af77495123cc229453084e47cd716d403cf"
+dependencies = [
+ "sval",
+]
+
+[[package]]
+name = "sval_fmt"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dc09e9364c2045ab5fa38f7b04d077b3359d30c4c2b3ec4bae67a358bd64326"
+dependencies = [
+ "itoa",
+ "ryu",
+ "sval",
+]
+
+[[package]]
+name = "sval_json"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ada6f627e38cbb8860283649509d87bc4a5771141daa41c78fd31f2b9485888d"
+dependencies = [
+ "itoa",
+ "ryu",
+ "sval",
+]
+
+[[package]]
+name = "sval_ref"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703ca1942a984bd0d9b5a4c0a65ab8b4b794038d080af4eb303c71bc6bf22d7c"
+dependencies = [
+ "sval",
+]
+
+[[package]]
+name = "sval_serde"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830926cd0581f7c3e5d51efae4d35c6b6fc4db583842652891ba2f1bed8db046"
+dependencies = [
+ "serde",
+ "sval",
+ "sval_buffer",
+ "sval_fmt",
+]
+
+[[package]]
+name = "svg_fmt"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
+
+[[package]]
+name = "svgfilters"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb0dce2fee79ac40c21dafba48565ff7a5fa275e23ffe9ce047a40c9574ba34e"
+dependencies = [
+ "float-cmp",
+ "rgb",
+]
+
+[[package]]
+name = "svgtypes"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff"
+dependencies = [
+ "float-cmp",
+ "siphasher",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "take-until"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb"
+
+[[package]]
+name = "thiserror"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiff"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
+dependencies = [
+ "jpeg-decoder",
+ "miniz_oxide 0.4.4",
+ "weezl",
+]
+
+[[package]]
+name = "time"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"
+dependencies = [
+ "itoa",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+
+[[package]]
+name = "time-macros"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"
+dependencies = [
+ "time-core",
+]
+
+[[package]]
+name = "tiny-skia"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe"
+dependencies = [
+ "arrayref",
+ "arrayvec 0.5.2",
+ "bytemuck",
+ "cfg-if",
+ "png",
+ "safe_arch",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.25",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "tracing-futures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
+dependencies = [
+ "pin-project",
+ "tracing",
+]
+
+[[package]]
+name = "ttf-parser"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca"
+
+[[package]]
+name = "ttf-parser"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6"
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
+
+[[package]]
+name = "unicode-general-category"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-script"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc"
+
+[[package]]
+name = "unicode-vo"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
+
+[[package]]
+name = "url"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "usvg"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8352f317d8f9a918ba5154797fb2a93e2730244041cf7d5be35148266adfa5"
+dependencies = [
+ "base64",
+ "data-url",
+ "flate2",
+ "fontdb",
+ "kurbo",
+ "log",
+ "memmap2",
+ "pico-args",
+ "rctree",
+ "roxmltree",
+ "rustybuzz",
+ "simplecss",
+ "siphasher",
+ "svgtypes",
+ "ttf-parser 0.12.3",
+ "unicode-bidi",
+ "unicode-script",
+ "unicode-vo",
+ "xmlwriter",
+]
+
+[[package]]
+name = "util"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "backtrace",
+ "dirs",
+ "futures",
+ "isahc",
+ "lazy_static",
+ "log",
+ "rand",
+ "rust-embed",
+ "serde",
+ "serde_json",
+ "smol",
+ "take-until",
+ "url",
+]
+
+[[package]]
+name = "uuid"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
+
+[[package]]
+name = "uuid"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "value-bag"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3"
+dependencies = [
+ "value-bag-serde1",
+ "value-bag-sval2",
+]
+
+[[package]]
+name = "value-bag-serde1"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0b9f3feef403a50d4d67e9741a6d8fc688bcbb4e4f31bd4aab72cc690284394"
+dependencies = [
+ "erased-serde",
+ "serde",
+ "serde_fmt",
+]
+
+[[package]]
+name = "value-bag-sval2"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b24f4146b6f3361e91cbf527d1fb35e9376c3c0cef72ca5ec5af6d640fad7d"
+dependencies = [
+ "sval",
+ "sval_buffer",
+ "sval_dynamic",
+ "sval_fmt",
+ "sval_json",
+ "sval_ref",
+ "sval_serde",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
+[[package]]
+name = "walkdir"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "weezl"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
+
+[[package]]
+name = "which"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "wio"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "xmlparser"
+version = "0.13.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
+
+[[package]]
+name = "xmlwriter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
+
+[[package]]
+name = "yeslogic-fontconfig-sys"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386"
+dependencies = [
+ "const-cstr",
+ "dlib",
+ "once_cell",
+ "pkg-config",
+]

crates/gpui/playground/Cargo.toml 🔗

@@ -0,0 +1,26 @@
+[package]
+name = "playground"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[[bin]]
+name = "playground"
+path = "src/playground.rs"
+
+[dependencies]
+anyhow.workspace = true
+derive_more.workspace = true
+gpui = { path = ".." }
+log.workspace = true
+playground_macros = { path = "../playground_macros" }
+parking_lot.workspace = true
+refineable.workspace = true
+serde.workspace = true
+simplelog = "0.9"
+smallvec.workspace = true
+taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
+util = { path = "../../util" }
+
+[dev-dependencies]
+gpui = { path = "..", features = ["test-support"] }

crates/gpui/playground/docs/thoughts.md 🔗

@@ -0,0 +1,72 @@
+Much of element styling is now handled by an external engine.
+
+
+How do I make an element hover.
+
+There's a hover style.
+
+Hoverable needs to wrap another element. That element can be styled.
+
+```rs
+struct Hoverable<E: Element> {
+
+}
+
+impl<V> Element<V> for Hoverable {
+
+}
+
+```
+
+
+
+```rs
+#[derive(Styled, Interactive)]
+pub struct Div {
+    declared_style: StyleRefinement,
+    interactions: Interactions
+}
+
+pub trait Styled {
+    fn declared_style(&mut self) -> &mut StyleRefinement;
+    fn compute_style(&mut self) -> Style {
+        Style::default().refine(self.declared_style())
+    }
+
+    // All the tailwind classes, modifying self.declared_style()
+}
+
+impl Style {
+    pub fn paint_background<V>(layout: Layout, cx: &mut PaintContext<V>);
+    pub fn paint_foreground<V>(layout: Layout, cx: &mut PaintContext<V>);
+}
+
+pub trait Interactive<V> {
+    fn interactions(&mut self) -> &mut Interactions<V>;
+
+    fn on_click(self, )
+}
+
+struct Interactions<V> {
+    click: SmallVec<[<Rc<dyn Fn(&mut V, &dyn Any, )>; 1]>,
+}
+
+
+```
+
+
+```rs
+
+
+trait Stylable {
+    type Style;
+
+    fn with_style(self, style: Self::Style) -> Self;
+}
+
+
+
+
+
+
+```

crates/gpui/playground/src/adapter.rs 🔗

@@ -0,0 +1,78 @@
+use crate::{layout_context::LayoutContext, paint_context::PaintContext};
+use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
+use util::ResultExt;
+
+/// Makes a new, playground-style element into a legacy element.
+pub struct AdapterElement<V>(pub(crate) crate::element::AnyElement<V>);
+
+impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
+    type LayoutState = Option<(LayoutEngine, LayoutId)>;
+    type PaintState = ();
+
+    fn layout(
+        &mut self,
+        constraint: gpui::SizeConstraint,
+        view: &mut V,
+        cx: &mut gpui::LayoutContext<V>,
+    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
+        cx.push_layout_engine(LayoutEngine::new());
+
+        let size = constraint.max;
+        let mut cx = LayoutContext::new(cx);
+        let layout_id = self.0.layout(view, &mut cx).log_err();
+        if let Some(layout_id) = layout_id {
+            cx.layout_engine()
+                .unwrap()
+                .compute_layout(layout_id, constraint.max)
+                .log_err();
+        }
+
+        let layout_engine = cx.pop_layout_engine();
+        debug_assert!(layout_engine.is_some(),
+            "unexpected layout stack state. is there an unmatched pop_layout_engine in the called code?"
+        );
+
+        (constraint.max, layout_engine.zip(layout_id))
+    }
+
+    fn paint(
+        &mut self,
+        scene: &mut gpui::SceneBuilder,
+        bounds: RectF,
+        visible_bounds: RectF,
+        layout_data: &mut Option<(LayoutEngine, LayoutId)>,
+        view: &mut V,
+        legacy_cx: &mut gpui::PaintContext<V>,
+    ) -> Self::PaintState {
+        let (layout_engine, layout_id) = layout_data.take().unwrap();
+        legacy_cx.push_layout_engine(layout_engine);
+        let mut cx = PaintContext::new(legacy_cx, scene);
+        self.0.paint(view, &mut cx);
+        *layout_data = legacy_cx.pop_layout_engine().zip(Some(layout_id));
+        debug_assert!(layout_data.is_some());
+    }
+
+    fn rect_for_text_range(
+        &self,
+        range_utf16: std::ops::Range<usize>,
+        bounds: RectF,
+        visible_bounds: RectF,
+        layout: &Self::LayoutState,
+        paint: &Self::PaintState,
+        view: &V,
+        cx: &gpui::ViewContext<V>,
+    ) -> Option<RectF> {
+        todo!("implement before merging to main")
+    }
+
+    fn debug(
+        &self,
+        bounds: RectF,
+        layout: &Self::LayoutState,
+        paint: &Self::PaintState,
+        view: &V,
+        cx: &gpui::ViewContext<V>,
+    ) -> gpui::serde_json::Value {
+        todo!("implement before merging to main")
+    }
+}

crates/gpui/playground/src/color.rs 🔗

@@ -0,0 +1,276 @@
+#![allow(dead_code)]
+
+use std::{num::ParseIntError, ops::Range};
+
+use smallvec::SmallVec;
+
+pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
+    let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
+    let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
+    let b = (hex & 0xFF) as f32 / 255.0;
+    Rgba { r, g, b, a: 1.0 }.into()
+}
+
+#[derive(Clone, Copy, Default, Debug)]
+pub struct Rgba {
+    pub r: f32,
+    pub g: f32,
+    pub b: f32,
+    pub a: f32,
+}
+
+pub trait Lerp {
+    fn lerp(&self, level: f32) -> Hsla;
+}
+
+impl Lerp for Range<Hsla> {
+    fn lerp(&self, level: f32) -> Hsla {
+        let level = level.clamp(0., 1.);
+        Hsla {
+            h: self.start.h + (level * (self.end.h - self.start.h)),
+            s: self.start.s + (level * (self.end.s - self.start.s)),
+            l: self.start.l + (level * (self.end.l - self.start.l)),
+            a: self.start.a + (level * (self.end.a - self.start.a)),
+        }
+    }
+}
+
+impl From<gpui::color::Color> for Rgba {
+    fn from(value: gpui::color::Color) -> Self {
+        Self {
+            r: value.0.r as f32 / 255.0,
+            g: value.0.g as f32 / 255.0,
+            b: value.0.b as f32 / 255.0,
+            a: value.0.a as f32 / 255.0,
+        }
+    }
+}
+
+impl From<Hsla> for Rgba {
+    fn from(color: Hsla) -> Self {
+        let h = color.h;
+        let s = color.s;
+        let l = color.l;
+
+        let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
+        let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
+        let m = l - c / 2.0;
+        let cm = c + m;
+        let xm = x + m;
+
+        let (r, g, b) = match (h * 6.0).floor() as i32 {
+            0 | 6 => (cm, xm, m),
+            1 => (xm, cm, m),
+            2 => (m, cm, xm),
+            3 => (m, xm, cm),
+            4 => (xm, m, cm),
+            _ => (cm, m, xm),
+        };
+
+        Rgba {
+            r,
+            g,
+            b,
+            a: color.a,
+        }
+    }
+}
+
+impl TryFrom<&'_ str> for Rgba {
+    type Error = ParseIntError;
+
+    fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
+        let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
+        let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
+        let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
+        let a = if value.len() > 7 {
+            u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
+        } else {
+            1.0
+        };
+
+        Ok(Rgba { r, g, b, a })
+    }
+}
+
+impl Into<gpui::color::Color> for Rgba {
+    fn into(self) -> gpui::color::Color {
+        gpui::color::rgba(self.r, self.g, self.b, self.a)
+    }
+}
+
+#[derive(Default, Copy, Clone, Debug, PartialEq)]
+pub struct Hsla {
+    pub h: f32,
+    pub s: f32,
+    pub l: f32,
+    pub a: f32,
+}
+
+pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
+    Hsla {
+        h: h.clamp(0., 1.),
+        s: s.clamp(0., 1.),
+        l: l.clamp(0., 1.),
+        a: a.clamp(0., 1.),
+    }
+}
+
+pub fn black() -> Hsla {
+    Hsla {
+        h: 0.,
+        s: 0.,
+        l: 0.,
+        a: 1.,
+    }
+}
+
+impl From<Rgba> for Hsla {
+    fn from(color: Rgba) -> Self {
+        let r = color.r;
+        let g = color.g;
+        let b = color.b;
+
+        let max = r.max(g.max(b));
+        let min = r.min(g.min(b));
+        let delta = max - min;
+
+        let l = (max + min) / 2.0;
+        let s = if l == 0.0 || l == 1.0 {
+            0.0
+        } else if l < 0.5 {
+            delta / (2.0 * l)
+        } else {
+            delta / (2.0 - 2.0 * l)
+        };
+
+        let h = if delta == 0.0 {
+            0.0
+        } else if max == r {
+            ((g - b) / delta).rem_euclid(6.0) / 6.0
+        } else if max == g {
+            ((b - r) / delta + 2.0) / 6.0
+        } else {
+            ((r - g) / delta + 4.0) / 6.0
+        };
+
+        Hsla {
+            h,
+            s,
+            l,
+            a: color.a,
+        }
+    }
+}
+
+impl Hsla {
+    /// Scales the saturation and lightness by the given values, clamping at 1.0.
+    pub fn scale_sl(mut self, s: f32, l: f32) -> Self {
+        self.s = (self.s * s).clamp(0., 1.);
+        self.l = (self.l * l).clamp(0., 1.);
+        self
+    }
+
+    /// Increases the saturation of the color by a certain amount, with a max
+    /// value of 1.0.
+    pub fn saturate(mut self, amount: f32) -> Self {
+        self.s += amount;
+        self.s = self.s.clamp(0.0, 1.0);
+        self
+    }
+
+    /// Decreases the saturation of the color by a certain amount, with a min
+    /// value of 0.0.
+    pub fn desaturate(mut self, amount: f32) -> Self {
+        self.s -= amount;
+        self.s = self.s.max(0.0);
+        if self.s < 0.0 {
+            self.s = 0.0;
+        }
+        self
+    }
+
+    /// Brightens the color by increasing the lightness by a certain amount,
+    /// with a max value of 1.0.
+    pub fn brighten(mut self, amount: f32) -> Self {
+        self.l += amount;
+        self.l = self.l.clamp(0.0, 1.0);
+        self
+    }
+
+    /// Darkens the color by decreasing the lightness by a certain amount,
+    /// with a max value of 0.0.
+    pub fn darken(mut self, amount: f32) -> Self {
+        self.l -= amount;
+        self.l = self.l.clamp(0.0, 1.0);
+        self
+    }
+}
+
+impl From<gpui::color::Color> for Hsla {
+    fn from(value: gpui::color::Color) -> Self {
+        Rgba::from(value).into()
+    }
+}
+
+impl Into<gpui::color::Color> for Hsla {
+    fn into(self) -> gpui::color::Color {
+        Rgba::from(self).into()
+    }
+}
+
+pub struct ColorScale {
+    colors: SmallVec<[Hsla; 2]>,
+    positions: SmallVec<[f32; 2]>,
+}
+
+pub fn scale<I, C>(colors: I) -> ColorScale
+where
+    I: IntoIterator<Item = C>,
+    C: Into<Hsla>,
+{
+    let mut scale = ColorScale {
+        colors: colors.into_iter().map(Into::into).collect(),
+        positions: SmallVec::new(),
+    };
+    let num_colors: f32 = scale.colors.len() as f32 - 1.0;
+    scale.positions = (0..scale.colors.len())
+        .map(|i| i as f32 / num_colors)
+        .collect();
+    scale
+}
+
+impl ColorScale {
+    fn at(&self, t: f32) -> Hsla {
+        // Ensure that the input is within [0.0, 1.0]
+        debug_assert!(
+            0.0 <= t && t <= 1.0,
+            "t value {} is out of range. Expected value in range 0.0 to 1.0",
+            t
+        );
+
+        let position = match self
+            .positions
+            .binary_search_by(|a| a.partial_cmp(&t).unwrap())
+        {
+            Ok(index) | Err(index) => index,
+        };
+        let lower_bound = position.saturating_sub(1);
+        let upper_bound = position.min(self.colors.len() - 1);
+        let lower_color = &self.colors[lower_bound];
+        let upper_color = &self.colors[upper_bound];
+
+        match upper_bound.checked_sub(lower_bound) {
+            Some(0) | None => *lower_color,
+            Some(_) => {
+                let interval_t = (t - self.positions[lower_bound])
+                    / (self.positions[upper_bound] - self.positions[lower_bound]);
+                let h = lower_color.h + interval_t * (upper_color.h - lower_color.h);
+                let s = lower_color.s + interval_t * (upper_color.s - lower_color.s);
+                let l = lower_color.l + interval_t * (upper_color.l - lower_color.l);
+                let a = lower_color.a + interval_t * (upper_color.a - lower_color.a);
+                Hsla { h, s, l, a }
+            }
+        }
+    }
+}

crates/gpui/playground/src/components.rs 🔗

@@ -0,0 +1,100 @@
+use crate::{
+    div::div,
+    element::{Element, ParentElement},
+    style::StyleHelpers,
+    text::ArcCow,
+    themes::rose_pine,
+};
+use gpui::ViewContext;
+use playground_macros::Element;
+use std::{marker::PhantomData, rc::Rc};
+
+struct ButtonHandlers<V, D> {
+    click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
+}
+
+impl<V, D> Default for ButtonHandlers<V, D> {
+    fn default() -> Self {
+        Self { click: None }
+    }
+}
+
+use crate as playground;
+#[derive(Element)]
+pub struct Button<V: 'static, D: 'static> {
+    handlers: ButtonHandlers<V, D>,
+    label: Option<ArcCow<'static, str>>,
+    icon: Option<ArcCow<'static, str>>,
+    data: Rc<D>,
+    view_type: PhantomData<V>,
+}
+
+// Impl block for buttons without data.
+// See below for an impl block for any button.
+impl<V: 'static> Button<V, ()> {
+    fn new() -> Self {
+        Self {
+            handlers: ButtonHandlers::default(),
+            label: None,
+            icon: None,
+            data: Rc::new(()),
+            view_type: PhantomData,
+        }
+    }
+
+    pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
+        Button {
+            handlers: ButtonHandlers::default(),
+            label: self.label,
+            icon: self.icon,
+            data: Rc::new(data),
+            view_type: PhantomData,
+        }
+    }
+}
+
+// Impl block for *any* button.
+impl<V: 'static, D: 'static> Button<V, D> {
+    pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
+        self.label = Some(label.into());
+        self
+    }
+
+    pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
+        self.icon = Some(icon.into());
+        self
+    }
+
+    // pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
+    //     let data = self.data.clone();
+    //     Self::click(self, MouseButton::Left, move |view, _, cx| {
+    //         handler(view, data.as_ref(), cx);
+    //     })
+    // }
+}
+
+pub fn button<V>() -> Button<V, ()> {
+    Button::new()
+}
+
+impl<V: 'static, D: 'static> Button<V, D> {
+    fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+        // TODO: Drive theme from the context
+        let button = div()
+            .fill(rose_pine::dawn().error(0.5))
+            .h_4()
+            .children(self.label.clone());
+
+        button
+
+        // TODO: Event handling
+        // if let Some(handler) = self.handlers.click.clone() {
+        //     let data = self.data.clone();
+        //     // button.mouse_down(MouseButton::Left, move |view, event, cx| {
+        //     //     handler(view, data.as_ref(), cx)
+        //     // })
+        // } else {
+        //     button
+        // }
+    }
+}

crates/gpui/playground/src/div.rs 🔗

@@ -0,0 +1,108 @@
+use crate::{
+    element::{AnyElement, Element, Layout, ParentElement},
+    interactive::{InteractionHandlers, Interactive},
+    layout_context::LayoutContext,
+    paint_context::PaintContext,
+    style::{Style, StyleHelpers, StyleRefinement, Styleable},
+};
+use anyhow::Result;
+use gpui::LayoutId;
+use smallvec::SmallVec;
+
+pub struct Div<V: 'static> {
+    style: StyleRefinement,
+    handlers: InteractionHandlers<V>,
+    children: SmallVec<[AnyElement<V>; 2]>,
+}
+
+pub fn div<V>() -> Div<V> {
+    Div {
+        style: Default::default(),
+        handlers: Default::default(),
+        children: Default::default(),
+    }
+}
+
+impl<V: 'static> Element<V> for Div<V> {
+    type Layout = ();
+
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, ()>>
+    where
+        Self: Sized,
+    {
+        let children = self
+            .children
+            .iter_mut()
+            .map(|child| child.layout(view, cx))
+            .collect::<Result<Vec<LayoutId>>>()?;
+
+        cx.add_layout_node(self.style(), (), children)
+    }
+
+    fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
+    where
+        Self: Sized,
+    {
+        let style = self.style();
+
+        style.paint_background::<V, Self>(layout, cx);
+        for child in &mut self.children {
+            child.paint(view, cx);
+        }
+    }
+}
+
+impl<V> Styleable for Div<V> {
+    type Style = Style;
+
+    fn declared_style(&mut self) -> &mut StyleRefinement {
+        &mut self.style
+    }
+}
+
+impl<V> StyleHelpers for Div<V> {}
+
+impl<V> Interactive<V> for Div<V> {
+    fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
+        &mut self.handlers
+    }
+}
+
+impl<V: 'static> ParentElement<V> for Div<V> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+        &mut self.children
+    }
+}
+
+#[test]
+fn test() {
+    // let elt = div().w_auto();
+}
+
+// trait Element<V: 'static> {
+//     type Style;
+
+//     fn layout()
+// }
+
+// trait Stylable<V: 'static>: Element<V> {
+//     type Style;
+
+//     fn with_style(self, style: Self::Style) -> Self;
+// }
+
+// pub struct HoverStyle<S> {
+//     default: S,
+//     hovered: S,
+// }
+
+// struct Hover<V: 'static, C: Stylable<V>> {
+//     child: C,
+//     style: HoverStyle<C::Style>,
+// }
+
+// impl<V: 'static, C: Stylable<V>> Hover<V, C> {
+//     fn new(child: C, style: HoverStyle<C::Style>) -> Self {
+//         Self { child, style }
+//     }
+// }

crates/gpui/playground/src/element.rs 🔗

@@ -0,0 +1,158 @@
+use anyhow::Result;
+use derive_more::{Deref, DerefMut};
+use gpui::{geometry::rect::RectF, EngineLayout};
+use smallvec::SmallVec;
+use std::marker::PhantomData;
+use util::ResultExt;
+
+pub use crate::layout_context::LayoutContext;
+pub use crate::paint_context::PaintContext;
+
+type LayoutId = gpui::LayoutId;
+
+pub trait Element<V: 'static>: 'static {
+    type Layout;
+
+    fn layout(
+        &mut self,
+        view: &mut V,
+        cx: &mut LayoutContext<V>,
+    ) -> Result<Layout<V, Self::Layout>>
+    where
+        Self: Sized;
+
+    fn paint(
+        &mut self,
+        view: &mut V,
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) where
+        Self: Sized;
+
+    fn into_any(self) -> AnyElement<V>
+    where
+        Self: 'static + Sized,
+    {
+        AnyElement(Box::new(ElementState {
+            element: self,
+            layout: None,
+        }))
+    }
+}
+
+/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
+trait ElementStateObject<V> {
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
+    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>);
+}
+
+/// A wrapper around an element that stores its layout state.
+struct ElementState<V: 'static, E: Element<V>> {
+    element: E,
+    layout: Option<Layout<V, E::Layout>>,
+}
+
+/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
+impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
+        let layout = self.element.layout(view, cx)?;
+        let layout_id = layout.id;
+        self.layout = Some(layout);
+        Ok(layout_id)
+    }
+
+    fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+        let layout = self.layout.as_mut().expect("paint called before layout");
+        if layout.engine_layout.is_none() {
+            layout.engine_layout = cx.computed_layout(layout.id).log_err()
+        }
+        self.element.paint(view, layout, cx)
+    }
+}
+
+/// A dynamic element.
+pub struct AnyElement<V>(Box<dyn ElementStateObject<V>>);
+
+impl<V> AnyElement<V> {
+    pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
+        self.0.layout(view, cx)
+    }
+
+    pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+        self.0.paint(view, cx)
+    }
+}
+
+#[derive(Deref, DerefMut)]
+pub struct Layout<V, D> {
+    id: LayoutId,
+    engine_layout: Option<EngineLayout>,
+    #[deref]
+    #[deref_mut]
+    element_data: D,
+    view_type: PhantomData<V>,
+}
+
+impl<V: 'static, D> Layout<V, D> {
+    pub fn new(id: LayoutId, element_data: D) -> Self {
+        Self {
+            id,
+            engine_layout: None,
+            element_data: element_data,
+            view_type: PhantomData,
+        }
+    }
+
+    pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
+        self.engine_layout(cx).bounds
+    }
+
+    pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
+        self.engine_layout(cx).order
+    }
+
+    fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
+        self.engine_layout
+            .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
+    }
+}
+
+impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
+    pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
+        let mut element = self.element_data.take().unwrap();
+        element.paint(view, cx);
+        self.element_data = Some(element);
+    }
+}
+
+pub trait ParentElement<V: 'static> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
+
+    fn child(mut self, child: impl IntoElement<V>) -> Self
+    where
+        Self: Sized,
+    {
+        self.children_mut().push(child.into_element().into_any());
+        self
+    }
+
+    fn children<I, E>(mut self, children: I) -> Self
+    where
+        I: IntoIterator<Item = E>,
+        E: IntoElement<V>,
+        Self: Sized,
+    {
+        self.children_mut().extend(
+            children
+                .into_iter()
+                .map(|child| child.into_element().into_any()),
+        );
+        self
+    }
+}
+
+pub trait IntoElement<V: 'static> {
+    type Element: Element<V>;
+
+    fn into_element(self) -> Self::Element;
+}

crates/gpui/playground/src/hoverable.rs 🔗

@@ -0,0 +1,76 @@
+use crate::{
+    element::{Element, Layout},
+    layout_context::LayoutContext,
+    paint_context::PaintContext,
+    style::{StyleRefinement, Styleable},
+};
+use anyhow::Result;
+use gpui::platform::MouseMovedEvent;
+use refineable::Refineable;
+use std::{cell::Cell, marker::PhantomData};
+
+pub struct Hoverable<V: 'static, E: Element<V> + Styleable> {
+    hovered: Cell<bool>,
+    child_style: StyleRefinement,
+    hovered_style: StyleRefinement,
+    child: E,
+    view_type: PhantomData<V>,
+}
+
+pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
+    Hoverable {
+        hovered: Cell::new(false),
+        child_style: child.declared_style().clone(),
+        hovered_style: Default::default(),
+        child,
+        view_type: PhantomData,
+    }
+}
+
+impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
+    type Style = E::Style;
+
+    fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
+        self.child.declared_style()
+    }
+}
+
+impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
+    type Layout = E::Layout;
+
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+    where
+        Self: Sized,
+    {
+        self.child.layout(view, cx)
+    }
+
+    fn paint(
+        &mut self,
+        view: &mut V,
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) where
+        Self: Sized,
+    {
+        if self.hovered.get() {
+            // If hovered, refine the child's style with this element's style.
+            self.child.declared_style().refine(&self.hovered_style);
+        } else {
+            // Otherwise, set the child's style back to its original style.
+            *self.child.declared_style() = self.child_style.clone();
+        }
+
+        let bounds = layout.bounds(cx);
+        let order = layout.order(cx);
+        self.hovered.set(bounds.contains_point(cx.mouse_position()));
+        let was_hovered = self.hovered.clone();
+        cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
+            let is_hovered = bounds.contains_point(event.position);
+            if is_hovered != was_hovered.get() {
+                was_hovered.set(is_hovered);
+                cx.repaint();
+            }
+        });
+    }
+}

crates/gpui/playground/src/interactive.rs 🔗

@@ -0,0 +1,34 @@
+use gpui::{platform::MouseMovedEvent, EventContext};
+use smallvec::SmallVec;
+use std::rc::Rc;
+
+pub trait Interactive<V: 'static> {
+    fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
+
+    fn on_mouse_move<H>(mut self, handler: H) -> Self
+    where
+        H: 'static + Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>),
+        Self: Sized,
+    {
+        self.interaction_handlers()
+            .mouse_moved
+            .push(Rc::new(move |view, event, hit_test, cx| {
+                handler(view, event, hit_test, cx);
+                cx.bubble
+            }));
+        self
+    }
+}
+
+pub struct InteractionHandlers<V: 'static> {
+    mouse_moved:
+        SmallVec<[Rc<dyn Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>) -> bool>; 2]>,
+}
+
+impl<V> Default for InteractionHandlers<V> {
+    fn default() -> Self {
+        Self {
+            mouse_moved: Default::default(),
+        }
+    }
+}

crates/gpui/playground/src/layout_context.rs 🔗

@@ -0,0 +1,54 @@
+use anyhow::{anyhow, Result};
+use derive_more::{Deref, DerefMut};
+pub use gpui::LayoutContext as LegacyLayoutContext;
+use gpui::{RenderContext, ViewContext};
+pub use taffy::tree::NodeId;
+
+use crate::{element::Layout, style::Style};
+
+#[derive(Deref, DerefMut)]
+pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
+    #[deref]
+    #[deref_mut]
+    pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
+}
+
+impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
+    fn text_style(&self) -> gpui::fonts::TextStyle {
+        self.legacy_cx.text_style()
+    }
+
+    fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
+        self.legacy_cx.push_text_style(style)
+    }
+
+    fn pop_text_style(&mut self) {
+        self.legacy_cx.pop_text_style()
+    }
+
+    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+        &mut self.view_context
+    }
+}
+
+impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
+    pub fn new(legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>) -> Self {
+        Self { legacy_cx }
+    }
+
+    pub fn add_layout_node<D>(
+        &mut self,
+        style: Style,
+        element_data: D,
+        children: impl IntoIterator<Item = NodeId>,
+    ) -> Result<Layout<V, D>> {
+        let rem_size = self.rem_pixels();
+        let id = self
+            .legacy_cx
+            .layout_engine()
+            .ok_or_else(|| anyhow!("no layout engine"))?
+            .add_node(style.to_taffy(rem_size), children)?;
+
+        Ok(Layout::new(id, element_data))
+    }
+}

crates/gpui/playground/src/paint_context.rs 🔗

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

crates/gpui/playground/src/playground.rs 🔗

@@ -0,0 +1,83 @@
+#![allow(dead_code, unused_variables)]
+use crate::{color::black, style::StyleHelpers};
+use element::Element;
+use gpui::{
+    geometry::{rect::RectF, vector::vec2f},
+    platform::WindowOptions,
+};
+use log::LevelFilter;
+use simplelog::SimpleLogger;
+use themes::{rose_pine, ThemeColors};
+use view::view;
+
+mod adapter;
+mod color;
+mod components;
+mod div;
+mod element;
+mod hoverable;
+mod interactive;
+mod layout_context;
+mod paint_context;
+mod style;
+mod text;
+mod themes;
+mod view;
+
+fn main() {
+    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
+
+    gpui::App::new(()).unwrap().run(|cx| {
+        cx.add_window(
+            WindowOptions {
+                bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
+                    vec2f(0., 0.),
+                    vec2f(400., 300.),
+                )),
+                center: true,
+                ..Default::default()
+            },
+            |_| view(|_| playground(&rose_pine::moon())),
+        );
+        cx.platform().activate(true);
+    });
+}
+
+fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+    use div::div;
+
+    div()
+        .text_color(black())
+        .h_full()
+        .w_1_2()
+        .fill(theme.success(0.5))
+    // .hover()
+    // .fill(theme.error(0.5))
+    // .child(button().label("Hello").click(|_, _, _| println!("click!")))
+}
+
+//     todo!()
+//     // column()
+//     // .size(auto())
+//     // .fill(theme.base(0.5))
+//     // .text_color(theme.text(0.5))
+//     // .child(title_bar(theme))
+//     // .child(stage(theme))
+//     // .child(status_bar(theme))
+// }
+
+// fn title_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+//     row()
+//         .fill(theme.base(0.2))
+//         .justify(0.)
+//         .width(auto())
+//         .child(text("Zed Playground"))
+// }
+
+// fn stage<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+//     row().fill(theme.surface(0.9))
+// }
+
+// fn status_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+//     row().fill(theme.surface(0.1))
+// }

crates/gpui/playground/src/style.rs 🔗

@@ -0,0 +1,286 @@
+use crate::{
+    color::Hsla,
+    element::{Element, Layout},
+    paint_context::PaintContext,
+};
+use gpui::{
+    fonts::TextStyleRefinement,
+    geometry::{
+        AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point, PointRefinement,
+        Size, SizeRefinement,
+    },
+};
+use playground_macros::styleable_helpers;
+use refineable::Refineable;
+pub use taffy::style::{
+    AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
+    Overflow, Position,
+};
+
+#[derive(Clone, Refineable)]
+pub struct Style {
+    /// What layout strategy should be used?
+    pub display: Display,
+
+    // Overflow properties
+    /// How children overflowing their container should affect layout
+    #[refineable]
+    pub overflow: Point<Overflow>,
+    /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
+    pub scrollbar_width: f32,
+
+    // Position properties
+    /// What should the `position` value of this struct use as a base offset?
+    pub position: Position,
+    /// How should the position of this element be tweaked relative to the layout defined?
+    #[refineable]
+    pub inset: Edges<Length>,
+
+    // Size properies
+    /// Sets the initial size of the item
+    #[refineable]
+    pub size: Size<Length>,
+    /// Controls the minimum size of the item
+    #[refineable]
+    pub min_size: Size<Length>,
+    /// Controls the maximum size of the item
+    #[refineable]
+    pub max_size: Size<Length>,
+    /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
+    pub aspect_ratio: Option<f32>,
+
+    // Spacing Properties
+    /// How large should the margin be on each side?
+    #[refineable]
+    pub margin: Edges<Length>,
+    /// How large should the padding be on each side?
+    #[refineable]
+    pub padding: Edges<DefiniteLength>,
+    /// How large should the border be on each side?
+    #[refineable]
+    pub border: Edges<DefiniteLength>,
+
+    // Alignment properties
+    /// How this node's children aligned in the cross/block axis?
+    pub align_items: Option<AlignItems>,
+    /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
+    pub align_self: Option<AlignSelf>,
+    /// How should content contained within this item be aligned in the cross/block axis
+    pub align_content: Option<AlignContent>,
+    /// How should contained within this item be aligned in the main/inline axis
+    pub justify_content: Option<JustifyContent>,
+    /// How large should the gaps between items in a flex container be?
+    #[refineable]
+    pub gap: Size<DefiniteLength>,
+
+    // Flexbox properies
+    /// Which direction does the main axis flow in?
+    pub flex_direction: FlexDirection,
+    /// Should elements wrap, or stay in a single line?
+    pub flex_wrap: FlexWrap,
+    /// Sets the initial main axis size of the item
+    pub flex_basis: Length,
+    /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
+    pub flex_grow: f32,
+    /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
+    pub flex_shrink: f32,
+
+    /// The fill color of this element
+    pub fill: Option<Fill>,
+    /// The radius of the corners of this element
+    #[refineable]
+    pub corner_radii: CornerRadii,
+    /// The color of text within this element. Cascades to children unless overridden.
+    pub text_color: Option<Hsla>,
+}
+
+impl Style {
+    pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
+        taffy::style::Style {
+            display: self.display,
+            overflow: self.overflow.clone().into(),
+            scrollbar_width: self.scrollbar_width,
+            position: self.position,
+            inset: self.inset.to_taffy(rem_size),
+            size: self.size.to_taffy(rem_size),
+            min_size: self.min_size.to_taffy(rem_size),
+            max_size: self.max_size.to_taffy(rem_size),
+            aspect_ratio: self.aspect_ratio,
+            margin: self.margin.to_taffy(rem_size),
+            padding: self.padding.to_taffy(rem_size),
+            border: self.border.to_taffy(rem_size),
+            align_items: self.align_items,
+            align_self: self.align_self,
+            align_content: self.align_content,
+            justify_content: self.justify_content,
+            gap: self.gap.to_taffy(rem_size),
+            flex_direction: self.flex_direction,
+            flex_wrap: self.flex_wrap,
+            flex_basis: self.flex_basis.to_taffy(rem_size).into(),
+            flex_grow: self.flex_grow,
+            flex_shrink: self.flex_shrink,
+            ..Default::default() // Ignore grid properties for now
+        }
+    }
+
+    /// Paints the background of an element styled with this style.
+    /// Return the bounds in which to paint the content.
+    pub fn paint_background<V: 'static, E: Element<V>>(
+        &self,
+        layout: &mut Layout<V, E::Layout>,
+        cx: &mut PaintContext<V>,
+    ) {
+        let bounds = layout.bounds(cx);
+        let rem_size = cx.rem_pixels();
+        if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
+            cx.scene.push_quad(gpui::Quad {
+                bounds,
+                background: Some(color.into()),
+                corner_radii: self.corner_radii.to_gpui(rem_size),
+                border: Default::default(),
+            });
+        }
+    }
+}
+
+impl Default for Style {
+    fn default() -> Self {
+        Style {
+            display: Display::DEFAULT,
+            overflow: Point {
+                x: Overflow::Visible,
+                y: Overflow::Visible,
+            },
+            scrollbar_width: 0.0,
+            position: Position::Relative,
+            inset: Edges::auto(),
+            margin: Edges::<Length>::zero(),
+            padding: Edges::<DefiniteLength>::zero(),
+            border: Edges::<DefiniteLength>::zero(),
+            size: Size::auto(),
+            min_size: Size::auto(),
+            max_size: Size::auto(),
+            aspect_ratio: None,
+            gap: Size::zero(),
+            // Aligment
+            align_items: None,
+            align_self: None,
+            align_content: None,
+            justify_content: None,
+            // Flexbox
+            flex_direction: FlexDirection::Row,
+            flex_wrap: FlexWrap::NoWrap,
+            flex_grow: 0.0,
+            flex_shrink: 1.0,
+            flex_basis: Length::Auto,
+            fill: None,
+            text_color: None,
+            corner_radii: CornerRadii::default(),
+        }
+    }
+}
+
+impl StyleRefinement {
+    pub fn text_style(&self) -> Option<TextStyleRefinement> {
+        self.text_color.map(|color| TextStyleRefinement {
+            color: Some(color.into()),
+            ..Default::default()
+        })
+    }
+}
+
+pub struct OptionalTextStyle {
+    color: Option<Hsla>,
+}
+
+impl OptionalTextStyle {
+    pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
+        if let Some(color) = self.color {
+            style.color = color.into();
+        }
+    }
+}
+
+#[derive(Clone)]
+pub enum Fill {
+    Color(Hsla),
+}
+
+impl Fill {
+    pub fn color(&self) -> Option<Hsla> {
+        match self {
+            Fill::Color(color) => Some(*color),
+        }
+    }
+}
+
+impl Default for Fill {
+    fn default() -> Self {
+        Self::Color(Hsla::default())
+    }
+}
+
+impl From<Hsla> for Fill {
+    fn from(color: Hsla) -> Self {
+        Self::Color(color)
+    }
+}
+
+#[derive(Clone, Refineable, Default)]
+pub struct CornerRadii {
+    top_left: AbsoluteLength,
+    top_right: AbsoluteLength,
+    bottom_left: AbsoluteLength,
+    bottom_right: AbsoluteLength,
+}
+
+impl CornerRadii {
+    pub fn to_gpui(&self, rem_size: f32) -> gpui::scene::CornerRadii {
+        gpui::scene::CornerRadii {
+            top_left: self.top_left.to_pixels(rem_size),
+            top_right: self.top_right.to_pixels(rem_size),
+            bottom_left: self.bottom_left.to_pixels(rem_size),
+            bottom_right: self.bottom_right.to_pixels(rem_size),
+        }
+    }
+}
+
+pub trait Styleable {
+    type Style: refineable::Refineable;
+
+    fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
+
+    fn style(&mut self) -> playground::style::Style {
+        let mut style = playground::style::Style::default();
+        style.refine(self.declared_style());
+        style
+    }
+}
+
+// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.
+//
+// Example:
+// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
+// fn p_2(mut self) -> Self where Self: Sized;
+use crate as playground; // Macro invocation references this crate as playground.
+pub trait StyleHelpers: Styleable<Style = Style> {
+    styleable_helpers!();
+
+    fn fill<F>(mut self, fill: F) -> Self
+    where
+        F: Into<Fill>,
+        Self: Sized,
+    {
+        self.declared_style().fill = Some(fill.into());
+        self
+    }
+
+    fn text_color<C>(mut self, color: C) -> Self
+    where
+        C: Into<Hsla>,
+        Self: Sized,
+    {
+        self.declared_style().text_color = Some(color.into());
+        self
+    }
+}

crates/gpui/playground/src/text.rs 🔗

@@ -0,0 +1,151 @@
+use crate::{
+    element::{Element, IntoElement, Layout},
+    layout_context::LayoutContext,
+    paint_context::PaintContext,
+};
+use anyhow::Result;
+use gpui::text_layout::LineLayout;
+use parking_lot::Mutex;
+use std::sync::Arc;
+
+impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
+    type Element = Text;
+
+    fn into_element(self) -> Self::Element {
+        Text { text: self.into() }
+    }
+}
+
+pub struct Text {
+    text: ArcCow<'static, str>,
+}
+
+impl<V: 'static> Element<V> for Text {
+    type Layout = Arc<Mutex<Option<TextLayout>>>;
+
+    fn layout(
+        &mut self,
+        view: &mut V,
+        cx: &mut LayoutContext<V>,
+    ) -> Result<Layout<V, Self::Layout>> {
+        // let rem_size = cx.rem_pixels();
+        // let fonts = cx.platform().fonts();
+        // let text_style = cx.text_style();
+        // let line_height = cx.font_cache().line_height(text_style.font_size);
+        // let layout_engine = cx.layout_engine().expect("no layout engine present");
+        // let text = self.text.clone();
+        // let layout = Arc::new(Mutex::new(None));
+
+        // let style: Style = Style::default().refined(&self.metadata.style);
+        // let node_id = layout_engine.add_measured_node(style.to_taffy(rem_size), {
+        //     let layout = layout.clone();
+        //     move |params| {
+        //         let line_layout = fonts.layout_line(
+        //             text.as_ref(),
+        //             text_style.font_size,
+        //             &[(text.len(), text_style.to_run())],
+        //         );
+
+        //         let size = Size {
+        //             width: line_layout.width,
+        //             height: line_height,
+        //         };
+
+        //         layout.lock().replace(TextLayout {
+        //             line_layout: Arc::new(line_layout),
+        //             line_height,
+        //         });
+
+        //         size
+        //     }
+        // })?;
+
+        // Ok((node_id, layout))
+        todo!()
+    }
+
+    fn paint<'a>(
+        &mut self,
+        view: &mut V,
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) {
+        // ) {
+        //     let element_layout_lock = layout.from_element.lock();
+        //     let element_layout = element_layout_lock
+        //         .as_ref()
+        //         .expect("layout has not been performed");
+        //     let line_layout = element_layout.line_layout.clone();
+        //     let line_height = element_layout.line_height;
+        //     drop(element_layout_lock);
+
+        //     let text_style = cx.text_style();
+        //     let line =
+        //         gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
+        //     line.paint(
+        //         cx.scene,
+        //         layout.from_engine.bounds.origin(),
+        //         layout.from_engine.bounds,
+        //         line_height,
+        //         cx.legacy_cx,
+        //     );
+        todo!()
+    }
+}
+
+pub struct TextLayout {
+    line_layout: Arc<LineLayout>,
+    line_height: f32,
+}
+
+pub enum ArcCow<'a, T: ?Sized> {
+    Borrowed(&'a T),
+    Owned(Arc<T>),
+}
+
+impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
+    fn clone(&self) -> Self {
+        match self {
+            Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
+            Self::Owned(owned) => Self::Owned(owned.clone()),
+        }
+    }
+}
+
+impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
+    fn from(s: &'a T) -> Self {
+        Self::Borrowed(s)
+    }
+}
+
+impl<T> From<Arc<T>> for ArcCow<'_, T> {
+    fn from(s: Arc<T>) -> Self {
+        Self::Owned(s)
+    }
+}
+
+impl From<String> for ArcCow<'_, str> {
+    fn from(value: String) -> Self {
+        Self::Owned(value.into())
+    }
+}
+
+impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        match self {
+            ArcCow::Borrowed(s) => s,
+            ArcCow::Owned(s) => s.as_ref(),
+        }
+    }
+}
+
+impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
+    fn as_ref(&self) -> &T {
+        match self {
+            ArcCow::Borrowed(borrowed) => borrowed,
+            ArcCow::Owned(owned) => owned.as_ref(),
+        }
+    }
+}

crates/gpui/playground/src/themes.rs 🔗

@@ -0,0 +1,84 @@
+use crate::color::{Hsla, Lerp};
+use std::ops::Range;
+
+pub mod rose_pine;
+
+pub struct ThemeColors {
+    pub base: Range<Hsla>,
+    pub surface: Range<Hsla>,
+    pub overlay: Range<Hsla>,
+    pub muted: Range<Hsla>,
+    pub subtle: Range<Hsla>,
+    pub text: Range<Hsla>,
+    pub highlight_low: Range<Hsla>,
+    pub highlight_med: Range<Hsla>,
+    pub highlight_high: Range<Hsla>,
+    pub success: Range<Hsla>,
+    pub warning: Range<Hsla>,
+    pub error: Range<Hsla>,
+    pub inserted: Range<Hsla>,
+    pub deleted: Range<Hsla>,
+    pub modified: Range<Hsla>,
+}
+
+impl ThemeColors {
+    pub fn base(&self, level: f32) -> Hsla {
+        self.base.lerp(level)
+    }
+
+    pub fn surface(&self, level: f32) -> Hsla {
+        self.surface.lerp(level)
+    }
+
+    pub fn overlay(&self, level: f32) -> Hsla {
+        self.overlay.lerp(level)
+    }
+
+    pub fn muted(&self, level: f32) -> Hsla {
+        self.muted.lerp(level)
+    }
+
+    pub fn subtle(&self, level: f32) -> Hsla {
+        self.subtle.lerp(level)
+    }
+
+    pub fn text(&self, level: f32) -> Hsla {
+        self.text.lerp(level)
+    }
+
+    pub fn highlight_low(&self, level: f32) -> Hsla {
+        self.highlight_low.lerp(level)
+    }
+
+    pub fn highlight_med(&self, level: f32) -> Hsla {
+        self.highlight_med.lerp(level)
+    }
+
+    pub fn highlight_high(&self, level: f32) -> Hsla {
+        self.highlight_high.lerp(level)
+    }
+
+    pub fn success(&self, level: f32) -> Hsla {
+        self.success.lerp(level)
+    }
+
+    pub fn warning(&self, level: f32) -> Hsla {
+        self.warning.lerp(level)
+    }
+
+    pub fn error(&self, level: f32) -> Hsla {
+        self.error.lerp(level)
+    }
+
+    pub fn inserted(&self, level: f32) -> Hsla {
+        self.inserted.lerp(level)
+    }
+
+    pub fn deleted(&self, level: f32) -> Hsla {
+        self.deleted.lerp(level)
+    }
+
+    pub fn modified(&self, level: f32) -> Hsla {
+        self.modified.lerp(level)
+    }
+}

crates/gpui/playground/src/themes/rose_pine.rs 🔗

@@ -0,0 +1,133 @@
+use std::ops::Range;
+
+use crate::{
+    color::{hsla, rgb, Hsla},
+    ThemeColors,
+};
+
+pub struct RosePineThemes {
+    pub default: RosePinePalette,
+    pub dawn: RosePinePalette,
+    pub moon: RosePinePalette,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct RosePinePalette {
+    pub base: Hsla,
+    pub surface: Hsla,
+    pub overlay: Hsla,
+    pub muted: Hsla,
+    pub subtle: Hsla,
+    pub text: Hsla,
+    pub love: Hsla,
+    pub gold: Hsla,
+    pub rose: Hsla,
+    pub pine: Hsla,
+    pub foam: Hsla,
+    pub iris: Hsla,
+    pub highlight_low: Hsla,
+    pub highlight_med: Hsla,
+    pub highlight_high: Hsla,
+}
+
+impl RosePinePalette {
+    pub fn default() -> RosePinePalette {
+        RosePinePalette {
+            base: rgb(0x191724),
+            surface: rgb(0x1f1d2e),
+            overlay: rgb(0x26233a),
+            muted: rgb(0x6e6a86),
+            subtle: rgb(0x908caa),
+            text: rgb(0xe0def4),
+            love: rgb(0xeb6f92),
+            gold: rgb(0xf6c177),
+            rose: rgb(0xebbcba),
+            pine: rgb(0x31748f),
+            foam: rgb(0x9ccfd8),
+            iris: rgb(0xc4a7e7),
+            highlight_low: rgb(0x21202e),
+            highlight_med: rgb(0x403d52),
+            highlight_high: rgb(0x524f67),
+        }
+    }
+
+    pub fn moon() -> RosePinePalette {
+        RosePinePalette {
+            base: rgb(0x232136),
+            surface: rgb(0x2a273f),
+            overlay: rgb(0x393552),
+            muted: rgb(0x6e6a86),
+            subtle: rgb(0x908caa),
+            text: rgb(0xe0def4),
+            love: rgb(0xeb6f92),
+            gold: rgb(0xf6c177),
+            rose: rgb(0xea9a97),
+            pine: rgb(0x3e8fb0),
+            foam: rgb(0x9ccfd8),
+            iris: rgb(0xc4a7e7),
+            highlight_low: rgb(0x2a283e),
+            highlight_med: rgb(0x44415a),
+            highlight_high: rgb(0x56526e),
+        }
+    }
+
+    pub fn dawn() -> RosePinePalette {
+        RosePinePalette {
+            base: rgb(0xfaf4ed),
+            surface: rgb(0xfffaf3),
+            overlay: rgb(0xf2e9e1),
+            muted: rgb(0x9893a5),
+            subtle: rgb(0x797593),
+            text: rgb(0x575279),
+            love: rgb(0xb4637a),
+            gold: rgb(0xea9d34),
+            rose: rgb(0xd7827e),
+            pine: rgb(0x286983),
+            foam: rgb(0x56949f),
+            iris: rgb(0x907aa9),
+            highlight_low: rgb(0xf4ede8),
+            highlight_med: rgb(0xdfdad9),
+            highlight_high: rgb(0xcecacd),
+        }
+    }
+}
+
+pub fn default() -> ThemeColors {
+    theme_colors(&RosePinePalette::default())
+}
+
+pub fn moon() -> ThemeColors {
+    theme_colors(&RosePinePalette::moon())
+}
+
+pub fn dawn() -> ThemeColors {
+    theme_colors(&RosePinePalette::dawn())
+}
+
+fn theme_colors(p: &RosePinePalette) -> ThemeColors {
+    ThemeColors {
+        base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
+        surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
+        overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
+        muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
+        subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
+        text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
+        highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
+        highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
+        highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
+        success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+        warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
+        error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+        inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+        deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+        modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
+    }
+}
+
+/// Produces a range by multiplying the saturation and lightness of the base color by the given
+/// start and end factors.
+fn scale_sl(base: Hsla, (start_s, start_l): (f32, f32), (end_s, end_l): (f32, f32)) -> Range<Hsla> {
+    let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
+    let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
+    Range { start, end }
+}

crates/gpui/playground/src/view.rs 🔗

@@ -0,0 +1,26 @@
+use crate::{
+    adapter::AdapterElement,
+    element::{AnyElement, Element},
+};
+use gpui::ViewContext;
+
+pub fn view<F, E>(mut render: F) -> ViewFn
+where
+    F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
+    E: Element<ViewFn>,
+{
+    ViewFn(Box::new(move |cx| (render)(cx).into_any()))
+}
+
+pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
+
+impl gpui::Entity for ViewFn {
+    type Event = ();
+}
+
+impl gpui::View for ViewFn {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
+        use gpui::Element as _;
+        AdapterElement((self.0)(cx)).into_any()
+    }
+}

crates/gpui/playground_macros/Cargo.toml 🔗

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

crates/gpui/playground_macros/src/derive_element.rs 🔗

@@ -0,0 +1,91 @@
+use proc_macro::TokenStream;
+use proc_macro2::Ident;
+use quote::quote;
+use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics};
+
+use crate::derive_into_element::impl_into_element;
+
+pub fn derive_element(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 (impl_generics, type_generics, where_clause, view_type_name, lifetimes) =
+        if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
+            if let GenericParam::Type(type_param) = param {
+                Some(type_param.ident.clone())
+            } else {
+                None
+            }
+        }) {
+            let mut lifetimes = vec![];
+            for param in ast.generics.params.iter() {
+                if let GenericParam::Lifetime(lifetime_def) = param {
+                    lifetimes.push(lifetime_def.lifetime.clone());
+                }
+            }
+            let generics = ast.generics.split_for_impl();
+            (
+                generics.0,
+                Some(generics.1),
+                generics.2,
+                first_type_param,
+                lifetimes,
+            )
+        } else {
+            let generics = placeholder_view_generics.split_for_impl();
+            let placeholder_view_type_name: Ident = parse_quote! { V };
+            (
+                generics.0,
+                None,
+                generics.2,
+                placeholder_view_type_name,
+                vec![],
+            )
+        };
+
+    let lifetimes = if !lifetimes.is_empty() {
+        quote! { <#(#lifetimes),*> }
+    } else {
+        quote! {}
+    };
+
+    let impl_into_element = impl_into_element(
+        &impl_generics,
+        &view_type_name,
+        &type_name,
+        &type_generics,
+        &where_clause,
+    );
+
+    let gen = quote! {
+        impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
+        #where_clause
+        {
+            type Layout = Option<playground::element::AnyElement<#view_type_name #lifetimes>>;
+
+            fn layout(
+                &mut self,
+                view: &mut V,
+                cx: &mut playground::element::LayoutContext<V>,
+            ) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
+                let mut element = self.render(view, cx).into_any();
+                let layout_id = element.layout(view, cx)?;
+                Ok(playground::element::Layout::new(layout_id, Some(element)))
+            }
+
+            fn paint(
+                &mut self,
+                view: &mut V,
+                layout: &mut playground::element::Layout<V, Self::Layout>,
+                cx: &mut playground::element::PaintContext<V>,
+            ) {
+                layout.paint(view, cx);
+            }
+        }
+
+        #impl_into_element
+    };
+
+    gen.into()
+}

crates/gpui/playground_macros/src/derive_into_element.rs 🔗

@@ -0,0 +1,69 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{
+    parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, WhereClause,
+};
+
+pub fn derive_into_element(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;
+        }
+    }
+
+    impl_into_element(
+        &impl_generics,
+        &view_type_name,
+        &type_name,
+        &type_generics,
+        &where_clause,
+    )
+    .into()
+}
+
+pub fn impl_into_element(
+    impl_generics: &syn::ImplGenerics<'_>,
+    view_type_name: &Ident,
+    type_name: &Ident,
+    type_generics: &Option<syn::TypeGenerics<'_>>,
+    where_clause: &Option<&WhereClause>,
+) -> proc_macro2::TokenStream {
+    quote! {
+        impl #impl_generics playground::element::IntoElement<#view_type_name> for #type_name #type_generics
+        #where_clause
+        {
+            type Element = Self;
+
+            fn into_element(self) -> Self {
+                self
+            }
+        }
+    }
+}

crates/gpui/playground_macros/src/playground_macros.rs 🔗

@@ -0,0 +1,26 @@
+use proc_macro::TokenStream;
+
+mod derive_element;
+mod derive_into_element;
+mod styleable_helpers;
+mod tailwind_lengths;
+
+#[proc_macro]
+pub fn styleable_helpers(args: TokenStream) -> TokenStream {
+    styleable_helpers::styleable_helpers(args)
+}
+
+#[proc_macro_derive(Element, attributes(element_crate))]
+pub fn derive_element(input: TokenStream) -> TokenStream {
+    derive_element::derive_element(input)
+}
+
+#[proc_macro_derive(IntoElement, attributes(element_crate))]
+pub fn derive_into_element(input: TokenStream) -> TokenStream {
+    derive_into_element::derive_into_element(input)
+}
+
+#[proc_macro_attribute]
+pub fn tailwind_lengths(attr: TokenStream, item: TokenStream) -> TokenStream {
+    tailwind_lengths::tailwind_lengths(attr, item)
+}

crates/gpui/playground_macros/src/styleable_helpers.rs 🔗

@@ -0,0 +1,147 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{
+    parse::{Parse, ParseStream, Result},
+    parse_macro_input,
+};
+
+struct StyleableMacroInput;
+
+impl Parse for StyleableMacroInput {
+    fn parse(_input: ParseStream) -> Result<Self> {
+        Ok(StyleableMacroInput)
+    }
+}
+
+pub fn styleable_helpers(input: TokenStream) -> TokenStream {
+    let _ = parse_macro_input!(input as StyleableMacroInput);
+    let methods = generate_methods();
+    let output = quote! {
+        #(#methods)*
+    };
+    output.into()
+}
+
+fn generate_methods() -> Vec<TokenStream2> {
+    let mut methods = Vec::new();
+
+    for (prefix, auto_allowed, fields) in tailwind_prefixes() {
+        for (suffix, length_tokens) in tailwind_lengths() {
+            if !auto_allowed && suffix == "auto" {
+                // Conditional to skip "auto"
+                continue;
+            }
+
+            let method_name = format_ident!("{}_{}", prefix, suffix);
+            let field_assignments = fields
+                .iter()
+                .map(|field_tokens| {
+                    quote! {
+                        style.#field_tokens = Some(gpui::geometry::#length_tokens);
+                    }
+                })
+                .collect::<Vec<_>>();
+
+            let method = quote! {
+                fn #method_name(mut self) -> Self where Self: std::marker::Sized {
+                    let mut style = self.declared_style();
+                    #(#field_assignments)*
+                    self
+                }
+            };
+
+            methods.push(method);
+        }
+    }
+
+    methods
+}
+
+fn tailwind_lengths() -> Vec<(&'static str, TokenStream2)> {
+    vec![
+        ("0", quote! { pixels(0.) }),
+        ("1", quote! { rems(0.25) }),
+        ("2", quote! { rems(0.5) }),
+        ("3", quote! { rems(0.75) }),
+        ("4", quote! { rems(1.) }),
+        ("5", quote! { rems(1.25) }),
+        ("6", quote! { rems(1.5) }),
+        ("8", quote! { rems(2.0) }),
+        ("10", quote! { rems(2.5) }),
+        ("12", quote! { rems(3.) }),
+        ("16", quote! { rems(4.) }),
+        ("20", quote! { rems(5.) }),
+        ("24", quote! { rems(6.) }),
+        ("32", quote! { rems(8.) }),
+        ("40", quote! { rems(10.) }),
+        ("48", quote! { rems(12.) }),
+        ("56", quote! { rems(14.) }),
+        ("64", quote! { rems(16.) }),
+        ("72", quote! { rems(18.) }),
+        ("80", quote! { rems(20.) }),
+        ("96", quote! { rems(24.) }),
+        ("auto", quote! { auto() }),
+        ("px", quote! { pixels(1.) }),
+        ("full", quote! { relative(1.) }),
+        ("1_2", quote! { relative(0.5) }),
+        ("1_3", quote! { relative(1./3.) }),
+        ("2_3", quote! { relative(2./3.) }),
+        ("1_4", quote! { relative(0.25) }),
+        ("2_4", quote! { relative(0.5) }),
+        ("3_4", quote! { relative(0.75) }),
+        ("1_5", quote! { relative(0.2) }),
+        ("2_5", quote! { relative(0.4) }),
+        ("3_5", quote! { relative(0.6) }),
+        ("4_5", quote! { relative(0.8) }),
+        ("1_6", quote! { relative(1./6.) }),
+        ("5_6", quote! { relative(5./6.) }),
+        ("1_12", quote! { relative(1./12.) }),
+        // ("screen_50", quote! { DefiniteLength::Vh(50.0) }),
+        // ("screen_75", quote! { DefiniteLength::Vh(75.0) }),
+        // ("screen", quote! { DefiniteLength::Vh(100.0) }),
+    ]
+}
+
+fn tailwind_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>)> {
+    vec![
+        ("w", true, vec![quote! { size.width }]),
+        ("h", true, vec![quote! { size.height }]),
+        ("min_w", false, vec![quote! { min_size.width }]),
+        ("min_h", false, vec![quote! { min_size.height }]),
+        ("max_w", false, vec![quote! { max_size.width }]),
+        ("max_h", false, vec![quote! { max_size.height }]),
+        (
+            "m",
+            true,
+            vec![quote! { margin.top }, quote! { margin.bottom }],
+        ),
+        ("mt", true, vec![quote! { margin.top }]),
+        ("mb", true, vec![quote! { margin.bottom }]),
+        (
+            "mx",
+            true,
+            vec![quote! { margin.left }, quote! { margin.right }],
+        ),
+        ("ml", true, vec![quote! { margin.left }]),
+        ("mr", true, vec![quote! { margin.right }]),
+        (
+            "p",
+            false,
+            vec![quote! { padding.top }, quote! { padding.bottom }],
+        ),
+        ("pt", false, vec![quote! { padding.top }]),
+        ("pb", false, vec![quote! { padding.bottom }]),
+        (
+            "px",
+            false,
+            vec![quote! { padding.left }, quote! { padding.right }],
+        ),
+        ("pl", false, vec![quote! { padding.left }]),
+        ("pr", false, vec![quote! { padding.right }]),
+        ("top", true, vec![quote! { inset.top }]),
+        ("bottom", true, vec![quote! { inset.bottom }]),
+        ("left", true, vec![quote! { inset.left }]),
+        ("right", true, vec![quote! { inset.right }]),
+    ]
+}

crates/gpui/playground_macros/src/tailwind_lengths.rs 🔗

@@ -0,0 +1,99 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{parse_macro_input, FnArg, ItemFn, PatType};
+
+pub fn tailwind_lengths(_attr: TokenStream, item: TokenStream) -> TokenStream {
+    let input_function = parse_macro_input!(item as ItemFn);
+
+    let visibility = &input_function.vis;
+    let function_signature = input_function.sig.clone();
+    let function_body = input_function.block;
+    let where_clause = &function_signature.generics.where_clause;
+
+    let argument_name = match function_signature.inputs.iter().nth(1) {
+        Some(FnArg::Typed(PatType { pat, .. })) => pat,
+        _ => panic!("Couldn't find the second argument in the function signature"),
+    };
+
+    let mut output_functions = TokenStream2::new();
+
+    for (length, value) in fixed_lengths() {
+        let function_name = format_ident!("{}{}", function_signature.ident, length);
+        output_functions.extend(quote! {
+            #visibility fn #function_name(mut self) -> Self #where_clause {
+                let #argument_name = #value.into();
+                #function_body
+            }
+        });
+    }
+
+    output_functions.into()
+}
+
+fn fixed_lengths() -> Vec<(&'static str, TokenStream2)> {
+    vec![
+        ("0", quote! { DefinedLength::Pixels(0.) }),
+        ("px", quote! { DefinedLength::Pixels(1.) }),
+        ("0_5", quote! { DefinedLength::Rems(0.125) }),
+        ("1", quote! { DefinedLength::Rems(0.25) }),
+        ("1_5", quote! { DefinedLength::Rems(0.375) }),
+        ("2", quote! { DefinedLength::Rems(0.5) }),
+        ("2_5", quote! { DefinedLength::Rems(0.625) }),
+        ("3", quote! { DefinedLength::Rems(0.75) }),
+        ("3_5", quote! { DefinedLength::Rems(0.875) }),
+        ("4", quote! { DefinedLength::Rems(1.) }),
+        ("5", quote! { DefinedLength::Rems(1.25) }),
+        ("6", quote! { DefinedLength::Rems(1.5) }),
+        ("7", quote! { DefinedLength::Rems(1.75) }),
+        ("8", quote! { DefinedLength::Rems(2.) }),
+        ("9", quote! { DefinedLength::Rems(2.25) }),
+        ("10", quote! { DefinedLength::Rems(2.5) }),
+        ("11", quote! { DefinedLength::Rems(2.75) }),
+        ("12", quote! { DefinedLength::Rems(3.) }),
+        ("14", quote! { DefinedLength::Rems(3.5) }),
+        ("16", quote! { DefinedLength::Rems(4.) }),
+        ("20", quote! { DefinedLength::Rems(5.) }),
+        ("24", quote! { DefinedLength::Rems(6.) }),
+        ("28", quote! { DefinedLength::Rems(7.) }),
+        ("32", quote! { DefinedLength::Rems(8.) }),
+        ("36", quote! { DefinedLength::Rems(9.) }),
+        ("40", quote! { DefinedLength::Rems(10.) }),
+        ("44", quote! { DefinedLength::Rems(11.) }),
+        ("48", quote! { DefinedLength::Rems(12.) }),
+        ("52", quote! { DefinedLength::Rems(13.) }),
+        ("56", quote! { DefinedLength::Rems(14.) }),
+        ("60", quote! { DefinedLength::Rems(15.) }),
+        ("64", quote! { DefinedLength::Rems(16.) }),
+        ("72", quote! { DefinedLength::Rems(18.) }),
+        ("80", quote! { DefinedLength::Rems(20.) }),
+        ("96", quote! { DefinedLength::Rems(24.) }),
+        ("half", quote! { DefinedLength::Percent(50.) }),
+        ("1_3rd", quote! { DefinedLength::Percent(33.333333) }),
+        ("2_3rd", quote! { DefinedLength::Percent(66.666667) }),
+        ("1_4th", quote! { DefinedLength::Percent(25.) }),
+        ("2_4th", quote! { DefinedLength::Percent(50.) }),
+        ("3_4th", quote! { DefinedLength::Percent(75.) }),
+        ("1_5th", quote! { DefinedLength::Percent(20.) }),
+        ("2_5th", quote! { DefinedLength::Percent(40.) }),
+        ("3_5th", quote! { DefinedLength::Percent(60.) }),
+        ("4_5th", quote! { DefinedLength::Percent(80.) }),
+        ("1_6th", quote! { DefinedLength::Percent(16.666667) }),
+        ("2_6th", quote! { DefinedLength::Percent(33.333333) }),
+        ("3_6th", quote! { DefinedLength::Percent(50.) }),
+        ("4_6th", quote! { DefinedLength::Percent(66.666667) }),
+        ("5_6th", quote! { DefinedLength::Percent(83.333333) }),
+        ("1_12th", quote! { DefinedLength::Percent(8.333333) }),
+        ("2_12th", quote! { DefinedLength::Percent(16.666667) }),
+        ("3_12th", quote! { DefinedLength::Percent(25.) }),
+        ("4_12th", quote! { DefinedLength::Percent(33.333333) }),
+        ("5_12th", quote! { DefinedLength::Percent(41.666667) }),
+        ("6_12th", quote! { DefinedLength::Percent(50.) }),
+        ("7_12th", quote! { DefinedLength::Percent(58.333333) }),
+        ("8_12th", quote! { DefinedLength::Percent(66.666667) }),
+        ("9_12th", quote! { DefinedLength::Percent(75.) }),
+        ("10_12th", quote! { DefinedLength::Percent(83.333333) }),
+        ("11_12th", quote! { DefinedLength::Percent(91.666667) }),
+        ("full", quote! { DefinedLength::Percent(100.) }),
+    ]
+}

crates/gpui/src/app.rs 🔗

@@ -7,6 +7,34 @@ pub mod test_app_context;
 pub(crate) mod window;
 mod window_input_handler;
 
+use crate::{
+    elements::{AnyElement, AnyRootElement, RootElement},
+    executor::{self, Task},
+    fonts::TextStyle,
+    json,
+    keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
+    platform::{
+        self, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
+        PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions,
+    },
+    util::post_inc,
+    window::{Window, WindowContext},
+    AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId,
+};
+pub use action::*;
+use anyhow::{anyhow, Context, Result};
+use callback_collection::CallbackCollection;
+use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
+use derive_more::Deref;
+pub use menu::*;
+use parking_lot::Mutex;
+use platform::Event;
+use postage::oneshot;
+#[cfg(any(test, feature = "test-support"))]
+use ref_counts::LeakDetector;
+use ref_counts::RefCounts;
+use smallvec::SmallVec;
+use smol::prelude::*;
 use std::{
     any::{type_name, Any, TypeId},
     cell::RefCell,
@@ -21,45 +49,12 @@ use std::{
     sync::{Arc, Weak},
     time::Duration,
 };
-
-use anyhow::{anyhow, Context, Result};
-
-use derive_more::Deref;
-use parking_lot::Mutex;
-use postage::oneshot;
-use smallvec::SmallVec;
-use smol::prelude::*;
-use util::ResultExt;
-use uuid::Uuid;
-
-pub use action::*;
-use callback_collection::CallbackCollection;
-use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
-pub use menu::*;
-use platform::Event;
-#[cfg(any(test, feature = "test-support"))]
-use ref_counts::LeakDetector;
 #[cfg(any(test, feature = "test-support"))]
 pub use test_app_context::{ContextHandle, TestAppContext};
+use util::ResultExt;
+use uuid::Uuid;
 use window_input_handler::WindowInputHandler;
 
-use crate::{
-    elements::{AnyElement, AnyRootElement, RootElement},
-    executor::{self, Task},
-    fonts::TextStyle,
-    json,
-    keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
-    platform::{
-        self, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
-        PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions,
-    },
-    util::post_inc,
-    window::{Window, WindowContext},
-    AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId,
-};
-
-use self::ref_counts::RefCounts;
-
 pub trait Entity: 'static {
     type Event;
 
@@ -73,10 +68,12 @@ pub trait Entity: 'static {
 }
 
 pub trait View: Entity + Sized {
-    fn ui_name() -> &'static str;
     fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement<Self>;
     fn focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
     fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
+    fn ui_name() -> &'static str {
+        type_name::<Self>()
+    }
     fn key_down(&mut self, _: &KeyDownEvent, _: &mut ViewContext<Self>) -> bool {
         false
     }
@@ -640,7 +637,7 @@ impl AppContext {
     pub fn add_action<A, V, F, R>(&mut self, handler: F)
     where
         A: Action,
-        V: View,
+        V: 'static,
         F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
     {
         self.add_action_internal(handler, false)
@@ -649,7 +646,7 @@ impl AppContext {
     pub fn capture_action<A, V, F>(&mut self, handler: F)
     where
         A: Action,
-        V: View,
+        V: 'static,
         F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
     {
         self.add_action_internal(handler, true)
@@ -658,7 +655,7 @@ impl AppContext {
     fn add_action_internal<A, V, F, R>(&mut self, mut handler: F, capture: bool)
     where
         A: Action,
-        V: View,
+        V: 'static,
         F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
     {
         let handler = Box::new(
@@ -699,7 +696,7 @@ impl AppContext {
     pub fn add_async_action<A, V, F>(&mut self, mut handler: F)
     where
         A: Action,
-        V: View,
+        V: 'static,
         F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> Option<Task<Result<()>>>,
     {
         self.add_action(move |view, action, cx| {
@@ -898,8 +895,8 @@ impl AppContext {
 
     fn observe_focus<F, V>(&mut self, handle: &ViewHandle<V>, mut callback: F) -> Subscription
     where
+        V: 'static,
         F: 'static + FnMut(ViewHandle<V>, bool, &mut WindowContext) -> bool,
-        V: View,
     {
         let subscription_id = post_inc(&mut self.next_subscription_id);
         let observed = handle.downgrade();
@@ -1382,15 +1379,15 @@ impl AppContext {
         self.windows.keys().copied()
     }
 
-    pub fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
+    pub fn read_view<V: 'static>(&self, handle: &ViewHandle<V>) -> &V {
         if let Some(view) = self.views.get(&(handle.window, handle.view_id)) {
             view.as_any().downcast_ref().expect("downcast is type safe")
         } else {
-            panic!("circular view reference for type {}", type_name::<T>());
+            panic!("circular view reference for type {}", type_name::<V>());
         }
     }
 
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
+    fn upgrade_view_handle<V: 'static>(&self, handle: &WeakViewHandle<V>) -> Option<ViewHandle<V>> {
         if self.ref_counts.lock().is_entity_alive(handle.view_id) {
             Some(ViewHandle::new(
                 handle.window,
@@ -1659,6 +1656,9 @@ impl AppContext {
                             subscription_id,
                             callback,
                         ),
+                        Effect::RepaintWindow { window } => {
+                            self.handle_repaint_window_effect(window)
+                        }
                     }
                     self.pending_notifications.clear();
                 } else {
@@ -1896,6 +1896,15 @@ impl AppContext {
         });
     }
 
+    fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
+        self.update_window(window, |cx| {
+            cx.layout(false).log_err();
+            if let Some(scene) = cx.paint().log_err() {
+                cx.window.platform_window.present_scene(scene);
+            }
+        });
+    }
+
     fn handle_window_activation_effect(&mut self, window: AnyWindowHandle, active: bool) -> bool {
         self.update_window(window, |cx| {
             if cx.window.is_active == active {
@@ -2151,7 +2160,7 @@ struct ViewMetadata {
     keymap_context: KeymapContext,
 }
 
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Debug)]
 pub struct WindowInvalidation {
     pub updated: HashSet<usize>,
     pub removed: Vec<usize>,
@@ -2255,6 +2264,9 @@ pub enum Effect {
         window: AnyWindowHandle,
         is_active: bool,
     },
+    RepaintWindow {
+        window: AnyWindowHandle,
+    },
     WindowActivationObservation {
         window: AnyWindowHandle,
         subscription_id: usize,
@@ -2448,6 +2460,10 @@ impl Debug for Effect {
                 .debug_struct("Effect::ActiveLabeledTasksObservation")
                 .field("subscription_id", subscription_id)
                 .finish(),
+            Effect::RepaintWindow { window } => f
+                .debug_struct("Effect::RepaintWindow")
+                .field("window_id", &window.id())
+                .finish(),
         }
     }
 }
@@ -2543,10 +2559,7 @@ pub trait AnyView {
     }
 }
 
-impl<V> AnyView for V
-where
-    V: View,
-{
+impl<V: View> AnyView for V {
     fn as_any(&self) -> &dyn Any {
         self
     }
@@ -2878,7 +2891,7 @@ pub struct ViewContext<'a, 'b, T: ?Sized> {
     view_type: PhantomData<T>,
 }
 
-impl<'a, 'b, T: View> Deref for ViewContext<'a, 'b, T> {
+impl<'a, 'b, V> Deref for ViewContext<'a, 'b, V> {
     type Target = WindowContext<'a>;
 
     fn deref(&self) -> &Self::Target {
@@ -2886,14 +2899,14 @@ impl<'a, 'b, T: View> Deref for ViewContext<'a, 'b, T> {
     }
 }
 
-impl<T: View> DerefMut for ViewContext<'_, '_, T> {
+impl<'a, 'b, V> DerefMut for ViewContext<'a, 'b, V> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.window_context
     }
 }
 
-impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
-    pub(crate) fn mutable(window_context: &'b mut WindowContext<'a>, view_id: usize) -> Self {
+impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
+    pub fn mutable(window_context: &'b mut WindowContext<'a>, view_id: usize) -> Self {
         Self {
             window_context: Reference::Mutable(window_context),
             view_id,
@@ -2901,7 +2914,7 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
         }
     }
 
-    pub(crate) fn immutable(window_context: &'b WindowContext<'a>, view_id: usize) -> Self {
+    pub fn immutable(window_context: &'b WindowContext<'a>, view_id: usize) -> Self {
         Self {
             window_context: Reference::Immutable(window_context),
             view_id,
@@ -2913,6 +2926,12 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
         &mut self.window_context
     }
 
+    pub fn notify(&mut self) {
+        let window = self.window_handle;
+        let view_id = self.view_id;
+        self.window_context.notify_view(window, view_id);
+    }
+
     pub fn handle(&self) -> ViewHandle<V> {
         ViewHandle::new(
             self.window_handle,
@@ -3226,21 +3245,6 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
         })
     }
 
-    pub fn emit(&mut self, payload: V::Event) {
-        self.window_context
-            .pending_effects
-            .push_back(Effect::Event {
-                entity_id: self.view_id,
-                payload: Box::new(payload),
-            });
-    }
-
-    pub fn notify(&mut self) {
-        let window = self.window_handle;
-        let view_id = self.view_id;
-        self.window_context.notify_view(window, view_id);
-    }
-
     pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>)) {
         let handle = self.handle();
         self.window_context
@@ -3341,6 +3345,10 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
         self.element_state::<Tag, T>(element_id, T::default())
     }
 
+    pub fn rem_pixels(&self) -> f32 {
+        16.
+    }
+
     pub fn default_element_state_dynamic<T: 'static + Default>(
         &mut self,
         tag: TypeTag,
@@ -3350,6 +3358,17 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
     }
 }
 
+impl<V: View> ViewContext<'_, '_, V> {
+    pub fn emit(&mut self, event: V::Event) {
+        self.window_context
+            .pending_effects
+            .push_back(Effect::Event {
+                entity_id: self.view_id,
+                payload: Box::new(event),
+            });
+    }
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct TypeTag {
     tag: TypeId,
@@ -3428,15 +3447,27 @@ impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
     }
 }
 
-pub struct LayoutContext<'a, 'b, 'c, V: View> {
-    view_context: &'c mut ViewContext<'a, 'b, V>,
+/// Methods shared by both LayoutContext and PaintContext
+///
+/// It's that PaintContext should be implemented in terms of layout context and
+/// deref to it, in which case we wouldn't need this.
+pub trait RenderContext<'a, 'b, V> {
+    fn text_style(&self) -> TextStyle;
+    fn push_text_style(&mut self, style: TextStyle);
+    fn pop_text_style(&mut self);
+    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V>;
+}
+
+pub struct LayoutContext<'a, 'b, 'c, V> {
+    // Nathan: Making this is public while I work on playground.
+    pub view_context: &'c mut ViewContext<'a, 'b, V>,
     new_parents: &'c mut HashMap<usize, usize>,
     views_to_notify_if_ancestors_change: &'c mut HashMap<usize, SmallVec<[usize; 2]>>,
-    text_style_stack: Vec<Arc<TextStyle>>,
+    text_style_stack: Vec<TextStyle>,
     pub refreshing: bool,
 }
 
-impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> {
     pub fn new(
         view_context: &'c mut ViewContext<'a, 'b, V>,
         new_parents: &'c mut HashMap<usize, usize>,
@@ -3500,26 +3531,39 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
             .push(self_view_id);
     }
 
-    pub fn text_style(&self) -> Arc<TextStyle> {
+    pub fn with_text_style<F, T>(&mut self, style: TextStyle, f: F) -> T
+    where
+        F: FnOnce(&mut Self) -> T,
+    {
+        self.push_text_style(style);
+        let result = f(self);
+        self.pop_text_style();
+        result
+    }
+}
+
+impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, 'c, V> {
+    fn text_style(&self) -> TextStyle {
         self.text_style_stack
             .last()
             .cloned()
-            .unwrap_or(Default::default())
+            .unwrap_or(TextStyle::default(&self.font_cache))
     }
 
-    pub fn with_text_style<S, F, T>(&mut self, style: S, f: F) -> T
-    where
-        S: Into<Arc<TextStyle>>,
-        F: FnOnce(&mut Self) -> T,
-    {
-        self.text_style_stack.push(style.into());
-        let result = f(self);
+    fn push_text_style(&mut self, style: TextStyle) {
+        self.text_style_stack.push(style);
+    }
+
+    fn pop_text_style(&mut self) {
         self.text_style_stack.pop();
-        result
+    }
+
+    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+        &mut self.view_context
     }
 }
 
-impl<'a, 'b, 'c, V: View> Deref for LayoutContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> Deref for LayoutContext<'a, 'b, 'c, V> {
     type Target = ViewContext<'a, 'b, V>;
 
     fn deref(&self) -> &Self::Target {
@@ -3527,13 +3571,13 @@ impl<'a, 'b, 'c, V: View> Deref for LayoutContext<'a, 'b, 'c, V> {
     }
 }
 
-impl<V: View> DerefMut for LayoutContext<'_, '_, '_, V> {
+impl<V> DerefMut for LayoutContext<'_, '_, '_, V> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.view_context
     }
 }
 
-impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
+impl<V> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
     fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
         BorrowAppContext::read_with(&*self.view_context, f)
     }
@@ -3543,7 +3587,7 @@ impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
     }
 }
 
-impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
+impl<V> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
     type Result<T> = T;
 
     fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
@@ -3573,39 +3617,42 @@ impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
     }
 }
 
-pub struct PaintContext<'a, 'b, 'c, V: View> {
-    view_context: &'c mut ViewContext<'a, 'b, V>,
-    text_style_stack: Vec<Arc<TextStyle>>,
+pub struct PaintContext<'a, 'b, 'c, V> {
+    pub view_context: &'c mut ViewContext<'a, 'b, V>,
+    text_style_stack: Vec<TextStyle>,
 }
 
-impl<'a, 'b, 'c, V: View> PaintContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> {
     pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
         Self {
             view_context,
             text_style_stack: Vec::new(),
         }
     }
+}
 
-    pub fn text_style(&self) -> Arc<TextStyle> {
+impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for PaintContext<'a, 'b, 'c, V> {
+    fn text_style(&self) -> TextStyle {
         self.text_style_stack
             .last()
             .cloned()
-            .unwrap_or(Default::default())
+            .unwrap_or(TextStyle::default(&self.font_cache))
     }
 
-    pub fn with_text_style<S, F, T>(&mut self, style: S, f: F) -> T
-    where
-        S: Into<Arc<TextStyle>>,
-        F: FnOnce(&mut Self) -> T,
-    {
-        self.text_style_stack.push(style.into());
-        let result = f(self);
+    fn push_text_style(&mut self, style: TextStyle) {
+        self.text_style_stack.push(style);
+    }
+
+    fn pop_text_style(&mut self) {
         self.text_style_stack.pop();
-        result
+    }
+
+    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
+        &mut self.view_context
     }
 }
 
-impl<'a, 'b, 'c, V: View> Deref for PaintContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> Deref for PaintContext<'a, 'b, 'c, V> {
     type Target = ViewContext<'a, 'b, V>;
 
     fn deref(&self) -> &Self::Target {
@@ -3613,13 +3660,13 @@ impl<'a, 'b, 'c, V: View> Deref for PaintContext<'a, 'b, 'c, V> {
     }
 }
 
-impl<V: View> DerefMut for PaintContext<'_, '_, '_, V> {
+impl<V> DerefMut for PaintContext<'_, '_, '_, V> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.view_context
     }
 }
 
-impl<V: View> BorrowAppContext for PaintContext<'_, '_, '_, V> {
+impl<V> BorrowAppContext for PaintContext<'_, '_, '_, V> {
     fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
         BorrowAppContext::read_with(&*self.view_context, f)
     }
@@ -3629,7 +3676,7 @@ impl<V: View> BorrowAppContext for PaintContext<'_, '_, '_, V> {
     }
 }
 
-impl<V: View> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
+impl<V> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
     type Result<T> = T;
 
     fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
@@ -3661,25 +3708,37 @@ impl<V: View> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
     }
 }
 
-pub struct EventContext<'a, 'b, 'c, V: View> {
+pub struct EventContext<'a, 'b, 'c, V> {
     view_context: &'c mut ViewContext<'a, 'b, V>,
     pub(crate) handled: bool,
+    // I would like to replace handled with this.
+    // Being additive for now.
+    pub bubble: bool,
 }
 
-impl<'a, 'b, 'c, V: View> EventContext<'a, 'b, 'c, V> {
-    pub(crate) fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
+impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
+    pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
         EventContext {
             view_context,
             handled: true,
+            bubble: false,
         }
     }
 
     pub fn propagate_event(&mut self) {
         self.handled = false;
     }
+
+    pub fn bubble_event(&mut self) {
+        self.bubble = true;
+    }
+
+    pub fn event_bubbled(&self) -> bool {
+        self.bubble
+    }
 }
 
-impl<'a, 'b, 'c, V: View> Deref for EventContext<'a, 'b, 'c, V> {
+impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> {
     type Target = ViewContext<'a, 'b, V>;
 
     fn deref(&self) -> &Self::Target {
@@ -3687,13 +3746,13 @@ impl<'a, 'b, 'c, V: View> Deref for EventContext<'a, 'b, 'c, V> {
     }
 }
 
-impl<V: View> DerefMut for EventContext<'_, '_, '_, V> {
+impl<V> DerefMut for EventContext<'_, '_, '_, V> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.view_context
     }
 }
 
-impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
+impl<V> BorrowAppContext for EventContext<'_, '_, '_, V> {
     fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
         BorrowAppContext::read_with(&*self.view_context, f)
     }
@@ -3703,7 +3762,7 @@ impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
     }
 }
 
-impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
+impl<V> BorrowWindowContext for EventContext<'_, '_, '_, V> {
     type Result<T> = T;
 
     fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
@@ -4031,7 +4090,7 @@ impl<V> Clone for WindowHandle<V> {
 
 impl<V> Copy for WindowHandle<V> {}
 
-impl<V: View> WindowHandle<V> {
+impl<V: 'static> WindowHandle<V> {
     fn new(window_id: usize) -> Self {
         WindowHandle {
             any_handle: AnyWindowHandle::new(window_id, TypeId::of::<V>()),
@@ -4069,7 +4128,9 @@ impl<V: View> WindowHandle<V> {
                 .update(cx, update)
         })
     }
+}
 
+impl<V: View> WindowHandle<V> {
     pub fn replace_root<C, F>(&self, cx: &mut C, build_root: F) -> C::Result<ViewHandle<V>>
     where
         C: BorrowWindowContext,
@@ -4149,7 +4210,7 @@ impl AnyWindowHandle {
         self.update(cx, |cx| cx.add_view(build_view))
     }
 
-    pub fn downcast<V: View>(self) -> Option<WindowHandle<V>> {
+    pub fn downcast<V: 'static>(self) -> Option<WindowHandle<V>> {
         if self.root_view_type == TypeId::of::<V>() {
             Some(WindowHandle {
                 any_handle: self,
@@ -4160,7 +4221,7 @@ impl AnyWindowHandle {
         }
     }
 
-    pub fn root_is<V: View>(&self) -> bool {
+    pub fn root_is<V: 'static>(&self) -> bool {
         self.root_view_type == TypeId::of::<V>()
     }
 
@@ -4238,9 +4299,9 @@ impl AnyWindowHandle {
 }
 
 #[repr(transparent)]
-pub struct ViewHandle<T> {
+pub struct ViewHandle<V> {
     any_handle: AnyViewHandle,
-    view_type: PhantomData<T>,
+    view_type: PhantomData<V>,
 }
 
 impl<T> Deref for ViewHandle<T> {
@@ -4251,15 +4312,15 @@ impl<T> Deref for ViewHandle<T> {
     }
 }
 
-impl<T: View> ViewHandle<T> {
+impl<V: 'static> ViewHandle<V> {
     fn new(window: AnyWindowHandle, view_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
         Self {
-            any_handle: AnyViewHandle::new(window, view_id, TypeId::of::<T>(), ref_counts.clone()),
+            any_handle: AnyViewHandle::new(window, view_id, TypeId::of::<V>(), ref_counts.clone()),
             view_type: PhantomData,
         }
     }
 
-    pub fn downgrade(&self) -> WeakViewHandle<T> {
+    pub fn downgrade(&self) -> WeakViewHandle<V> {
         WeakViewHandle::new(self.window, self.view_id)
     }
 
@@ -4275,14 +4336,14 @@ impl<T: View> ViewHandle<T> {
         self.view_id
     }
 
-    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
+    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
         cx.read_view(self)
     }
 
     pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::Result<S>
     where
         C: BorrowWindowContext,
-        F: FnOnce(&T, &ViewContext<T>) -> S,
+        F: FnOnce(&V, &ViewContext<V>) -> S,
     {
         cx.read_window(self.window, |cx| {
             let cx = ViewContext::immutable(cx, self.view_id);
@@ -4293,7 +4354,7 @@ impl<T: View> ViewHandle<T> {
     pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::Result<S>
     where
         C: BorrowWindowContext,
-        F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
+        F: FnOnce(&mut V, &mut ViewContext<V>) -> S,
     {
         let mut update = Some(update);
 
@@ -4429,8 +4490,8 @@ impl AnyViewHandle {
         TypeId::of::<T>() == self.view_type
     }
 
-    pub fn downcast<T: View>(self) -> Option<ViewHandle<T>> {
-        if self.is::<T>() {
+    pub fn downcast<V: 'static>(self) -> Option<ViewHandle<V>> {
+        if self.is::<V>() {
             Some(ViewHandle {
                 any_handle: self,
                 view_type: PhantomData,
@@ -4440,8 +4501,8 @@ impl AnyViewHandle {
         }
     }
 
-    pub fn downcast_ref<T: View>(&self) -> Option<&ViewHandle<T>> {
-        if self.is::<T>() {
+    pub fn downcast_ref<V: 'static>(&self) -> Option<&ViewHandle<V>> {
+        if self.is::<V>() {
             Some(unsafe { mem::transmute(self) })
         } else {
             None
@@ -4640,7 +4701,7 @@ impl<T> WeakHandle for WeakViewHandle<T> {
     }
 }
 
-impl<V: View> WeakViewHandle<V> {
+impl<V: 'static> WeakViewHandle<V> {
     fn new(window: AnyWindowHandle, view_id: usize) -> Self {
         Self {
             any_handle: AnyWeakViewHandle {
@@ -4680,28 +4741,47 @@ impl<V: View> WeakViewHandle<V> {
         cx.read(|cx| {
             let handle = cx
                 .upgrade_view_handle(self)
-                .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?;
+                .ok_or_else(|| anyhow!("view was dropped"))?;
             cx.read_window(self.window, |cx| handle.read_with(cx, read))
                 .ok_or_else(|| anyhow!("window was removed"))
         })
     }
 
-    pub fn update<T>(
+    pub fn update<T, B>(
         &self,
-        cx: &mut AsyncAppContext,
+        cx: &mut B,
         update: impl FnOnce(&mut V, &mut ViewContext<V>) -> T,
-    ) -> Result<T> {
-        cx.update(|cx| {
-            let handle = cx
-                .upgrade_view_handle(self)
-                .ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?;
-            cx.update_window(self.window, |cx| handle.update(cx, update))
-                .ok_or_else(|| anyhow!("window was removed"))
+    ) -> Result<T>
+    where
+        B: BorrowWindowContext,
+        B::Result<Option<T>>: Flatten<T>,
+    {
+        cx.update_window(self.window(), |cx| {
+            cx.upgrade_view_handle(self)
+                .map(|handle| handle.update(cx, update))
         })
+        .flatten()
+        .ok_or_else(|| anyhow!("window was removed"))
+    }
+}
+
+pub trait Flatten<T> {
+    fn flatten(self) -> Option<T>;
+}
+
+impl<T> Flatten<T> for Option<Option<T>> {
+    fn flatten(self) -> Option<T> {
+        self.flatten()
+    }
+}
+
+impl<T> Flatten<T> for Option<T> {
+    fn flatten(self) -> Option<T> {
+        self
     }
 }
 
-impl<T> Deref for WeakViewHandle<T> {
+impl<V> Deref for WeakViewHandle<V> {
     type Target = AnyWeakViewHandle;
 
     fn deref(&self) -> &Self::Target {
@@ -4709,7 +4789,7 @@ impl<T> Deref for WeakViewHandle<T> {
     }
 }
 
-impl<T> Clone for WeakViewHandle<T> {
+impl<V> Clone for WeakViewHandle<V> {
     fn clone(&self) -> Self {
         Self {
             any_handle: self.any_handle.clone(),
@@ -5263,6 +5343,7 @@ mod tests {
                     button: MouseButton::Left,
                     modifiers: Default::default(),
                     click_count: 1,
+                    is_down: true,
                 }),
                 false,
             );

crates/gpui/src/app/window.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     elements::AnyRootElement,
-    geometry::rect::RectF,
+    geometry::{rect::RectF, Size},
     json::ToJson,
     keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
     platform::{
@@ -8,8 +8,9 @@ use crate::{
         MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
     },
     scene::{
-        CursorRegion, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag, MouseEvent,
-        MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene,
+        CursorRegion, EventHandler, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
+        MouseEvent, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
+        Scene,
     },
     text_layout::TextLayoutCache,
     util::post_inc,
@@ -31,7 +32,11 @@ use sqlez::{
 use std::{
     any::TypeId,
     mem,
-    ops::{Deref, DerefMut, Range},
+    ops::{Deref, DerefMut, Range, Sub},
+};
+use taffy::{
+    tree::{Measurable, MeasureFunc},
+    Taffy,
 };
 use util::ResultExt;
 use uuid::Uuid;
@@ -39,6 +44,7 @@ use uuid::Uuid;
 use super::{Reference, ViewMetadata};
 
 pub struct Window {
+    layout_engines: Vec<LayoutEngine>,
     pub(crate) root_view: Option<AnyViewHandle>,
     pub(crate) focused_view_id: Option<usize>,
     pub(crate) parents: HashMap<usize, usize>,
@@ -51,6 +57,7 @@ pub struct Window {
     appearance: Appearance,
     cursor_regions: Vec<CursorRegion>,
     mouse_regions: Vec<(MouseRegion, usize)>,
+    event_handlers: Vec<EventHandler>,
     last_mouse_moved_event: Option<Event>,
     pub(crate) hovered_region_ids: Vec<MouseRegionId>,
     pub(crate) clicked_region_ids: Vec<MouseRegionId>,
@@ -67,12 +74,13 @@ impl Window {
         build_view: F,
     ) -> Self
     where
-        F: FnOnce(&mut ViewContext<V>) -> V,
         V: View,
+        F: FnOnce(&mut ViewContext<V>) -> V,
     {
         let titlebar_height = platform_window.titlebar_height();
         let appearance = platform_window.appearance();
         let mut window = Self {
+            layout_engines: Vec::new(),
             root_view: None,
             focused_view_id: None,
             parents: Default::default(),
@@ -83,6 +91,7 @@ impl Window {
             rendered_views: Default::default(),
             cursor_regions: Default::default(),
             mouse_regions: Default::default(),
+            event_handlers: Default::default(),
             text_layout_cache: TextLayoutCache::new(cx.font_system.clone()),
             last_mouse_moved_event: None,
             hovered_region_ids: Default::default(),
@@ -109,6 +118,10 @@ impl Window {
             .as_ref()
             .expect("root_view called during window construction")
     }
+
+    pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
+        mem::take(&mut self.event_handlers)
+    }
 }
 
 pub struct WindowContext<'a> {
@@ -207,6 +220,24 @@ impl<'a> WindowContext<'a> {
         }
     }
 
+    pub fn repaint(&mut self) {
+        let window = self.window();
+        self.pending_effects
+            .push_back(Effect::RepaintWindow { window });
+    }
+
+    pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
+        self.window.layout_engines.last_mut()
+    }
+
+    pub fn push_layout_engine(&mut self, engine: LayoutEngine) {
+        self.window.layout_engines.push(engine);
+    }
+
+    pub fn pop_layout_engine(&mut self) -> Option<LayoutEngine> {
+        self.window.layout_engines.pop()
+    }
+
     pub fn remove_window(&mut self) {
         self.removed = true;
     }
@@ -227,6 +258,10 @@ impl<'a> WindowContext<'a> {
         self.window.platform_window.content_size()
     }
 
+    pub fn mouse_position(&self) -> Vector2F {
+        self.window.mouse_position
+    }
+
     pub fn text_layout_cache(&self) -> &TextLayoutCache {
         &self.window.text_layout_cache
     }
@@ -242,14 +277,11 @@ impl<'a> WindowContext<'a> {
         Some(result)
     }
 
-    pub(crate) fn update_view<T, S>(
+    pub(crate) fn update_view<V: 'static, S>(
         &mut self,
-        handle: &ViewHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-    ) -> S
-    where
-        T: View,
-    {
+        handle: &ViewHandle<V>,
+        update: &mut dyn FnMut(&mut V, &mut ViewContext<V>) -> S,
+    ) -> S {
         self.update_any_view(handle.view_id, |view, cx| {
             let mut cx = ViewContext::mutable(cx, handle.view_id);
             update(
@@ -475,6 +507,8 @@ impl<'a> WindowContext<'a> {
     }
 
     pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
+        self.dispatch_to_new_event_handlers(&event);
+
         let mut mouse_events = SmallVec::<[_; 2]>::new();
         let mut notified_views: HashSet<usize> = Default::default();
         let handle = self.window_handle;
@@ -852,6 +886,18 @@ impl<'a> WindowContext<'a> {
         any_event_handled
     }
 
+    fn dispatch_to_new_event_handlers(&mut self, event: &Event) {
+        if let Some(mouse_event) = event.mouse_event() {
+            let event_handlers = self.window.take_event_handlers();
+            for event_handler in event_handlers.iter().rev() {
+                if event_handler.event_type == mouse_event.type_id() {
+                    (event_handler.handler)(mouse_event, self);
+                }
+            }
+            self.window.event_handlers = event_handlers;
+        }
+    }
+
     pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
         let handle = self.window_handle;
         if let Some(focused_view_id) = self.window.focused_view_id {
@@ -942,14 +988,16 @@ impl<'a> WindowContext<'a> {
         Ok(element)
     }
 
-    pub(crate) fn layout(&mut self, refreshing: bool) -> Result<HashMap<usize, usize>> {
+    pub fn layout(&mut self, refreshing: bool) -> Result<HashMap<usize, usize>> {
         let window_size = self.window.platform_window.content_size();
         let root_view_id = self.window.root_view().id();
+
         let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
+
         let mut new_parents = HashMap::default();
         let mut views_to_notify_if_ancestors_change = HashMap::default();
         rendered_root.layout(
-            SizeConstraint::strict(window_size),
+            SizeConstraint::new(window_size, window_size),
             &mut new_parents,
             &mut views_to_notify_if_ancestors_change,
             refreshing,
@@ -982,7 +1030,7 @@ impl<'a> WindowContext<'a> {
         Ok(old_parents)
     }
 
-    pub(crate) fn paint(&mut self) -> Result<Scene> {
+    pub fn paint(&mut self) -> Result<Scene> {
         let window_size = self.window.platform_window.content_size();
         let scale_factor = self.window.platform_window.scale_factor();
 
@@ -1001,9 +1049,10 @@ impl<'a> WindowContext<'a> {
             .insert(root_view_id, rendered_root);
 
         self.window.text_layout_cache.finish_frame();
-        let scene = scene_builder.build();
+        let mut scene = scene_builder.build();
         self.window.cursor_regions = scene.cursor_regions();
         self.window.mouse_regions = scene.mouse_regions();
+        self.window.event_handlers = scene.take_event_handlers();
 
         if self.window_is_active() {
             if let Some(event) = self.window.last_mouse_moved_event.clone() {
@@ -1014,6 +1063,11 @@ impl<'a> WindowContext<'a> {
         Ok(scene)
     }
 
+    pub fn root_element(&self) -> &Box<dyn AnyRootElement> {
+        let view_id = self.window.root_view().id();
+        self.window.rendered_views.get(&view_id).unwrap()
+    }
+
     pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
         let focused_view_id = self.window.focused_view_id?;
         self.window
@@ -1216,6 +1270,119 @@ impl<'a> WindowContext<'a> {
     }
 }
 
+#[derive(Default)]
+pub struct LayoutEngine(Taffy);
+pub use taffy::style::Style as LayoutStyle;
+
+impl LayoutEngine {
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    pub fn add_node<C>(&mut self, style: LayoutStyle, children: C) -> Result<LayoutId>
+    where
+        C: IntoIterator<Item = LayoutId>,
+    {
+        Ok(self
+            .0
+            .new_with_children(style, &children.into_iter().collect::<Vec<_>>())?)
+    }
+
+    pub fn add_measured_node<F>(&mut self, style: LayoutStyle, measure: F) -> Result<LayoutId>
+    where
+        F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
+    {
+        Ok(self
+            .0
+            .new_leaf_with_measure(style, MeasureFunc::Boxed(Box::new(MeasureFn(measure))))?)
+    }
+
+    pub fn compute_layout(&mut self, root: LayoutId, available_space: Vector2F) -> Result<()> {
+        self.0.compute_layout(
+            root,
+            taffy::geometry::Size {
+                width: available_space.x().into(),
+                height: available_space.y().into(),
+            },
+        )?;
+        Ok(())
+    }
+
+    pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
+        Ok(self.0.layout(node)?.into())
+    }
+}
+
+pub struct MeasureFn<F>(F);
+
+impl<F: Send + Sync> Measurable for MeasureFn<F>
+where
+    F: Fn(MeasureParams) -> Size<f32>,
+{
+    fn measure(
+        &self,
+        known_dimensions: taffy::prelude::Size<Option<f32>>,
+        available_space: taffy::prelude::Size<taffy::style::AvailableSpace>,
+    ) -> taffy::prelude::Size<f32> {
+        (self.0)(MeasureParams {
+            known_dimensions: known_dimensions.into(),
+            available_space: available_space.into(),
+        })
+        .into()
+    }
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct EngineLayout {
+    pub bounds: RectF,
+    pub order: u32,
+}
+
+pub struct MeasureParams {
+    pub known_dimensions: Size<Option<f32>>,
+    pub available_space: Size<AvailableSpace>,
+}
+
+#[derive(Clone)]
+pub enum AvailableSpace {
+    /// The amount of space available is the specified number of pixels
+    Pixels(f32),
+    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
+    MinContent,
+    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
+    MaxContent,
+}
+
+impl Default for AvailableSpace {
+    fn default() -> Self {
+        Self::Pixels(0.)
+    }
+}
+
+impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
+    fn from(value: taffy::prelude::AvailableSpace) -> Self {
+        match value {
+            taffy::prelude::AvailableSpace::Definite(pixels) => Self::Pixels(pixels),
+            taffy::prelude::AvailableSpace::MinContent => Self::MinContent,
+            taffy::prelude::AvailableSpace::MaxContent => Self::MaxContent,
+        }
+    }
+}
+
+impl From<&taffy::tree::Layout> for EngineLayout {
+    fn from(value: &taffy::tree::Layout) -> Self {
+        Self {
+            bounds: RectF::new(
+                vec2f(value.location.x, value.location.y),
+                vec2f(value.size.width, value.size.height),
+            ),
+            order: value.order,
+        }
+    }
+}
+
+pub type LayoutId = taffy::prelude::NodeId;
+
 pub struct RenderParams {
     pub view_id: usize,
     pub titlebar_height: f32,
@@ -1324,6 +1491,12 @@ impl SizeConstraint {
             max: size,
         }
     }
+    pub fn loose(max: Vector2F) -> Self {
+        Self {
+            min: Vector2F::zero(),
+            max,
+        }
+    }
 
     pub fn strict_along(axis: Axis, max: f32) -> Self {
         match axis {
@@ -1360,6 +1533,17 @@ impl SizeConstraint {
     }
 }
 
+impl Sub<Vector2F> for SizeConstraint {
+    type Output = SizeConstraint;
+
+    fn sub(self, rhs: Vector2F) -> SizeConstraint {
+        SizeConstraint {
+            min: self.min - rhs,
+            max: self.max - rhs,
+        }
+    }
+}
+
 impl Default for SizeConstraint {
     fn default() -> Self {
         SizeConstraint {
@@ -1378,6 +1562,7 @@ impl ToJson for SizeConstraint {
     }
 }
 
+#[derive(Clone)]
 pub struct ChildView {
     view_id: usize,
     view_name: &'static str,
@@ -1393,7 +1578,7 @@ impl ChildView {
     }
 }
 
-impl<V: View> Element<V> for ChildView {
+impl<V: 'static> Element<V> for ChildView {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/color.rs 🔗

@@ -15,35 +15,75 @@ use serde_json::json;
 
 #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
 #[repr(transparent)]
-pub struct Color(#[schemars(with = "String")] ColorU);
+pub struct Color(#[schemars(with = "String")] pub ColorU);
+
+pub fn color(rgba: u32) -> Color {
+    Color::from_u32(rgba)
+}
+
+pub fn rgb(r: f32, g: f32, b: f32) -> Color {
+    Color(ColorF::new(r, g, b, 1.).to_u8())
+}
+
+pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
+    Color(ColorF::new(r, g, b, a).to_u8())
+}
+
+pub fn transparent_black() -> Color {
+    Color(ColorU::transparent_black())
+}
+
+pub fn black() -> Color {
+    Color(ColorU::black())
+}
+
+pub fn white() -> Color {
+    Color(ColorU::white())
+}
+
+pub fn red() -> Color {
+    color(0xff0000ff)
+}
+
+pub fn green() -> Color {
+    color(0x00ff00ff)
+}
+
+pub fn blue() -> Color {
+    color(0x0000ffff)
+}
+
+pub fn yellow() -> Color {
+    color(0xffff00ff)
+}
 
 impl Color {
     pub fn transparent_black() -> Self {
-        Self(ColorU::transparent_black())
+        transparent_black()
     }
 
     pub fn black() -> Self {
-        Self(ColorU::black())
+        black()
     }
 
     pub fn white() -> Self {
-        Self(ColorU::white())
+        white()
     }
 
     pub fn red() -> Self {
-        Self(ColorU::from_u32(0xff0000ff))
+        Color::from_u32(0xff0000ff)
     }
 
     pub fn green() -> Self {
-        Self(ColorU::from_u32(0x00ff00ff))
+        Color::from_u32(0x00ff00ff)
     }
 
     pub fn blue() -> Self {
-        Self(ColorU::from_u32(0x0000ffff))
+        Color::from_u32(0x0000ffff)
     }
 
     pub fn yellow() -> Self {
-        Self(ColorU::from_u32(0xffff00ff))
+        Color::from_u32(0xffff00ff)
     }
 
     pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
@@ -101,6 +141,12 @@ impl<'de> Deserialize<'de> for Color {
     }
 }
 
+impl From<u32> for Color {
+    fn from(value: u32) -> Self {
+        Self(ColorU::from_u32(value))
+    }
+}
+
 impl ToJson for Color {
     fn to_json(&self) -> serde_json::Value {
         json!(format!(

crates/gpui/src/elements.rs 🔗

@@ -34,7 +34,7 @@ use crate::{
         rect::RectF,
         vector::{vec2f, Vector2F},
     },
-    json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
+    json, Action, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
     ViewContext, WeakViewHandle, WindowContext,
 };
 use anyhow::{anyhow, Result};
@@ -42,14 +42,19 @@ use collections::HashMap;
 use core::panic;
 use json::ToJson;
 use smallvec::SmallVec;
-use std::{any::Any, borrow::Cow, mem, ops::Range};
+use std::{
+    any::{type_name, Any},
+    borrow::Cow,
+    mem,
+    ops::Range,
+};
 
-pub trait Element<V: View>: 'static {
+pub trait Element<V: 'static>: 'static {
     type LayoutState;
     type PaintState;
 
     fn view_name(&self) -> &'static str {
-        V::ui_name()
+        type_name::<V>()
     }
 
     fn layout(
@@ -231,11 +236,7 @@ pub trait Element<V: View>: 'static {
     }
 }
 
-pub trait RenderElement {
-    fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
-}
-
-trait AnyElementState<V: View> {
+trait AnyElementState<V> {
     fn layout(
         &mut self,
         constraint: SizeConstraint,
@@ -249,7 +250,7 @@ trait AnyElementState<V: View> {
         origin: Vector2F,
         visible_bounds: RectF,
         view: &mut V,
-        cx: &mut ViewContext<V>,
+        cx: &mut PaintContext<V>,
     );
 
     fn rect_for_text_range(
@@ -266,7 +267,7 @@ trait AnyElementState<V: View> {
     fn metadata(&self) -> Option<&dyn Any>;
 }
 
-enum ElementState<V: View, E: Element<V>> {
+enum ElementState<V: 'static, E: Element<V>> {
     Empty,
     Init {
         element: E,
@@ -287,7 +288,7 @@ enum ElementState<V: View, E: Element<V>> {
     },
 }
 
-impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
+impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
     fn layout(
         &mut self,
         constraint: SizeConstraint,
@@ -330,7 +331,7 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
         origin: Vector2F,
         visible_bounds: RectF,
         view: &mut V,
-        cx: &mut ViewContext<V>,
+        cx: &mut PaintContext<V>,
     ) {
         *self = match mem::take(self) {
             ElementState::PostLayout {
@@ -469,18 +470,18 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
     }
 }
 
-impl<V: View, E: Element<V>> Default for ElementState<V, E> {
+impl<V, E: Element<V>> Default for ElementState<V, E> {
     fn default() -> Self {
         Self::Empty
     }
 }
 
-pub struct AnyElement<V: View> {
+pub struct AnyElement<V> {
     state: Box<dyn AnyElementState<V>>,
     name: Option<Cow<'static, str>>,
 }
 
-impl<V: View> AnyElement<V> {
+impl<V> AnyElement<V> {
     pub fn name(&self) -> Option<&str> {
         self.name.as_deref()
     }
@@ -506,7 +507,7 @@ impl<V: View> AnyElement<V> {
         origin: Vector2F,
         visible_bounds: RectF,
         view: &mut V,
-        cx: &mut ViewContext<V>,
+        cx: &mut PaintContext<V>,
     ) {
         self.state.paint(scene, origin, visible_bounds, view, cx);
     }
@@ -548,7 +549,7 @@ impl<V: View> AnyElement<V> {
     }
 }
 
-impl<V: View> Element<V> for AnyElement<V> {
+impl<V: 'static> Element<V> for AnyElement<V> {
     type LayoutState = ();
     type PaintState = ();
 
@@ -606,12 +607,18 @@ impl<V: View> Element<V> for AnyElement<V> {
     }
 }
 
-pub struct RootElement<V: View> {
+impl Entity for AnyElement<()> {
+    type Event = ();
+}
+
+// impl View for AnyElement<()> {}
+
+pub struct RootElement<V> {
     element: AnyElement<V>,
     view: WeakViewHandle<V>,
 }
 
-impl<V: View> RootElement<V> {
+impl<V> RootElement<V> {
     pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
         Self { element, view }
     }
@@ -679,7 +686,9 @@ impl<V: View> AnyRootElement for RootElement<V> {
             .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
 
         view.update(cx, |view, cx| {
-            self.element.paint(scene, origin, visible_bounds, view, cx);
+            let mut cx = PaintContext::new(cx);
+            self.element
+                .paint(scene, origin, visible_bounds, view, &mut cx);
             Ok(())
         })
     }
@@ -719,7 +728,7 @@ impl<V: View> AnyRootElement for RootElement<V> {
     }
 }
 
-pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
+pub trait ParentElement<'a, V: 'static>: Extend<AnyElement<V>> + Sized {
     fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
         self.extend(children.into_iter().map(|child| child.into_any()));
     }
@@ -739,7 +748,12 @@ pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
     }
 }
 
-impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
+impl<'a, V, T> ParentElement<'a, V> for T
+where
+    V: 'static,
+    T: Extend<AnyElement<V>>,
+{
+}
 
 pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
     if max_size.x().is_infinite() && max_size.y().is_infinite() {

crates/gpui/src/elements/align.rs 🔗

@@ -1,18 +1,18 @@
 use crate::{
     geometry::{rect::RectF, vector::Vector2F},
-    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
     ViewContext,
 };
 use json::ToJson;
 
 use serde_json::json;
 
-pub struct Align<V: View> {
+pub struct Align<V> {
     child: AnyElement<V>,
     alignment: Vector2F,
 }
 
-impl<V: View> Align<V> {
+impl<V> Align<V> {
     pub fn new(child: AnyElement<V>) -> Self {
         Self {
             child,
@@ -41,7 +41,7 @@ impl<V: View> Align<V> {
     }
 }
 
-impl<V: View> Element<V> for Align<V> {
+impl<V: 'static> Element<V> for Align<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/canvas.rs 🔗

@@ -3,7 +3,7 @@ use std::marker::PhantomData;
 use super::Element;
 use crate::{
     json::{self, json},
-    PaintContext, SceneBuilder, View, ViewContext,
+    PaintContext, SceneBuilder, ViewContext,
 };
 use json::ToJson;
 use pathfinder_geometry::{
@@ -15,7 +15,6 @@ pub struct Canvas<V, F>(F, PhantomData<V>);
 
 impl<V, F> Canvas<V, F>
 where
-    V: View,
     F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
 {
     pub fn new(f: F) -> Self {
@@ -23,7 +22,7 @@ where
     }
 }
 
-impl<V: View, F> Element<V> for Canvas<V, F>
+impl<V: 'static, F> Element<V> for Canvas<V, F>
 where
     F: 'static + FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
 {

crates/gpui/src/elements/clipped.rs 🔗

@@ -4,21 +4,21 @@ use pathfinder_geometry::{rect::RectF, vector::Vector2F};
 use serde_json::json;
 
 use crate::{
-    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
     ViewContext,
 };
 
-pub struct Clipped<V: View> {
+pub struct Clipped<V> {
     child: AnyElement<V>,
 }
 
-impl<V: View> Clipped<V> {
+impl<V> Clipped<V> {
     pub fn new(child: AnyElement<V>) -> Self {
         Self { child }
     }
 }
 
-impl<V: View> Element<V> for Clipped<V> {
+impl<V: 'static> Element<V> for Clipped<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/constrained_box.rs 🔗

@@ -5,21 +5,21 @@ use serde_json::json;
 
 use crate::{
     geometry::{rect::RectF, vector::Vector2F},
-    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
     ViewContext,
 };
 
-pub struct ConstrainedBox<V: View> {
+pub struct ConstrainedBox<V> {
     child: AnyElement<V>,
     constraint: Constraint<V>,
 }
 
-pub enum Constraint<V: View> {
+pub enum Constraint<V> {
     Static(SizeConstraint),
     Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint>),
 }
 
-impl<V: View> ToJson for Constraint<V> {
+impl<V> ToJson for Constraint<V> {
     fn to_json(&self) -> serde_json::Value {
         match self {
             Constraint::Static(constraint) => constraint.to_json(),
@@ -28,7 +28,7 @@ impl<V: View> ToJson for Constraint<V> {
     }
 }
 
-impl<V: View> ConstrainedBox<V> {
+impl<V: 'static> ConstrainedBox<V> {
     pub fn new(child: impl Element<V>) -> Self {
         Self {
             child: child.into_any(),
@@ -132,7 +132,7 @@ impl<V: View> ConstrainedBox<V> {
     }
 }
 
-impl<V: View> Element<V> for ConstrainedBox<V> {
+impl<V: 'static> Element<V> for ConstrainedBox<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/container.rs 🔗

@@ -10,8 +10,7 @@ use crate::{
     json::ToJson,
     platform::CursorStyle,
     scene::{self, Border, CornerRadii, CursorRegion, Quad},
-    AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
-    ViewContext,
+    AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
 };
 use schemars::JsonSchema;
 use serde::Deserialize;
@@ -47,12 +46,12 @@ impl ContainerStyle {
     }
 }
 
-pub struct Container<V: View> {
+pub struct Container<V> {
     child: AnyElement<V>,
     style: ContainerStyle,
 }
 
-impl<V: View> Container<V> {
+impl<V> Container<V> {
     pub fn new(child: AnyElement<V>) -> Self {
         Self {
             child,
@@ -199,7 +198,7 @@ impl<V: View> Container<V> {
     }
 }
 
-impl<V: View> Element<V> for Container<V> {
+impl<V: 'static> Element<V> for Container<V> {
     type LayoutState = ();
     type PaintState = ();
 
@@ -350,8 +349,8 @@ impl ToJson for ContainerStyle {
 #[derive(Clone, Copy, Debug, Default, JsonSchema)]
 pub struct Margin {
     pub top: f32,
-    pub left: f32,
     pub bottom: f32,
+    pub left: f32,
     pub right: f32,
 }
 

crates/gpui/src/elements/empty.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
         vector::{vec2f, Vector2F},
     },
     json::{json, ToJson},
-    LayoutContext, PaintContext, SceneBuilder, View, ViewContext,
+    LayoutContext, PaintContext, SceneBuilder, ViewContext,
 };
 use crate::{Element, SizeConstraint};
 
@@ -26,7 +26,7 @@ impl Empty {
     }
 }
 
-impl<V: View> Element<V> for Empty {
+impl<V: 'static> Element<V> for Empty {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/expanded.rs 🔗

@@ -2,18 +2,18 @@ use std::ops::Range;
 
 use crate::{
     geometry::{rect::RectF, vector::Vector2F},
-    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
+    json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
     ViewContext,
 };
 use serde_json::json;
 
-pub struct Expanded<V: View> {
+pub struct Expanded<V> {
     child: AnyElement<V>,
     full_width: bool,
     full_height: bool,
 }
 
-impl<V: View> Expanded<V> {
+impl<V: 'static> Expanded<V> {
     pub fn new(child: impl Element<V>) -> Self {
         Self {
             child: child.into_any(),
@@ -35,7 +35,7 @@ impl<V: View> Expanded<V> {
     }
 }
 
-impl<V: View> Element<V> for Expanded<V> {
+impl<V: 'static> Element<V> for Expanded<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/flex.rs 🔗

@@ -3,7 +3,7 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
 use crate::{
     json::{self, ToJson, Value},
     AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder,
-    SizeConstraint, Vector2FExt, View, ViewContext,
+    SizeConstraint, Vector2FExt, ViewContext,
 };
 use pathfinder_geometry::{
     rect::RectF,
@@ -17,14 +17,14 @@ struct ScrollState {
     scroll_position: Cell<f32>,
 }
 
-pub struct Flex<V: View> {
+pub struct Flex<V> {
     axis: Axis,
     children: Vec<AnyElement<V>>,
     scroll_state: Option<(ElementStateHandle<Rc<ScrollState>>, usize)>,
     child_alignment: f32,
 }
 
-impl<V: View> Flex<V> {
+impl<V: 'static> Flex<V> {
     pub fn new(axis: Axis) -> Self {
         Self {
             axis,
@@ -115,13 +115,13 @@ impl<V: View> Flex<V> {
     }
 }
 
-impl<V: View> Extend<AnyElement<V>> for Flex<V> {
+impl<V> Extend<AnyElement<V>> for Flex<V> {
     fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
         self.children.extend(children);
     }
 }
 
-impl<V: View> Element<V> for Flex<V> {
+impl<V: 'static> Element<V> for Flex<V> {
     type LayoutState = f32;
     type PaintState = ();
 
@@ -401,12 +401,12 @@ struct FlexParentData {
     float: bool,
 }
 
-pub struct FlexItem<V: View> {
+pub struct FlexItem<V> {
     metadata: FlexParentData,
     child: AnyElement<V>,
 }
 
-impl<V: View> FlexItem<V> {
+impl<V: 'static> FlexItem<V> {
     pub fn new(child: impl Element<V>) -> Self {
         FlexItem {
             metadata: FlexParentData {
@@ -428,7 +428,7 @@ impl<V: View> FlexItem<V> {
     }
 }
 
-impl<V: View> Element<V> for FlexItem<V> {
+impl<V: 'static> Element<V> for FlexItem<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/hook.rs 🔗

@@ -3,16 +3,15 @@ use std::ops::Range;
 use crate::{
     geometry::{rect::RectF, vector::Vector2F},
     json::json,
-    AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
-    ViewContext,
+    AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
 };
 
-pub struct Hook<V: View> {
+pub struct Hook<V> {
     child: AnyElement<V>,
     after_layout: Option<Box<dyn FnMut(Vector2F, &mut ViewContext<V>)>>,
 }
 
-impl<V: View> Hook<V> {
+impl<V: 'static> Hook<V> {
     pub fn new(child: impl Element<V>) -> Self {
         Self {
             child: child.into_any(),
@@ -29,7 +28,7 @@ impl<V: View> Hook<V> {
     }
 }
 
-impl<V: View> Element<V> for Hook<V> {
+impl<V: 'static> Element<V> for Hook<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/image.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
     },
     json::{json, ToJson},
     scene, Border, Element, ImageData, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
-    View, ViewContext,
+    ViewContext,
 };
 use schemars::JsonSchema;
 use serde::Deserialize;
@@ -57,7 +57,7 @@ impl Image {
     }
 }
 
-impl<V: View> Element<V> for Image {
+impl<V: 'static> Element<V> for Image {
     type LayoutState = Option<Arc<ImageData>>;
     type PaintState = ();
 

crates/gpui/src/elements/keystroke_label.rs 🔗

@@ -31,7 +31,7 @@ impl KeystrokeLabel {
     }
 }
 
-impl<V: View> Element<V> for KeystrokeLabel {
+impl<V: 'static> Element<V> for KeystrokeLabel {
     type LayoutState = AnyElement<V>;
     type PaintState = ();
 

crates/gpui/src/elements/label.rs 🔗

@@ -8,7 +8,7 @@ use crate::{
     },
     json::{ToJson, Value},
     text_layout::{Line, RunStyle},
-    Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
+    Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
 };
 use schemars::JsonSchema;
 use serde::Deserialize;
@@ -128,7 +128,7 @@ impl Label {
     }
 }
 
-impl<V: View> Element<V> for Label {
+impl<V: 'static> Element<V> for Label {
     type LayoutState = Line;
     type PaintState = ();
 

crates/gpui/src/elements/list.rs 🔗

@@ -5,16 +5,16 @@ use crate::{
     },
     json::json,
     AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, SizeConstraint,
-    View, ViewContext,
+    ViewContext,
 };
 use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
 use sum_tree::{Bias, SumTree};
 
-pub struct List<V: View> {
+pub struct List<V> {
     state: ListState<V>,
 }
 
-pub struct ListState<V: View>(Rc<RefCell<StateInner<V>>>);
+pub struct ListState<V>(Rc<RefCell<StateInner<V>>>);
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum Orientation {
@@ -22,7 +22,7 @@ pub enum Orientation {
     Bottom,
 }
 
-struct StateInner<V: View> {
+struct StateInner<V> {
     last_layout_width: Option<f32>,
     render_item: Box<dyn FnMut(&mut V, usize, &mut ViewContext<V>) -> AnyElement<V>>,
     rendered_range: Range<usize>,
@@ -40,13 +40,13 @@ pub struct ListOffset {
     pub offset_in_item: f32,
 }
 
-enum ListItem<V: View> {
+enum ListItem<V> {
     Unrendered,
     Rendered(Rc<RefCell<AnyElement<V>>>),
     Removed(f32),
 }
 
-impl<V: View> Clone for ListItem<V> {
+impl<V> Clone for ListItem<V> {
     fn clone(&self) -> Self {
         match self {
             Self::Unrendered => Self::Unrendered,
@@ -56,7 +56,7 @@ impl<V: View> Clone for ListItem<V> {
     }
 }
 
-impl<V: View> Debug for ListItem<V> {
+impl<V> Debug for ListItem<V> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
             Self::Unrendered => write!(f, "Unrendered"),
@@ -86,13 +86,13 @@ struct UnrenderedCount(usize);
 #[derive(Clone, Debug, Default)]
 struct Height(f32);
 
-impl<V: View> List<V> {
+impl<V> List<V> {
     pub fn new(state: ListState<V>) -> Self {
         Self { state }
     }
 }
 
-impl<V: View> Element<V> for List<V> {
+impl<V: 'static> Element<V> for List<V> {
     type LayoutState = ListOffset;
     type PaintState = ();
 
@@ -347,7 +347,7 @@ impl<V: View> Element<V> for List<V> {
     }
 }
 
-impl<V: View> ListState<V> {
+impl<V: 'static> ListState<V> {
     pub fn new<D, F>(
         element_count: usize,
         orientation: Orientation,
@@ -440,13 +440,13 @@ impl<V: View> ListState<V> {
     }
 }
 
-impl<V: View> Clone for ListState<V> {
+impl<V> Clone for ListState<V> {
     fn clone(&self) -> Self {
         Self(self.0.clone())
     }
 }
 
-impl<V: View> StateInner<V> {
+impl<V: 'static> StateInner<V> {
     fn render_item(
         &mut self,
         ix: usize,
@@ -560,7 +560,7 @@ impl<V: View> StateInner<V> {
     }
 }
 
-impl<V: View> ListItem<V> {
+impl<V> ListItem<V> {
     fn remove(&self) -> Self {
         match self {
             ListItem::Unrendered => ListItem::Unrendered,
@@ -570,7 +570,7 @@ impl<V: View> ListItem<V> {
     }
 }
 
-impl<V: View> sum_tree::Item for ListItem<V> {
+impl<V> sum_tree::Item for ListItem<V> {
     type Summary = ListItemSummary;
 
     fn summary(&self) -> Self::Summary {
@@ -944,7 +944,7 @@ mod tests {
         type Event = ();
     }
 
-    impl View for TestView {
+    impl crate::View for TestView {
         fn ui_name() -> &'static str {
             "TestView"
         }
@@ -968,7 +968,7 @@ mod tests {
         }
     }
 
-    impl<V: View> Element<V> for TestElement {
+    impl<V: 'static> Element<V> for TestElement {
         type LayoutState = ();
         type PaintState = ();
 

crates/gpui/src/elements/mouse_event_handler.rs 🔗

@@ -11,12 +11,12 @@ use crate::{
         MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
     },
     AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
-    SceneBuilder, SizeConstraint, TypeTag, View, ViewContext,
+    SceneBuilder, SizeConstraint, TypeTag, ViewContext,
 };
 use serde_json::json;
 use std::ops::Range;
 
-pub struct MouseEventHandler<V: View> {
+pub struct MouseEventHandler<V: 'static> {
     child: AnyElement<V>,
     region_id: usize,
     cursor_style: Option<CursorStyle>,
@@ -31,7 +31,7 @@ pub struct MouseEventHandler<V: View> {
 
 /// Element which provides a render_child callback with a MouseState and paints a mouse
 /// region under (or above) it for easy mouse event handling.
-impl<V: View> MouseEventHandler<V> {
+impl<V: 'static> MouseEventHandler<V> {
     pub fn for_child<Tag: 'static>(child: impl Element<V>, region_id: usize) -> Self {
         Self {
             child: child.into_any(),
@@ -267,7 +267,7 @@ impl<V: View> MouseEventHandler<V> {
     }
 }
 
-impl<V: View> Element<V> for MouseEventHandler<V> {
+impl<V: 'static> Element<V> for MouseEventHandler<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/overlay.rs 🔗

@@ -4,11 +4,11 @@ use crate::{
     geometry::{rect::RectF, vector::Vector2F},
     json::ToJson,
     AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
-    SizeConstraint, View, ViewContext,
+    SizeConstraint, ViewContext,
 };
 use serde_json::json;
 
-pub struct Overlay<V: View> {
+pub struct Overlay<V> {
     child: AnyElement<V>,
     anchor_position: Option<Vector2F>,
     anchor_corner: AnchorCorner,
@@ -73,7 +73,7 @@ impl AnchorCorner {
     }
 }
 
-impl<V: View> Overlay<V> {
+impl<V: 'static> Overlay<V> {
     pub fn new(child: impl Element<V>) -> Self {
         Self {
             child: child.into_any(),
@@ -117,7 +117,7 @@ impl<V: View> Overlay<V> {
     }
 }
 
-impl<V: View> Element<V> for Overlay<V> {
+impl<V: 'static> Element<V> for Overlay<V> {
     type LayoutState = Vector2F;
     type PaintState = ();
 

crates/gpui/src/elements/resizable.rs 🔗

@@ -59,7 +59,7 @@ where
         .and_then(|map| map.0.get(&tag))
 }
 
-pub struct Resizable<V: View> {
+pub struct Resizable<V: 'static> {
     child: AnyElement<V>,
     tag: TypeTag,
     handle_side: HandleSide,
@@ -69,7 +69,7 @@ pub struct Resizable<V: View> {
 
 const DEFAULT_HANDLE_SIZE: f32 = 4.0;
 
-impl<V: View> Resizable<V> {
+impl<V: 'static> Resizable<V> {
     pub fn new<Tag: 'static>(
         child: AnyElement<V>,
         handle_side: HandleSide,
@@ -97,7 +97,7 @@ impl<V: View> Resizable<V> {
     }
 }
 
-impl<V: View> Element<V> for Resizable<V> {
+impl<V: 'static> Element<V> for Resizable<V> {
     type LayoutState = SizeConstraint;
     type PaintState = ();
 
@@ -219,12 +219,12 @@ impl<V: View> Element<V> for Resizable<V> {
 #[derive(Debug, Default)]
 struct ProviderMap(HashMap<TypeTag, (RectF, RectF)>);
 
-pub struct BoundsProvider<V: View, P> {
+pub struct BoundsProvider<V: 'static, P> {
     child: AnyElement<V>,
     phantom: std::marker::PhantomData<P>,
 }
 
-impl<V: View, P: 'static> BoundsProvider<V, P> {
+impl<V: 'static, P: 'static> BoundsProvider<V, P> {
     pub fn new(child: AnyElement<V>) -> Self {
         Self {
             child,

crates/gpui/src/elements/stack.rs 🔗

@@ -3,17 +3,16 @@ use std::ops::Range;
 use crate::{
     geometry::{rect::RectF, vector::Vector2F},
     json::{self, json, ToJson},
-    AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
-    ViewContext,
+    AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
 };
 
 /// Element which renders it's children in a stack on top of each other.
 /// The first child determines the size of the others.
-pub struct Stack<V: View> {
+pub struct Stack<V> {
     children: Vec<AnyElement<V>>,
 }
 
-impl<V: View> Default for Stack<V> {
+impl<V> Default for Stack<V> {
     fn default() -> Self {
         Self {
             children: Vec::new(),
@@ -21,13 +20,13 @@ impl<V: View> Default for Stack<V> {
     }
 }
 
-impl<V: View> Stack<V> {
+impl<V> Stack<V> {
     pub fn new() -> Self {
         Self::default()
     }
 }
 
-impl<V: View> Element<V> for Stack<V> {
+impl<V: 'static> Element<V> for Stack<V> {
     type LayoutState = ();
     type PaintState = ();
 
@@ -99,7 +98,7 @@ impl<V: View> Element<V> for Stack<V> {
     }
 }
 
-impl<V: View> Extend<AnyElement<V>> for Stack<V> {
+impl<V> Extend<AnyElement<V>> for Stack<V> {
     fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
         self.children.extend(children)
     }

crates/gpui/src/elements/svg.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
         rect::RectF,
         vector::{vec2f, Vector2F},
     },
-    scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
+    scene, Element, LayoutContext, SceneBuilder, SizeConstraint, ViewContext,
 };
 use schemars::JsonSchema;
 use serde_derive::Deserialize;
@@ -27,7 +27,7 @@ impl Svg {
         }
     }
 
-    pub fn for_style<V: View>(style: SvgStyle) -> impl Element<V> {
+    pub fn for_style<V: 'static>(style: SvgStyle) -> impl Element<V> {
         Self::new(style.asset)
             .with_color(style.color)
             .constrained()
@@ -41,7 +41,7 @@ impl Svg {
     }
 }
 
-impl<V: View> Element<V> for Svg {
+impl<V: 'static> Element<V> for Svg {
     type LayoutState = Option<usvg::Tree>;
     type PaintState = ();
 

crates/gpui/src/elements/text.rs 🔗

@@ -8,7 +8,7 @@ use crate::{
     json::{ToJson, Value},
     text_layout::{Line, RunStyle, ShapedBoundary},
     AppContext, Element, FontCache, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
-    TextLayoutCache, View, ViewContext,
+    TextLayoutCache, ViewContext,
 };
 use log::warn;
 use serde_json::json;
@@ -70,7 +70,7 @@ impl Text {
     }
 }
 
-impl<V: View> Element<V> for Text {
+impl<V: 'static> Element<V> for Text {
     type LayoutState = LayoutState;
     type PaintState = ();
 
@@ -338,7 +338,7 @@ impl<V: View> Element<V> for Text {
 }
 
 /// Perform text layout on a series of highlighted chunks of text.
-fn layout_highlighted_chunks<'a>(
+pub fn layout_highlighted_chunks<'a>(
     chunks: impl Iterator<Item = (&'a str, Option<HighlightStyle>)>,
     text_style: &TextStyle,
     text_layout_cache: &TextLayoutCache,

crates/gpui/src/elements/tooltip.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
     geometry::{rect::RectF, vector::Vector2F},
     json::json,
     Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
-    Task, TypeTag, View, ViewContext,
+    Task, TypeTag, ViewContext,
 };
 use schemars::JsonSchema;
 use serde::Deserialize;
@@ -22,7 +22,7 @@ use util::ResultExt;
 
 const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(500);
 
-pub struct Tooltip<V: View> {
+pub struct Tooltip<V> {
     child: AnyElement<V>,
     tooltip: Option<AnyElement<V>>,
     _state: ElementStateHandle<Rc<TooltipState>>,
@@ -52,7 +52,7 @@ pub struct KeystrokeStyle {
     text: TextStyle,
 }
 
-impl<V: View> Tooltip<V> {
+impl<V: 'static> Tooltip<V> {
     pub fn new<Tag: 'static>(
         id: usize,
         text: impl Into<Cow<'static, str>>,
@@ -181,7 +181,7 @@ impl<V: View> Tooltip<V> {
     }
 }
 
-impl<V: View> Element<V> for Tooltip<V> {
+impl<V: 'static> Element<V> for Tooltip<V> {
     type LayoutState = ();
     type PaintState = ();
 

crates/gpui/src/elements/uniform_list.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
     },
     json::{self, json},
     platform::ScrollWheelEvent,
-    AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, View, ViewContext,
+    AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, ViewContext,
 };
 use json::ToJson;
 use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -36,13 +36,13 @@ struct StateInner {
     scroll_to: Option<ScrollTarget>,
 }
 
-pub struct UniformListLayoutState<V: View> {
+pub struct UniformListLayoutState<V> {
     scroll_max: f32,
     item_height: f32,
     items: Vec<AnyElement<V>>,
 }
 
-pub struct UniformList<V: View> {
+pub struct UniformList<V> {
     state: UniformListState,
     item_count: usize,
     #[allow(clippy::type_complexity)]
@@ -53,7 +53,7 @@ pub struct UniformList<V: View> {
     view_id: usize,
 }
 
-impl<V: View> UniformList<V> {
+impl<V: 'static> UniformList<V> {
     pub fn new<F>(
         state: UniformListState,
         item_count: usize,
@@ -61,7 +61,6 @@ impl<V: View> UniformList<V> {
         append_items: F,
     ) -> Self
     where
-        V: View,
         F: 'static + Fn(&mut V, Range<usize>, &mut Vec<AnyElement<V>>, &mut ViewContext<V>),
     {
         Self {
@@ -151,7 +150,7 @@ impl<V: View> UniformList<V> {
     }
 }
 
-impl<V: View> Element<V> for UniformList<V> {
+impl<V: 'static> Element<V> for UniformList<V> {
     type LayoutState = UniformListLayoutState<V>;
     type PaintState = ();
 

crates/gpui/src/fonts.rs 🔗

@@ -11,6 +11,7 @@ pub use font_kit::{
     properties::{Properties, Stretch, Style, Weight},
 };
 use ordered_float::OrderedFloat;
+use refineable::Refineable;
 use schemars::JsonSchema;
 use serde::{de, Deserialize, Serialize};
 use serde_json::Value;
@@ -59,7 +60,7 @@ pub struct Features {
     pub zero: Option<bool>,
 }
 
-#[derive(Clone, Debug, JsonSchema)]
+#[derive(Clone, Debug, JsonSchema, Refineable)]
 pub struct TextStyle {
     pub color: Color,
     pub font_family_name: Arc<str>,
@@ -69,6 +70,7 @@ pub struct TextStyle {
     #[schemars(with = "PropertiesDef")]
     pub font_properties: Properties,
     pub underline: Underline,
+    pub soft_wrap: bool,
 }
 
 impl TextStyle {
@@ -90,20 +92,11 @@ impl TextStyle {
             font_size: refinement.font_size.unwrap_or(self.font_size),
             font_properties: refinement.font_properties.unwrap_or(self.font_properties),
             underline: refinement.underline.unwrap_or(self.underline),
+            soft_wrap: refinement.soft_wrap.unwrap_or(self.soft_wrap),
         }
     }
 }
 
-pub struct TextStyleRefinement {
-    pub color: Option<Color>,
-    pub font_family_name: Option<Arc<str>>,
-    pub font_family_id: Option<FamilyId>,
-    pub font_id: Option<FontId>,
-    pub font_size: Option<f32>,
-    pub font_properties: Option<Properties>,
-    pub underline: Option<Underline>,
-}
-
 #[derive(JsonSchema)]
 #[serde(remote = "Properties")]
 pub struct PropertiesDef {
@@ -222,9 +215,31 @@ impl TextStyle {
             font_size,
             font_properties,
             underline,
+            soft_wrap: false,
         })
     }
 
+    pub fn default(font_cache: &FontCache) -> Self {
+        let font_family_id = font_cache.known_existing_family();
+        let font_id = font_cache
+            .select_font(font_family_id, &Default::default())
+            .expect("did not have any font in system-provided family");
+        let font_family_name = font_cache
+            .family_name(font_family_id)
+            .expect("we loaded this family from the font cache, so this should work");
+
+        Self {
+            color: Color::default(),
+            font_family_name,
+            font_family_id,
+            font_id,
+            font_size: 14.,
+            font_properties: Default::default(),
+            underline: Default::default(),
+            soft_wrap: true,
+        }
+    }
+
     pub fn with_font_size(mut self, font_size: f32) -> Self {
         self.font_size = font_size;
         self
@@ -352,24 +367,7 @@ impl Default for TextStyle {
             let font_cache = font_cache
                 .as_ref()
                 .expect("TextStyle::default can only be called within a call to with_font_cache");
-
-            let font_family_id = font_cache.known_existing_family();
-            let font_id = font_cache
-                .select_font(font_family_id, &Default::default())
-                .expect("did not have any font in system-provided family");
-            let font_family_name = font_cache
-                .family_name(font_family_id)
-                .expect("we loaded this family from the font cache, so this should work");
-
-            Self {
-                color: Default::default(),
-                font_family_name,
-                font_family_id,
-                font_id,
-                font_size: 14.,
-                font_properties: Default::default(),
-                underline: Default::default(),
-            }
+            Self::default(font_cache)
         })
     }
 }

crates/gpui/src/geometry.rs 🔗

@@ -2,6 +2,7 @@ use super::scene::{Path, PathVertex};
 use crate::{color::Color, json::ToJson};
 pub use pathfinder_geometry::*;
 use rect::RectF;
+use refineable::Refineable;
 use serde::{Deserialize, Deserializer};
 use serde_json::json;
 use vector::{vec2f, Vector2F};
@@ -131,3 +132,258 @@ impl ToJson for RectF {
         json!({"origin": self.origin().to_json(), "size": self.size().to_json()})
     }
 }
+
+#[derive(Refineable)]
+pub struct Point<T: Clone + Default> {
+    pub x: T,
+    pub y: T,
+}
+
+impl<T: Clone + Default> Clone for Point<T> {
+    fn clone(&self) -> Self {
+        Self {
+            x: self.x.clone(),
+            y: self.y.clone(),
+        }
+    }
+}
+
+impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
+    fn into(self) -> taffy::geometry::Point<T> {
+        taffy::geometry::Point {
+            x: self.x,
+            y: self.y,
+        }
+    }
+}
+
+#[derive(Clone, Refineable)]
+pub struct Size<T: Clone + Default> {
+    pub width: T,
+    pub height: T,
+}
+
+impl<S, T: Clone + Default> From<taffy::geometry::Size<S>> for Size<T>
+where
+    S: Into<T>,
+{
+    fn from(value: taffy::geometry::Size<S>) -> Self {
+        Self {
+            width: value.width.into(),
+            height: value.height.into(),
+        }
+    }
+}
+
+impl<S, T: Clone + Default> Into<taffy::geometry::Size<S>> for Size<T>
+where
+    T: Into<S>,
+{
+    fn into(self) -> taffy::geometry::Size<S> {
+        taffy::geometry::Size {
+            width: self.width.into(),
+            height: self.height.into(),
+        }
+    }
+}
+
+impl Size<DefiniteLength> {
+    pub fn zero() -> Self {
+        Self {
+            width: pixels(0.),
+            height: pixels(0.),
+        }
+    }
+
+    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Size<taffy::style::LengthPercentage> {
+        taffy::geometry::Size {
+            width: self.width.to_taffy(rem_size),
+            height: self.height.to_taffy(rem_size),
+        }
+    }
+}
+
+impl Size<Length> {
+    pub fn auto() -> Self {
+        Self {
+            width: Length::Auto,
+            height: Length::Auto,
+        }
+    }
+
+    pub fn to_taffy<T: From<taffy::prelude::LengthPercentageAuto>>(
+        &self,
+        rem_size: f32,
+    ) -> taffy::geometry::Size<T> {
+        taffy::geometry::Size {
+            width: self.width.to_taffy(rem_size).into(),
+            height: self.height.to_taffy(rem_size).into(),
+        }
+    }
+}
+
+#[derive(Clone, Default, Refineable)]
+pub struct Edges<T: Clone + Default> {
+    pub top: T,
+    pub right: T,
+    pub bottom: T,
+    pub left: T,
+}
+
+impl Edges<DefiniteLength> {
+    pub fn zero() -> Self {
+        Self {
+            top: pixels(0.),
+            right: pixels(0.),
+            bottom: pixels(0.),
+            left: pixels(0.),
+        }
+    }
+
+    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
+        taffy::geometry::Rect {
+            top: self.top.to_taffy(rem_size),
+            right: self.right.to_taffy(rem_size),
+            bottom: self.bottom.to_taffy(rem_size),
+            left: self.left.to_taffy(rem_size),
+        }
+    }
+}
+
+impl Edges<Length> {
+    pub fn auto() -> Self {
+        Self {
+            top: Length::Auto,
+            right: Length::Auto,
+            bottom: Length::Auto,
+            left: Length::Auto,
+        }
+    }
+
+    pub fn zero() -> Self {
+        Self {
+            top: pixels(0.),
+            right: pixels(0.),
+            bottom: pixels(0.),
+            left: pixels(0.),
+        }
+    }
+
+    pub fn to_taffy(
+        &self,
+        rem_size: f32,
+    ) -> taffy::geometry::Rect<taffy::style::LengthPercentageAuto> {
+        taffy::geometry::Rect {
+            top: self.top.to_taffy(rem_size),
+            right: self.right.to_taffy(rem_size),
+            bottom: self.bottom.to_taffy(rem_size),
+            left: self.left.to_taffy(rem_size),
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+pub enum AbsoluteLength {
+    Pixels(f32),
+    Rems(f32),
+}
+
+impl AbsoluteLength {
+    pub fn to_pixels(&self, rem_size: f32) -> f32 {
+        match self {
+            AbsoluteLength::Pixels(pixels) => *pixels,
+            AbsoluteLength::Rems(rems) => rems * rem_size,
+        }
+    }
+}
+
+impl Default for AbsoluteLength {
+    fn default() -> Self {
+        Self::Pixels(0.0)
+    }
+}
+
+/// A non-auto length that can be defined in pixels, rems, or percent of parent.
+#[derive(Clone, Copy)]
+pub enum DefiniteLength {
+    Absolute(AbsoluteLength),
+    Relative(f32), // Percent, from 0 to 100.
+}
+
+impl DefiniteLength {
+    fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
+        match self {
+            DefiniteLength::Absolute(length) => match length {
+                AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
+                AbsoluteLength::Rems(rems) => {
+                    taffy::style::LengthPercentage::Length(rems * rem_size)
+                }
+            },
+            DefiniteLength::Relative(fraction) => {
+                taffy::style::LengthPercentage::Percent(*fraction)
+            }
+        }
+    }
+}
+
+impl From<AbsoluteLength> for DefiniteLength {
+    fn from(length: AbsoluteLength) -> Self {
+        Self::Absolute(length)
+    }
+}
+
+impl Default for DefiniteLength {
+    fn default() -> Self {
+        Self::Absolute(AbsoluteLength::default())
+    }
+}
+
+/// A length that can be defined in pixels, rems, percent of parent, or auto.
+#[derive(Clone, Copy)]
+pub enum Length {
+    Definite(DefiniteLength),
+    Auto,
+}
+
+pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
+    DefiniteLength::Relative(fraction).into()
+}
+
+pub fn rems<T: From<AbsoluteLength>>(rems: f32) -> T {
+    AbsoluteLength::Rems(rems).into()
+}
+
+pub fn pixels<T: From<AbsoluteLength>>(pixels: f32) -> T {
+    AbsoluteLength::Pixels(pixels).into()
+}
+
+pub fn auto() -> Length {
+    Length::Auto
+}
+
+impl Length {
+    pub fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
+        match self {
+            Length::Definite(length) => length.to_taffy(rem_size).into(),
+            Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
+        }
+    }
+}
+
+impl From<DefiniteLength> for Length {
+    fn from(length: DefiniteLength) -> Self {
+        Self::Definite(length)
+    }
+}
+
+impl From<AbsoluteLength> for Length {
+    fn from(length: AbsoluteLength) -> Self {
+        Self::Definite(length.into())
+    }
+}
+
+impl Default for Length {
+    fn default() -> Self {
+        Self::Definite(DefiniteLength::default())
+    }
+}

crates/gpui/src/gpui.rs 🔗

@@ -27,7 +27,10 @@ pub mod json;
 pub mod keymap_matcher;
 pub mod platform;
 pub use gpui_macros::{test, Element};
-pub use window::{Axis, RectFExt, SizeConstraint, Vector2FExt, WindowContext};
+pub use window::{
+    Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt,
+    WindowContext,
+};
 
 pub use anyhow;
 pub use serde_json;

crates/gpui/src/platform.rs 🔗

@@ -192,7 +192,7 @@ impl<'a> WindowOptions<'a> {
     }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Default)]
 pub struct TitlebarOptions<'a> {
     pub title: Option<&'a str>,
     pub appears_transparent: bool,

crates/gpui/src/platform/event.rs 🔗

@@ -1,4 +1,4 @@
-use std::ops::Deref;
+use std::{any::Any, ops::Deref};
 
 use pathfinder_geometry::vector::vec2f;
 
@@ -142,6 +142,7 @@ pub struct MouseButtonEvent {
     pub position: Vector2F,
     pub modifiers: Modifiers,
     pub click_count: usize,
+    pub is_down: bool,
 }
 
 impl Deref for MouseButtonEvent {
@@ -174,6 +175,7 @@ impl MouseMovedEvent {
             button: self.pressed_button.unwrap_or(button),
             modifiers: self.modifiers,
             click_count: 0,
+            is_down: self.pressed_button.is_some(),
         }
     }
 }
@@ -211,10 +213,24 @@ impl Event {
             Event::KeyDown { .. } => None,
             Event::KeyUp { .. } => None,
             Event::ModifiersChanged { .. } => None,
-            Event::MouseDown(event) | Event::MouseUp(event) => Some(event.position),
+            Event::MouseDown(event) => Some(event.position),
+            Event::MouseUp(event) => Some(event.position),
             Event::MouseMoved(event) => Some(event.position),
             Event::MouseExited(event) => Some(event.position),
             Event::ScrollWheel(event) => Some(event.position),
         }
     }
+
+    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
+        match self {
+            Event::KeyDown { .. } => None,
+            Event::KeyUp { .. } => None,
+            Event::ModifiersChanged { .. } => None,
+            Event::MouseDown(event) => Some(event),
+            Event::MouseUp(event) => Some(event),
+            Event::MouseMoved(event) => Some(event),
+            Event::MouseExited(event) => Some(event),
+            Event::ScrollWheel(event) => Some(event),
+        }
+    }
 }

crates/gpui/src/platform/mac/event.rs 🔗

@@ -132,6 +132,7 @@ impl Event {
                         ),
                         modifiers: read_modifiers(native_event),
                         click_count: native_event.clickCount() as usize,
+                        is_down: true,
                     })
                 })
             }
@@ -158,6 +159,7 @@ impl Event {
                         ),
                         modifiers: read_modifiers(native_event),
                         click_count: native_event.clickCount() as usize,
+                        is_down: false,
                     })
                 })
             }

crates/gpui/src/scene.rs 🔗

@@ -1,5 +1,6 @@
 mod mouse_event;
 mod mouse_region;
+mod region;
 
 #[cfg(debug_assertions)]
 use collections::HashSet;
@@ -8,7 +9,12 @@ use schemars::JsonSchema;
 use serde::Deserialize;
 use serde_derive::Serialize;
 use serde_json::json;
-use std::{borrow::Cow, sync::Arc};
+use std::{
+    any::{Any, TypeId},
+    borrow::Cow,
+    rc::Rc,
+    sync::Arc,
+};
 
 use crate::{
     color::Color,
@@ -16,7 +22,7 @@ use crate::{
     geometry::{rect::RectF, vector::Vector2F},
     json::ToJson,
     platform::{current::Surface, CursorStyle},
-    ImageData,
+    ImageData, WindowContext,
 };
 pub use mouse_event::*;
 pub use mouse_region::*;
@@ -25,6 +31,8 @@ pub struct SceneBuilder {
     scale_factor: f32,
     stacking_contexts: Vec<StackingContext>,
     active_stacking_context_stack: Vec<usize>,
+    /// Used by the playground crate.
+    pub event_handlers: Vec<EventHandler>,
     #[cfg(debug_assertions)]
     mouse_region_ids: HashSet<MouseRegionId>,
 }
@@ -32,6 +40,7 @@ pub struct SceneBuilder {
 pub struct Scene {
     scale_factor: f32,
     stacking_contexts: Vec<StackingContext>,
+    event_handlers: Vec<EventHandler>,
 }
 
 struct StackingContext {
@@ -272,6 +281,12 @@ impl Scene {
             })
             .collect()
     }
+
+    pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
+        self.event_handlers
+            .sort_by(|a, b| a.order.cmp(&b.order).reverse());
+        std::mem::take(&mut self.event_handlers)
+    }
 }
 
 impl SceneBuilder {
@@ -283,6 +298,7 @@ impl SceneBuilder {
             active_stacking_context_stack: vec![0],
             #[cfg(debug_assertions)]
             mouse_region_ids: Default::default(),
+            event_handlers: Vec::new(),
         }
     }
 
@@ -292,6 +308,7 @@ impl SceneBuilder {
         Scene {
             scale_factor: self.scale_factor,
             stacking_contexts: self.stacking_contexts,
+            event_handlers: self.event_handlers,
         }
     }
 
@@ -688,6 +705,13 @@ impl MouseRegion {
     }
 }
 
+pub struct EventHandler {
+    pub order: u32,
+    // The &dyn Any parameter below expects an event.
+    pub handler: Rc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>,
+    pub event_type: TypeId,
+}
+
 fn can_draw(bounds: RectF) -> bool {
     let size = bounds.size();
     size.x() > 0. && size.y() > 0.

crates/gpui/src/scene/mouse_region.rs 🔗

@@ -1,6 +1,4 @@
-use crate::{
-    platform::MouseButton, window::WindowContext, EventContext, TypeTag, View, ViewContext,
-};
+use crate::{platform::MouseButton, window::WindowContext, EventContext, TypeTag, ViewContext};
 use collections::HashMap;
 use pathfinder_geometry::rect::RectF;
 use smallvec::SmallVec;
@@ -72,7 +70,7 @@ impl MouseRegion {
 
     pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_down(button, handler);
@@ -81,7 +79,7 @@ impl MouseRegion {
 
     pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_up(button, handler);
@@ -90,7 +88,7 @@ impl MouseRegion {
 
     pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_click(button, handler);
@@ -99,7 +97,7 @@ impl MouseRegion {
 
     pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_click_out(button, handler);
@@ -108,7 +106,7 @@ impl MouseRegion {
 
     pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_down_out(button, handler);
@@ -117,7 +115,7 @@ impl MouseRegion {
 
     pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_up_out(button, handler);
@@ -126,7 +124,7 @@ impl MouseRegion {
 
     pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_drag(button, handler);
@@ -135,7 +133,7 @@ impl MouseRegion {
 
     pub fn on_hover<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_hover(handler);
@@ -144,7 +142,7 @@ impl MouseRegion {
 
     pub fn on_move<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_move(handler);
@@ -153,7 +151,7 @@ impl MouseRegion {
 
     pub fn on_move_out<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_move_out(handler);
@@ -162,7 +160,7 @@ impl MouseRegion {
 
     pub fn on_scroll<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
     {
         self.handlers = self.handlers.on_scroll(handler);
@@ -314,7 +312,7 @@ impl HandlerSet {
 
     pub fn on_move<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::move_disc(), None,
@@ -336,7 +334,7 @@ impl HandlerSet {
 
     pub fn on_move_out<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::move_out_disc(), None,
@@ -358,7 +356,7 @@ impl HandlerSet {
 
     pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::down_disc(), Some(button),
@@ -380,7 +378,7 @@ impl HandlerSet {
 
     pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::up_disc(), Some(button),
@@ -402,7 +400,7 @@ impl HandlerSet {
 
     pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::click_disc(), Some(button),
@@ -424,7 +422,7 @@ impl HandlerSet {
 
     pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::click_out_disc(), Some(button),
@@ -446,7 +444,7 @@ impl HandlerSet {
 
     pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::down_out_disc(), Some(button),
@@ -468,7 +466,7 @@ impl HandlerSet {
 
     pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::up_out_disc(), Some(button),
@@ -490,7 +488,7 @@ impl HandlerSet {
 
     pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::drag_disc(), Some(button),
@@ -512,7 +510,7 @@ impl HandlerSet {
 
     pub fn on_hover<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::hover_disc(), None,
@@ -534,7 +532,7 @@ impl HandlerSet {
 
     pub fn on_scroll<V, F>(mut self, handler: F) -> Self
     where
-        V: View,
+        V: 'static,
         F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
     {
         self.insert(MouseEvent::scroll_wheel_disc(), None,

crates/gpui/src/scene/region.rs 🔗

@@ -0,0 +1,7 @@
+// use crate::geometry::rect::RectF;
+// use crate::WindowContext;
+
+// struct Region {
+//     pub bounds: RectF,
+//     pub click_handler: Option<Rc<dyn Fn(&dyn Any, MouseEvent, &mut WindowContext)>>,
+// }

crates/gpui/src/text_layout.rs 🔗

@@ -212,7 +212,7 @@ pub struct Glyph {
 }
 
 impl Line {
-    fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
+    pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
         let mut style_runs = SmallVec::new();
         for (len, style) in runs {
             style_runs.push(StyleRun {

crates/gpui/tests/test.rs 🔗

@@ -1,14 +1,14 @@
-use gpui::{elements::RenderElement, View, ViewContext};
-use gpui_macros::Element;
+use gpui::{elements::Empty, Element, ViewContext};
+// use gpui_macros::Element;
 
 #[test]
 fn test_derive_render_element() {
     #[derive(Element)]
     struct TestElement {}
 
-    impl RenderElement for TestElement {
-        fn render<V: View>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> gpui::AnyElement<V> {
-            unimplemented!()
+    impl TestElement {
+        fn render<V: 'static>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> impl Element<V> {
+            Empty::new()
         }
     }
 }

crates/gpui_macros/Cargo.toml 🔗

@@ -10,6 +10,7 @@ proc-macro = true
 doctest = false
 
 [dependencies]
+lazy_static.workspace = true
+proc-macro2 = "1.0"
 syn = "1.0"
 quote = "1.0"
-proc-macro2 = "1.0"

crates/gpui_macros/src/gpui_macros.rs 🔗

@@ -4,7 +4,7 @@ use quote::{format_ident, quote};
 use std::mem;
 use syn::{
     parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, DeriveInput, FnArg,
-    ItemFn, Lit, Meta, NestedMeta, Type,
+    GenericParam, Generics, ItemFn, Lit, Meta, NestedMeta, Type, WhereClause,
 };
 
 #[proc_macro_attribute]
@@ -278,18 +278,44 @@ fn parse_bool(literal: &Lit) -> Result<bool, TokenStream> {
 
 #[proc_macro_derive(Element)]
 pub fn element_derive(input: TokenStream) -> TokenStream {
-    // Parse the input tokens into a syntax tree
-    let input = parse_macro_input!(input as DeriveInput);
+    let ast = parse_macro_input!(input as DeriveInput);
+    let type_name = ast.ident;
 
-    // The name of the struct/enum
-    let name = input.ident;
-    let must_implement = format_ident!("{}MustImplementRenderElement", name);
+    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>;
 
-    let expanded = quote! {
-        trait #must_implement : gpui::elements::RenderElement {}
-        impl #must_implement for #name {}
+    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
+        {
 
-        impl<V: gpui::View> gpui::elements::Element<V> for #name {
             type LayoutState = gpui::elements::AnyElement<V>;
             type PaintState = ();
 
@@ -299,7 +325,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
                 view: &mut V,
                 cx: &mut gpui::LayoutContext<V>,
             ) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) {
-                let mut element = self.render(view, cx);
+                let mut element = self.render(view, cx).into_any();
                 let size = element.layout(constraint, view, cx);
                 (size, element)
             }
@@ -336,11 +362,11 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
                 _: &(),
                 view: &V,
                 cx: &gpui::ViewContext<V>,
-            ) -> gpui::serde_json::Value {
+            ) -> gpui::json::Value {
                 element.debug(view, cx)
             }
         }
     };
-    // Return generated code
-    TokenStream::from(expanded)
+
+    gen.into()
 }

crates/language/src/buffer.rs 🔗

@@ -2192,13 +2192,16 @@ impl BufferSnapshot {
         let mut end = start;
         let mut next_chars = self.chars_at(start).peekable();
         let mut prev_chars = self.reversed_chars_at(start).peekable();
+
+        let language = self.language_at(start);
+        let kind = |c| char_kind(language, c);
         let word_kind = cmp::max(
-            prev_chars.peek().copied().map(char_kind),
-            next_chars.peek().copied().map(char_kind),
+            prev_chars.peek().copied().map(kind),
+            next_chars.peek().copied().map(kind),
         );
 
         for ch in prev_chars {
-            if Some(char_kind(ch)) == word_kind && ch != '\n' {
+            if Some(kind(ch)) == word_kind && ch != '\n' {
                 start -= ch.len_utf8();
             } else {
                 break;
@@ -2206,7 +2209,7 @@ impl BufferSnapshot {
         }
 
         for ch in next_chars {
-            if Some(char_kind(ch)) == word_kind && ch != '\n' {
+            if Some(kind(ch)) == word_kind && ch != '\n' {
                 end += ch.len_utf8();
             } else {
                 break;
@@ -3003,14 +3006,18 @@ pub fn contiguous_ranges(
     })
 }
 
-pub fn char_kind(c: char) -> CharKind {
+pub fn char_kind(language: Option<&Arc<Language>>, c: char) -> CharKind {
     if c.is_whitespace() {
-        CharKind::Whitespace
+        return CharKind::Whitespace;
     } else if c.is_alphanumeric() || c == '_' {
-        CharKind::Word
-    } else {
-        CharKind::Punctuation
+        return CharKind::Word;
+    }
+    if let Some(language) = language {
+        if language.config.word_characters.contains(&c) {
+            return CharKind::Word;
+        }
     }
+    CharKind::Punctuation
 }
 
 /// Find all of the ranges of whitespace that occur at the ends of lines

crates/language/src/language.rs 🔗

@@ -11,7 +11,7 @@ mod buffer_tests;
 
 use anyhow::{anyhow, Context, Result};
 use async_trait::async_trait;
-use collections::HashMap;
+use collections::{HashMap, HashSet};
 use futures::{
     channel::oneshot,
     future::{BoxFuture, Shared},
@@ -344,6 +344,8 @@ pub struct LanguageConfig {
     pub block_comment: Option<(Arc<str>, Arc<str>)>,
     #[serde(default)]
     pub overrides: HashMap<String, LanguageConfigOverride>,
+    #[serde(default)]
+    pub word_characters: HashSet<char>,
 }
 
 #[derive(Debug, Default)]
@@ -411,6 +413,7 @@ impl Default for LanguageConfig {
             block_comment: Default::default(),
             overrides: Default::default(),
             collapsed_placeholder: Default::default(),
+            word_characters: Default::default(),
         }
     }
 }

crates/language_tools/src/lsp_log.rs 🔗

@@ -450,7 +450,7 @@ impl View for LspLogView {
 }
 
 impl Item for LspLogView {
-    fn tab_content<V: View>(
+    fn tab_content<V: 'static>(
         &self,
         _: Option<usize>,
         style: &theme::Tab,

crates/language_tools/src/syntax_tree_view.rs 🔗

@@ -373,6 +373,7 @@ impl View for SyntaxTreeView {
             font_size,
             font_properties: Default::default(),
             underline: Default::default(),
+            soft_wrap: false,
         };
 
         let line_height = cx.font_cache().line_height(font_size);
@@ -451,7 +452,7 @@ impl View for SyntaxTreeView {
 }
 
 impl Item for SyntaxTreeView {
-    fn tab_content<V: View>(
+    fn tab_content<V: 'static>(
         &self,
         _: Option<usize>,
         style: &theme::Tab,

crates/project/src/project.rs 🔗

@@ -5180,7 +5180,7 @@ impl Project {
                                         snapshot.file().map(|file| file.path().as_ref()),
                                     ) {
                                         query
-                                            .search(snapshot.as_rope())
+                                            .search(&snapshot, None)
                                             .await
                                             .iter()
                                             .map(|range| {

crates/project/src/search.rs 🔗

@@ -3,7 +3,7 @@ use anyhow::{Context, Result};
 use client::proto;
 use globset::{Glob, GlobMatcher};
 use itertools::Itertools;
-use language::{char_kind, Rope};
+use language::{char_kind, BufferSnapshot};
 use regex::{Regex, RegexBuilder};
 use smol::future::yield_now;
 use std::{
@@ -39,6 +39,7 @@ pub enum SearchQuery {
         case_sensitive: bool,
         inner: SearchInputs,
     },
+
     Regex {
         regex: Regex,
 
@@ -214,12 +215,24 @@ impl SearchQuery {
         }
     }
 
-    pub async fn search(&self, rope: &Rope) -> Vec<Range<usize>> {
+    pub async fn search(
+        &self,
+        buffer: &BufferSnapshot,
+        subrange: Option<Range<usize>>,
+    ) -> Vec<Range<usize>> {
         const YIELD_INTERVAL: usize = 20000;
 
         if self.as_str().is_empty() {
             return Default::default();
         }
+        let language = buffer.language_at(0);
+        let rope = if let Some(range) = subrange {
+            buffer.as_rope().slice(range)
+        } else {
+            buffer.as_rope().clone()
+        };
+
+        let kind = |c| char_kind(language, c);
 
         let mut matches = Vec::new();
         match self {
@@ -236,10 +249,10 @@ impl SearchQuery {
 
                     let mat = mat.unwrap();
                     if *whole_word {
-                        let prev_kind = rope.reversed_chars_at(mat.start()).next().map(char_kind);
-                        let start_kind = char_kind(rope.chars_at(mat.start()).next().unwrap());
-                        let end_kind = char_kind(rope.reversed_chars_at(mat.end()).next().unwrap());
-                        let next_kind = rope.chars_at(mat.end()).next().map(char_kind);
+                        let prev_kind = rope.reversed_chars_at(mat.start()).next().map(kind);
+                        let start_kind = kind(rope.chars_at(mat.start()).next().unwrap());
+                        let end_kind = kind(rope.reversed_chars_at(mat.end()).next().unwrap());
+                        let next_kind = rope.chars_at(mat.end()).next().map(kind);
                         if Some(start_kind) == prev_kind || Some(end_kind) == next_kind {
                             continue;
                         }
@@ -247,6 +260,7 @@ impl SearchQuery {
                     matches.push(mat.start()..mat.end())
                 }
             }
+
             Self::Regex {
                 regex, multiline, ..
             } => {
@@ -284,6 +298,7 @@ impl SearchQuery {
                 }
             }
         }
+
         matches
     }
 

crates/project_panel/src/project_panel.rs 🔗

@@ -1320,7 +1320,7 @@ impl ProjectPanel {
         }
     }
 
-    fn render_entry_visual_element<V: View>(
+    fn render_entry_visual_element<V: 'static>(
         details: &EntryDetails,
         editor: Option<&ViewHandle<Editor>>,
         padding: f32,

crates/recent_projects/src/highlighted_workspace_location.rs 🔗

@@ -3,7 +3,7 @@ use std::path::Path;
 use fuzzy::StringMatch;
 use gpui::{
     elements::{Label, LabelStyle},
-    AnyElement, Element, View,
+    AnyElement, Element,
 };
 use util::paths::PathExt;
 use workspace::WorkspaceLocation;
@@ -43,7 +43,7 @@ impl HighlightedText {
         }
     }
 
-    pub fn render<V: View>(self, style: impl Into<LabelStyle>) -> AnyElement<V> {
+    pub fn render<V: 'static>(self, style: impl Into<LabelStyle>) -> AnyElement<V> {
         Label::new(self.text, style)
             .with_highlights(self.highlight_positions)
             .into_any()

crates/refineable/Cargo.toml 🔗

@@ -0,0 +1,15 @@
+[package]
+name = "refineable"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/refineable.rs"
+doctest = false
+
+[dependencies]
+syn = "1.0.72"
+quote = "1.0.9"
+proc-macro2 = "1.0.66"
+derive_refineable = { path = "./derive_refineable" }

crates/refineable/derive_refineable/Cargo.toml 🔗

@@ -0,0 +1,15 @@
+[package]
+name = "derive_refineable"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/derive_refineable.rs"
+proc-macro = true
+doctest = false
+
+[dependencies]
+syn = "1.0.72"
+quote = "1.0.9"
+proc-macro2 = "1.0.66"

crates/refineable/derive_refineable/src/derive_refineable.rs 🔗

@@ -0,0 +1,188 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{
+    parse_macro_input, parse_quote, DeriveInput, Field, FieldsNamed, PredicateType, TraitBound,
+    Type, TypeParamBound, WhereClause, WherePredicate,
+};
+
+#[proc_macro_derive(Refineable, attributes(refineable))]
+pub fn derive_refineable(input: TokenStream) -> TokenStream {
+    let DeriveInput {
+        ident,
+        data,
+        generics,
+        ..
+    } = parse_macro_input!(input);
+
+    let refinement_ident = format_ident!("{}Refinement", ident);
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let fields = match data {
+        syn::Data::Struct(syn::DataStruct {
+            fields: syn::Fields::Named(FieldsNamed { named, .. }),
+            ..
+        }) => named.into_iter().collect::<Vec<Field>>(),
+        _ => panic!("This derive macro only supports structs with named fields"),
+    };
+
+    let field_names: Vec<_> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
+    let field_visibilities: Vec<_> = fields.iter().map(|f| &f.vis).collect();
+    let wrapped_types: Vec<_> = fields.iter().map(|f| get_wrapper_type(f, &f.ty)).collect();
+
+    // Create trait bound that each wrapped type must implement Clone & Default
+    let type_param_bounds: Vec<_> = wrapped_types
+        .iter()
+        .map(|ty| {
+            WherePredicate::Type(PredicateType {
+                lifetimes: None,
+                bounded_ty: ty.clone(),
+                colon_token: Default::default(),
+                bounds: {
+                    let mut punctuated = syn::punctuated::Punctuated::new();
+                    punctuated.push_value(TypeParamBound::Trait(TraitBound {
+                        paren_token: None,
+                        modifier: syn::TraitBoundModifier::None,
+                        lifetimes: None,
+                        path: parse_quote!(Clone),
+                    }));
+                    punctuated.push_punct(syn::token::Add::default());
+                    punctuated.push_value(TypeParamBound::Trait(TraitBound {
+                        paren_token: None,
+                        modifier: syn::TraitBoundModifier::None,
+                        lifetimes: None,
+                        path: parse_quote!(Default),
+                    }));
+                    punctuated
+                },
+            })
+        })
+        .collect();
+
+    // Append to where_clause or create a new one if it doesn't exist
+    let where_clause = match where_clause.cloned() {
+        Some(mut where_clause) => {
+            where_clause
+                .predicates
+                .extend(type_param_bounds.into_iter());
+            where_clause.clone()
+        }
+        None => WhereClause {
+            where_token: Default::default(),
+            predicates: type_param_bounds.into_iter().collect(),
+        },
+    };
+
+    let field_assignments: Vec<TokenStream2> = fields
+        .iter()
+        .map(|field| {
+            let name = &field.ident;
+            let is_refineable = is_refineable_field(field);
+            let is_optional = is_optional_field(field);
+
+            if is_refineable {
+                quote! {
+                    self.#name.refine(&refinement.#name);
+                }
+            } else if is_optional {
+                quote! {
+                    if let Some(ref value) = &refinement.#name {
+                        self.#name = Some(value.clone());
+                    }
+                }
+            } else {
+                quote! {
+                    if let Some(ref value) = &refinement.#name {
+                        self.#name = value.clone();
+                    }
+                }
+            }
+        })
+        .collect();
+
+    let refinement_field_assignments: Vec<TokenStream2> = fields
+        .iter()
+        .map(|field| {
+            let name = &field.ident;
+            let is_refineable = is_refineable_field(field);
+
+            if is_refineable {
+                quote! {
+                    self.#name.refine(&refinement.#name);
+                }
+            } else {
+                quote! {
+                    if let Some(ref value) = &refinement.#name {
+                        self.#name = Some(value.clone());
+                    }
+                }
+            }
+        })
+        .collect();
+
+    let gen = quote! {
+        #[derive(Default, Clone)]
+        pub struct #refinement_ident #impl_generics {
+            #( #field_visibilities #field_names: #wrapped_types ),*
+        }
+
+        impl #impl_generics Refineable for #ident #ty_generics
+            #where_clause
+        {
+            type Refinement = #refinement_ident #ty_generics;
+
+            fn refine(&mut self, refinement: &Self::Refinement) {
+                #( #field_assignments )*
+            }
+        }
+
+        impl #impl_generics Refineable for #refinement_ident #ty_generics
+            #where_clause
+        {
+            type Refinement = #refinement_ident #ty_generics;
+
+            fn refine(&mut self, refinement: &Self::Refinement) {
+                #( #refinement_field_assignments )*
+            }
+        }
+    };
+
+    gen.into()
+}
+
+fn is_refineable_field(f: &Field) -> bool {
+    f.attrs.iter().any(|attr| attr.path.is_ident("refineable"))
+}
+
+fn is_optional_field(f: &Field) -> bool {
+    if let Type::Path(typepath) = &f.ty {
+        if typepath.qself.is_none() {
+            let segments = &typepath.path.segments;
+            if segments.len() == 1 && segments.iter().any(|s| s.ident == "Option") {
+                return true;
+            }
+        }
+    }
+    false
+}
+
+fn get_wrapper_type(field: &Field, ty: &Type) -> syn::Type {
+    if is_refineable_field(field) {
+        let struct_name = if let Type::Path(tp) = ty {
+            tp.path.segments.last().unwrap().ident.clone()
+        } else {
+            panic!("Expected struct type for a refineable field");
+        };
+        let refinement_struct_name = format_ident!("{}Refinement", struct_name);
+        let generics = if let Type::Path(tp) = ty {
+            &tp.path.segments.last().unwrap().arguments
+        } else {
+            &syn::PathArguments::None
+        };
+        parse_quote!(#refinement_struct_name #generics)
+    } else if is_optional_field(field) {
+        ty.clone()
+    } else {
+        parse_quote!(Option<#ty>)
+    }
+}

crates/refineable/src/refineable.rs 🔗

@@ -0,0 +1,14 @@
+pub use derive_refineable::Refineable;
+
+pub trait Refineable {
+    type Refinement: Default;
+
+    fn refine(&mut self, refinement: &Self::Refinement);
+    fn refined(mut self, refinement: &Self::Refinement) -> Self
+    where
+        Self: Sized,
+    {
+        self.refine(refinement);
+        self
+    }
+}

crates/search/src/project_search.rs 🔗

@@ -482,7 +482,7 @@ impl Item for ProjectSearchView {
             .update(cx, |editor, cx| editor.deactivated(cx));
     }
 
-    fn tab_content<T: View>(
+    fn tab_content<T: 'static>(
         &self,
         _detail: Option<usize>,
         tab_theme: &theme::Tab,

crates/settings/src/settings_store.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, Context, Result};
 use collections::{btree_map, hash_map, BTreeMap, HashMap};
 use gpui::AppContext;
 use lazy_static::lazy_static;
@@ -162,6 +162,7 @@ impl SettingsStore {
 
             if let Some(setting) = setting_value
                 .load_setting(&default_settings, &user_values_stack, cx)
+                .context("A default setting must be added to the `default.json` file")
                 .log_err()
             {
                 setting_value.set_global_value(setting);

crates/terminal_view/src/terminal_element.rs 🔗

@@ -567,6 +567,7 @@ impl Element<TerminalView> for TerminalElement {
             font_size,
             font_properties: Default::default(),
             underline: Default::default(),
+            soft_wrap: false,
         };
         let selection_color = settings.theme.editor.selection.selection;
         let match_color = settings.theme.search.match_background;

crates/terminal_view/src/terminal_view.rs 🔗

@@ -661,7 +661,7 @@ impl Item for TerminalView {
         Some(self.terminal().read(cx).title().into())
     }
 
-    fn tab_content<T: View>(
+    fn tab_content<T: 'static>(
         &self,
         _detail: Option<usize>,
         tab_theme: &theme::Tab,

crates/theme/src/ui.rs 🔗

@@ -10,7 +10,7 @@ use gpui::{
     platform,
     platform::MouseButton,
     scene::MouseClick,
-    Action, Element, EventContext, MouseState, View, ViewContext,
+    Action, Element, EventContext, MouseState, ViewContext,
 };
 use schemars::JsonSchema;
 use serde::Deserialize;
@@ -37,7 +37,7 @@ pub fn checkbox<Tag, V, F>(
 ) -> MouseEventHandler<V>
 where
     Tag: 'static,
-    V: View,
+    V: 'static,
     F: 'static + Fn(&mut V, bool, &mut EventContext<V>),
 {
     let label = Label::new(label, style.label.text.clone())
@@ -57,7 +57,7 @@ pub fn checkbox_with_label<Tag, D, V, F>(
 where
     Tag: 'static,
     D: Element<V>,
-    V: View,
+    V: 'static,
     F: 'static + Fn(&mut V, bool, &mut EventContext<V>),
 {
     MouseEventHandler::new::<Tag, _>(id, cx, |state, _| {
@@ -93,7 +93,7 @@ where
     .with_cursor_style(platform::CursorStyle::PointingHand)
 }
 
-pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
+pub fn svg<V: 'static>(style: &SvgStyle) -> ConstrainedBox<V> {
     Svg::new(style.asset.clone())
         .with_color(style.color)
         .constrained()
@@ -117,11 +117,11 @@ impl IconStyle {
     }
 }
 
-pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
+pub fn icon<V: 'static>(style: &IconStyle) -> Container<V> {
     svg(&style.icon).contained().with_style(style.container)
 }
 
-pub fn keystroke_label<V: View>(
+pub fn keystroke_label<V: 'static>(
     label_text: &'static str,
     label_style: &ContainedText,
     keystroke_style: &ContainedText,
@@ -157,7 +157,7 @@ pub fn cta_button<Tag, L, V, F>(
 where
     Tag: 'static,
     L: Into<Cow<'static, str>>,
-    V: View,
+    V: 'static,
     F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
 {
     MouseEventHandler::new::<Tag, _>(0, cx, |state, _| {
@@ -196,9 +196,9 @@ pub fn modal<Tag, V, I, D, F>(
 ) -> impl Element<V>
 where
     Tag: 'static,
-    V: View,
     I: Into<Cow<'static, str>>,
     D: Element<V>,
+    V: 'static,
     F: FnOnce(&mut gpui::ViewContext<V>) -> D,
 {
     const TITLEBAR_HEIGHT: f32 = 28.;

crates/vim/src/motion.rs 🔗

@@ -439,11 +439,12 @@ pub(crate) fn next_word_start(
     ignore_punctuation: bool,
     times: usize,
 ) -> DisplayPoint {
+    let language = map.buffer_snapshot.language_at(point.to_point(map));
     for _ in 0..times {
         let mut crossed_newline = false;
         point = movement::find_boundary(map, point, |left, right| {
-            let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
-            let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
+            let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
+            let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
             let at_newline = right == '\n';
 
             let found = (left_kind != right_kind && right_kind != CharKind::Whitespace)
@@ -463,11 +464,12 @@ fn next_word_end(
     ignore_punctuation: bool,
     times: usize,
 ) -> DisplayPoint {
+    let language = map.buffer_snapshot.language_at(point.to_point(map));
     for _ in 0..times {
         *point.column_mut() += 1;
         point = movement::find_boundary(map, point, |left, right| {
-            let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
-            let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
+            let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
+            let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
 
             left_kind != right_kind && left_kind != CharKind::Whitespace
         });
@@ -493,12 +495,13 @@ fn previous_word_start(
     ignore_punctuation: bool,
     times: usize,
 ) -> DisplayPoint {
+    let language = map.buffer_snapshot.language_at(point.to_point(map));
     for _ in 0..times {
         // This works even though find_preceding_boundary is called for every character in the line containing
         // cursor because the newline is checked only once.
         point = movement::find_preceding_boundary(map, point, |left, right| {
-            let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
-            let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
+            let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
+            let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
 
             (left_kind != right_kind && !right.is_whitespace()) || left == '\n'
         });
@@ -508,6 +511,7 @@ fn previous_word_start(
 
 fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoint {
     let mut last_point = DisplayPoint::new(from.row(), 0);
+    let language = map.buffer_snapshot.language_at(from.to_point(map));
     for (ch, point) in map.chars_at(last_point) {
         if ch == '\n' {
             return from;
@@ -515,7 +519,7 @@ fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoi
 
         last_point = point;
 
-        if char_kind(ch) != CharKind::Whitespace {
+        if char_kind(language, ch) != CharKind::Whitespace {
             break;
         }
     }

crates/vim/src/normal.rs 🔗

@@ -1,12 +1,13 @@
 mod case;
 mod change;
 mod delete;
+mod paste;
 mod scroll;
 mod search;
 pub mod substitute;
 mod yank;
 
-use std::{borrow::Cow, sync::Arc};
+use std::sync::Arc;
 
 use crate::{
     motion::Motion,
@@ -14,13 +15,11 @@ use crate::{
     state::{Mode, Operator},
     Vim,
 };
-use collections::{HashMap, HashSet};
-use editor::{
-    display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Anchor, Bias, ClipboardSelection,
-    DisplayPoint,
-};
+use collections::HashSet;
+use editor::scroll::autoscroll::Autoscroll;
+use editor::{Bias, DisplayPoint};
 use gpui::{actions, AppContext, ViewContext, WindowContext};
-use language::{AutoindentMode, Point, SelectionGoal};
+use language::SelectionGoal;
 use log::error;
 use workspace::Workspace;
 
@@ -44,7 +43,6 @@ actions!(
         DeleteRight,
         ChangeToEndOfLine,
         DeleteToEndOfLine,
-        Paste,
         Yank,
         Substitute,
         ChangeCase,
@@ -89,9 +87,8 @@ pub fn init(cx: &mut AppContext) {
             delete_motion(vim, Motion::EndOfLine, times, cx);
         })
     });
-    cx.add_action(paste);
-
     scroll::init(cx);
+    paste::init(cx);
 }
 
 pub fn normal_motion(
@@ -250,144 +247,6 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex
     });
 }
 
-fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.transact(cx, |editor, cx| {
-                editor.set_clip_at_line_ends(false, cx);
-                if let Some(item) = cx.read_from_clipboard() {
-                    let mut clipboard_text = Cow::Borrowed(item.text());
-                    if let Some(mut clipboard_selections) =
-                        item.metadata::<Vec<ClipboardSelection>>()
-                    {
-                        let (display_map, selections) = editor.selections.all_display(cx);
-                        let all_selections_were_entire_line =
-                            clipboard_selections.iter().all(|s| s.is_entire_line);
-                        if clipboard_selections.len() != selections.len() {
-                            let mut newline_separated_text = String::new();
-                            let mut clipboard_selections =
-                                clipboard_selections.drain(..).peekable();
-                            let mut ix = 0;
-                            while let Some(clipboard_selection) = clipboard_selections.next() {
-                                newline_separated_text
-                                    .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
-                                ix += clipboard_selection.len;
-                                if clipboard_selections.peek().is_some() {
-                                    newline_separated_text.push('\n');
-                                }
-                            }
-                            clipboard_text = Cow::Owned(newline_separated_text);
-                        }
-
-                        // If the pasted text is a single line, the cursor should be placed after
-                        // the newly pasted text. This is easiest done with an anchor after the
-                        // insertion, and then with a fixup to move the selection back one position.
-                        // However if the pasted text is linewise, the cursor should be placed at the start
-                        // of the new text on the following line. This is easiest done with a manually adjusted
-                        // point.
-                        // This enum lets us represent both cases
-                        enum NewPosition {
-                            Inside(Point),
-                            After(Anchor),
-                        }
-                        let mut new_selections: HashMap<usize, NewPosition> = Default::default();
-                        editor.buffer().update(cx, |buffer, cx| {
-                            let snapshot = buffer.snapshot(cx);
-                            let mut start_offset = 0;
-                            let mut edits = Vec::new();
-                            for (ix, selection) in selections.iter().enumerate() {
-                                let to_insert;
-                                let linewise;
-                                if let Some(clipboard_selection) = clipboard_selections.get(ix) {
-                                    let end_offset = start_offset + clipboard_selection.len;
-                                    to_insert = &clipboard_text[start_offset..end_offset];
-                                    linewise = clipboard_selection.is_entire_line;
-                                    start_offset = end_offset;
-                                } else {
-                                    to_insert = clipboard_text.as_str();
-                                    linewise = all_selections_were_entire_line;
-                                }
-
-                                // If the clipboard text was copied linewise, and the current selection
-                                // is empty, then paste the text after this line and move the selection
-                                // to the start of the pasted text
-                                let insert_at = if linewise {
-                                    let (point, _) = display_map
-                                        .next_line_boundary(selection.start.to_point(&display_map));
-
-                                    if !to_insert.starts_with('\n') {
-                                        // Add newline before pasted text so that it shows up
-                                        edits.push((point..point, "\n"));
-                                    }
-                                    // Drop selection at the start of the next line
-                                    new_selections.insert(
-                                        selection.id,
-                                        NewPosition::Inside(Point::new(point.row + 1, 0)),
-                                    );
-                                    point
-                                } else {
-                                    let mut point = selection.end;
-                                    // Paste the text after the current selection
-                                    *point.column_mut() = point.column() + 1;
-                                    let point = display_map
-                                        .clip_point(point, Bias::Right)
-                                        .to_point(&display_map);
-
-                                    new_selections.insert(
-                                        selection.id,
-                                        if to_insert.contains('\n') {
-                                            NewPosition::Inside(point)
-                                        } else {
-                                            NewPosition::After(snapshot.anchor_after(point))
-                                        },
-                                    );
-                                    point
-                                };
-
-                                if linewise && to_insert.ends_with('\n') {
-                                    edits.push((
-                                        insert_at..insert_at,
-                                        &to_insert[0..to_insert.len().saturating_sub(1)],
-                                    ))
-                                } else {
-                                    edits.push((insert_at..insert_at, to_insert));
-                                }
-                            }
-                            drop(snapshot);
-                            buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
-                        });
-
-                        editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                            s.move_with(|map, selection| {
-                                if let Some(new_position) = new_selections.get(&selection.id) {
-                                    match new_position {
-                                        NewPosition::Inside(new_point) => {
-                                            selection.collapse_to(
-                                                new_point.to_display_point(map),
-                                                SelectionGoal::None,
-                                            );
-                                        }
-                                        NewPosition::After(after_point) => {
-                                            let mut new_point = after_point.to_display_point(map);
-                                            *new_point.column_mut() =
-                                                new_point.column().saturating_sub(1);
-                                            new_point = map.clip_point(new_point, Bias::Left);
-                                            selection.collapse_to(new_point, SelectionGoal::None);
-                                        }
-                                    }
-                                }
-                            });
-                        });
-                    } else {
-                        editor.insert(&clipboard_text, cx);
-                    }
-                }
-                editor.set_clip_at_line_ends(true, cx);
-            });
-        });
-    });
-}
-
 pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
     Vim::update(cx, |vim, cx| {
         vim.update_active_editor(cx, |editor, cx| {
@@ -883,36 +742,6 @@ mod test {
             .await;
     }
 
-    #[gpui::test]
-    async fn test_p(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.set_shared_state(indoc! {"
-                The quick brown
-                fox juˇmps over
-                the lazy dog"})
-            .await;
-
-        cx.simulate_shared_keystrokes(["d", "d"]).await;
-        cx.assert_state_matches().await;
-
-        cx.simulate_shared_keystroke("p").await;
-        cx.assert_state_matches().await;
-
-        cx.set_shared_state(indoc! {"
-                The quick brown
-                fox ˇjumps over
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "w", "y"]).await;
-        cx.set_shared_state(indoc! {"
-                The quick brown
-                fox jumps oveˇr
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystroke("p").await;
-        cx.assert_state_matches().await;
-    }
-
     #[gpui::test]
     async fn test_repeated_word(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;

crates/vim/src/normal/change.rs 🔗

@@ -82,16 +82,19 @@ fn expand_changed_word_selection(
     ignore_punctuation: bool,
 ) -> bool {
     if times.is_none() || times.unwrap() == 1 {
+        let language = map
+            .buffer_snapshot
+            .language_at(selection.start.to_point(map));
         let in_word = map
             .chars_at(selection.head())
             .next()
-            .map(|(c, _)| char_kind(c) != CharKind::Whitespace)
+            .map(|(c, _)| char_kind(language, c) != CharKind::Whitespace)
             .unwrap_or_default();
 
         if in_word {
             selection.end = movement::find_boundary(map, selection.end, |left, right| {
-                let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
-                let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
+                let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
+                let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
 
                 left_kind != right_kind && left_kind != CharKind::Whitespace
             });

crates/vim/src/normal/paste.rs 🔗

@@ -0,0 +1,468 @@
+use std::{borrow::Cow, cmp};
+
+use editor::{
+    display_map::ToDisplayPoint, movement, scroll::autoscroll::Autoscroll, ClipboardSelection,
+    DisplayPoint,
+};
+use gpui::{impl_actions, AppContext, ViewContext};
+use language::{Bias, SelectionGoal};
+use serde::Deserialize;
+use workspace::Workspace;
+
+use crate::{state::Mode, utils::copy_selections_content, Vim};
+
+#[derive(Clone, Deserialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+struct Paste {
+    #[serde(default)]
+    before: bool,
+    #[serde(default)]
+    preserve_clipboard: bool,
+}
+
+impl_actions!(vim, [Paste]);
+
+pub(crate) fn init(cx: &mut AppContext) {
+    cx.add_action(paste);
+}
+
+fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext<Workspace>) {
+    Vim::update(cx, |vim, cx| {
+        vim.update_active_editor(cx, |editor, cx| {
+            editor.transact(cx, |editor, cx| {
+                editor.set_clip_at_line_ends(false, cx);
+
+                let Some(item) = cx.read_from_clipboard() else {
+                    return
+                };
+                let clipboard_text = Cow::Borrowed(item.text());
+                if clipboard_text.is_empty() {
+                    return;
+                }
+
+                if !action.preserve_clipboard && vim.state().mode.is_visual() {
+                    copy_selections_content(editor, vim.state().mode == Mode::VisualLine, cx);
+                }
+
+                // if we are copying from multi-cursor (of visual block mode), we want
+                // to
+                let clipboard_selections =
+                    item.metadata::<Vec<ClipboardSelection>>()
+                        .filter(|clipboard_selections| {
+                            clipboard_selections.len() > 1 && vim.state().mode != Mode::VisualLine
+                        });
+
+                let (display_map, current_selections) = editor.selections.all_adjusted_display(cx);
+
+                // unlike zed, if you have a multi-cursor selection from vim block mode,
+                // pasting it will paste it on subsequent lines, even if you don't yet
+                // have a cursor there.
+                let mut selections_to_process = Vec::new();
+                let mut i = 0;
+                while i < current_selections.len() {
+                    selections_to_process
+                        .push((current_selections[i].start..current_selections[i].end, true));
+                    i += 1;
+                }
+                if let Some(clipboard_selections) = clipboard_selections.as_ref() {
+                    let left = current_selections
+                        .iter()
+                        .map(|selection| cmp::min(selection.start.column(), selection.end.column()))
+                        .min()
+                        .unwrap();
+                    let mut row = current_selections.last().unwrap().end.row() + 1;
+                    while i < clipboard_selections.len() {
+                        let cursor =
+                            display_map.clip_point(DisplayPoint::new(row, left), Bias::Left);
+                        selections_to_process.push((cursor..cursor, false));
+                        i += 1;
+                        row += 1;
+                    }
+                }
+
+                let first_selection_indent_column =
+                    clipboard_selections.as_ref().and_then(|zed_selections| {
+                        zed_selections
+                            .first()
+                            .map(|selection| selection.first_line_indent)
+                    });
+                let before = action.before || vim.state().mode == Mode::VisualLine;
+
+                let mut edits = Vec::new();
+                let mut new_selections = Vec::new();
+                let mut original_indent_columns = Vec::new();
+                let mut start_offset = 0;
+
+                for (ix, (selection, preserve)) in selections_to_process.iter().enumerate() {
+                    let (mut to_insert, original_indent_column) =
+                        if let Some(clipboard_selections) = &clipboard_selections {
+                            if let Some(clipboard_selection) = clipboard_selections.get(ix) {
+                                let end_offset = start_offset + clipboard_selection.len;
+                                let text = clipboard_text[start_offset..end_offset].to_string();
+                                start_offset = end_offset + 1;
+                                (text, Some(clipboard_selection.first_line_indent))
+                            } else {
+                                ("".to_string(), first_selection_indent_column)
+                            }
+                        } else {
+                            (clipboard_text.to_string(), first_selection_indent_column)
+                        };
+                    let line_mode = to_insert.ends_with("\n");
+                    let is_multiline = to_insert.contains("\n");
+
+                    if line_mode && !before {
+                        if selection.is_empty() {
+                            to_insert =
+                                "\n".to_owned() + &to_insert[..to_insert.len() - "\n".len()];
+                        } else {
+                            to_insert = "\n".to_owned() + &to_insert;
+                        }
+                    } else if !line_mode && vim.state().mode == Mode::VisualLine {
+                        to_insert = to_insert + "\n";
+                    }
+
+                    let display_range = if !selection.is_empty() {
+                        selection.start..selection.end
+                    } else if line_mode {
+                        let point = if before {
+                            movement::line_beginning(&display_map, selection.start, false)
+                        } else {
+                            movement::line_end(&display_map, selection.start, false)
+                        };
+                        point..point
+                    } else {
+                        let point = if before {
+                            selection.start
+                        } else {
+                            movement::saturating_right(&display_map, selection.start)
+                        };
+                        point..point
+                    };
+
+                    let point_range = display_range.start.to_point(&display_map)
+                        ..display_range.end.to_point(&display_map);
+                    let anchor = if is_multiline || vim.state().mode == Mode::VisualLine {
+                        display_map.buffer_snapshot.anchor_before(point_range.start)
+                    } else {
+                        display_map.buffer_snapshot.anchor_after(point_range.end)
+                    };
+
+                    if *preserve {
+                        new_selections.push((anchor, line_mode, is_multiline));
+                    }
+                    edits.push((point_range, to_insert));
+                    original_indent_columns.extend(original_indent_column);
+                }
+
+                editor.edit_with_block_indent(edits, original_indent_columns, cx);
+
+                // in line_mode vim will insert the new text on the next (or previous if before) line
+                // and put the cursor on the first non-blank character of the first inserted line (or at the end if the first line is blank).
+                // otherwise vim will insert the next text at (or before) the current cursor position,
+                // the cursor will go to the last (or first, if is_multiline) inserted character.
+                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                    s.replace_cursors_with(|map| {
+                        let mut cursors = Vec::new();
+                        for (anchor, line_mode, is_multiline) in &new_selections {
+                            let mut cursor = anchor.to_display_point(map);
+                            if *line_mode {
+                                if !before {
+                                    cursor =
+                                        movement::down(map, cursor, SelectionGoal::None, false).0;
+                                }
+                                cursor = movement::indented_line_beginning(map, cursor, true);
+                            } else if !is_multiline {
+                                cursor = movement::saturating_left(map, cursor)
+                            }
+                            cursors.push(cursor);
+                            if vim.state().mode == Mode::VisualBlock {
+                                break;
+                            }
+                        }
+
+                        cursors
+                    });
+                })
+            });
+        });
+        vim.switch_mode(Mode::Normal, true, cx);
+    });
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        state::Mode,
+        test::{NeovimBackedTestContext, VimTestContext},
+    };
+    use indoc::indoc;
+
+    #[gpui::test]
+    async fn test_paste(cx: &mut gpui::TestAppContext) {
+        let mut cx = NeovimBackedTestContext::new(cx).await;
+
+        // single line
+        cx.set_shared_state(indoc! {"
+            The quick brown
+            fox ˇjumps over
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["v", "w", "y"]).await;
+        cx.assert_shared_clipboard("jumps o").await;
+        cx.set_shared_state(indoc! {"
+            The quick brown
+            fox jumps oveˇr
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystroke("p").await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            fox jumps overjumps ˇo
+            the lazy dog"})
+            .await;
+
+        cx.set_shared_state(indoc! {"
+            The quick brown
+            fox jumps oveˇr
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystroke("shift-p").await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            fox jumps ovejumps ˇor
+            the lazy dog"})
+            .await;
+
+        // line mode
+        cx.set_shared_state(indoc! {"
+            The quick brown
+            fox juˇmps over
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["d", "d"]).await;
+        cx.assert_shared_clipboard("fox jumps over\n").await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            the laˇzy dog"})
+            .await;
+        cx.simulate_shared_keystroke("p").await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            the lazy dog
+            ˇfox jumps over"})
+            .await;
+        cx.simulate_shared_keystrokes(["k", "shift-p"]).await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            ˇfox jumps over
+            the lazy dog
+            fox jumps over"})
+            .await;
+
+        // multiline, cursor to first character of pasted text.
+        cx.set_shared_state(indoc! {"
+            The quick brown
+            fox jumps ˇover
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["v", "j", "y"]).await;
+        cx.assert_shared_clipboard("over\nthe lazy do").await;
+
+        cx.simulate_shared_keystroke("p").await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            fox jumps oˇover
+            the lazy dover
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["u", "shift-p"]).await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            fox jumps ˇover
+            the lazy doover
+            the lazy dog"})
+            .await;
+    }
+
+    #[gpui::test]
+    async fn test_paste_visual(cx: &mut gpui::TestAppContext) {
+        let mut cx = NeovimBackedTestContext::new(cx).await;
+
+        // copy in visual mode
+        cx.set_shared_state(indoc! {"
+                The quick brown
+                fox jˇumps over
+                the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["v", "i", "w", "y"]).await;
+        cx.assert_shared_state(indoc! {"
+                The quick brown
+                fox ˇjumps over
+                the lazy dog"})
+            .await;
+        // paste in visual mode
+        cx.simulate_shared_keystrokes(["w", "v", "i", "w", "p"])
+            .await;
+        cx.assert_shared_state(indoc! {"
+                The quick brown
+                fox jumps jumpˇs
+                the lazy dog"})
+            .await;
+        cx.assert_shared_clipboard("over").await;
+        // paste in visual line mode
+        cx.simulate_shared_keystrokes(["up", "shift-v", "shift-p"])
+            .await;
+        cx.assert_shared_state(indoc! {"
+            ˇover
+            fox jumps jumps
+            the lazy dog"})
+            .await;
+        cx.assert_shared_clipboard("over").await;
+        // paste in visual block mode
+        cx.simulate_shared_keystrokes(["ctrl-v", "down", "down", "p"])
+            .await;
+        cx.assert_shared_state(indoc! {"
+            oveˇrver
+            overox jumps jumps
+            overhe lazy dog"})
+            .await;
+
+        // copy in visual line mode
+        cx.set_shared_state(indoc! {"
+                The quick brown
+                fox juˇmps over
+                the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["shift-v", "d"]).await;
+        cx.assert_shared_state(indoc! {"
+                The quick brown
+                the laˇzy dog"})
+            .await;
+        // paste in visual mode
+        cx.simulate_shared_keystrokes(["v", "i", "w", "p"]).await;
+        cx.assert_shared_state(
+            &indoc! {"
+                The quick brown
+                the_
+                ˇfox jumps over
+                _dog"}
+            .replace("_", " "), // Hack for trailing whitespace
+        )
+        .await;
+        cx.assert_shared_clipboard("lazy").await;
+        cx.set_shared_state(indoc! {"
+            The quick brown
+            fox juˇmps over
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["shift-v", "d"]).await;
+        cx.assert_shared_state(indoc! {"
+            The quick brown
+            the laˇzy dog"})
+            .await;
+        // paste in visual line mode
+        cx.simulate_shared_keystrokes(["k", "shift-v", "p"]).await;
+        cx.assert_shared_state(indoc! {"
+            ˇfox jumps over
+            the lazy dog"})
+            .await;
+        cx.assert_shared_clipboard("The quick brown\n").await;
+    }
+
+    #[gpui::test]
+    async fn test_paste_visual_block(cx: &mut gpui::TestAppContext) {
+        let mut cx = NeovimBackedTestContext::new(cx).await;
+        // copy in visual block mode
+        cx.set_shared_state(indoc! {"
+            The ˇquick brown
+            fox jumps over
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["ctrl-v", "2", "j", "y"])
+            .await;
+        cx.assert_shared_clipboard("q\nj\nl").await;
+        cx.simulate_shared_keystrokes(["p"]).await;
+        cx.assert_shared_state(indoc! {"
+            The qˇquick brown
+            fox jjumps over
+            the llazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"])
+            .await;
+        cx.assert_shared_state(indoc! {"
+            The ˇq brown
+            fox jjjumps over
+            the lllazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"])
+            .await;
+
+        cx.set_shared_state(indoc! {"
+            The ˇquick brown
+            fox jumps over
+            the lazy dog"})
+            .await;
+        cx.simulate_shared_keystrokes(["ctrl-v", "j", "y"]).await;
+        cx.assert_shared_clipboard("q\nj").await;
+        cx.simulate_shared_keystrokes(["l", "ctrl-v", "2", "j", "shift-p"])
+            .await;
+        cx.assert_shared_state(indoc! {"
+            The qˇqick brown
+            fox jjmps over
+            the lzy dog"})
+            .await;
+
+        cx.simulate_shared_keystrokes(["shift-v", "p"]).await;
+        cx.assert_shared_state(indoc! {"
+            ˇq
+            j
+            fox jjmps over
+            the lzy dog"})
+            .await;
+    }
+
+    #[gpui::test]
+    async fn test_paste_indent(cx: &mut gpui::TestAppContext) {
+        let mut cx = VimTestContext::new_typescript(cx).await;
+
+        cx.set_state(
+            indoc! {"
+            class A {ˇ
+            }
+        "},
+            Mode::Normal,
+        );
+        cx.simulate_keystrokes(["o", "a", "(", ")", "{", "escape"]);
+        cx.assert_state(
+            indoc! {"
+            class A {
+                a()ˇ{}
+            }
+            "},
+            Mode::Normal,
+        );
+        // cursor goes to the first non-blank character in the line;
+        cx.simulate_keystrokes(["y", "y", "p"]);
+        cx.assert_state(
+            indoc! {"
+            class A {
+                a(){}
+                ˇa(){}
+            }
+            "},
+            Mode::Normal,
+        );
+        // indentation is preserved when pasting
+        cx.simulate_keystrokes(["u", "shift-v", "up", "y", "shift-p"]);
+        cx.assert_state(
+            indoc! {"
+                ˇclass A {
+                    a(){}
+                class A {
+                    a(){}
+                }
+                "},
+            Mode::Normal,
+        );
+    }
+}

crates/vim/src/object.rs 🔗

@@ -177,17 +177,18 @@ fn in_word(
     ignore_punctuation: bool,
 ) -> Option<Range<DisplayPoint>> {
     // Use motion::right so that we consider the character under the cursor when looking for the start
+    let language = map.buffer_snapshot.language_at(relative_to.to_point(map));
     let start = movement::find_preceding_boundary_in_line(
         map,
         right(map, relative_to, 1),
         |left, right| {
-            char_kind(left).coerce_punctuation(ignore_punctuation)
-                != char_kind(right).coerce_punctuation(ignore_punctuation)
+            char_kind(language, left).coerce_punctuation(ignore_punctuation)
+                != char_kind(language, right).coerce_punctuation(ignore_punctuation)
         },
     );
     let end = movement::find_boundary_in_line(map, relative_to, |left, right| {
-        char_kind(left).coerce_punctuation(ignore_punctuation)
-            != char_kind(right).coerce_punctuation(ignore_punctuation)
+        char_kind(language, left).coerce_punctuation(ignore_punctuation)
+            != char_kind(language, right).coerce_punctuation(ignore_punctuation)
     });
 
     Some(start..end)
@@ -210,10 +211,11 @@ fn around_word(
     relative_to: DisplayPoint,
     ignore_punctuation: bool,
 ) -> Option<Range<DisplayPoint>> {
+    let language = map.buffer_snapshot.language_at(relative_to.to_point(map));
     let in_word = map
         .chars_at(relative_to)
         .next()
-        .map(|(c, _)| char_kind(c) != CharKind::Whitespace)
+        .map(|(c, _)| char_kind(language, c) != CharKind::Whitespace)
         .unwrap_or(false);
 
     if in_word {
@@ -237,20 +239,21 @@ fn around_next_word(
     relative_to: DisplayPoint,
     ignore_punctuation: bool,
 ) -> Option<Range<DisplayPoint>> {
+    let language = map.buffer_snapshot.language_at(relative_to.to_point(map));
     // Get the start of the word
     let start = movement::find_preceding_boundary_in_line(
         map,
         right(map, relative_to, 1),
         |left, right| {
-            char_kind(left).coerce_punctuation(ignore_punctuation)
-                != char_kind(right).coerce_punctuation(ignore_punctuation)
+            char_kind(language, left).coerce_punctuation(ignore_punctuation)
+                != char_kind(language, right).coerce_punctuation(ignore_punctuation)
         },
     );
 
     let mut word_found = false;
     let end = movement::find_boundary(map, relative_to, |left, right| {
-        let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
-        let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
+        let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
+        let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
 
         let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n';
 

crates/vim/src/test.rs 🔗

@@ -261,3 +261,29 @@ async fn test_status_indicator(
         assert!(mode_indicator.read(cx).mode.is_some());
     });
 }
+
+#[gpui::test]
+async fn test_word_characters(cx: &mut gpui::TestAppContext) {
+    let mut cx = VimTestContext::new_typescript(cx).await;
+    cx.set_state(
+        indoc! { "
+        class A {
+            #ˇgoop = 99;
+            $ˇgoop () { return this.#gˇoop };
+        };
+        console.log(new A().$gooˇp())
+    "},
+        Mode::Normal,
+    );
+    cx.simulate_keystrokes(["v", "i", "w"]);
+    cx.assert_state(
+        indoc! {"
+        class A {
+            «#goopˇ» = 99;
+            «$goopˇ» () { return this.«#goopˇ» };
+        };
+        console.log(new A().«$goopˇ»())
+    "},
+        Mode::Visual,
+    )
+}

crates/vim/src/test/neovim_backed_test_context.rs 🔗

@@ -129,14 +129,23 @@ impl<'a> NeovimBackedTestContext<'a> {
 
     pub async fn assert_shared_state(&mut self, marked_text: &str) {
         let neovim = self.neovim_state().await;
-        if neovim != marked_text {
-            let initial_state = self
-                .last_set_state
-                .as_ref()
-                .unwrap_or(&"N/A".to_string())
-                .clone();
-            panic!(
-                indoc! {"Test is incorrect (currently expected != neovim state)
+        let editor = self.editor_state();
+        if neovim == marked_text && neovim == editor {
+            return;
+        }
+        let initial_state = self
+            .last_set_state
+            .as_ref()
+            .unwrap_or(&"N/A".to_string())
+            .clone();
+
+        let message = if neovim != marked_text {
+            "Test is incorrect (currently expected != neovim_state)"
+        } else {
+            "Editor does not match nvim behaviour"
+        };
+        panic!(
+            indoc! {"{}
                 # initial state:
                 {}
                 # keystrokes:
@@ -147,14 +156,59 @@ impl<'a> NeovimBackedTestContext<'a> {
                 {}
                 # zed state:
                 {}"},
-                initial_state,
-                self.recent_keystrokes.join(" "),
-                marked_text,
-                neovim,
-                self.editor_state(),
-            )
+            message,
+            initial_state,
+            self.recent_keystrokes.join(" "),
+            marked_text,
+            neovim,
+            editor
+        )
+    }
+
+    pub async fn assert_shared_clipboard(&mut self, text: &str) {
+        let neovim = self.neovim.read_register('"').await;
+        let editor = self
+            .platform()
+            .read_from_clipboard()
+            .unwrap()
+            .text()
+            .clone();
+
+        if text == neovim && text == editor {
+            return;
         }
-        self.assert_editor_state(marked_text)
+
+        let message = if neovim != text {
+            "Test is incorrect (currently expected != neovim)"
+        } else {
+            "Editor does not match nvim behaviour"
+        };
+
+        let initial_state = self
+            .last_set_state
+            .as_ref()
+            .unwrap_or(&"N/A".to_string())
+            .clone();
+
+        panic!(
+            indoc! {"{}
+                # initial state:
+                {}
+                # keystrokes:
+                {}
+                # currently expected:
+                {}
+                # neovim clipboard:
+                {}
+                # zed clipboard:
+                {}"},
+            message,
+            initial_state,
+            self.recent_keystrokes.join(" "),
+            text,
+            neovim,
+            editor
+        )
     }
 
     pub async fn neovim_state(&mut self) -> String {

crates/vim/src/test/neovim_connection.rs 🔗

@@ -40,6 +40,7 @@ pub enum NeovimData {
     Put { state: String },
     Key(String),
     Get { state: String, mode: Option<Mode> },
+    ReadRegister { name: char, value: String },
 }
 
 pub struct NeovimConnection {
@@ -221,6 +222,36 @@ impl NeovimConnection {
         );
     }
 
+    #[cfg(not(feature = "neovim"))]
+    pub async fn read_register(&mut self, register: char) -> String {
+        if let Some(NeovimData::Get { .. }) = self.data.front() {
+            self.data.pop_front();
+        };
+        if let Some(NeovimData::ReadRegister { name, value }) = self.data.pop_front() {
+            if name == register {
+                return value;
+            }
+        }
+
+        panic!("operation does not match recorded script. re-record with --features=neovim")
+    }
+
+    #[cfg(feature = "neovim")]
+    pub async fn read_register(&mut self, name: char) -> String {
+        let value = self
+            .nvim
+            .command_output(format!("echo getreg('{}')", name).as_str())
+            .await
+            .unwrap();
+
+        self.data.push_back(NeovimData::ReadRegister {
+            name,
+            value: value.clone(),
+        });
+
+        value
+    }
+
     #[cfg(feature = "neovim")]
     async fn read_position(&mut self, cmd: &str) -> u32 {
         self.nvim

crates/vim/src/test/vim_test_context.rs 🔗

@@ -16,8 +16,18 @@ pub struct VimTestContext<'a> {
 
 impl<'a> VimTestContext<'a> {
     pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
-        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
+        let lsp = EditorLspTestContext::new_rust(Default::default(), cx).await;
+        Self::new_with_lsp(lsp, enabled)
+    }
+
+    pub async fn new_typescript(cx: &'a mut gpui::TestAppContext) -> VimTestContext<'a> {
+        Self::new_with_lsp(
+            EditorLspTestContext::new_typescript(Default::default(), cx).await,
+            true,
+        )
+    }
 
+    pub fn new_with_lsp(mut cx: EditorLspTestContext<'a>, enabled: bool) -> VimTestContext<'a> {
         cx.update(|cx| {
             search::init(cx);
             crate::init(cx);

crates/vim/src/utils.rs 🔗

@@ -7,10 +7,16 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App
     let mut text = String::new();
     let mut clipboard_selections = Vec::with_capacity(selections.len());
     {
+        let mut is_first = true;
         for selection in selections.iter() {
-            let initial_len = text.len();
             let start = selection.start;
             let end = selection.end;
+            if is_first {
+                is_first = false;
+            } else {
+                text.push_str("\n");
+            }
+            let initial_len = text.len();
             for chunk in buffer.text_for_range(start..end) {
                 text.push_str(chunk);
             }

crates/vim/src/visual.rs 🔗

@@ -1,14 +1,14 @@
-use std::{borrow::Cow, cmp, sync::Arc};
+use std::{cmp, sync::Arc};
 
 use collections::HashMap;
 use editor::{
     display_map::{DisplaySnapshot, ToDisplayPoint},
     movement,
     scroll::autoscroll::Autoscroll,
-    Bias, ClipboardSelection, DisplayPoint, Editor,
+    Bias, DisplayPoint, Editor,
 };
 use gpui::{actions, AppContext, ViewContext, WindowContext};
-use language::{AutoindentMode, Selection, SelectionGoal};
+use language::{Selection, SelectionGoal};
 use workspace::Workspace;
 
 use crate::{
@@ -27,7 +27,6 @@ actions!(
         ToggleVisualBlock,
         VisualDelete,
         VisualYank,
-        VisualPaste,
         OtherEnd,
     ]
 );
@@ -47,7 +46,6 @@ pub fn init(cx: &mut AppContext) {
     cx.add_action(other_end);
     cx.add_action(delete);
     cx.add_action(yank);
-    cx.add_action(paste);
 }
 
 pub fn visual_motion(motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
@@ -331,110 +329,6 @@ pub fn yank(_: &mut Workspace, _: &VisualYank, cx: &mut ViewContext<Workspace>)
     });
 }
 
-pub fn paste(_: &mut Workspace, _: &VisualPaste, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.transact(cx, |editor, cx| {
-                if let Some(item) = cx.read_from_clipboard() {
-                    copy_selections_content(editor, editor.selections.line_mode, cx);
-                    let mut clipboard_text = Cow::Borrowed(item.text());
-                    if let Some(mut clipboard_selections) =
-                        item.metadata::<Vec<ClipboardSelection>>()
-                    {
-                        let (display_map, selections) = editor.selections.all_adjusted_display(cx);
-                        let all_selections_were_entire_line =
-                            clipboard_selections.iter().all(|s| s.is_entire_line);
-                        if clipboard_selections.len() != selections.len() {
-                            let mut newline_separated_text = String::new();
-                            let mut clipboard_selections =
-                                clipboard_selections.drain(..).peekable();
-                            let mut ix = 0;
-                            while let Some(clipboard_selection) = clipboard_selections.next() {
-                                newline_separated_text
-                                    .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
-                                ix += clipboard_selection.len;
-                                if clipboard_selections.peek().is_some() {
-                                    newline_separated_text.push('\n');
-                                }
-                            }
-                            clipboard_text = Cow::Owned(newline_separated_text);
-                        }
-
-                        let mut new_selections = Vec::new();
-                        editor.buffer().update(cx, |buffer, cx| {
-                            let snapshot = buffer.snapshot(cx);
-                            let mut start_offset = 0;
-                            let mut edits = Vec::new();
-                            for (ix, selection) in selections.iter().enumerate() {
-                                let to_insert;
-                                let linewise;
-                                if let Some(clipboard_selection) = clipboard_selections.get(ix) {
-                                    let end_offset = start_offset + clipboard_selection.len;
-                                    to_insert = &clipboard_text[start_offset..end_offset];
-                                    linewise = clipboard_selection.is_entire_line;
-                                    start_offset = end_offset;
-                                } else {
-                                    to_insert = clipboard_text.as_str();
-                                    linewise = all_selections_were_entire_line;
-                                }
-
-                                let mut selection = selection.clone();
-                                if !selection.reversed {
-                                    let adjusted = selection.end;
-                                    // If the selection is empty, move both the start and end forward one
-                                    // character
-                                    if selection.is_empty() {
-                                        selection.start = adjusted;
-                                        selection.end = adjusted;
-                                    } else {
-                                        selection.end = adjusted;
-                                    }
-                                }
-
-                                let range = selection.map(|p| p.to_point(&display_map)).range();
-
-                                let new_position = if linewise {
-                                    edits.push((range.start..range.start, "\n"));
-                                    let mut new_position = range.start;
-                                    new_position.column = 0;
-                                    new_position.row += 1;
-                                    new_position
-                                } else {
-                                    range.start
-                                };
-
-                                new_selections.push(selection.map(|_| new_position));
-
-                                if linewise && to_insert.ends_with('\n') {
-                                    edits.push((
-                                        range.clone(),
-                                        &to_insert[0..to_insert.len().saturating_sub(1)],
-                                    ))
-                                } else {
-                                    edits.push((range.clone(), to_insert));
-                                }
-
-                                if linewise {
-                                    edits.push((range.end..range.end, "\n"));
-                                }
-                            }
-                            drop(snapshot);
-                            buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
-                        });
-
-                        editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                            s.select(new_selections)
-                        });
-                    } else {
-                        editor.insert(&clipboard_text, cx);
-                    }
-                }
-            });
-        });
-        vim.switch_mode(Mode::Normal, true, cx);
-    });
-}
-
 pub(crate) fn visual_replace(text: Arc<str>, cx: &mut WindowContext) {
     Vim::update(cx, |vim, cx| {
         vim.update_active_editor(cx, |editor, cx| {
@@ -796,65 +690,6 @@ mod test {
             fox jumps o"}));
     }
 
-    #[gpui::test]
-    async fn test_visual_paste(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-        cx.set_state(
-            indoc! {"
-                The quick brown
-                fox «jumpsˇ» over
-                the lazy dog"},
-            Mode::Visual,
-        );
-        cx.simulate_keystroke("y");
-        cx.set_state(
-            indoc! {"
-                The quick brown
-                fox jumpˇs over
-                the lazy dog"},
-            Mode::Normal,
-        );
-        cx.simulate_keystroke("p");
-        cx.assert_state(
-            indoc! {"
-                The quick brown
-                fox jumpsjumpˇs over
-                the lazy dog"},
-            Mode::Normal,
-        );
-
-        cx.set_state(
-            indoc! {"
-                The quick brown
-                fox ju«mˇ»ps over
-                the lazy dog"},
-            Mode::VisualLine,
-        );
-        cx.simulate_keystroke("d");
-        cx.assert_state(
-            indoc! {"
-                The quick brown
-                the laˇzy dog"},
-            Mode::Normal,
-        );
-        cx.set_state(
-            indoc! {"
-                The quick brown
-                the «lazyˇ» dog"},
-            Mode::Visual,
-        );
-        cx.simulate_keystroke("p");
-        cx.assert_state(
-            &indoc! {"
-                The quick brown
-                the_
-                ˇfox jumps over
-                dog"}
-            .replace("_", " "), // Hack for trailing whitespace
-            Mode::Normal,
-        );
-    }
-
     #[gpui::test]
     async fn test_visual_block_mode(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;

crates/vim/test_data/test_p.json 🔗

@@ -1,13 +0,0 @@
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
-{"Key":"p"}
-{"Get":{"state":"The quick brown\nthe lazy dog\nˇfox jumps over","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"y"}
-{"Put":{"state":"The quick brown\nfox jumps oveˇr\nthe lazy dog"}}
-{"Key":"p"}
-{"Get":{"state":"The quick brown\nfox jumps overjumps ˇo\nthe lazy dog","mode":"Normal"}}

crates/vim/test_data/test_paste.json 🔗

@@ -0,0 +1,31 @@
+{"Put":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog"}}
+{"Key":"v"}
+{"Key":"w"}
+{"Key":"y"}
+{"ReadRegister":{"name":"\"","value":"jumps o"}}
+{"Put":{"state":"The quick brown\nfox jumps oveˇr\nthe lazy dog"}}
+{"Key":"p"}
+{"Get":{"state":"The quick brown\nfox jumps overjumps ˇo\nthe lazy dog","mode":"Normal"}}
+{"Put":{"state":"The quick brown\nfox jumps oveˇr\nthe lazy dog"}}
+{"Key":"shift-p"}
+{"Get":{"state":"The quick brown\nfox jumps ovejumps ˇor\nthe lazy dog","mode":"Normal"}}
+{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
+{"Key":"d"}
+{"Key":"d"}
+{"ReadRegister":{"name":"\"","value":"fox jumps over\n"}}
+{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
+{"Key":"p"}
+{"Get":{"state":"The quick brown\nthe lazy dog\nˇfox jumps over","mode":"Normal"}}
+{"Key":"k"}
+{"Key":"shift-p"}
+{"Get":{"state":"The quick brown\nˇfox jumps over\nthe lazy dog\nfox jumps over","mode":"Normal"}}
+{"Put":{"state":"The quick brown\nfox jumps ˇover\nthe lazy dog"}}
+{"Key":"v"}
+{"Key":"j"}
+{"Key":"y"}
+{"ReadRegister":{"name":"\"","value":"over\nthe lazy do"}}
+{"Key":"p"}
+{"Get":{"state":"The quick brown\nfox jumps oˇover\nthe lazy dover\nthe lazy dog","mode":"Normal"}}
+{"Key":"u"}
+{"Key":"shift-p"}
+{"Get":{"state":"The quick brown\nfox jumps ˇover\nthe lazy doover\nthe lazy dog","mode":"Normal"}}

crates/vim/test_data/test_paste_visual.json 🔗

@@ -0,0 +1,42 @@
+{"Put":{"state":"The quick brown\nfox jˇumps over\nthe lazy dog"}}
+{"Key":"v"}
+{"Key":"i"}
+{"Key":"w"}
+{"Key":"y"}
+{"Get":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog","mode":"Normal"}}
+{"Key":"w"}
+{"Key":"v"}
+{"Key":"i"}
+{"Key":"w"}
+{"Key":"p"}
+{"Get":{"state":"The quick brown\nfox jumps jumpˇs\nthe lazy dog","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"over"}}
+{"Key":"up"}
+{"Key":"shift-v"}
+{"Key":"shift-p"}
+{"Get":{"state":"ˇover\nfox jumps jumps\nthe lazy dog","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"over"}}
+{"Key":"ctrl-v"}
+{"Key":"down"}
+{"Key":"down"}
+{"Key":"p"}
+{"Get":{"state":"oveˇrver\noverox jumps jumps\noverhe lazy dog","mode":"Normal"}}
+{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
+{"Key":"shift-v"}
+{"Key":"d"}
+{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
+{"Key":"v"}
+{"Key":"i"}
+{"Key":"w"}
+{"Key":"p"}
+{"Get":{"state":"The quick brown\nthe \nˇfox jumps over\n dog","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"lazy"}}
+{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
+{"Key":"shift-v"}
+{"Key":"d"}
+{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
+{"Key":"k"}
+{"Key":"shift-v"}
+{"Key":"p"}
+{"Get":{"state":"ˇfox jumps over\nthe lazy dog","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"The quick brown\n"}}

crates/vim/test_data/test_paste_visual_block.json 🔗

@@ -0,0 +1,31 @@
+{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
+{"Key":"ctrl-v"}
+{"Key":"2"}
+{"Key":"j"}
+{"Key":"y"}
+{"ReadRegister":{"name":"\"","value":"q\nj\nl"}}
+{"Key":"p"}
+{"Get":{"state":"The qˇquick brown\nfox jjumps over\nthe llazy dog","mode":"Normal"}}
+{"Key":"v"}
+{"Key":"i"}
+{"Key":"w"}
+{"Key":"shift-p"}
+{"Get":{"state":"The ˇq brown\nfox jjjumps over\nthe lllazy dog","mode":"Normal"}}
+{"Key":"v"}
+{"Key":"i"}
+{"Key":"w"}
+{"Key":"shift-p"}
+{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
+{"Key":"ctrl-v"}
+{"Key":"j"}
+{"Key":"y"}
+{"ReadRegister":{"name":"\"","value":"q\nj"}}
+{"Key":"l"}
+{"Key":"ctrl-v"}
+{"Key":"2"}
+{"Key":"j"}
+{"Key":"shift-p"}
+{"Get":{"state":"The qˇqick brown\nfox jjmps over\nthe lzy dog","mode":"Normal"}}
+{"Key":"shift-v"}
+{"Key":"p"}
+{"Get":{"state":"ˇq\nj\nfox jjmps over\nthe lzy dog","mode":"Normal"}}

crates/vim/test_data/test_visual_paste.json 🔗

@@ -0,0 +1,26 @@
+{"Put":{"state":"The quick brown\nfox jˇumps over\nthe lazy dog"}}
+{"Key":"v"}
+{"Key":"i"}
+{"Key":"w"}
+{"Key":"y"}
+{"Get":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog","mode":"Normal"}}
+{"Key":"p"}
+{"Get":{"state":"The quick brown\nfox jjumpˇsumps over\nthe lazy dog","mode":"Normal"}}
+{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
+{"Key":"shift-v"}
+{"Key":"d"}
+{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
+{"Key":"v"}
+{"Key":"i"}
+{"Key":"w"}
+{"Key":"p"}
+{"Get":{"state":"The quick brown\nthe \nˇfox jumps over\n dog","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"lazy"}}
+{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
+{"Key":"shift-v"}
+{"Key":"d"}
+{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
+{"Key":"k"}
+{"Key":"shift-v"}
+{"Key":"p"}
+{"Get":{"state":"ˇfox jumps over\nthe lazy dog","mode":"Normal"}}

crates/welcome/src/welcome.rs 🔗

@@ -232,7 +232,7 @@ impl Item for WelcomePage {
         Some("Welcome to Zed!".into())
     }
 
-    fn tab_content<T: View>(
+    fn tab_content<T: 'static>(
         &self,
         _detail: Option<usize>,
         style: &theme::Tab,

crates/workspace/src/item.rs 🔗

@@ -101,7 +101,7 @@ pub trait Item: View {
     fn tab_description<'a>(&'a self, _: usize, _: &'a AppContext) -> Option<Cow<str>> {
         None
     }
-    fn tab_content<V: View>(
+    fn tab_content<V: 'static>(
         &self,
         detail: Option<usize>,
         style: &theme::Tab,
@@ -943,7 +943,7 @@ pub mod test {
             })
         }
 
-        fn tab_content<V: View>(
+        fn tab_content<V: 'static>(
             &self,
             detail: Option<usize>,
             _: &theme::Tab,

crates/workspace/src/pane.rs 🔗

@@ -1976,12 +1976,12 @@ impl NavHistoryState {
     }
 }
 
-pub struct PaneBackdrop<V: View> {
+pub struct PaneBackdrop<V> {
     child_view: usize,
     child: AnyElement<V>,
 }
 
-impl<V: View> PaneBackdrop<V> {
+impl<V> PaneBackdrop<V> {
     pub fn new(pane_item_view: usize, child: AnyElement<V>) -> Self {
         PaneBackdrop {
             child,
@@ -1990,7 +1990,7 @@ impl<V: View> PaneBackdrop<V> {
     }
 }
 
-impl<V: View> Element<V> for PaneBackdrop<V> {
+impl<V: 'static> Element<V> for PaneBackdrop<V> {
     type LayoutState = ();
 
     type PaintState = ();

crates/workspace/src/pane/dragged_item_receiver.rs 🔗

@@ -7,7 +7,7 @@ use gpui::{
     geometry::{rect::RectF, vector::Vector2F},
     platform::MouseButton,
     scene::MouseUp,
-    AppContext, Element, EventContext, MouseState, Quad, View, ViewContext, WeakViewHandle,
+    AppContext, Element, EventContext, MouseState, Quad, ViewContext, WeakViewHandle,
 };
 use project::ProjectEntryId;
 
@@ -107,7 +107,7 @@ where
     handler
 }
 
-pub fn handle_dropped_item<V: View>(
+pub fn handle_dropped_item<V: 'static>(
     event: MouseUp,
     workspace: WeakViewHandle<Workspace>,
     pane: &WeakViewHandle<Pane>,

crates/zed/Cargo.toml 🔗

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
 description = "The fast, collaborative code editor."
 edition = "2021"
 name = "zed"
-version = "0.101.0"
+version = "0.102.0"
 publish = false
 
 [lib]
@@ -93,7 +93,7 @@ postage.workspace = true
 rand.workspace = true
 regex.workspace = true
 rsa = "0.4"
-rust-embed = { version = "6.3", features = ["include-exclude"] }
+rust-embed = { version = "6.8.1" }
 serde.workspace = true
 serde_derive.workspace = true
 serde_json.workspace = true

crates/zed/src/languages/javascript/config.toml 🔗

@@ -13,6 +13,7 @@ brackets = [
     { start = "`", end = "`", close = true, newline = false, not_in = ["comment", "string"] },
     { start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
 ]
+word_characters = ["$", "#"]
 
 [overrides.element]
 line_comment = { remove = true }

crates/zed/src/languages/php/config.toml 🔗

@@ -10,3 +10,4 @@ brackets = [
     { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
 ]
 collapsed_placeholder = "/* ... */"
+word_characters = ["$"]

crates/zed/src/languages/tsx/config.toml 🔗

@@ -12,6 +12,7 @@ brackets = [
     { start = "`", end = "`", close = true, newline = false, not_in = ["string"] },
     { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
 ]
+word_characters = ["#", "$"]
 
 [overrides.element]
 line_comment = { remove = true }

crates/zed/src/languages/typescript/config.toml 🔗

@@ -12,3 +12,4 @@ brackets = [
     { start = "`", end = "`", close = true, newline = false, not_in = ["string"] },
     { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
 ]
+word_characters = ["#", "$"]

test.rs 🔗

@@ -0,0 +1,5670 @@
+#![feature(prelude_import)]
+#![allow(dead_code, unused_variables)]
+#[prelude_import]
+use std::prelude::rust_2021::*;
+#[macro_use]
+extern crate std;
+use color::black;
+use components::button;
+use element::Element;
+use frame::frame;
+use gpui::{
+    geometry::{rect::RectF, vector::vec2f},
+    platform::WindowOptions,
+};
+use log::LevelFilter;
+use simplelog::SimpleLogger;
+use themes::{rose_pine, ThemeColors};
+use view::view;
+mod adapter {
+    use crate::element::{LayoutContext, PaintContext};
+    use gpui::{geometry::rect::RectF, LayoutEngine};
+    use util::ResultExt;
+    use crate::element::AnyElement;
+    pub struct Adapter<V>(pub(crate) AnyElement<V>);
+    impl<V: 'static> gpui::Element<V> for Adapter<V> {
+        type LayoutState = Option<LayoutEngine>;
+        type PaintState = ();
+        fn layout(
+            &mut self,
+            constraint: gpui::SizeConstraint,
+            view: &mut V,
+            cx: &mut LayoutContext<V>,
+        ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
+            cx.push_layout_engine(LayoutEngine::new());
+            let node = self.0.layout(view, cx).log_err();
+            if let Some(node) = node {
+                let layout_engine = cx.layout_engine().unwrap();
+                layout_engine.compute_layout(node, constraint.max).log_err();
+            }
+            let layout_engine = cx.pop_layout_engine();
+            if true {
+                if !layout_engine.is_some() {
+                    ::core::panicking::panic("assertion failed: layout_engine.is_some()")
+                }
+            }
+            (constraint.max, layout_engine)
+        }
+        fn paint(
+            &mut self,
+            scene: &mut gpui::SceneBuilder,
+            bounds: RectF,
+            visible_bounds: RectF,
+            layout_engine: &mut Option<LayoutEngine>,
+            view: &mut V,
+            legacy_cx: &mut gpui::PaintContext<V>,
+        ) -> Self::PaintState {
+            legacy_cx.push_layout_engine(layout_engine.take().unwrap());
+            let mut cx = PaintContext::new(legacy_cx, scene);
+            self.0.paint(view, &mut cx).log_err();
+            *layout_engine = legacy_cx.pop_layout_engine();
+            if true {
+                if !layout_engine.is_some() {
+                    ::core::panicking::panic("assertion failed: layout_engine.is_some()")
+                }
+            }
+        }
+        fn rect_for_text_range(
+            &self,
+            range_utf16: std::ops::Range<usize>,
+            bounds: RectF,
+            visible_bounds: RectF,
+            layout: &Self::LayoutState,
+            paint: &Self::PaintState,
+            view: &V,
+            cx: &gpui::ViewContext<V>,
+        ) -> Option<RectF> {
+            ::core::panicking::panic("not yet implemented")
+        }
+        fn debug(
+            &self,
+            bounds: RectF,
+            layout: &Self::LayoutState,
+            paint: &Self::PaintState,
+            view: &V,
+            cx: &gpui::ViewContext<V>,
+        ) -> gpui::serde_json::Value {
+            ::core::panicking::panic("not yet implemented")
+        }
+    }
+}
+mod color {
+    #![allow(dead_code)]
+    use std::{num::ParseIntError, ops::Range};
+    use smallvec::SmallVec;
+    pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
+        let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
+        let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
+        let b = (hex & 0xFF) as f32 / 255.0;
+        Rgba { r, g, b, a: 1.0 }.into()
+    }
+    pub struct Rgba {
+        pub r: f32,
+        pub g: f32,
+        pub b: f32,
+        pub a: f32,
+    }
+    #[automatically_derived]
+    impl ::core::clone::Clone for Rgba {
+        #[inline]
+        fn clone(&self) -> Rgba {
+            let _: ::core::clone::AssertParamIsClone<f32>;
+            *self
+        }
+    }
+    #[automatically_derived]
+    impl ::core::marker::Copy for Rgba {}
+    #[automatically_derived]
+    impl ::core::default::Default for Rgba {
+        #[inline]
+        fn default() -> Rgba {
+            Rgba {
+                r: ::core::default::Default::default(),
+                g: ::core::default::Default::default(),
+                b: ::core::default::Default::default(),
+                a: ::core::default::Default::default(),
+            }
+        }
+    }
+    #[automatically_derived]
+    impl ::core::fmt::Debug for Rgba {
+        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+            ::core::fmt::Formatter::debug_struct_field4_finish(
+                f,
+                "Rgba",
+                "r",
+                &self.r,
+                "g",
+                &self.g,
+                "b",
+                &self.b,
+                "a",
+                &&self.a,
+            )
+        }
+    }
+    pub trait Lerp {
+        fn lerp(&self, level: f32) -> Hsla;
+    }
+    impl Lerp for Range<Hsla> {
+        fn lerp(&self, level: f32) -> Hsla {
+            let level = level.clamp(0., 1.);
+            Hsla {
+                h: self.start.h + (level * (self.end.h - self.start.h)),
+                s: self.start.s + (level * (self.end.s - self.start.s)),
+                l: self.start.l + (level * (self.end.l - self.start.l)),
+                a: self.start.a + (level * (self.end.a - self.start.a)),
+            }
+        }
+    }
+    impl From<gpui::color::Color> for Rgba {
+        fn from(value: gpui::color::Color) -> Self {
+            Self {
+                r: value.0.r as f32 / 255.0,
+                g: value.0.g as f32 / 255.0,
+                b: value.0.b as f32 / 255.0,
+                a: value.0.a as f32 / 255.0,
+            }
+        }
+    }
+    impl From<Hsla> for Rgba {
+        fn from(color: Hsla) -> Self {
+            let h = color.h;
+            let s = color.s;
+            let l = color.l;
+            let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
+            let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
+            let m = l - c / 2.0;
+            let cm = c + m;
+            let xm = x + m;
+            let (r, g, b) = match (h * 6.0).floor() as i32 {
+                0 | 6 => (cm, xm, m),
+                1 => (xm, cm, m),
+                2 => (m, cm, xm),
+                3 => (m, xm, cm),
+                4 => (xm, m, cm),
+                _ => (cm, m, xm),
+            };
+            Rgba { r, g, b, a: color.a }
+        }
+    }
+    impl TryFrom<&'_ str> for Rgba {
+        type Error = ParseIntError;
+        fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
+            let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
+            let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
+            let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
+            let a = if value.len() > 7 {
+                u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
+            } else {
+                1.0
+            };
+            Ok(Rgba { r, g, b, a })
+        }
+    }
+    impl Into<gpui::color::Color> for Rgba {
+        fn into(self) -> gpui::color::Color {
+            gpui::color::rgba(self.r, self.g, self.b, self.a)
+        }
+    }
+    pub struct Hsla {
+        pub h: f32,
+        pub s: f32,
+        pub l: f32,
+        pub a: f32,
+    }
+    #[automatically_derived]
+    impl ::core::default::Default for Hsla {
+        #[inline]
+        fn default() -> Hsla {
+            Hsla {
+                h: ::core::default::Default::default(),
+                s: ::core::default::Default::default(),
+                l: ::core::default::Default::default(),
+                a: ::core::default::Default::default(),
+            }
+        }
+    }
+    #[automatically_derived]
+    impl ::core::marker::Copy for Hsla {}
+    #[automatically_derived]
+    impl ::core::clone::Clone for Hsla {
+        #[inline]
+        fn clone(&self) -> Hsla {
+            let _: ::core::clone::AssertParamIsClone<f32>;
+            *self
+        }
+    }
+    #[automatically_derived]
+    impl ::core::fmt::Debug for Hsla {
+        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+            ::core::fmt::Formatter::debug_struct_field4_finish(
+                f,
+                "Hsla",
+                "h",
+                &self.h,
+                "s",
+                &self.s,
+                "l",
+                &self.l,
+                "a",
+                &&self.a,
+            )
+        }
+    }
+    #[automatically_derived]
+    impl ::core::marker::StructuralPartialEq for Hsla {}
+    #[automatically_derived]
+    impl ::core::cmp::PartialEq for Hsla {
+        #[inline]
+        fn eq(&self, other: &Hsla) -> bool {
+            self.h == other.h && self.s == other.s && self.l == other.l
+                && self.a == other.a
+        }
+    }
+    pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
+        Hsla {
+            h: h.clamp(0., 1.),
+            s: s.clamp(0., 1.),
+            l: l.clamp(0., 1.),
+            a: a.clamp(0., 1.),
+        }
+    }
+    pub fn black() -> Hsla {
+        Hsla { h: 0., s: 0., l: 0., a: 1. }
+    }
+    impl From<Rgba> for Hsla {
+        fn from(color: Rgba) -> Self {
+            let r = color.r;
+            let g = color.g;
+            let b = color.b;
+            let max = r.max(g.max(b));
+            let min = r.min(g.min(b));
+            let delta = max - min;
+            let l = (max + min) / 2.0;
+            let s = if l == 0.0 || l == 1.0 {
+                0.0
+            } else if l < 0.5 {
+                delta / (2.0 * l)
+            } else {
+                delta / (2.0 - 2.0 * l)
+            };
+            let h = if delta == 0.0 {
+                0.0
+            } else if max == r {
+                ((g - b) / delta).rem_euclid(6.0) / 6.0
+            } else if max == g {
+                ((b - r) / delta + 2.0) / 6.0
+            } else {
+                ((r - g) / delta + 4.0) / 6.0
+            };
+            Hsla { h, s, l, a: color.a }
+        }
+    }
+    impl Hsla {
+        /// Scales the saturation and lightness by the given values, clamping at 1.0.
+        pub fn scale_sl(mut self, s: f32, l: f32) -> Self {
+            self.s = (self.s * s).clamp(0., 1.);
+            self.l = (self.l * l).clamp(0., 1.);
+            self
+        }
+        /// Increases the saturation of the color by a certain amount, with a max
+        /// value of 1.0.
+        pub fn saturate(mut self, amount: f32) -> Self {
+            self.s += amount;
+            self.s = self.s.clamp(0.0, 1.0);
+            self
+        }
+        /// Decreases the saturation of the color by a certain amount, with a min
+        /// value of 0.0.
+        pub fn desaturate(mut self, amount: f32) -> Self {
+            self.s -= amount;
+            self.s = self.s.max(0.0);
+            if self.s < 0.0 {
+                self.s = 0.0;
+            }
+            self
+        }
+        /// Brightens the color by increasing the lightness by a certain amount,
+        /// with a max value of 1.0.
+        pub fn brighten(mut self, amount: f32) -> Self {
+            self.l += amount;
+            self.l = self.l.clamp(0.0, 1.0);
+            self
+        }
+        /// Darkens the color by decreasing the lightness by a certain amount,
+        /// with a max value of 0.0.
+        pub fn darken(mut self, amount: f32) -> Self {
+            self.l -= amount;
+            self.l = self.l.clamp(0.0, 1.0);
+            self
+        }
+    }
+    impl From<gpui::color::Color> for Hsla {
+        fn from(value: gpui::color::Color) -> Self {
+            Rgba::from(value).into()
+        }
+    }
+    impl Into<gpui::color::Color> for Hsla {
+        fn into(self) -> gpui::color::Color {
+            Rgba::from(self).into()
+        }
+    }
+    pub struct ColorScale {
+        colors: SmallVec<[Hsla; 2]>,
+        positions: SmallVec<[f32; 2]>,
+    }
+    pub fn scale<I, C>(colors: I) -> ColorScale
+    where
+        I: IntoIterator<Item = C>,
+        C: Into<Hsla>,
+    {
+        let mut scale = ColorScale {
+            colors: colors.into_iter().map(Into::into).collect(),
+            positions: SmallVec::new(),
+        };
+        let num_colors: f32 = scale.colors.len() as f32 - 1.0;
+        scale
+            .positions = (0..scale.colors.len())
+            .map(|i| i as f32 / num_colors)
+            .collect();
+        scale
+    }
+    impl ColorScale {
+        fn at(&self, t: f32) -> Hsla {
+            if true {
+                if !(0.0 <= t && t <= 1.0) {
+                    {
+                        ::core::panicking::panic_fmt(
+                            format_args!(
+                                "t value {0} is out of range. Expected value in range 0.0 to 1.0",
+                                t,
+                            ),
+                        );
+                    }
+                }
+            }
+            let position = match self
+                .positions
+                .binary_search_by(|a| a.partial_cmp(&t).unwrap())
+            {
+                Ok(index) | Err(index) => index,
+            };
+            let lower_bound = position.saturating_sub(1);
+            let upper_bound = position.min(self.colors.len() - 1);
+            let lower_color = &self.colors[lower_bound];
+            let upper_color = &self.colors[upper_bound];
+            match upper_bound.checked_sub(lower_bound) {
+                Some(0) | None => *lower_color,
+                Some(_) => {
+                    let interval_t = (t - self.positions[lower_bound])
+                        / (self.positions[upper_bound] - self.positions[lower_bound]);
+                    let h = lower_color.h + interval_t * (upper_color.h - lower_color.h);
+                    let s = lower_color.s + interval_t * (upper_color.s - lower_color.s);
+                    let l = lower_color.l + interval_t * (upper_color.l - lower_color.l);
+                    let a = lower_color.a + interval_t * (upper_color.a - lower_color.a);
+                    Hsla { h, s, l, a }
+                }
+            }
+        }
+    }
+}
+mod components {
+    use crate::{
+        element::{Element, ElementMetadata},
+        frame, text::ArcCow, themes::rose_pine,
+    };
+    use gpui::{platform::MouseButton, ViewContext};
+    use playground_macros::Element;
+    use std::{marker::PhantomData, rc::Rc};
+    struct ButtonHandlers<V, D> {
+        click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
+    }
+    impl<V, D> Default for ButtonHandlers<V, D> {
+        fn default() -> Self {
+            Self { click: None }
+        }
+    }
+    #[element_crate = "crate"]
+    pub struct Button<V: 'static, D: 'static> {
+        metadata: ElementMetadata<V>,
+        handlers: ButtonHandlers<V, D>,
+        label: Option<ArcCow<'static, str>>,
+        icon: Option<ArcCow<'static, str>>,
+        data: Rc<D>,
+        view_type: PhantomData<V>,
+    }
+    impl<V: 'static, D: 'static> crate::element::Element<V> for Button<V, D> {
+        type Layout = crate::element::AnyElement<V>;
+        fn declared_style(&mut self) -> &mut crate::style::OptionalStyle {
+            &mut self.metadata.style
+        }
+        fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
+            &mut self.metadata.handlers
+        }
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut crate::element::LayoutContext<V>,
+        ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+            let mut element = self.render(view, cx).into_any();
+            let node_id = element.layout(view, cx)?;
+            Ok((node_id, element))
+        }
+        fn paint<'a>(
+            &mut self,
+            layout: crate::element::Layout<'a, Self::Layout>,
+            view: &mut V,
+            cx: &mut crate::element::PaintContext<V>,
+        ) -> anyhow::Result<()> {
+            layout.from_element.paint(view, cx)?;
+            Ok(())
+        }
+    }
+    impl<V: 'static, D: 'static> crate::element::IntoElement<V> for Button<V, D> {
+        type Element = Self;
+        fn into_element(self) -> Self {
+            self
+        }
+    }
+    impl<V: 'static> Button<V, ()> {
+        fn new() -> Self {
+            Self {
+                metadata: Default::default(),
+                handlers: ButtonHandlers::default(),
+                label: None,
+                icon: None,
+                data: Rc::new(()),
+                view_type: PhantomData,
+            }
+        }
+        pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
+            Button {
+                metadata: Default::default(),
+                handlers: ButtonHandlers::default(),
+                label: self.label,
+                icon: self.icon,
+                data: Rc::new(data),
+                view_type: PhantomData,
+            }
+        }
+    }
+    impl<V: 'static, D: 'static> Button<V, D> {
+        pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
+            self.label = Some(label.into());
+            self
+        }
+        pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
+            self.icon = Some(icon.into());
+            self
+        }
+        pub fn click(
+            self,
+            handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static,
+        ) -> Self {
+            let data = self.data.clone();
+            Element::click(
+                self,
+                MouseButton::Left,
+                move |view, _, cx| {
+                    handler(view, data.as_ref(), cx);
+                },
+            )
+        }
+    }
+    pub fn button<V>() -> Button<V, ()> {
+        Button::new()
+    }
+    impl<V: 'static, D: 'static> Button<V, D> {
+        fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+            let button = frame()
+                .fill(rose_pine::dawn().error(0.5))
+                .h_4()
+                .children(self.label.clone());
+            if let Some(handler) = self.handlers.click.clone() {
+                let data = self.data.clone();
+                button
+                    .mouse_down(
+                        MouseButton::Left,
+                        move |view, event, cx| { handler(view, data.as_ref(), cx) },
+                    )
+            } else {
+                button
+            }
+        }
+    }
+}
+mod element {
+    use crate::{
+        adapter::Adapter, color::Hsla, hoverable::Hoverable,
+        style::{Display, Fill, OptionalStyle, Overflow, Position},
+    };
+    use anyhow::Result;
+    pub use gpui::LayoutContext;
+    use gpui::{
+        geometry::{DefinedLength, Length, OptionalPoint},
+        platform::{MouseButton, MouseButtonEvent},
+        EngineLayout, EventContext, RenderContext, ViewContext,
+    };
+    use playground_macros::tailwind_lengths;
+    use std::{
+        any::{Any, TypeId},
+        cell::Cell, rc::Rc,
+    };
+    pub use crate::paint_context::PaintContext;
+    pub use taffy::tree::NodeId;
+    pub struct Layout<'a, E: ?Sized> {
+        pub from_engine: EngineLayout,
+        pub from_element: &'a mut E,
+    }
+    pub struct ElementMetadata<V> {
+        pub style: OptionalStyle,
+        pub handlers: Vec<EventHandler<V>>,
+    }
+    pub struct EventHandler<V> {
+        handler: Rc<dyn Fn(&mut V, &dyn Any, &mut EventContext<V>)>,
+        event_type: TypeId,
+        outside_bounds: bool,
+    }
+    impl<V> Clone for EventHandler<V> {
+        fn clone(&self) -> Self {
+            Self {
+                handler: self.handler.clone(),
+                event_type: self.event_type,
+                outside_bounds: self.outside_bounds,
+            }
+        }
+    }
+    impl<V> Default for ElementMetadata<V> {
+        fn default() -> Self {
+            Self {
+                style: OptionalStyle::default(),
+                handlers: Vec::new(),
+            }
+        }
+    }
+    pub trait Element<V: 'static>: 'static {
+        type Layout: 'static;
+        fn declared_style(&mut self) -> &mut OptionalStyle;
+        fn computed_style(&mut self) -> &OptionalStyle {
+            self.declared_style()
+        }
+        fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut LayoutContext<V>,
+        ) -> Result<(NodeId, Self::Layout)>;
+        fn paint<'a>(
+            &mut self,
+            layout: Layout<Self::Layout>,
+            view: &mut V,
+            cx: &mut PaintContext<V>,
+        ) -> Result<()>;
+        /// Convert to a dynamically-typed element suitable for layout and paint.
+        fn into_any(self) -> AnyElement<V>
+        where
+            Self: 'static + Sized,
+        {
+            AnyElement {
+                element: Box::new(self) as Box<dyn ElementObject<V>>,
+                layout: None,
+            }
+        }
+        fn adapt(self) -> Adapter<V>
+        where
+            Self: Sized,
+            Self: Element<V>,
+        {
+            Adapter(self.into_any())
+        }
+        fn click(
+            self,
+            button: MouseButton,
+            handler: impl Fn(&mut V, &MouseButtonEvent, &mut ViewContext<V>) + 'static,
+        ) -> Self
+        where
+            Self: Sized,
+        {
+            let pressed: Rc<Cell<bool>> = Default::default();
+            self.mouse_down(
+                    button,
+                    {
+                        let pressed = pressed.clone();
+                        move |_, _, _| {
+                            pressed.set(true);
+                        }
+                    },
+                )
+                .mouse_up_outside(
+                    button,
+                    {
+                        let pressed = pressed.clone();
+                        move |_, _, _| {
+                            pressed.set(false);
+                        }
+                    },
+                )
+                .mouse_up(
+                    button,
+                    move |view, event, event_cx| {
+                        if pressed.get() {
+                            pressed.set(false);
+                            handler(view, event, event_cx);
+                        }
+                    },
+                )
+        }
+        fn mouse_down(
+            mut self,
+            button: MouseButton,
+            handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+        ) -> Self
+        where
+            Self: Sized,
+        {
+            self.handlers_mut()
+                .push(EventHandler {
+                    handler: Rc::new(move |view, event, event_cx| {
+                        let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+                        if event.button == button && event.is_down {
+                            handler(view, event, event_cx);
+                        }
+                    }),
+                    event_type: TypeId::of::<MouseButtonEvent>(),
+                    outside_bounds: false,
+                });
+            self
+        }
+        fn mouse_down_outside(
+            mut self,
+            button: MouseButton,
+            handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+        ) -> Self
+        where
+            Self: Sized,
+        {
+            self.handlers_mut()
+                .push(EventHandler {
+                    handler: Rc::new(move |view, event, event_cx| {
+                        let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+                        if event.button == button && event.is_down {
+                            handler(view, event, event_cx);
+                        }
+                    }),
+                    event_type: TypeId::of::<MouseButtonEvent>(),
+                    outside_bounds: true,
+                });
+            self
+        }
+        fn mouse_up(
+            mut self,
+            button: MouseButton,
+            handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+        ) -> Self
+        where
+            Self: Sized,
+        {
+            self.handlers_mut()
+                .push(EventHandler {
+                    handler: Rc::new(move |view, event, event_cx| {
+                        let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+                        if event.button == button && !event.is_down {
+                            handler(view, event, event_cx);
+                        }
+                    }),
+                    event_type: TypeId::of::<MouseButtonEvent>(),
+                    outside_bounds: false,
+                });
+            self
+        }
+        fn mouse_up_outside(
+            mut self,
+            button: MouseButton,
+            handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+        ) -> Self
+        where
+            Self: Sized,
+        {
+            self.handlers_mut()
+                .push(EventHandler {
+                    handler: Rc::new(move |view, event, event_cx| {
+                        let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
+                        if event.button == button && !event.is_down {
+                            handler(view, event, event_cx);
+                        }
+                    }),
+                    event_type: TypeId::of::<MouseButtonEvent>(),
+                    outside_bounds: true,
+                });
+            self
+        }
+        fn block(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().display = Some(Display::Block);
+            self
+        }
+        fn flex(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().display = Some(Display::Flex);
+            self
+        }
+        fn grid(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().display = Some(Display::Grid);
+            self
+        }
+        fn overflow_visible(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self
+                .declared_style()
+                .overflow = OptionalPoint {
+                x: Some(Overflow::Visible),
+                y: Some(Overflow::Visible),
+            };
+            self
+        }
+        fn overflow_hidden(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self
+                .declared_style()
+                .overflow = OptionalPoint {
+                x: Some(Overflow::Hidden),
+                y: Some(Overflow::Hidden),
+            };
+            self
+        }
+        fn overflow_scroll(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self
+                .declared_style()
+                .overflow = OptionalPoint {
+                x: Some(Overflow::Scroll),
+                y: Some(Overflow::Scroll),
+            };
+            self
+        }
+        fn overflow_x_visible(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().overflow.x = Some(Overflow::Visible);
+            self
+        }
+        fn overflow_x_hidden(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().overflow.x = Some(Overflow::Hidden);
+            self
+        }
+        fn overflow_x_scroll(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().overflow.x = Some(Overflow::Scroll);
+            self
+        }
+        fn overflow_y_visible(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().overflow.y = Some(Overflow::Visible);
+            self
+        }
+        fn overflow_y_hidden(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().overflow.y = Some(Overflow::Hidden);
+            self
+        }
+        fn overflow_y_scroll(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().overflow.y = Some(Overflow::Scroll);
+            self
+        }
+        fn relative(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().position = Some(Position::Relative);
+            self
+        }
+        fn absolute(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().position = Some(Position::Absolute);
+            self
+        }
+        fn inset_0(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(0.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_px(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(1.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_0_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.125).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_1(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.25).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_1_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.375).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_2(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.5).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_2_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.625).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_3(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.75).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_3_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.875).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_4(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.25).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_6(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.5).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_7(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.75).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_8(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_9(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.25).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_10(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.5).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_11(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.75).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_12(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_14(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.5).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_16(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(4.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_20(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(5.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_24(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(6.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_28(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(7.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_32(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(8.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_36(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(9.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_40(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(10.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_44(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(11.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_48(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(12.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_52(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(13.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_56(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(14.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_60(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(15.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_64(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(16.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_72(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(18.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_80(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(20.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_96(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(24.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_half(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_1_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_2_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_1_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_2_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_3_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_1_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(20.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_2_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(40.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_3_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(60.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_4_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(80.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_1_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_2_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_3_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_4_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_5_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_1_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(8.333333).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_2_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_3_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_4_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_5_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(41.666667).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_6_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_7_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(58.333333).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_8_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_9_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_10_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_11_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(91.666667).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn inset_full(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(100.).into();
+            {
+                let inset = self
+                    .computed_style()
+                    .inset
+                    .get_or_insert_with(Default::default);
+                inset.top = length;
+                inset.right = length;
+                inset.bottom = length;
+                inset.left = length;
+                self
+            }
+        }
+        fn w(mut self, width: impl Into<Length>) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().size.width = Some(width.into());
+            self
+        }
+        fn w_auto(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().size.width = Some(Length::Auto);
+            self
+        }
+        fn w_0(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(0.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_px(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(1.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_0_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.125).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_1(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.25).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_1_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.375).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_2(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.5).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_2_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.625).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_3(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.75).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_3_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.875).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_4(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.25).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_6(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.5).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_7(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.75).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_8(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_9(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.25).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_10(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.5).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_11(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.75).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_12(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_14(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.5).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_16(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(4.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_20(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(5.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_24(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(6.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_28(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(7.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_32(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(8.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_36(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(9.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_40(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(10.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_44(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(11.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_48(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(12.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_52(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(13.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_56(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(14.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_60(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(15.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_64(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(16.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_72(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(18.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_80(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(20.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_96(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(24.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_half(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_1_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_2_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_1_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_2_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_3_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_1_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(20.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_2_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(40.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_3_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(60.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_4_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(80.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_1_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_2_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_3_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_4_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_5_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_1_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(8.333333).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_2_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_3_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_4_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_5_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(41.666667).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_6_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_7_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(58.333333).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_8_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_9_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_10_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_11_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(91.666667).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn w_full(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(100.).into();
+            {
+                self.declared_style().size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_0(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(0.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_px(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(1.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_0_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.125).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_1(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.25).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_1_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.375).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_2(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.5).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_2_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.625).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_3(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.75).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_3_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.875).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_4(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.25).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_6(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.5).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_7(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.75).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_8(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_9(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.25).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_10(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.5).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_11(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.75).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_12(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_14(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.5).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_16(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(4.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_20(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(5.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_24(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(6.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_28(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(7.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_32(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(8.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_36(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(9.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_40(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(10.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_44(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(11.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_48(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(12.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_52(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(13.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_56(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(14.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_60(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(15.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_64(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(16.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_72(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(18.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_80(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(20.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_96(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(24.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_half(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_1_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_2_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_1_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_2_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_3_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_1_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(20.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_2_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(40.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_3_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(60.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_4_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(80.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_1_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_2_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_3_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_4_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_5_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_1_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(8.333333).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_2_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_3_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_4_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_5_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(41.666667).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_6_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_7_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(58.333333).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_8_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_9_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_10_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_11_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(91.666667).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn min_w_full(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(100.).into();
+            {
+                self.declared_style().min_size.width = Some(length);
+                self
+            }
+        }
+        fn h(mut self, height: impl Into<Length>) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().size.height = Some(height.into());
+            self
+        }
+        fn h_auto(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().size.height = Some(Length::Auto);
+            self
+        }
+        fn h_0(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Pixels(0.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_px(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Pixels(1.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_0_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(0.125).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_1(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(0.25).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_1_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(0.375).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_2(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(0.5).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_2_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(0.625).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_3(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(0.75).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_3_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(0.875).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_4(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(1.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(1.25).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_6(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(1.5).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_7(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(1.75).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_8(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(2.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_9(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(2.25).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_10(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(2.5).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_11(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(2.75).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_12(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(3.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_14(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(3.5).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_16(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(4.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_20(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(5.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_24(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(6.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_28(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(7.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_32(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(8.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_36(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(9.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_40(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(10.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_44(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(11.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_48(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(12.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_52(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(13.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_56(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(14.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_60(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(15.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_64(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(16.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_72(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(18.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_80(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(20.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_96(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Rems(24.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_half(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_1_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_2_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_1_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_2_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_3_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_1_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(20.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_2_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(40.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_3_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(60.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_4_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(80.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_1_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_2_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_3_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_4_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_5_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_1_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(8.333333).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_2_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_3_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_4_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_5_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(41.666667).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_6_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_7_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(58.333333).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_8_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_9_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_10_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_11_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(91.666667).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn h_full(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let height = DefinedLength::Percent(100.).into();
+            {
+                self.declared_style().size.height = Some(height);
+                self
+            }
+        }
+        fn min_h_0(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(0.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_px(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Pixels(1.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_0_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.125).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_1(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.25).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_1_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.375).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_2(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.5).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_2_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.625).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_3(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.75).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_3_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(0.875).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_4(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_5(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.25).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_6(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.5).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_7(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(1.75).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_8(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_9(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.25).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_10(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.5).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_11(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(2.75).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_12(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_14(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(3.5).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_16(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(4.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_20(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(5.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_24(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(6.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_28(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(7.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_32(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(8.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_36(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(9.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_40(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(10.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_44(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(11.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_48(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(12.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_52(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(13.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_56(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(14.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_60(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(15.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_64(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(16.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_72(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(18.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_80(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(20.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_96(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Rems(24.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_half(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_1_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_2_3rd(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_1_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_2_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_3_4th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_1_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(20.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_2_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(40.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_3_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(60.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_4_5th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(80.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_1_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_2_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_3_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_4_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_5_6th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_1_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(8.333333).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_2_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(16.666667).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_3_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(25.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_4_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(33.333333).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_5_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(41.666667).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_6_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(50.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_7_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(58.333333).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_8_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(66.666667).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_9_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(75.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_10_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(83.333333).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_11_12th(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(91.666667).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn min_h_full(mut self) -> Self
+        where
+            Self: Sized,
+        {
+            let length = DefinedLength::Percent(100.).into();
+            {
+                self.declared_style().min_size.height = Some(length);
+                self
+            }
+        }
+        fn hoverable(self) -> Hoverable<V, Self>
+        where
+            Self: Sized,
+        {
+            Hoverable::new(self)
+        }
+        fn fill(mut self, fill: impl Into<Fill>) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().fill = Some(Some(fill.into()));
+            self
+        }
+        fn text_color(mut self, color: impl Into<Hsla>) -> Self
+        where
+            Self: Sized,
+        {
+            self.declared_style().text_color = Some(Some(color.into()));
+            self
+        }
+    }
+    trait ElementObject<V> {
+        fn style(&mut self) -> &mut OptionalStyle;
+        fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut LayoutContext<V>,
+        ) -> Result<(NodeId, Box<dyn Any>)>;
+        fn paint(
+            &mut self,
+            layout: Layout<dyn Any>,
+            view: &mut V,
+            cx: &mut PaintContext<V>,
+        ) -> Result<()>;
+    }
+    impl<V: 'static, E: Element<V>> ElementObject<V> for E {
+        fn style(&mut self) -> &mut OptionalStyle {
+            Element::declared_style(self)
+        }
+        fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+            Element::handlers_mut(self)
+        }
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut LayoutContext<V>,
+        ) -> Result<(NodeId, Box<dyn Any>)> {
+            let (node_id, layout) = self.layout(view, cx)?;
+            let layout = Box::new(layout) as Box<dyn Any>;
+            Ok((node_id, layout))
+        }
+        fn paint(
+            &mut self,
+            layout: Layout<dyn Any>,
+            view: &mut V,
+            cx: &mut PaintContext<V>,
+        ) -> Result<()> {
+            let layout = Layout {
+                from_engine: layout.from_engine,
+                from_element: layout.from_element.downcast_mut::<E::Layout>().unwrap(),
+            };
+            self.paint(layout, view, cx)
+        }
+    }
+    /// A dynamically typed element.
+    pub struct AnyElement<V> {
+        element: Box<dyn ElementObject<V>>,
+        layout: Option<(NodeId, Box<dyn Any>)>,
+    }
+    impl<V: 'static> AnyElement<V> {
+        pub fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut LayoutContext<V>,
+        ) -> Result<NodeId> {
+            let pushed_text_style = self.push_text_style(cx);
+            let (node_id, layout) = self.element.layout(view, cx)?;
+            self.layout = Some((node_id, layout));
+            if pushed_text_style {
+                cx.pop_text_style();
+            }
+            Ok(node_id)
+        }
+        pub fn push_text_style(&mut self, cx: &mut impl RenderContext) -> bool {
+            let text_style = self.element.style().text_style();
+            if let Some(text_style) = text_style {
+                let mut current_text_style = cx.text_style();
+                text_style.apply(&mut current_text_style);
+                cx.push_text_style(current_text_style);
+                true
+            } else {
+                false
+            }
+        }
+        pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
+            let pushed_text_style = self.push_text_style(cx);
+            let (layout_node_id, element_layout) = self
+                .layout
+                .as_mut()
+                .expect("paint called before layout");
+            let layout = Layout {
+                from_engine: cx
+                    .layout_engine()
+                    .unwrap()
+                    .computed_layout(*layout_node_id)
+                    .expect(
+                        "you can currently only use playground elements within an adapter",
+                    ),
+                from_element: element_layout.as_mut(),
+            };
+            let style = self.element.style();
+            let fill_color = style.fill.flatten().and_then(|fill| fill.color());
+            if let Some(fill_color) = fill_color {
+                cx.scene
+                    .push_quad(gpui::scene::Quad {
+                        bounds: layout.from_engine.bounds,
+                        background: Some(fill_color.into()),
+                        border: Default::default(),
+                        corner_radii: Default::default(),
+                    });
+            }
+            for event_handler in self.element.handlers_mut().iter().cloned() {
+                let EngineLayout { order, bounds } = layout.from_engine;
+                let view_id = cx.view_id();
+                let view_event_handler = event_handler.handler.clone();
+                cx.scene
+                    .interactive_regions
+                    .push(gpui::scene::InteractiveRegion {
+                        order,
+                        bounds,
+                        outside_bounds: event_handler.outside_bounds,
+                        event_handler: Rc::new(move |view, event, window_cx, view_id| {
+                            let mut view_context = ViewContext::mutable(
+                                window_cx,
+                                view_id,
+                            );
+                            let mut event_context = EventContext::new(&mut view_context);
+                            view_event_handler(
+                                view.downcast_mut().unwrap(),
+                                event,
+                                &mut event_context,
+                            );
+                        }),
+                        event_type: event_handler.event_type,
+                        view_id,
+                    });
+            }
+            self.element.paint(layout, view, cx)?;
+            if pushed_text_style {
+                cx.pop_text_style();
+            }
+            Ok(())
+        }
+    }
+    impl<V: 'static> Element<V> for AnyElement<V> {
+        type Layout = ();
+        fn declared_style(&mut self) -> &mut OptionalStyle {
+            self.element.style()
+        }
+        fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+            self.element.handlers_mut()
+        }
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut LayoutContext<V>,
+        ) -> Result<(NodeId, Self::Layout)> {
+            Ok((self.layout(view, cx)?, ()))
+        }
+        fn paint(
+            &mut self,
+            layout: Layout<()>,
+            view: &mut V,
+            cx: &mut PaintContext<V>,
+        ) -> Result<()> {
+            self.paint(view, cx)
+        }
+    }
+    pub trait IntoElement<V: 'static> {
+        type Element: Element<V>;
+        fn into_element(self) -> Self::Element;
+        fn into_any_element(self) -> AnyElement<V>
+        where
+            Self: Sized,
+        {
+            self.into_element().into_any()
+        }
+    }
+}
+mod frame {
+    use crate::{
+        element::{
+            AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext,
+            NodeId, PaintContext,
+        },
+        style::{OptionalStyle, Style},
+    };
+    use anyhow::{anyhow, Result};
+    use gpui::LayoutNodeId;
+    use playground_macros::IntoElement;
+    #[element_crate = "crate"]
+    pub struct Frame<V: 'static> {
+        style: OptionalStyle,
+        handlers: Vec<EventHandler<V>>,
+        children: Vec<AnyElement<V>>,
+    }
+    impl<V: 'static> crate::element::IntoElement<V> for Frame<V> {
+        type Element = Self;
+        fn into_element(self) -> Self {
+            self
+        }
+    }
+    pub fn frame<V>() -> Frame<V> {
+        Frame {
+            style: OptionalStyle::default(),
+            handlers: Vec::new(),
+            children: Vec::new(),
+        }
+    }
+    impl<V: 'static> Element<V> for Frame<V> {
+        type Layout = ();
+        fn declared_style(&mut self) -> &mut OptionalStyle {
+            &mut self.style
+        }
+        fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+            &mut self.handlers
+        }
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut LayoutContext<V>,
+        ) -> Result<(NodeId, Self::Layout)> {
+            let child_layout_node_ids = self
+                .children
+                .iter_mut()
+                .map(|child| child.layout(view, cx))
+                .collect::<Result<Vec<LayoutNodeId>>>()?;
+            let rem_size = cx.rem_pixels();
+            let style: Style = self.style.into();
+            let node_id = cx
+                .layout_engine()
+                .ok_or_else(|| ::anyhow::__private::must_use({
+                    let error = ::anyhow::__private::format_err(
+                        format_args!("no layout engine"),
+                    );
+                    error
+                }))?
+                .add_node(style.to_taffy(rem_size), child_layout_node_ids)?;
+            Ok((node_id, ()))
+        }
+        fn paint(
+            &mut self,
+            layout: Layout<()>,
+            view: &mut V,
+            cx: &mut PaintContext<V>,
+        ) -> Result<()> {
+            for child in &mut self.children {
+                child.paint(view, cx)?;
+            }
+            Ok(())
+        }
+    }
+    impl<V: 'static> Frame<V> {
+        pub fn child(mut self, child: impl IntoElement<V>) -> Self {
+            self.children.push(child.into_any_element());
+            self
+        }
+        pub fn children<I, E>(mut self, children: I) -> Self
+        where
+            I: IntoIterator<Item = E>,
+            E: IntoElement<V>,
+        {
+            self.children.extend(children.into_iter().map(|e| e.into_any_element()));
+            self
+        }
+    }
+}
+mod hoverable {
+    use std::{cell::Cell, marker::PhantomData, rc::Rc};
+    use gpui::{
+        geometry::{rect::RectF, vector::Vector2F},
+        scene::MouseMove, EngineLayout,
+    };
+    use crate::{element::Element, style::{OptionalStyle, Style}};
+    pub struct Hoverable<V, E> {
+        hover_style: OptionalStyle,
+        computed_style: Option<Style>,
+        view_type: PhantomData<V>,
+        child: E,
+    }
+    impl<V, E> Hoverable<V, E> {
+        pub fn new(child: E) -> Self {
+            Self {
+                hover_style: OptionalStyle::default(),
+                computed_style: None,
+                view_type: PhantomData,
+                child,
+            }
+        }
+    }
+    impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
+        type Layout = E::Layout;
+        fn declared_style(&mut self) -> &mut OptionalStyle {
+            &mut self.hover_style
+        }
+        fn computed_style(&mut self) -> &OptionalStyle {
+            ::core::panicking::panic("not yet implemented")
+        }
+        fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
+            self.child.handlers_mut()
+        }
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut gpui::LayoutContext<V>,
+        ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+            self.child.layout(view, cx)
+        }
+        fn paint<'a>(
+            &mut self,
+            layout: crate::element::Layout<Self::Layout>,
+            view: &mut V,
+            cx: &mut crate::element::PaintContext<V>,
+        ) -> anyhow::Result<()> {
+            let EngineLayout { bounds, order } = layout.from_engine;
+            let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
+            let was_hovered = Rc::new(Cell::new(false));
+            self.child.paint(layout, view, cx)?;
+            cx.draw_interactive_region(
+                order,
+                window_bounds,
+                false,
+                move |view, event: &MouseMove, cx| {
+                    let is_hovered = bounds.contains_point(cx.mouse_position());
+                    if is_hovered != was_hovered.get() {
+                        was_hovered.set(is_hovered);
+                        cx.repaint();
+                    }
+                },
+            );
+            Ok(())
+        }
+    }
+}
+mod paint_context {
+    use std::{any::TypeId, rc::Rc};
+    use derive_more::{Deref, DerefMut};
+    use gpui::{geometry::rect::RectF, EventContext, RenderContext, ViewContext};
+    pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
+    pub use taffy::tree::NodeId;
+    pub struct PaintContext<'a, 'b, 'c, 'd, V> {
+        #[deref]
+        #[deref_mut]
+        pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
+        pub(crate) scene: &'d mut gpui::SceneBuilder,
+    }
+    impl<'a, 'b, 'c, 'd, V> ::core::ops::Deref for PaintContext<'a, 'b, 'c, 'd, V> {
+        type Target = &'d mut LegacyPaintContext<'a, 'b, 'c, V>;
+        #[inline]
+        fn deref(&self) -> &Self::Target {
+            &self.legacy_cx
+        }
+    }
+    impl<'a, 'b, 'c, 'd, V> ::core::ops::DerefMut for PaintContext<'a, 'b, 'c, 'd, V> {
+        #[inline]
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.legacy_cx
+        }
+    }
+    impl<V> RenderContext for PaintContext<'_, '_, '_, '_, V> {
+        fn text_style(&self) -> gpui::fonts::TextStyle {
+            self.legacy_cx.text_style()
+        }
+        fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
+            self.legacy_cx.push_text_style(style)
+        }
+        fn pop_text_style(&mut self) {
+            self.legacy_cx.pop_text_style()
+        }
+    }
+    impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
+        pub fn new(
+            legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
+            scene: &'d mut gpui::SceneBuilder,
+        ) -> Self {
+            Self { legacy_cx, scene }
+        }
+        pub fn draw_interactive_region<E: 'static>(
+            &mut self,
+            order: u32,
+            bounds: RectF,
+            outside_bounds: bool,
+            event_handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
+        ) {
+            self.scene
+                .interactive_regions
+                .push(gpui::scene::InteractiveRegion {
+                    order,
+                    bounds,
+                    outside_bounds,
+                    event_handler: Rc::new(move |view, event, window_cx, view_id| {
+                        let mut view_context = ViewContext::mutable(window_cx, view_id);
+                        let mut event_context = EventContext::new(&mut view_context);
+                        event_handler(
+                            view.downcast_mut().unwrap(),
+                            event.downcast_ref().unwrap(),
+                            &mut event_context,
+                        );
+                    }),
+                    event_type: TypeId::of::<E>(),
+                    view_id: self.view_id(),
+                });
+        }
+    }
+}
+mod style {
+    use crate::color::Hsla;
+    use gpui::geometry::{
+        DefinedLength, Edges, Length, OptionalEdges, OptionalPoint, OptionalSize, Point,
+        Size,
+    };
+    use optional::Optional;
+    pub use taffy::style::{
+        AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap,
+        JustifyContent, Overflow, Position,
+    };
+    pub struct Style {
+        /// What layout strategy should be used?
+        pub display: Display,
+        /// How children overflowing their container should affect layout
+        #[optional]
+        pub overflow: Point<Overflow>,
+        /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
+        pub scrollbar_width: f32,
+        /// What should the `position` value of this struct use as a base offset?
+        pub position: Position,
+        /// How should the position of this element be tweaked relative to the layout defined?
+        pub inset: Edges<Length>,
+        /// Sets the initial size of the item
+        #[optional]
+        pub size: Size<Length>,
+        /// Controls the minimum size of the item
+        #[optional]
+        pub min_size: Size<Length>,
+        /// Controls the maximum size of the item
+        #[optional]
+        pub max_size: Size<Length>,
+        /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
+        pub aspect_ratio: Option<f32>,
+        /// How large should the margin be on each side?
+        #[optional]
+        pub margin: Edges<Length>,
+        /// How large should the padding be on each side?
+        pub padding: Edges<DefinedLength>,
+        /// How large should the border be on each side?
+        pub border: Edges<DefinedLength>,
+        /// How this node's children aligned in the cross/block axis?
+        pub align_items: Option<AlignItems>,
+        /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
+        pub align_self: Option<AlignSelf>,
+        /// How should content contained within this item be aligned in the cross/block axis
+        pub align_content: Option<AlignContent>,
+        /// How should contained within this item be aligned in the main/inline axis
+        pub justify_content: Option<JustifyContent>,
+        /// How large should the gaps between items in a flex container be?
+        pub gap: Size<DefinedLength>,
+        /// Which direction does the main axis flow in?
+        pub flex_direction: FlexDirection,
+        /// Should elements wrap, or stay in a single line?
+        pub flex_wrap: FlexWrap,
+        /// Sets the initial main axis size of the item
+        pub flex_basis: Length,
+        /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
+        pub flex_grow: f32,
+        /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
+        pub flex_shrink: f32,
+        /// The fill color of this element
+        pub fill: Option<Fill>,
+        /// The color of text within this element. Cascades to children unless overridden.
+        pub text_color: Option<Hsla>,
+    }
+    #[automatically_derived]
+    impl ::core::clone::Clone for Style {
+        #[inline]
+        fn clone(&self) -> Style {
+            Style {
+                display: ::core::clone::Clone::clone(&self.display),
+                overflow: ::core::clone::Clone::clone(&self.overflow),
+                scrollbar_width: ::core::clone::Clone::clone(&self.scrollbar_width),
+                position: ::core::clone::Clone::clone(&self.position),
+                inset: ::core::clone::Clone::clone(&self.inset),
+                size: ::core::clone::Clone::clone(&self.size),
+                min_size: ::core::clone::Clone::clone(&self.min_size),
+                max_size: ::core::clone::Clone::clone(&self.max_size),
+                aspect_ratio: ::core::clone::Clone::clone(&self.aspect_ratio),
+                margin: ::core::clone::Clone::clone(&self.margin),
+                padding: ::core::clone::Clone::clone(&self.padding),
+                border: ::core::clone::Clone::clone(&self.border),
+                align_items: ::core::clone::Clone::clone(&self.align_items),
+                align_self: ::core::clone::Clone::clone(&self.align_self),
+                align_content: ::core::clone::Clone::clone(&self.align_content),
+                justify_content: ::core::clone::Clone::clone(&self.justify_content),
+                gap: ::core::clone::Clone::clone(&self.gap),
+                flex_direction: ::core::clone::Clone::clone(&self.flex_direction),
+                flex_wrap: ::core::clone::Clone::clone(&self.flex_wrap),
+                flex_basis: ::core::clone::Clone::clone(&self.flex_basis),
+                flex_grow: ::core::clone::Clone::clone(&self.flex_grow),
+                flex_shrink: ::core::clone::Clone::clone(&self.flex_shrink),
+                fill: ::core::clone::Clone::clone(&self.fill),
+                text_color: ::core::clone::Clone::clone(&self.text_color),
+            }
+        }
+    }
+    pub struct OptionalStyle {
+        pub display: Option<Display>,
+        pub overflow: OptionalPoint<Overflow>,
+        pub scrollbar_width: Option<f32>,
+        pub position: Option<Position>,
+        pub inset: Option<Edges<Length>>,
+        pub size: OptionalSize<Length>,
+        pub min_size: OptionalSize<Length>,
+        pub max_size: OptionalSize<Length>,
+        pub aspect_ratio: Option<Option<f32>>,
+        pub margin: OptionalEdges<Length>,
+        pub padding: Option<Edges<DefinedLength>>,
+        pub border: Option<Edges<DefinedLength>>,
+        pub align_items: Option<Option<AlignItems>>,
+        pub align_self: Option<Option<AlignSelf>>,
+        pub align_content: Option<Option<AlignContent>>,
+        pub justify_content: Option<Option<JustifyContent>>,
+        pub gap: Option<Size<DefinedLength>>,
+        pub flex_direction: Option<FlexDirection>,
+        pub flex_wrap: Option<FlexWrap>,
+        pub flex_basis: Option<Length>,
+        pub flex_grow: Option<f32>,
+        pub flex_shrink: Option<f32>,
+        pub fill: Option<Option<Fill>>,
+        pub text_color: Option<Option<Hsla>>,
+    }
+    #[automatically_derived]
+    impl ::core::default::Default for OptionalStyle {
+        #[inline]
+        fn default() -> OptionalStyle {
+            OptionalStyle {
+                display: ::core::default::Default::default(),
+                overflow: ::core::default::Default::default(),
+                scrollbar_width: ::core::default::Default::default(),
+                position: ::core::default::Default::default(),
+                inset: ::core::default::Default::default(),
+                size: ::core::default::Default::default(),
+                min_size: ::core::default::Default::default(),
+                max_size: ::core::default::Default::default(),
+                aspect_ratio: ::core::default::Default::default(),
+                margin: ::core::default::Default::default(),
+                padding: ::core::default::Default::default(),
+                border: ::core::default::Default::default(),
+                align_items: ::core::default::Default::default(),
+                align_self: ::core::default::Default::default(),
+                align_content: ::core::default::Default::default(),
+                justify_content: ::core::default::Default::default(),
+                gap: ::core::default::Default::default(),
+                flex_direction: ::core::default::Default::default(),
+                flex_wrap: ::core::default::Default::default(),
+                flex_basis: ::core::default::Default::default(),
+                flex_grow: ::core::default::Default::default(),
+                flex_shrink: ::core::default::Default::default(),
+                fill: ::core::default::Default::default(),
+                text_color: ::core::default::Default::default(),
+            }
+        }
+    }
+    #[automatically_derived]
+    impl ::core::clone::Clone for OptionalStyle {
+        #[inline]
+        fn clone(&self) -> OptionalStyle {
+            OptionalStyle {
+                display: ::core::clone::Clone::clone(&self.display),
+                overflow: ::core::clone::Clone::clone(&self.overflow),
+                scrollbar_width: ::core::clone::Clone::clone(&self.scrollbar_width),
+                position: ::core::clone::Clone::clone(&self.position),
+                inset: ::core::clone::Clone::clone(&self.inset),
+                size: ::core::clone::Clone::clone(&self.size),
+                min_size: ::core::clone::Clone::clone(&self.min_size),
+                max_size: ::core::clone::Clone::clone(&self.max_size),
+                aspect_ratio: ::core::clone::Clone::clone(&self.aspect_ratio),
+                margin: ::core::clone::Clone::clone(&self.margin),
+                padding: ::core::clone::Clone::clone(&self.padding),
+                border: ::core::clone::Clone::clone(&self.border),
+                align_items: ::core::clone::Clone::clone(&self.align_items),
+                align_self: ::core::clone::Clone::clone(&self.align_self),
+                align_content: ::core::clone::Clone::clone(&self.align_content),
+                justify_content: ::core::clone::Clone::clone(&self.justify_content),
+                gap: ::core::clone::Clone::clone(&self.gap),
+                flex_direction: ::core::clone::Clone::clone(&self.flex_direction),
+                flex_wrap: ::core::clone::Clone::clone(&self.flex_wrap),
+                flex_basis: ::core::clone::Clone::clone(&self.flex_basis),
+                flex_grow: ::core::clone::Clone::clone(&self.flex_grow),
+                flex_shrink: ::core::clone::Clone::clone(&self.flex_shrink),
+                fill: ::core::clone::Clone::clone(&self.fill),
+                text_color: ::core::clone::Clone::clone(&self.text_color),
+            }
+        }
+    }
+    impl Optional for OptionalStyle {
+        type Base = Style;
+        fn assign(&self, base: &mut Self::Base) {
+            if let Some(value) = self.display.clone() {
+                base.display = value;
+            }
+            if let Some(value) = self.overflow.clone() {
+                base.overflow = value;
+            }
+            if let Some(value) = self.scrollbar_width.clone() {
+                base.scrollbar_width = value;
+            }
+            if let Some(value) = self.position.clone() {
+                base.position = value;
+            }
+            if let Some(value) = self.inset.clone() {
+                base.inset = value;
+            }
+            if let Some(value) = self.size.clone() {
+                base.size = value;
+            }
+            if let Some(value) = self.min_size.clone() {
+                base.min_size = value;
+            }
+            if let Some(value) = self.max_size.clone() {
+                base.max_size = value;
+            }
+            if let Some(value) = self.aspect_ratio.clone() {
+                base.aspect_ratio = value;
+            }
+            if let Some(value) = self.margin.clone() {
+                base.margin = value;
+            }
+            if let Some(value) = self.padding.clone() {
+                base.padding = value;
+            }
+            if let Some(value) = self.border.clone() {
+                base.border = value;
+            }
+            if let Some(value) = self.align_items.clone() {
+                base.align_items = value;
+            }
+            if let Some(value) = self.align_self.clone() {
+                base.align_self = value;
+            }
+            if let Some(value) = self.align_content.clone() {
+                base.align_content = value;
+            }
+            if let Some(value) = self.justify_content.clone() {
+                base.justify_content = value;
+            }
+            if let Some(value) = self.gap.clone() {
+                base.gap = value;
+            }
+            if let Some(value) = self.flex_direction.clone() {
+                base.flex_direction = value;
+            }
+            if let Some(value) = self.flex_wrap.clone() {
+                base.flex_wrap = value;
+            }
+            if let Some(value) = self.flex_basis.clone() {
+                base.flex_basis = value;
+            }
+            if let Some(value) = self.flex_grow.clone() {
+                base.flex_grow = value;
+            }
+            if let Some(value) = self.flex_shrink.clone() {
+                base.flex_shrink = value;
+            }
+            if let Some(value) = self.fill.clone() {
+                base.fill = value;
+            }
+            if let Some(value) = self.text_color.clone() {
+                base.text_color = value;
+            }
+        }
+    }
+    impl From<OptionalStyle> for Style
+    where
+        Style: Default,
+    {
+        fn from(wrapper: OptionalStyle) -> Self {
+            let mut base = Self::default();
+            wrapper.assign(&mut base);
+            base
+        }
+    }
+    impl Style {
+        pub const DEFAULT: Style = Style {
+            display: Display::DEFAULT,
+            overflow: Point {
+                x: Overflow::Visible,
+                y: Overflow::Visible,
+            },
+            scrollbar_width: 0.0,
+            position: Position::Relative,
+            inset: Edges::auto(),
+            margin: Edges::<Length>::zero(),
+            padding: Edges::<DefinedLength>::zero(),
+            border: Edges::<DefinedLength>::zero(),
+            size: Size::auto(),
+            min_size: Size::auto(),
+            max_size: Size::auto(),
+            aspect_ratio: None,
+            gap: Size::zero(),
+            align_items: None,
+            align_self: None,
+            align_content: None,
+            justify_content: None,
+            flex_direction: FlexDirection::Row,
+            flex_wrap: FlexWrap::NoWrap,
+            flex_grow: 0.0,
+            flex_shrink: 1.0,
+            flex_basis: Length::Auto,
+            fill: None,
+            text_color: None,
+        };
+        pub fn new() -> Self {
+            Self::DEFAULT.clone()
+        }
+        pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
+            taffy::style::Style {
+                display: self.display,
+                overflow: self.overflow.clone().into(),
+                scrollbar_width: self.scrollbar_width,
+                position: self.position,
+                inset: self.inset.to_taffy(rem_size),
+                size: self.size.to_taffy(rem_size),
+                min_size: self.min_size.to_taffy(rem_size),
+                max_size: self.max_size.to_taffy(rem_size),
+                aspect_ratio: self.aspect_ratio,
+                margin: self.margin.to_taffy(rem_size),
+                padding: self.padding.to_taffy(rem_size),
+                border: self.border.to_taffy(rem_size),
+                align_items: self.align_items,
+                align_self: self.align_self,
+                align_content: self.align_content,
+                justify_content: self.justify_content,
+                gap: self.gap.to_taffy(rem_size),
+                flex_direction: self.flex_direction,
+                flex_wrap: self.flex_wrap,
+                flex_basis: self.flex_basis.to_taffy(rem_size).into(),
+                flex_grow: self.flex_grow,
+                flex_shrink: self.flex_shrink,
+                ..Default::default()
+            }
+        }
+    }
+    impl Default for Style {
+        fn default() -> Self {
+            Self::DEFAULT.clone()
+        }
+    }
+    impl OptionalStyle {
+        pub fn text_style(&self) -> Option<OptionalTextStyle> {
+            self.text_color.map(|color| OptionalTextStyle { color })
+        }
+    }
+    pub struct OptionalTextStyle {
+        color: Option<Hsla>,
+    }
+    impl OptionalTextStyle {
+        pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
+            if let Some(color) = self.color {
+                style.color = color.into();
+            }
+        }
+    }
+    pub enum Fill {
+        Color(Hsla),
+    }
+    #[automatically_derived]
+    impl ::core::clone::Clone for Fill {
+        #[inline]
+        fn clone(&self) -> Fill {
+            match self {
+                Fill::Color(__self_0) => {
+                    Fill::Color(::core::clone::Clone::clone(__self_0))
+                }
+            }
+        }
+    }
+    impl Fill {
+        pub fn color(&self) -> Option<Hsla> {
+            match self {
+                Fill::Color(color) => Some(*color),
+            }
+        }
+    }
+    impl Default for Fill {
+        fn default() -> Self {
+            Self::Color(Hsla::default())
+        }
+    }
+    impl From<Hsla> for Fill {
+        fn from(color: Hsla) -> Self {
+            Self::Color(color)
+        }
+    }
+}
+mod text {
+    use crate::{
+        element::{Element, ElementMetadata, EventHandler, IntoElement},
+        style::Style,
+    };
+    use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
+    use parking_lot::Mutex;
+    use std::sync::Arc;
+    impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
+        type Element = Text<V>;
+        fn into_element(self) -> Self::Element {
+            Text {
+                text: self.into(),
+                metadata: Default::default(),
+            }
+        }
+    }
+    pub struct Text<V> {
+        text: ArcCow<'static, str>,
+        metadata: ElementMetadata<V>,
+    }
+    impl<V: 'static> Element<V> for Text<V> {
+        type Layout = Arc<Mutex<Option<TextLayout>>>;
+        fn declared_style(&mut self) -> &mut crate::style::OptionalStyle {
+            &mut self.metadata.style
+        }
+        fn layout(
+            &mut self,
+            view: &mut V,
+            cx: &mut gpui::LayoutContext<V>,
+        ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+            let rem_size = cx.rem_pixels();
+            let fonts = cx.platform().fonts();
+            let text_style = cx.text_style();
+            let line_height = cx.font_cache().line_height(text_style.font_size);
+            let layout_engine = cx.layout_engine().expect("no layout engine present");
+            let text = self.text.clone();
+            let layout = Arc::new(Mutex::new(None));
+            let style: Style = self.metadata.style.into();
+            let node_id = layout_engine
+                .add_measured_node(
+                    style.to_taffy(rem_size),
+                    {
+                        let layout = layout.clone();
+                        move |params| {
+                            let line_layout = fonts
+                                .layout_line(
+                                    text.as_ref(),
+                                    text_style.font_size,
+                                    &[(text.len(), text_style.to_run())],
+                                );
+                            let size = Size {
+                                width: line_layout.width,
+                                height: line_height,
+                            };
+                            layout
+                                .lock()
+                                .replace(TextLayout {
+                                    line_layout: Arc::new(line_layout),
+                                    line_height,
+                                });
+                            size
+                        }
+                    },
+                )?;
+            Ok((node_id, layout))
+        }
+        fn paint<'a>(
+            &mut self,
+            layout: crate::element::Layout<Arc<Mutex<Option<TextLayout>>>>,
+            view: &mut V,
+            cx: &mut crate::element::PaintContext<V>,
+        ) -> anyhow::Result<()> {
+            let element_layout_lock = layout.from_element.lock();
+            let element_layout = element_layout_lock
+                .as_ref()
+                .expect("layout has not been performed");
+            let line_layout = element_layout.line_layout.clone();
+            let line_height = element_layout.line_height;
+            drop(element_layout_lock);
+            let text_style = cx.text_style();
+            let line = gpui::text_layout::Line::new(
+                line_layout,
+                &[(self.text.len(), text_style.to_run())],
+            );
+            line.paint(
+                cx.scene,
+                layout.from_engine.bounds.origin(),
+                layout.from_engine.bounds,
+                line_height,
+                cx.legacy_cx,
+            );
+            Ok(())
+        }
+        fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
+            &mut self.metadata.handlers
+        }
+    }
+    pub struct TextLayout {
+        line_layout: Arc<LineLayout>,
+        line_height: f32,
+    }
+    pub enum ArcCow<'a, T: ?Sized> {
+        Borrowed(&'a T),
+        Owned(Arc<T>),
+    }
+    impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
+        fn clone(&self) -> Self {
+            match self {
+                Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
+                Self::Owned(owned) => Self::Owned(owned.clone()),
+            }
+        }
+    }
+    impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
+        fn from(s: &'a T) -> Self {
+            Self::Borrowed(s)
+        }
+    }
+    impl<T> From<Arc<T>> for ArcCow<'_, T> {
+        fn from(s: Arc<T>) -> Self {
+            Self::Owned(s)
+        }
+    }
+    impl From<String> for ArcCow<'_, str> {
+        fn from(value: String) -> Self {
+            Self::Owned(value.into())
+        }
+    }
+    impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
+        type Target = T;
+        fn deref(&self) -> &Self::Target {
+            match self {
+                ArcCow::Borrowed(s) => s,
+                ArcCow::Owned(s) => s.as_ref(),
+            }
+        }
+    }
+    impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
+        fn as_ref(&self) -> &T {
+            match self {
+                ArcCow::Borrowed(borrowed) => borrowed,
+                ArcCow::Owned(owned) => owned.as_ref(),
+            }
+        }
+    }
+}
+mod themes {
+    use crate::color::{Hsla, Lerp};
+    use std::ops::Range;
+    pub mod rose_pine {
+        use std::ops::Range;
+        use crate::{
+            color::{hsla, rgb, Hsla},
+            ThemeColors,
+        };
+        pub struct RosePineThemes {
+            pub default: RosePinePalette,
+            pub dawn: RosePinePalette,
+            pub moon: RosePinePalette,
+        }
+        pub struct RosePinePalette {
+            pub base: Hsla,
+            pub surface: Hsla,
+            pub overlay: Hsla,
+            pub muted: Hsla,
+            pub subtle: Hsla,
+            pub text: Hsla,
+            pub love: Hsla,
+            pub gold: Hsla,
+            pub rose: Hsla,
+            pub pine: Hsla,
+            pub foam: Hsla,
+            pub iris: Hsla,
+            pub highlight_low: Hsla,
+            pub highlight_med: Hsla,
+            pub highlight_high: Hsla,
+        }
+        #[automatically_derived]
+        impl ::core::clone::Clone for RosePinePalette {
+            #[inline]
+            fn clone(&self) -> RosePinePalette {
+                let _: ::core::clone::AssertParamIsClone<Hsla>;
+                *self
+            }
+        }
+        #[automatically_derived]
+        impl ::core::marker::Copy for RosePinePalette {}
+        #[automatically_derived]
+        impl ::core::fmt::Debug for RosePinePalette {
+            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+                let names: &'static _ = &[
+                    "base",
+                    "surface",
+                    "overlay",
+                    "muted",
+                    "subtle",
+                    "text",
+                    "love",
+                    "gold",
+                    "rose",
+                    "pine",
+                    "foam",
+                    "iris",
+                    "highlight_low",
+                    "highlight_med",
+                    "highlight_high",
+                ];
+                let values: &[&dyn ::core::fmt::Debug] = &[
+                    &self.base,
+                    &self.surface,
+                    &self.overlay,
+                    &self.muted,
+                    &self.subtle,
+                    &self.text,
+                    &self.love,
+                    &self.gold,
+                    &self.rose,
+                    &self.pine,
+                    &self.foam,
+                    &self.iris,
+                    &self.highlight_low,
+                    &self.highlight_med,
+                    &&self.highlight_high,
+                ];
+                ::core::fmt::Formatter::debug_struct_fields_finish(
+                    f,
+                    "RosePinePalette",
+                    names,
+                    values,
+                )
+            }
+        }
+        impl RosePinePalette {
+            pub fn default() -> RosePinePalette {
+                RosePinePalette {
+                    base: rgb(0x191724),
+                    surface: rgb(0x1f1d2e),
+                    overlay: rgb(0x26233a),
+                    muted: rgb(0x6e6a86),
+                    subtle: rgb(0x908caa),
+                    text: rgb(0xe0def4),
+                    love: rgb(0xeb6f92),
+                    gold: rgb(0xf6c177),
+                    rose: rgb(0xebbcba),
+                    pine: rgb(0x31748f),
+                    foam: rgb(0x9ccfd8),
+                    iris: rgb(0xc4a7e7),
+                    highlight_low: rgb(0x21202e),
+                    highlight_med: rgb(0x403d52),
+                    highlight_high: rgb(0x524f67),
+                }
+            }
+            pub fn moon() -> RosePinePalette {
+                RosePinePalette {
+                    base: rgb(0x232136),
+                    surface: rgb(0x2a273f),
+                    overlay: rgb(0x393552),
+                    muted: rgb(0x6e6a86),
+                    subtle: rgb(0x908caa),
+                    text: rgb(0xe0def4),
+                    love: rgb(0xeb6f92),
+                    gold: rgb(0xf6c177),
+                    rose: rgb(0xea9a97),
+                    pine: rgb(0x3e8fb0),
+                    foam: rgb(0x9ccfd8),
+                    iris: rgb(0xc4a7e7),
+                    highlight_low: rgb(0x2a283e),
+                    highlight_med: rgb(0x44415a),
+                    highlight_high: rgb(0x56526e),
+                }
+            }
+            pub fn dawn() -> RosePinePalette {
+                RosePinePalette {
+                    base: rgb(0xfaf4ed),
+                    surface: rgb(0xfffaf3),
+                    overlay: rgb(0xf2e9e1),
+                    muted: rgb(0x9893a5),
+                    subtle: rgb(0x797593),
+                    text: rgb(0x575279),
+                    love: rgb(0xb4637a),
+                    gold: rgb(0xea9d34),
+                    rose: rgb(0xd7827e),
+                    pine: rgb(0x286983),
+                    foam: rgb(0x56949f),
+                    iris: rgb(0x907aa9),
+                    highlight_low: rgb(0xf4ede8),
+                    highlight_med: rgb(0xdfdad9),
+                    highlight_high: rgb(0xcecacd),
+                }
+            }
+        }
+        pub fn default() -> ThemeColors {
+            theme_colors(&RosePinePalette::default())
+        }
+        pub fn moon() -> ThemeColors {
+            theme_colors(&RosePinePalette::moon())
+        }
+        pub fn dawn() -> ThemeColors {
+            theme_colors(&RosePinePalette::dawn())
+        }
+        fn theme_colors(p: &RosePinePalette) -> ThemeColors {
+            ThemeColors {
+                base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
+                surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
+                overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
+                muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
+                subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
+                text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
+                highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
+                highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
+                highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
+                success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+                warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
+                error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+                inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
+                deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
+                modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
+            }
+        }
+        /// Produces a range by multiplying the saturation and lightness of the base color by the given
+        /// start and end factors.
+        fn scale_sl(
+            base: Hsla,
+            (start_s, start_l): (f32, f32),
+            (end_s, end_l): (f32, f32),
+        ) -> Range<Hsla> {
+            let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
+            let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
+            Range { start, end }
+        }
+    }
+    pub struct ThemeColors {
+        pub base: Range<Hsla>,
+        pub surface: Range<Hsla>,
+        pub overlay: Range<Hsla>,
+        pub muted: Range<Hsla>,
+        pub subtle: Range<Hsla>,
+        pub text: Range<Hsla>,
+        pub highlight_low: Range<Hsla>,
+        pub highlight_med: Range<Hsla>,
+        pub highlight_high: Range<Hsla>,
+        pub success: Range<Hsla>,
+        pub warning: Range<Hsla>,
+        pub error: Range<Hsla>,
+        pub inserted: Range<Hsla>,
+        pub deleted: Range<Hsla>,
+        pub modified: Range<Hsla>,
+    }
+    impl ThemeColors {
+        pub fn base(&self, level: f32) -> Hsla {
+            self.base.lerp(level)
+        }
+        pub fn surface(&self, level: f32) -> Hsla {
+            self.surface.lerp(level)
+        }
+        pub fn overlay(&self, level: f32) -> Hsla {
+            self.overlay.lerp(level)
+        }
+        pub fn muted(&self, level: f32) -> Hsla {
+            self.muted.lerp(level)
+        }
+        pub fn subtle(&self, level: f32) -> Hsla {
+            self.subtle.lerp(level)
+        }
+        pub fn text(&self, level: f32) -> Hsla {
+            self.text.lerp(level)
+        }
+        pub fn highlight_low(&self, level: f32) -> Hsla {
+            self.highlight_low.lerp(level)
+        }
+        pub fn highlight_med(&self, level: f32) -> Hsla {
+            self.highlight_med.lerp(level)
+        }
+        pub fn highlight_high(&self, level: f32) -> Hsla {
+            self.highlight_high.lerp(level)
+        }
+        pub fn success(&self, level: f32) -> Hsla {
+            self.success.lerp(level)
+        }
+        pub fn warning(&self, level: f32) -> Hsla {
+            self.warning.lerp(level)
+        }
+        pub fn error(&self, level: f32) -> Hsla {
+            self.error.lerp(level)
+        }
+        pub fn inserted(&self, level: f32) -> Hsla {
+            self.inserted.lerp(level)
+        }
+        pub fn deleted(&self, level: f32) -> Hsla {
+            self.deleted.lerp(level)
+        }
+        pub fn modified(&self, level: f32) -> Hsla {
+            self.modified.lerp(level)
+        }
+    }
+}
+mod view {
+    use crate::element::{AnyElement, Element};
+    use gpui::{Element as _, ViewContext};
+    pub fn view<F, E>(mut render: F) -> ViewFn
+    where
+        F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
+        E: Element<ViewFn>,
+    {
+        ViewFn(Box::new(move |cx| (render)(cx).into_any()))
+    }
+    pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
+    impl gpui::Entity for ViewFn {
+        type Event = ();
+    }
+    impl gpui::View for ViewFn {
+        fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
+            (self.0)(cx).adapt().into_any()
+        }
+    }
+}
+fn main() {
+    SimpleLogger::init(LevelFilter::Info, Default::default())
+        .expect("could not initialize logger");
+    gpui::App::new(())
+        .unwrap()
+        .run(|cx| {
+            cx.add_window(
+                WindowOptions {
+                    bounds: gpui::platform::WindowBounds::Fixed(
+                        RectF::new(vec2f(0., 0.), vec2f(400., 300.)),
+                    ),
+                    center: true,
+                    ..Default::default()
+                },
+                |_| view(|_| playground(&rose_pine::moon())),
+            );
+            cx.platform().activate(true);
+        });
+}
+fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
+    frame()
+        .text_color(black())
+        .h_full()
+        .w_half()
+        .fill(theme.success(0.5))
+        .child(
+            button()
+                .label("Hello")
+                .click(|_, _, _| {
+                    ::std::io::_print(format_args!("click!\n"));
+                }),
+        )
+}