From 60436515630ef254bfa17d51d1bb717a51d66f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Carter=20=E7=A5=81=E6=98=8E=E6=80=9D?= Date: Sat, 8 Aug 2026 07:48:02 +0300 Subject: [PATCH] Add letter spacing and text transforms to GPUI (#111) * Add text spacing and transform style plumbing * Apply byte-stable transforms and spacing-aware truncation * Add platform letter spacing support * Fix scheduler clippy warning * Refresh dependency lockfile * Fix CI for text spacing changes * Add text transform preview example --- Cargo.lock | 779 +++++++++--------- crates/gpui/Cargo.toml | 2 +- .../gpui/examples/text_transform_preview.rs | 84 ++ crates/gpui/src/elements/text.rs | 133 ++- crates/gpui/src/platform.rs | 24 +- crates/gpui/src/style.rs | 43 + crates/gpui/src/styled.rs | 14 +- crates/gpui/src/text_system.rs | 27 +- crates/gpui/src/text_system/line_layout.rs | 20 +- crates/gpui/src/text_system/line_wrapper.rs | 149 +++- .../src/editable_text/element.rs | 1 + crates/gpui_macos/src/platform.rs | 2 +- crates/gpui_macos/src/text_system.rs | 55 +- crates/gpui_scheduler/src/test_scheduler.rs | 2 +- crates/gpui_wgpu/benches/layout_line.rs | 2 + crates/gpui_wgpu/src/cosmic_text_system.rs | 16 +- crates/gpui_windows/src/direct_write.rs | 16 +- crates/gpui_zed_util/src/paths.rs | 1 - 18 files changed, 952 insertions(+), 418 deletions(-) create mode 100644 crates/gpui/examples/text_transform_preview.rs diff --git a/Cargo.lock b/Cargo.lock index 736c991615..93c3893a8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,12 +13,12 @@ dependencies = [ [[package]] name = "accesskit_atspi_common" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e98018dbef3583d751dbb96e07b8728fb99581360e1c3df408af16f4a80b821" +checksum = "023da0e5097f46df7092d5280b02efb9bbf8d93298daeced42652463e357d636" dependencies = [ "accesskit", - "accesskit_consumer 0.37.0", + "accesskit_consumer 0.38.0", "atspi-common", "phf", "serde", @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950720ce064757a1b629caad3a408e8d2c63bb01f29b8a3ff8daa331053ffeb" +checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245" dependencies = [ "accesskit", "hashbrown 0.16.1", @@ -47,12 +47,12 @@ dependencies = [ [[package]] name = "accesskit_macos" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cb8b66cef272d48161b02a6317cc2bdd5f98bb0a5e79c68f704a5862aa396b" +checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076" dependencies = [ "accesskit", - "accesskit_consumer 0.37.0", + "accesskit_consumer 0.38.0", "hashbrown 0.16.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "accesskit_unix" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5376ba4cc23312587634abb5250b1ce8618f01a55915608209aafd01efb4bf8c" +checksum = "03e156ed3802e35eefe894ef2671bc6c889303d8a7e110b5e1b48f504b91362f" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -166,9 +166,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -231,17 +231,17 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" dependencies = [ - "object", + "object 0.39.1", ] [[package]] @@ -258,7 +258,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.13.12" +version = "0.13.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281e6645758940dee594495e28807a7672ce40f11ebf4df6c22c4fcd59e2689f" +checksum = "fb8421aaa9644a5faf26735f258b669b15f063313ef8f8e2bdb28912a1a6f111" dependencies = [ "enumflags2", "futures-channel", @@ -341,9 +341,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" +checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" dependencies = [ "compression-codecs", "compression-core", @@ -359,7 +359,7 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.4.1", + "fastrand 2.5.0", "futures-lite 2.6.1", "pin-project-lite", "slab", @@ -442,7 +442,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -471,13 +471,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -490,7 +490,7 @@ dependencies = [ "crc32fast", "futures-lite 2.6.1", "pin-project", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -563,7 +563,7 @@ dependencies = [ "num-traits", "pastey", "rayon", - "thiserror 2.0.18", + "thiserror 2.0.19", "v_frame", "y4m", ] @@ -601,17 +601,11 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.37.3", "rustc-demangle", "windows-link 0.2.1", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -624,7 +618,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -635,7 +629,7 @@ dependencies = [ "regex", "rustc-hash 2.1.3", "shlex 1.3.0", - "syn", + "syn 2.0.119", ] [[package]] @@ -682,9 +676,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "bitstream-io" @@ -761,9 +755,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" dependencies = [ "bytes", "cfg_aliases", @@ -771,9 +765,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" dependencies = [ "memchr", "serde_core", @@ -793,22 +787,22 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.1" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5" +checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -835,7 +829,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "polling", "rustix 1.1.4", "slab", @@ -882,16 +876,16 @@ dependencies = [ "quote", "serde", "serde_json", - "syn", + "syn 2.0.119", "tempfile", "toml 0.8.23", ] [[package]] name = "cc" -version = "1.2.67" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" dependencies = [ "find-msvc-tools", "jobserver", @@ -916,9 +910,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "cgl" @@ -983,9 +977,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ "glob", "libc", @@ -994,18 +988,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstyle", "clap_lex", @@ -1039,7 +1033,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "cocoa-foundation 0.2.0", "core-foundation 0.10.0", @@ -1069,7 +1063,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "core-foundation 0.10.0", "core-graphics-types 0.2.0", @@ -1107,7 +1101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b60b5124979fccd9addd89d8b97a1d6eebb4950694520c75ddd722535ea443f" dependencies = [ "nix 0.31.3", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -1235,7 +1229,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.0", "core-graphics-types 0.2.0", "foreign-types", @@ -1248,7 +1242,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.9.4", "core-graphics-types 0.1.3", "foreign-types", @@ -1272,7 +1266,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.0", "libc", ] @@ -1283,7 +1277,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4416167a69126e617f8d0a214af0e3c1dbdeffcb100ddf72dcd1a1ac9893c146" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "cfg-if", "core-foundation 0.10.0", @@ -1331,7 +1325,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "fontdb", "harfrust", "linebender_resource_handle", @@ -1482,9 +1476,9 @@ dependencies = [ [[package]] name = "ctor" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb22e947478ccf9dc44d8922042c677a63fbb88f2cb468521d1145816e5087cb" +checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f" dependencies = [ "link-section", "linktime-proc-macro", @@ -1521,7 +1515,7 @@ dependencies = [ "defmt-parser", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1530,7 +1524,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -1552,7 +1546,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.119", "unicode-xid", ] @@ -1638,7 +1632,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -1646,13 +1640,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -1705,9 +1699,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "embed-resource" @@ -1718,7 +1712,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "vswhom", "winreg", ] @@ -1756,7 +1750,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1799,7 +1793,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1850,11 +1844,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -1897,9 +1890,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fax" @@ -1929,9 +1922,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" [[package]] name = "fixedbitset" @@ -2006,6 +1999,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "font-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7299a780854a6d391be2ae1c8521c9368471b559dbfd6a8dbd9f407eaff100" +dependencies = [ + "bytemuck", +] + [[package]] name = "fontconfig-parser" version = "0.5.8" @@ -2041,13 +2043,13 @@ dependencies = [ [[package]] name = "foreign-types-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -2088,9 +2090,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" dependencies = [ "futures-channel", "futures-core", @@ -2103,9 +2105,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -2126,15 +2128,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -2143,9 +2145,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-lite" @@ -2168,7 +2170,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.4.1", + "fastrand 2.5.0", "futures-core", "futures-io", "parking", @@ -2177,32 +2179,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-channel", "futures-core", @@ -2305,7 +2307,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddddbf932745a6be37109b6112d3ee09696106f848449069d3a57bba937ab82e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "libgit2-sys", "log", @@ -2324,15 +2326,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", @@ -2372,7 +2374,7 @@ dependencies = [ "hashbrown 0.16.1", "log", "presser", - "thiserror 2.0.18", + "thiserror 2.0.19", "windows 0.62.2", ] @@ -2382,7 +2384,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -2393,7 +2395,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -2406,7 +2408,7 @@ dependencies = [ "async-task", "backtrace", "bindgen", - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "cbindgen", "chrono", @@ -2472,7 +2474,7 @@ dependencies = [ "stacksafe", "strum", "taffy", - "thiserror 2.0.18", + "thiserror 2.0.19", "ttf-parser", "unicode-segmentation", "url", @@ -2508,7 +2510,7 @@ version = "0.2.2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2530,7 +2532,7 @@ dependencies = [ "anyhow", "as-raw-xcb-connection", "ashpd", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "calloop", "calloop-wayland-source", @@ -2622,7 +2624,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2848,7 +2850,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "core_maths", "read-fonts 0.37.0", @@ -2898,15 +2900,15 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hdrhistogram" -version = "7.5.4" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +checksum = "f49d1053f4708f0af3cf9fc5bffc7e68a914a3c45becb231c80068c9c3f78bea" dependencies = [ - "base64 0.21.7", + "base64", "byteorder", "crossbeam-channel", "flate2", - "nom 7.1.3", + "nom 8.0.0", "num-traits", ] @@ -2979,9 +2981,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -2989,9 +2991,9 @@ dependencies = [ [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "typenum", ] @@ -3143,7 +3145,7 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core 0.5.1", + "zune-core 0.5.3", "zune-jpeg 0.5.15", ] @@ -3208,7 +3210,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3303,11 +3305,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.32" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "961d16382652bfdd8c6f68b223b26a8c93e0d475c672f414411db31c6c5c900e" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" dependencies = [ "defmt", + "jiff-core", "jiff-static", "log", "portable-atomic", @@ -3316,14 +3319,24 @@ dependencies = [ ] [[package]] -name = "jiff-static" -version = "0.2.32" +name = "jiff-core" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3351,7 +3364,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3366,9 +3379,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -3432,9 +3445,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libfuzzer-sys" @@ -3448,9 +3461,9 @@ dependencies = [ [[package]] name = "libgit2-sys" -version = "0.18.5+1.9.4" +version = "0.18.7+1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005d6ae6eac1912906073e069f7db60b1fa98e052a68227824afe3e3a1c59ca2" +checksum = "23c7391e4b9f4ffab1a624223cc1d7385ff9a678f490768add717de7ea2f4d89" dependencies = [ "cc", "libc", @@ -3476,9 +3489,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "2026a5056764a10b2bf5d56488cba40da507f5493a6a429340e2004d9ed085fa" dependencies = [ "libc", ] @@ -3503,15 +3516,15 @@ checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" [[package]] name = "link-section" -version = "0.19.0" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e333fe507b738576d6da5bb3f1a7d7a1c80307ed9ef31624c057d844c19c93e9" +checksum = "5ee1a0d6e252afe82e7bc2db42fba60e02ddf3b1accaf8cb21d96e34ba61f3d4" [[package]] name = "linktime-proc-macro" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7b0a3383c2a1002d11349c92c85a666a5fb679e96c79d782cf0dbe557fd6ee" +checksum = "348d0075b1fc163b26d72a7f75fc5141daf2fd1bdf128d873cbaf6785d495bdf" [[package]] name = "linux-raw-sys" @@ -3593,7 +3606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7755f08423275157ad1680aaecc9ccb7e0cc633da3240fea2d1522935cc15c72" dependencies = [ "lyon_path", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -3702,7 +3715,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "core-graphics-types 0.2.0", "foreign-types", @@ -3761,7 +3774,7 @@ checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" dependencies = [ "arrayvec", "bit-set 0.9.1", - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3775,7 +3788,7 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.18", + "thiserror 2.0.19", "unicode-ident", ] @@ -3809,7 +3822,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "cfg_aliases", "libc", @@ -3821,7 +3834,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "cfg_aliases", "libc", @@ -3929,7 +3942,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4034,7 +4047,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -4060,7 +4073,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4072,7 +4085,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "dispatch2", "objc2 0.6.4", ] @@ -4101,7 +4114,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -4113,7 +4126,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2 0.6.4", "objc2-core-foundation", ] @@ -4124,7 +4137,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4136,7 +4149,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.6.2", "dispatch2", "objc2 0.6.4", @@ -4150,7 +4163,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4163,7 +4176,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -4197,6 +4210,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -4252,9 +4274,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "open" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5" +checksum = "f9cfef937e9c486488c7e3d949ae31c0f1d06bdacd75b99c086cb35356e30408" dependencies = [ "is-wsl", "libc", @@ -4387,7 +4409,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ - "fastrand 2.4.1", + "fastrand 2.5.0", "phf_shared", ] @@ -4401,7 +4423,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4436,7 +4458,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4452,7 +4474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.4.1", + "fastrand 2.5.0", "futures-io", ] @@ -4509,7 +4531,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "crc32fast", "fdeflate", "flate2", @@ -4544,9 +4566,9 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" [[package]] name = "portable-atomic-util" @@ -4615,7 +4637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[package]] @@ -4624,14 +4646,14 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -4652,7 +4674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -4663,7 +4685,7 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set 0.8.0", "bit-vec 0.8.0", - "bitflags 2.13.0", + "bitflags 2.13.1", "num-traits", "proptest-macro", "rand 0.9.5", @@ -4684,14 +4706,14 @@ dependencies = [ "convert_case 0.11.0", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "psm" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" dependencies = [ "ar_archive_writer", "cc", @@ -4758,18 +4780,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.39.4" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -4896,7 +4918,7 @@ dependencies = [ "rand 0.9.5", "rand_chacha 0.9.0", "simd_helpers", - "thiserror 2.0.18", + "thiserror 2.0.19", "v_frame", "wasm-bindgen", ] @@ -4922,7 +4944,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -4971,17 +4993,18 @@ checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" dependencies = [ "bytemuck", "core_maths", - "font-types", + "font-types 0.11.3", ] [[package]] name = "read-fonts" -version = "0.39.2" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ed38b89c2c77ff968c524145ad65fb010f38af5c7a224b53b81d47ac2daa81" +checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" dependencies = [ "bytemuck", - "font-types", + "font-types 0.12.2", + "once_cell", ] [[package]] @@ -4996,7 +5019,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -5018,34 +5041,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "regex" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -5055,9 +5078,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.15" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -5129,7 +5152,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn", + "syn 2.0.119", "walkdir", ] @@ -5177,7 +5200,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5190,7 +5213,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -5221,7 +5244,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "core_maths", "log", @@ -5250,9 +5273,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", "indexmap", @@ -5264,14 +5287,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 3.0.3", ] [[package]] @@ -5317,9 +5340,9 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -5333,9 +5356,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -5353,33 +5376,33 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_derive_internals" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -5393,9 +5416,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "indexmap", "itoa", @@ -5420,13 +5443,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -5499,9 +5522,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_helpers" @@ -5539,12 +5562,12 @@ dependencies = [ [[package]] name = "skrifa" -version = "0.42.1" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c34617370ae968efb7161bb2beb517d9084659aae19e24b89e3db25b46e4564" +checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" dependencies = [ "bytemuck", - "read-fonts 0.39.2", + "read-fonts 0.41.0", ] [[package]] @@ -5619,7 +5642,7 @@ version = "0.4.0+sdk-1.4.341.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -5630,9 +5653,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" dependencies = [ "cc", "cfg-if", @@ -5643,9 +5666,9 @@ dependencies = [ [[package]] name = "stacksafe" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b738765988c43975135c78ade8b0ee940566ab87d7e324897662b7460c44ab83" +checksum = "95f9c34983ac74195c710c473db6fdf1085f64a47dbaa0090d1bea03be70da66" dependencies = [ "stacker", "stacksafe-macro", @@ -5653,13 +5676,13 @@ dependencies = [ [[package]] name = "stacksafe-macro" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e30778b21fdee0c7dbfcf5cfc2d9e2a0dbc135faf7093c245bf19f088f9fa10" +checksum = "6feeae42a2d6b0dcb8aeb2f08d9e48cdac600239cf8a20fc59f9e252e86bdfe1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -5695,7 +5718,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -5706,34 +5729,35 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sval" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb9efbae90f97301f4d25f3be63dfd99d6b7af9d088228a52ec960d649b2e7d" +checksum = "ec4a2a7d92fa86fcc6222e4c3845f8486cff899d9db32480b26c91a5dbf2e22d" [[package]] name = "sval_buffer" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff5c0280ea0af40b3a1fd0b532680b5067482ffb81412ee66ec04b1d9952b49a" +checksum = "f4324db9ac500c609d659b752edf9c8abbf2233f8afd61a503fd6f88ed625032" dependencies = [ "sval", "sval_ref", + "zerocopy", ] [[package]] name = "sval_dynamic" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b9067f2e68f58e110cf8019a268057e3791cf74b0fdb1b3c7c6e49104f44e6" +checksum = "4046add0eecf55e680b9e207edf5fc7737b18a1d950db363d97e7f1b2d7c629c" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ebdbf0e4b175884aa587fcf551c16dabe245ea04235abdd8cbbd160b27ed5a" +checksum = "911a3486b5984a0a4f25edefcf2c2dba23654c29f63e75493b671d338bf24243" dependencies = [ "itoa", "ryu", @@ -5742,9 +5766,9 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e448d9fa216a6c16670b28d624fbbcf5c04e41eb187bd7c52e01222ffa72a12" +checksum = "da53aae7c737b5b5f1be4bcb0ff20e057bf6b2ee4e9d025560075c5830d09f95" dependencies = [ "itoa", "ryu", @@ -5753,9 +5777,9 @@ dependencies = [ [[package]] name = "sval_nested" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021de5b5c26efd544c694cef9b8a9abe8633481bf3be1ea145a18a740818b291" +checksum = "df24df43cbdc4bb8c9f5ed19d0d57dc8f60a1a4259cdce52d597fe774ad3a71f" dependencies = [ "sval", "sval_buffer", @@ -5764,18 +5788,18 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ef5ffec8bc52ded04ee424ab8d959e25e64fd40a48a16d21f8991ce824c1bb" +checksum = "2bebc17f0f1fad060e57b778728d41ef87627e9111a6365d7463472cb58fc1b3" dependencies = [ "sval", ] [[package]] name = "sval_serde" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b983833a8a2390f89ebcf9b9acd06883017014b4ffd72ee28e0c9de6852039f5" +checksum = "9f26fe3f6a68b40e6c8d654ea48c00e4316272fddf68c80493714c1b034ae70b" dependencies = [ "serde_core", "sval", @@ -5800,20 +5824,31 @@ dependencies = [ [[package]] name = "swash" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0811b01ca2c4e8718760713911feaf4675c24f94e50530a015ec646cfb622f7c" +checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" dependencies = [ - "skrifa 0.42.1", + "skrifa 0.44.0", "yazi", "zeno", ] [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -5828,7 +5863,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -5890,7 +5925,7 @@ version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.4.1", + "fastrand 2.5.0", "getrandom 0.4.3", "once_cell", "rustix 1.1.4", @@ -5928,11 +5963,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.19", ] [[package]] @@ -5943,18 +5978,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -6043,9 +6078,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "pin-project-lite", ] @@ -6064,9 +6099,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -6111,9 +6146,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap", "toml_datetime 1.1.1+spec-1.1.0", @@ -6123,9 +6158,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow 1.0.4", ] @@ -6138,9 +6173,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tracing" @@ -6162,7 +6197,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -6302,7 +6337,7 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" dependencies = [ - "base64 0.22.1", + "base64", "data-url", "flate2", "fontdb", @@ -6343,9 +6378,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.5" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -6367,9 +6402,9 @@ dependencies = [ [[package]] name = "value-bag" -version = "1.13.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd4ec1eb1d240636e354a30110a1dfcb37047169a4d9bd6d9d3469df574b5c4" +checksum = "068e763e8279de7ab94b6afebded2cb701678af094feb1c12ccb061b4783c1be" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -6377,9 +6412,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.13.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8e44fd7ce9cf838a1e2dad56d2d83f2b2435ae1df9cb3cac31e759e455e88d" +checksum = "417d6197dd0ee696783d6be4276ac6ea74b985e00024c85ccfb37aff4f2bed82" dependencies = [ "erased-serde", "serde_core", @@ -6388,9 +6423,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.13.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9f1705b87b798b06a8d7a51f44ed0626eee413991555e8edfd3562b35a130d" +checksum = "c61f7251ecde2c9ed431bbe0659853e7991753447447bbf1ae59d8b31c578d4e" dependencies = [ "sval", "sval_buffer", @@ -6475,9 +6510,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -6488,9 +6523,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -6498,9 +6533,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6508,22 +6543,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] @@ -6542,9 +6577,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8" dependencies = [ "cc", "downcast-rs", @@ -6556,11 +6591,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.14" +version = "0.31.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -6583,7 +6618,7 @@ version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6595,7 +6630,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6608,7 +6643,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6617,12 +6652,12 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", - "quick-xml 0.39.4", + "quick-xml 0.41.0", "quote", ] @@ -6640,9 +6675,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -6671,7 +6706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" dependencies = [ "arrayvec", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "cfg-if", "cfg_aliases", @@ -6703,7 +6738,7 @@ dependencies = [ "arrayvec", "bit-set 0.9.1", "bit-vec 0.9.1", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "cfg_aliases", "document-features", @@ -6718,7 +6753,7 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.19", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", @@ -6764,7 +6799,7 @@ dependencies = [ "arrayvec", "ash", "bit-set 0.9.1", - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.6.2", "bytemuck", "cfg-if", @@ -6797,7 +6832,7 @@ dependencies = [ "raw-window-metal", "renderdoc-sys", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.19", "wasm-bindgen", "wayland-sys", "web-sys", @@ -6824,7 +6859,7 @@ version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "js-sys", "log", @@ -6918,7 +6953,7 @@ checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" dependencies = [ "parking_lot", "rayon", - "thiserror 2.0.18", + "thiserror 2.0.19", "windows 0.61.3", "windows-future 0.2.1", ] @@ -7009,7 +7044,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7020,7 +7055,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7031,7 +7066,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7042,7 +7077,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7408,9 +7443,9 @@ dependencies = [ [[package]] name = "xcursor" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" +checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" [[package]] name = "xim-ctext" @@ -7427,7 +7462,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dcee45f89572d5a65180af3a84e7ddb24f5ea690a6d3aa9de231281544dd7b7" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -7450,9 +7485,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" [[package]] name = "xmlwriter" @@ -7508,15 +7543,15 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] [[package]] name = "zbus" -version = "5.17.0" +version = "5.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b97f866896a4be7aefd2b5a8e01bb6773d19a775d54ab28b4d094b9a4480e" +checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a" dependencies = [ "async-broadcast", "async-executor", @@ -7565,7 +7600,7 @@ checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "zbus-lockstep", "zbus_xml", "zvariant", @@ -7573,14 +7608,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.17.0" +version = "5.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e05ad887425eecf5e8384dc2406a4a9313eb73468712fc1cdea362eb4fe0469" +checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "zbus_names", "zvariant", "zvariant_utils", @@ -7588,9 +7623,9 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.3" +version = "4.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039ca249fee9559680f3a9f05b55e0761fee51af4f6c1e7d8c1f31e549721d2" +checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" dependencies = [ "serde", "winnow 1.0.4", @@ -7599,9 +7634,9 @@ dependencies = [ [[package]] name = "zbus_xml" -version = "5.2.0" +version = "5.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ab0513c0a66a60a8718d5ad712a5094845a20d95b5c1c81e2bd8e904a52b4f" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" dependencies = [ "serde", "winnow 1.0.4", @@ -7615,7 +7650,7 @@ version = "0.14.1-zed" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3898e450f36f852edda72e3f985c34426042c4951790b23b107f93394f9bff5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "byteorder", "core-foundation 0.10.0", "core-graphics 0.24.0", @@ -7678,22 +7713,22 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7713,7 +7748,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -7734,7 +7769,7 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7767,7 +7802,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -7784,9 +7819,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-core" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" [[package]] name = "zune-inflate" @@ -7812,14 +7847,14 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ - "zune-core 0.5.1", + "zune-core 0.5.3", ] [[package]] name = "zvariant" -version = "5.13.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf057bb00bf5c9ad77abb6147b0ca4818236a1858416e9d988e40d6322fefa7" +checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" dependencies = [ "endi", "enumflags2", @@ -7832,14 +7867,14 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.13.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8118ca6bda77bfc0ab51d660db0c955f2505eef854c9a449435bccb616933b31" +checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "zvariant_utils", ] @@ -7852,6 +7887,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.119", "winnow 1.0.4", ] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 648ce4a5f5..8ecdfbbf07 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -90,6 +90,7 @@ sum_tree.workspace = true taffy = "=0.10.1" thiserror.workspace = true gpui_util.workspace = true +unicode-segmentation.workspace = true hdrhistogram = { workspace = true, optional = true } waker-fn = "1.2.0" lyon = "1.0" @@ -147,7 +148,6 @@ lyon = { version = "1.0", features = ["extra"] } proptest = { workspace = true } rand.workspace = true scheduler = { workspace = true, features = ["test-support"] } -unicode-segmentation = { workspace = true } [target.'cfg(target_family = "wasm")'.dev-dependencies] diff --git a/crates/gpui/examples/text_transform_preview.rs b/crates/gpui/examples/text_transform_preview.rs new file mode 100644 index 0000000000..64481fbe9d --- /dev/null +++ b/crates/gpui/examples/text_transform_preview.rs @@ -0,0 +1,84 @@ +use gpui::{ + App, Bounds, Context, FontWeight, Render, TextTransform, Window, WindowBounds, WindowOptions, + div, prelude::*, px, rgb, size, +}; + +struct TextTransformPreview; + +impl Render for TextTransformPreview { + fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { + div() + .flex() + .flex_col() + .gap_4() + .bg(rgb(0x10141c)) + .size(px(720.)) + .p_8() + .text_color(rgb(0xe5e7eb)) + .child( + div() + .text_xl() + .font_weight(FontWeight::BOLD) + .child("Text spacing and transforms"), + ) + .child( + div() + .flex() + .flex_col() + .gap_2() + .p_4() + .bg(rgb(0x1f2937)) + .rounded_md() + .child(div().text_sm().text_color(rgb(0x9ca3af)).child("Uppercase")) + .child( + div() + .text_2xl() + .font_weight(FontWeight::SEMIBOLD) + .letter_spacing(px(3.)) + .text_transform(TextTransform::Uppercase) + .text_color(rgb(0x93c5fd)) + .child("letter spacing works"), + ), + ) + .child( + div() + .flex() + .flex_col() + .gap_2() + .p_4() + .bg(rgb(0x1f2937)) + .rounded_md() + .child( + div() + .text_sm() + .text_color(rgb(0x9ca3af)) + .child("Capitalize"), + ) + .child( + div() + .text_2xl() + .letter_spacing(px(1.5)) + .text_transform(TextTransform::Capitalize) + .text_color(rgb(0xfcd34d)) + .child("each word keeps its byte offsets"), + ), + ) + } +} + +fn main() { + gpui_platform::application().run(|cx: &mut App| { + cx.activate(true); + let bounds = Bounds::centered(None, size(px(720.), px(480.)), cx); + if let Err(error) = cx.open_window( + WindowOptions { + window_bounds: Some(WindowBounds::Windowed(bounds)), + ..Default::default() + }, + |_, cx| cx.new(|_| TextTransformPreview), + ) { + eprintln!("failed to open preview window: {error}"); + return; + } + }); +} diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index 20b933816c..5a78bac223 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -2,7 +2,7 @@ use crate::{ ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId, HighlightStyle, Hitbox, HitboxBehavior, InspectorElementId, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextOverflow, - TextRun, TextStyle, TooltipId, TruncateFrom, WhiteSpace, Window, WrappedLine, + TextRun, TextStyle, TextTransform, TooltipId, TruncateFrom, WhiteSpace, Window, WrappedLine, WrappedLineLayout, register_tooltip_mouse_handlers, set_tooltip_on_window, }; use anyhow::Context as _; @@ -17,6 +17,7 @@ use std::{ rc::Rc, sync::Arc, }; +use unicode_segmentation::UnicodeSegmentation; /// An [`Element`] that renders text. /// @@ -622,6 +623,134 @@ struct TextLayoutInner { bounds: Option>, } +fn apply_text_transform_preserving_byte_len( + text: SharedString, + transform: Option, +) -> SharedString { + let Some(transform) = transform else { + return text; + }; + if matches!(transform, TextTransform::None) { + return text; + } + + let mut output = String::with_capacity(text.len()); + match transform { + TextTransform::Uppercase => { + for character in text.as_ref().chars() { + push_case_mapped_character(&mut output, character, CaseMapKind::Upper); + } + } + TextTransform::Lowercase => { + for character in text.as_ref().chars() { + push_case_mapped_character(&mut output, character, CaseMapKind::Lower); + } + } + TextTransform::Capitalize => { + for piece in text.as_ref().split_word_bounds() { + let mut seen_first_letter = false; + for character in piece.chars() { + if !seen_first_letter && character.is_alphabetic() { + push_case_mapped_character(&mut output, character, CaseMapKind::Upper); + seen_first_letter = true; + } else { + output.push(character); + } + } + } + } + TextTransform::None => return text, + } + + SharedString::from(output) +} + +#[derive(Copy, Clone)] +enum CaseMapKind { + Upper, + Lower, +} + +fn push_case_mapped_character(output: &mut String, character: char, kind: CaseMapKind) { + let mapped = match kind { + CaseMapKind::Upper => character.to_uppercase().collect::(), + CaseMapKind::Lower => character.to_lowercase().collect::(), + }; + + if mapped.len() == character.len_utf8() && mapped.chars().count() == 1 { + output.push_str(&mapped); + } else { + output.push(character); + } +} + +#[cfg(test)] +mod text_transform_tests { + use super::apply_text_transform_preserving_byte_len; + use crate::{SharedString, TextTransform}; + + #[test] + fn text_transforms_preserve_bytes_and_spacing() { + let input = SharedString::from("hello WORLD\tfoo-bar 123baz déjà vu"); + let uppercase = + apply_text_transform_preserving_byte_len(input.clone(), Some(TextTransform::Uppercase)); + let lowercase = + apply_text_transform_preserving_byte_len(input.clone(), Some(TextTransform::Lowercase)); + let capitalize = apply_text_transform_preserving_byte_len( + input.clone(), + Some(TextTransform::Capitalize), + ); + + assert_eq!(uppercase.as_ref(), "HELLO WORLD\tFOO-BAR 123BAZ DÉJÀ VU"); + assert_eq!(lowercase.as_ref(), "hello world\tfoo-bar 123baz déjà vu"); + assert_eq!(capitalize.as_ref(), "Hello WORLD\tFoo-Bar 123Baz Déjà Vu"); + assert_eq!(input.len(), uppercase.len()); + assert_eq!(input.len(), lowercase.len()); + assert_eq!(input.len(), capitalize.len()); + } + + #[test] + fn text_transforms_skip_expanding_unicode_mappings() { + let input = SharedString::from("straße İSTANBUL"); + let uppercase = + apply_text_transform_preserving_byte_len(input.clone(), Some(TextTransform::Uppercase)); + let lowercase = + apply_text_transform_preserving_byte_len(input.clone(), Some(TextTransform::Lowercase)); + + assert_eq!(uppercase.as_ref(), "STRAßE İSTANBUL"); + assert_eq!(lowercase.as_ref(), "straße İstanbul"); + assert_eq!(input.len(), uppercase.len()); + assert_eq!(input.len(), lowercase.len()); + } + + #[test] + fn capitalize_preserves_letters_after_digit_prefix() { + let input = SharedString::from("123BAZ"); + let output = apply_text_transform_preserving_byte_len( + input.clone(), + Some(TextTransform::Capitalize), + ); + assert_eq!(output.as_ref(), "123BAZ"); + assert_eq!(input.len(), output.len()); + } + + #[test] + fn capitalize_does_not_fold_remaining_letters() { + let input = SharedString::from("foo2BAR"); + let output = + apply_text_transform_preserving_byte_len(input, Some(TextTransform::Capitalize)); + assert_eq!(output.as_ref(), "Foo2BAR"); + } + + #[test] + fn capitalize_handles_apostrophe_contractions() { + let input = SharedString::from("don't panic"); + let output = + apply_text_transform_preserving_byte_len(input, Some(TextTransform::Capitalize)); + assert_eq!(output.as_ref(), "Don't Panic"); + } +} + /// Metadata about how text should be truncated. Generated during text layout via `TextLayout::evaluate_overflow`. pub struct TextLayoutTruncation { /// The width that the text can occupy before it is truncated. @@ -711,6 +840,7 @@ impl TextLayout { cx: &mut App, ) -> (SharedString, Cow<'runs, [TextRun]>) { let mut line_wrapper = cx.text_system().line_wrapper(text_style.font(), font_size); + line_wrapper.set_letter_spacing(text_style.letter_spacing); if truncation.width.is_some() { if let Some(max_lines) = text_style.line_clamp && let Some(wrap_width) = wrap_width @@ -745,6 +875,7 @@ impl TextLayout { _: &mut App, ) -> LayoutId { let text_style = window.text_style(); + let text = apply_text_transform_preserving_byte_len(text, text_style.text_transform); let font_size = text_style.font_size.to_pixels(window.rem_size()); let line_height = window.pixel_snap( text_style diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 4f4ebbe467..2fbbea87a5 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -945,7 +945,7 @@ impl PlatformTextSystem for NoopTextSystem { Ok((raster_bounds.size, Vec::new())) } - fn layout_line(&self, text: &str, font_size: Pixels, _runs: &[FontRun]) -> LineLayout { + fn layout_line(&self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout { let mut position = px(0.); let metrics = self.font_metrics(FontId(0)); let em_width = font_size @@ -972,9 +972,9 @@ impl PlatformTextSystem for NoopTextSystem { position += em_width } } - let mut runs = Vec::default(); + let mut shaped_runs = Vec::default(); if !glyphs.is_empty() { - runs.push(ShapedRun { + shaped_runs.push(ShapedRun { font_id: FontId(0), glyphs, }); @@ -982,12 +982,26 @@ impl PlatformTextSystem for NoopTextSystem { position = px(0.); } + let mut tracking = px(0.); + let mut byte_offset = 0usize; + for run in font_runs { + let end = byte_offset.saturating_add(run.len).min(text.len()); + let slice = text.get(byte_offset..end).unwrap_or(""); + let n = slice.chars().count(); + if n > 1 { + if let Some(spacing) = run.letter_spacing { + tracking += spacing * (n - 1) as f32; + } + } + byte_offset = byte_offset.saturating_add(run.len); + } + LineLayout { font_size, - width: position, + width: position + tracking, ascent: font_size * (metrics.ascent / metrics.units_per_em as f32), descent: font_size * (metrics.descent / metrics.units_per_em as f32), - runs, + runs: shaped_runs, len: text.len(), } } diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index b9e8ae16df..0ae46cdade 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -441,6 +441,33 @@ pub enum TextAlign { Right, } +/// Case mapping applied at layout time while keeping **UTF-8 byte lengths** unchanged. +/// +/// [`TextTransform::Uppercase`] and [`TextTransform::Lowercase`] use Unicode full case folding via +/// [`char::to_uppercase`] / [`char::to_lowercase`]. If mapping a code point would change its UTF-8 +/// length, or would replace one scalar value with more than one character, the **original** +/// character is kept. That keeps indices into the underlying buffer aligned with hit-testing and +/// editor-style caret positions that use raw byte offsets. +/// +/// [`TextTransform::Capitalize`] matches CSS / Tailwind [`capitalize`][tw-cap] using Unicode word +/// boundaries: only the first alphabetic code point in each word is uppercased; other characters +/// are unchanged. +/// +/// [tw-cap]: https://tailwindcss.com/docs/text-transform +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +pub enum TextTransform { + /// Do not transform text. + #[default] + None, + /// Uppercase text (Unicode, byte-length preserving — see [`TextTransform`]). + Uppercase, + /// Lowercase text (Unicode, byte-length preserving — see [`TextTransform`]). + Lowercase, + /// `text-transform: capitalize` semantics (Tailwind class `capitalize`): per Unicode word, only + /// the first alphabetic character is mapped to uppercase; the rest of the string is unchanged. + Capitalize, +} + /// The properties that can be used to style text in GPUI #[derive(Refineable, Clone, Debug, PartialEq)] #[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] @@ -489,6 +516,14 @@ pub struct TextStyle { /// The number of lines to display before truncating the text pub line_clamp: Option, + + /// Letter spacing added between characters, in pixels (positive widens, negative tightens). + /// + /// The platform text stack may clamp values outside the range it supports. + pub letter_spacing: Option, + + /// Case transformation applied at layout time. + pub text_transform: Option, } impl Default for TextStyle { @@ -510,6 +545,8 @@ impl Default for TextStyle { text_overflow: None, text_align: TextAlign::default(), line_clamp: None, + letter_spacing: None, + text_transform: None, } } } @@ -579,12 +616,18 @@ impl TextStyle { background_color: self.background_color, underline: self.underline, strikethrough: self.strikethrough, + letter_spacing: self.letter_spacing, } } } /// A highlight style to apply, similar to a `TextStyle` except /// for a single font, uniformly sized and spaced text. +/// +/// Layout extras on the base [`TextStyle`] — such as [`TextStyle::letter_spacing`] and +/// [`TextStyle::text_transform`] — are not stored here; highlighted segments inherit them +/// from the surrounding style (see [`TextStyle::highlight`] and +/// [`crate::StyledText::with_default_highlights`]). #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct HighlightStyle { /// The color of the text diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index d95d444186..6bd0cf88ed 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -3,7 +3,7 @@ use crate::{ DefiniteLength, Display, Fill, Filter, FlexDirection, FlexWrap, Font, FontFeatures, FontStyle, FontWeight, GridPlacement, GridTemplate, Hsla, JustifyContent, Length, Pixels, SharedString, StrikethroughStyle, StyleRefinement, TemplateColumnMinSize, TextAlign, TextOverflow, - TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems, + TextStyleRefinement, TextTransform, UnderlineStyle, WhiteSpace, px, relative, rems, }; pub use gpui_macros::{ border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods, @@ -173,6 +173,18 @@ pub trait Styled: Sized { self.text_align(TextAlign::Right) } + /// Sets the letter spacing for text in this element and its children. + fn letter_spacing(mut self, spacing: impl Into) -> Self { + self.text_style().letter_spacing = Some(spacing.into()); + self + } + + /// Sets the case transformation for text in this element and its children. + fn text_transform(mut self, transform: TextTransform) -> Self { + self.text_style().text_transform = Some(transform); + self + } + /// Sets the truncate to prevent text from wrapping and truncate overflowing text with an ellipsis (…) if needed. /// [Docs](https://tailwindcss.com/docs/text-overflow#truncate) fn truncate(mut self) -> Self { diff --git a/crates/gpui/src/text_system.rs b/crates/gpui/src/text_system.rs index 82adf0c7bb..2cfa061321 100644 --- a/crates/gpui/src/text_system.rs +++ b/crates/gpui/src/text_system.rs @@ -215,6 +215,7 @@ impl TextSystem { &[FontRun { len: buffer.len(), font_id, + letter_spacing: None, }], ) .width @@ -359,6 +360,14 @@ impl TextSystem { } } +#[cfg(test)] +impl TextSystem { + /// Reach the platform shaper from crate tests (e.g. `line_wrapper`) without a [`WindowTextSystem`]. + pub(crate) fn platform_text_system_for_tests(&self) -> Arc { + self.platform_text_system.clone() + } +} + /// The GPUI text layout subsystem. #[derive(Deref)] pub struct WindowTextSystem { @@ -563,8 +572,10 @@ impl WindowTextSystem { }; let font_id = self.resolve_font(&run.font); + let letter_spacing = run.letter_spacing; if let Some(font_run) = font_runs.last_mut() && font_id == font_run.font_id + && font_run.letter_spacing == letter_spacing && !decoration_changed { font_run.len += run_len_within_line; @@ -572,6 +583,7 @@ impl WindowTextSystem { font_runs.push(FontRun { len: run_len_within_line, font_id, + letter_spacing, }); } @@ -677,8 +689,10 @@ impl WindowTextSystem { }; let font_id = self.resolve_font(&run.font); + let letter_spacing = run.letter_spacing; if let Some(font_run) = font_runs.last_mut() && font_id == font_run.font_id + && font_run.letter_spacing == letter_spacing && !decoration_changed { font_run.len += run.len; @@ -686,6 +700,7 @@ impl WindowTextSystem { font_runs.push(FontRun { len: run.len, font_id, + letter_spacing, }); } } @@ -713,6 +728,7 @@ impl WindowTextSystem { &[FontRun { len: buffer.len(), font_id, + letter_spacing: None, }], None, ) @@ -759,8 +775,10 @@ impl WindowTextSystem { }; let font_id = self.resolve_font(&run.font); + let letter_spacing = run.letter_spacing; if let Some(font_run) = font_runs.last_mut() && font_id == font_run.font_id + && font_run.letter_spacing == letter_spacing && !decoration_changed { font_run.len += run.len; @@ -768,6 +786,7 @@ impl WindowTextSystem { font_runs.push(FontRun { len: run.len, font_id, + letter_spacing, }); } } @@ -821,8 +840,10 @@ impl WindowTextSystem { }; let font_id = self.resolve_font(&run.font); + let letter_spacing = run.letter_spacing; if let Some(font_run) = font_runs.last_mut() && font_id == font_run.font_id + && font_run.letter_spacing == letter_spacing && !decoration_changed { font_run.len += run.len; @@ -830,6 +851,7 @@ impl WindowTextSystem { font_runs.push(FontRun { len: run.len, font_id, + letter_spacing, }); } } @@ -864,7 +886,8 @@ pub struct LineWrapperHandle { impl Drop for LineWrapperHandle { fn drop(&mut self) { let mut state = self.text_system.wrapper_pool.lock(); - let wrapper = self.wrapper.take().unwrap(); + let mut wrapper = self.wrapper.take().unwrap(); + wrapper.set_letter_spacing(None); state .get_mut(&FontIdWithSize { font_id: wrapper.font_id, @@ -1006,6 +1029,8 @@ pub struct TextRun { pub underline: Option, /// The strikethrough style (if any) pub strikethrough: Option, + /// Letter spacing applied between glyphs, in pixels. + pub letter_spacing: Option, } #[cfg(all(target_os = "macos", test))] diff --git a/crates/gpui/src/text_system/line_layout.rs b/crates/gpui/src/text_system/line_layout.rs index 5e85cefb8c..83765038ee 100644 --- a/crates/gpui/src/text_system/line_layout.rs +++ b/crates/gpui/src/text_system/line_layout.rs @@ -810,11 +810,29 @@ fn apply_force_width_to_layout(layout: &mut LineLayout, force_width: Pixels) { } /// A run of text with a single font. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[expect(missing_docs)] pub struct FontRun { pub len: usize, pub font_id: FontId, + pub letter_spacing: Option, +} + +impl Hash for FontRun { + fn hash(&self, state: &mut H) { + self.len.hash(state); + self.font_id.hash(state); + self.letter_spacing + .map(Pixels::as_f32) + .map(|value| { + if value == 0.0 { + 0.0f32.to_bits() + } else { + value.to_bits() + } + }) + .hash(state); + } } trait AsCacheKeyRef { diff --git a/crates/gpui/src/text_system/line_wrapper.rs b/crates/gpui/src/text_system/line_wrapper.rs index 846c3b3bd4..1eec3cff1a 100644 --- a/crates/gpui/src/text_system/line_wrapper.rs +++ b/crates/gpui/src/text_system/line_wrapper.rs @@ -16,6 +16,7 @@ pub struct LineWrapper { text_system: Arc, pub(crate) font_id: FontId, pub(crate) font_size: Pixels, + letter_spacing: Option, cached_ascii_char_widths: [Option; 128], cached_other_char_widths: HashMap, } @@ -29,11 +30,16 @@ impl LineWrapper { text_system, font_id, font_size, + letter_spacing: None, cached_ascii_char_widths: [None; 128], cached_other_char_widths: HashMap::default(), } } + pub(crate) fn set_letter_spacing(&mut self, letter_spacing: Option) { + self.letter_spacing = letter_spacing; + } + /// Wrap a line of text to the given width with this wrapper's font and font size. pub fn wrap_line<'a>( &'a mut self, @@ -48,6 +54,7 @@ impl LineWrapper { let mut last_wrap_ix = 0; let mut prev_c = '\0'; let mut index = 0; + let mut previous_text_character = false; let mut candidates = fragments .iter() .flat_map(move |fragment| fragment.wrap_boundary_candidates()) @@ -57,9 +64,11 @@ impl LineWrapper { let ix = index; index += candidate.len_utf8(); let mut new_prev_c = prev_c; + let mut item_had_spacing = false; let item_width = match candidate { WrapBoundaryCandidate::Char { character: c } => { if c == '\n' { + previous_text_character = false; continue; } @@ -82,7 +91,15 @@ impl LineWrapper { new_prev_c = c; - self.width_for_char(c) + let width = self.width_for_char(c); + let spacing = if previous_text_character { + self.letter_spacing.unwrap_or_default() + } else { + px(0.) + }; + item_had_spacing = previous_text_character; + previous_text_character = true; + width + spacing } WrapBoundaryCandidate::Element { width: element_width, @@ -113,10 +130,15 @@ impl LineWrapper { if last_candidate_ix > 0 { last_wrap_ix = last_candidate_ix; width -= last_candidate_width; + width -= self.letter_spacing.unwrap_or_default(); last_candidate_ix = 0; } else { last_wrap_ix = ix; - width = item_width; + width = if item_had_spacing { + item_width - self.letter_spacing.unwrap_or_default() + } else { + item_width + }; } if let Some(indent) = indent { @@ -144,20 +166,24 @@ impl LineWrapper { truncate_from: TruncateFrom, ) -> Option { let mut width = px(0.); - let suffix_width = truncation_affix - .chars() - .map(|c| self.width_for_char(c)) - .fold(px(0.0), |a, x| a + x); + let suffix_width = self.width_for_text(truncation_affix) + + if truncation_affix.is_empty() { + px(0.) + } else { + self.letter_spacing.unwrap_or_default() + }; let mut truncate_ix = 0; match truncate_from { TruncateFrom::Start => { + let mut previous_text_character = false; for (ix, c) in line.char_indices().rev() { if width + suffix_width < truncate_width { truncate_ix = ix; } - let char_width = self.width_for_char(c); + let char_width = + self.width_for_char_with_spacing(c, &mut previous_text_character); width += char_width; if width.floor() > truncate_width { @@ -166,12 +192,14 @@ impl LineWrapper { } } TruncateFrom::End => { + let mut previous_text_character = false; for (ix, c) in line.char_indices() { if width + suffix_width < truncate_width { truncate_ix = ix; } - let char_width = self.width_for_char(c); + let char_width = + self.width_for_char_with_spacing(c, &mut previous_text_character); width += char_width; if width.floor() > truncate_width { @@ -243,10 +271,12 @@ impl LineWrapper { ); } - let affix_width: Pixels = truncation_affix - .chars() - .map(|c| self.width_for_char(c)) - .sum(); + let affix_width = self.width_for_text(truncation_affix) + + if truncation_affix.is_empty() { + px(0.) + } else { + self.letter_spacing.unwrap_or_default() + }; let mut width = px(0.); let mut line = 0usize; @@ -257,6 +287,7 @@ impl LineWrapper { let mut prev_c = '\0'; let mut indent: Option = None; let mut truncate_ix = 0usize; + let mut previous_text_character = false; for (ix, c) in text.char_indices() { if c == '\n' { @@ -286,10 +317,11 @@ impl LineWrapper { prev_c = '\0'; indent = None; truncate_ix = ix + 1; + previous_text_character = false; continue; } - let char_width = self.width_for_char(c); + let char_width = self.width_for_char_with_spacing(c, &mut previous_text_character); if Self::is_word_char(c) { if prev_c == ' ' && first_non_whitespace_ix.is_some() { @@ -317,10 +349,11 @@ impl LineWrapper { if last_candidate_ix > last_wrap_ix { last_wrap_ix = last_candidate_ix; width -= last_candidate_width; + width -= self.letter_spacing.unwrap_or_default(); last_candidate_ix = 0; } else { last_wrap_ix = ix; - width = char_width; + width = self.width_for_char(c); } if let Some(ind) = indent { @@ -416,6 +449,30 @@ impl LineWrapper { width } } + + fn width_for_char_with_spacing( + &mut self, + c: char, + previous_text_character: &mut bool, + ) -> Pixels { + let width = self.width_for_char(c); + let spacing = if *previous_text_character { + self.letter_spacing.unwrap_or_default() + } else { + px(0.) + }; + *previous_text_character = true; + width + spacing + } + + fn width_for_text(&mut self, text: &str) -> Pixels { + let mut previous_text_character = false; + text.chars() + .map(|character| { + self.width_for_char_with_spacing(character, &mut previous_text_character) + }) + .sum() + } } fn update_runs_after_truncation( @@ -528,9 +585,12 @@ impl Boundary { #[cfg(test)] mod tests { use super::*; - use crate::{Font, FontFeatures, FontStyle, FontWeight, TestAppContext, TestDispatcher, font}; + use crate::{ + Font, FontFeatures, FontRun, FontStyle, FontWeight, Hsla, TestAppContext, TestDispatcher, + TextRun, font, + }; #[cfg(target_os = "macos")] - use crate::{TextRun, WindowTextSystem, WrapBoundary}; + use crate::{WindowTextSystem, WrapBoundary}; fn build_wrapper() -> LineWrapper { let dispatcher = TestDispatcher::new(0); @@ -556,6 +616,63 @@ mod tests { .collect() } + #[test] + fn test_tracking_changes_measured_width() { + let dispatcher = TestDispatcher::new(1); + let cx = TestAppContext::build(dispatcher, None); + let base = TextRun { + len: 4, + font: font(".ZedMono"), + color: Hsla::default(), + background_color: None, + underline: None, + strikethrough: None, + ..Default::default() + }; + let font_id = cx.text_system().resolve_font(&base.font); + let platform = cx.text_system().platform_text_system_for_tests(); + let no_tracking = platform.layout_line( + "TEST", + px(16.), + &[FontRun { + len: 4, + font_id, + letter_spacing: None, + }], + ); + let wide = platform.layout_line( + "TEST", + px(16.), + &[FontRun { + len: 4, + font_id, + letter_spacing: Some(px(2.0)), + }], + ); + let tight = platform.layout_line( + "TEST", + px(16.), + &[FontRun { + len: 4, + font_id, + letter_spacing: Some(px(-0.5)), + }], + ); + + assert!(wide.width > no_tracking.width); + assert!(tight.width <= no_tracking.width); + } + + #[test] + fn test_tracking_changes_truncation_width() { + let mut wrapper = build_wrapper(); + let no_tracking = wrapper.width_for_text("TEST…"); + wrapper.set_letter_spacing(Some(px(2.0))); + let wide = wrapper.width_for_text("TEST…"); + + assert!(wide > no_tracking); + } + #[test] fn test_wrap_line() { let mut wrapper = build_wrapper(); diff --git a/crates/gpui_elements/src/editable_text/element.rs b/crates/gpui_elements/src/editable_text/element.rs index 5d5620ec6a..48812ba851 100644 --- a/crates/gpui_elements/src/editable_text/element.rs +++ b/crates/gpui_elements/src/editable_text/element.rs @@ -585,6 +585,7 @@ impl PrelayoutState { background_color: None, underline: None, strikethrough: None, + letter_spacing: None, }]; let wrap_width = TextLayout::evaluate_wrap_width( diff --git a/crates/gpui_macos/src/platform.rs b/crates/gpui_macos/src/platform.rs index e5c9623365..7b0fcef820 100644 --- a/crates/gpui_macos/src/platform.rs +++ b/crates/gpui_macos/src/platform.rs @@ -499,7 +499,7 @@ impl Platform for MacPlatform { pool.drain(); (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); - (*NSWindow::delegate(app)).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); + (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); } } diff --git a/crates/gpui_macos/src/text_system.rs b/crates/gpui_macos/src/text_system.rs index 2808e535ad..4ba444591e 100644 --- a/crates/gpui_macos/src/text_system.rs +++ b/crates/gpui_macos/src/text_system.rs @@ -22,7 +22,7 @@ use core_text::{ kCTFontWidthTrait, }, line::CTLine, - string_attributes::kCTFontAttributeName, + string_attributes::{kCTFontAttributeName, kCTKernAttributeName}, }; use font_kit::{ font::Font as FontKitFont, @@ -543,6 +543,13 @@ impl MacTextSystemState { kCTFontAttributeName, &font.native_font().clone_with_font_size(font_size.into()), ); + if let Some(spacing) = run.letter_spacing { + string.set_attribute( + cf_range, + kCTKernAttributeName, + &CFNumber::from(f64::from(spacing.as_f32())), + ); + } } break_ligature = !break_ligature; } @@ -753,6 +760,7 @@ mod tests { let mut style = FontRun { font_id, len: line.len(), + letter_spacing: None, }; let layout = fonts.layout_line(line, px(16.), &[style]); @@ -774,10 +782,12 @@ mod tests { FontRun { len: "\u{feff}".len(), font_id, + letter_spacing: None, }, FontRun { len: "ab".len(), font_id, + letter_spacing: None, }, ]; let layout = fonts.layout_line(line, px(16.), font_runs); @@ -796,8 +806,16 @@ mod tests { let text = "hello world"; let font_runs = &[ - FontRun { font_id, len: 5 }, // "hello" - FontRun { font_id, len: 6 }, // " world" + FontRun { + font_id, + len: 5, + letter_spacing: None, + }, // "hello" + FontRun { + font_id, + len: 6, + letter_spacing: None, + }, // " world" ]; let layout = fonts.layout_line(text, px(16.), font_runs); @@ -817,11 +835,16 @@ mod tests { // Test with different font runs - should not insert ZWNJ let font_id2 = fonts.font_id(&font("Times")).unwrap_or(font_id); let font_runs_different = &[ - FontRun { font_id, len: 5 }, // "hello" + FontRun { + font_id, + len: 5, + letter_spacing: None, + }, // "hello" // " world" FontRun { font_id: font_id2, len: 6, + letter_spacing: None, }, ]; @@ -846,15 +869,31 @@ mod tests { let font_id = fonts.font_id(&font("Helvetica")).unwrap(); let text = "hello"; - let font_runs = &[FontRun { font_id, len: 5 }]; + let font_runs = &[FontRun { + font_id, + len: 5, + letter_spacing: None, + }]; let layout = fonts.layout_line(text, px(16.), font_runs); assert_eq!(layout.len, text.len()); let text = "abc"; let font_runs = &[ - FontRun { font_id, len: 1 }, // "a" - FontRun { font_id, len: 1 }, // "b" - FontRun { font_id, len: 1 }, // "c" + FontRun { + font_id, + len: 1, + letter_spacing: None, + }, // "a" + FontRun { + font_id, + len: 1, + letter_spacing: None, + }, // "b" + FontRun { + font_id, + len: 1, + letter_spacing: None, + }, // "c" ]; let layout = fonts.layout_line(text, px(16.), font_runs); assert_eq!(layout.len, text.len()); diff --git a/crates/gpui_scheduler/src/test_scheduler.rs b/crates/gpui_scheduler/src/test_scheduler.rs index 4dc06a06e4..600de2439a 100644 --- a/crates/gpui_scheduler/src/test_scheduler.rs +++ b/crates/gpui_scheduler/src/test_scheduler.rs @@ -58,7 +58,7 @@ impl TestScheduler { .map(|seed| seed.parse().unwrap()) .unwrap_or(0); - let interactive = !std::env::var("SCHEDULER_NONINTERACTIVE").is_ok(); + let interactive = std::env::var("SCHEDULER_NONINTERACTIVE").is_err(); (seed..seed + num_iterations as u64) .map(|seed| { diff --git a/crates/gpui_wgpu/benches/layout_line.rs b/crates/gpui_wgpu/benches/layout_line.rs index f4a9289229..1b4a40163e 100644 --- a/crates/gpui_wgpu/benches/layout_line.rs +++ b/crates/gpui_wgpu/benches/layout_line.rs @@ -59,10 +59,12 @@ fn bench_layout_line(c: &mut Criterion) { let runs_no_fallback = vec![FontRun { len: text.len(), font_id: font_id_no_fallback, + letter_spacing: None, }]; let runs_with_fallback = vec![FontRun { len: text.len(), font_id: font_id_with_fallback, + letter_spacing: None, }]; let mut group = c.benchmark_group("layout_line"); diff --git a/crates/gpui_wgpu/src/cosmic_text_system.rs b/crates/gpui_wgpu/src/cosmic_text_system.rs index 456ba59c0a..ac2eceba79 100644 --- a/crates/gpui_wgpu/src/cosmic_text_system.rs +++ b/crates/gpui_wgpu/src/cosmic_text_system.rs @@ -483,26 +483,36 @@ impl CosmicTextSystemState { let primary_weight = face.weight; let primary_features = loaded_font.features.clone(); let fallback_chain = Arc::clone(&loaded_font.user_fallback_chain); + let letter_spacing = run + .letter_spacing + .map(|spacing| spacing.as_f32() / font_size.as_f32()); // build one `Attrs` per slot up front. each clone of span attrs // would otherwise re-allocate the `font_features` Vec. - let primary_attrs = Attrs::new() + let mut primary_attrs = Attrs::new() .metadata(run.font_id.0) .family(Family::Name(&primary_family_name)) .stretch(primary_stretch) .style(primary_style) .weight(primary_weight) .font_features(primary_features.clone()); + if let Some(letter_spacing) = letter_spacing { + primary_attrs = primary_attrs.letter_spacing(letter_spacing); + } let fallback_attrs: SmallVec<[Attrs<'_>; 4]> = fallback_chain .iter() .map(|(fb_id, fb_name)| { - Attrs::new() + let mut attrs = Attrs::new() .metadata(fb_id.0) .family(Family::Name(fb_name)) .stretch(primary_stretch) .style(primary_style) .weight(primary_weight) - .font_features(primary_features.clone()) + .font_features(primary_features.clone()); + if let Some(letter_spacing) = letter_spacing { + attrs = attrs.letter_spacing(letter_spacing); + } + attrs }) .collect(); diff --git a/crates/gpui_windows/src/direct_write.rs b/crates/gpui_windows/src/direct_write.rs index 5cdd01f70a..550d194da7 100644 --- a/crates/gpui_windows/src/direct_write.rs +++ b/crates/gpui_windows/src/direct_write.rs @@ -551,12 +551,10 @@ impl DirectWriteState { format.SetFontFallback(fallbacks)?; } - let layout = components.factory.CreateTextLayout( - text_wide, - &format, - f32::INFINITY, - f32::INFINITY, - )?; + let layout: IDWriteTextLayout1 = components + .factory + .CreateTextLayout(text_wide, &format, f32::INFINITY, f32::INFINITY)? + .cast()?; let current_text = &text[utf8_offset..(utf8_offset + first_run.len)]; utf8_offset += first_run.len; let current_text_utf16_length = current_text.encode_utf16().count() as u32; @@ -565,6 +563,9 @@ impl DirectWriteState { length: current_text_utf16_length, }; layout.SetTypography(&font_info.features, text_range)?; + if let Some(spacing) = first_run.letter_spacing { + layout.SetCharacterSpacing(0.0, spacing.as_f32(), 0.0, text_range)?; + } utf16_offset += current_text_utf16_length; layout @@ -603,6 +604,9 @@ impl DirectWriteState { text_layout.SetFontStyle(font_info.font_face.GetStyle(), text_range)?; text_layout.SetFontWeight(font_info.font_face.GetWeight(), text_range)?; text_layout.SetTypography(&font_info.features, text_range)?; + if let Some(spacing) = run.letter_spacing { + text_layout.SetCharacterSpacing(0.0, spacing.as_f32(), 0.0, text_range)?; + } break_ligatures = !break_ligatures; } diff --git a/crates/gpui_zed_util/src/paths.rs b/crates/gpui_zed_util/src/paths.rs index 47eb4eeca9..86e459cff7 100644 --- a/crates/gpui_zed_util/src/paths.rs +++ b/crates/gpui_zed_util/src/paths.rs @@ -2783,7 +2783,6 @@ mod tests { assert_eq!(path.extension_or_hidden_file_name(), Some("eslintrc.js")); } - #[test] // fn edge_of_glob() { // let path = Path::new("/work/node_modules"); // let path_matcher =