From 5de9652a22958d4d24d0fa69cfbb2bf320a7c4aa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 3 May 2023 16:59:44 -0700 Subject: [PATCH 01/25] Create proof-of-concept SettingStore struct --- Cargo.lock | 1660 ++++++++++++++----------- crates/settings/Cargo.toml | 1 + crates/settings/src/settings.rs | 1 + crates/settings/src/settings_store.rs | 608 +++++++++ crates/util/src/util.rs | 21 + 5 files changed, 1554 insertions(+), 737 deletions(-) create mode 100644 crates/settings/src/settings_store.rs diff --git a/Cargo.lock b/Cargo.lock index eee0873e5b15eed8b0db8c27dfd457bedbe5a8e4..02370dc98b0bd984911e38bf033cfc5e16d1ea53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,7 +14,7 @@ version = "0.1.0" dependencies = [ "auto_update", "editor", - "futures 0.3.25", + "futures 0.3.28", "gpui", "language", "project", @@ -30,7 +30,16 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "gimli", + "gimli 0.26.2", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.2", ] [[package]] @@ -51,7 +60,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", "once_cell", "version_check", ] @@ -65,6 +85,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "alacritty_config" version = "0.1.1-dev" @@ -82,7 +111,7 @@ source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -92,7 +121,7 @@ source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed dependencies = [ "alacritty_config", "alacritty_config_derive", - "base64", + "base64 0.13.1", "bitflags", "dirs 4.0.0", "libc", @@ -145,15 +174,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -232,9 +261,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ "async-lock", "async-task", @@ -273,32 +302,31 @@ dependencies = [ [[package]] name = "async-io" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg 1.1.0", + "cfg-if 1.0.0", "concurrent-queue", "futures-lite", - "libc", "log", "parking", "polling", + "rustix 0.37.19", "slab", "socket2", "waker-fn", - "windows-sys 0.42.0", ] [[package]] name = "async-lock" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener", - "futures-lite", ] [[package]] @@ -318,15 +346,15 @@ name = "async-pipe" version = "0.1.3" source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" dependencies = [ - "futures 0.3.25", + "futures 0.3.28", "log", ] [[package]] name = "async-process" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6381ead98388605d0d9ff86371043b5aa922a3905824244de40dc263a14fcba4" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" dependencies = [ "async-io", "async-lock", @@ -335,9 +363,9 @@ dependencies = [ "cfg-if 1.0.0", "event-listener", "futures-lite", - "libc", + "rustix 0.37.19", "signal-hook", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -348,18 +376,18 @@ checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "async-recursion" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -372,7 +400,7 @@ dependencies = [ "async-global-executor", "async-io", "async-lock", - "crossbeam-utils 0.8.14", + "crossbeam-utils 0.8.15", "futures-channel", "futures-core", "futures-io", @@ -390,23 +418,24 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite 0.2.9", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -419,7 +448,7 @@ dependencies = [ "filetime", "libc", "pin-project", - "redox_syscall", + "redox_syscall 0.2.16", "xattr", ] @@ -443,13 +472,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.59" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -486,9 +515,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" @@ -548,9 +577,9 @@ checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", "axum-core", - "base64", + "base64 0.13.1", "bitflags", - "bytes 1.3.0", + "bytes 1.4.0", "futures-util", "headers", "http", @@ -582,7 +611,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" dependencies = [ "async-trait", - "bytes 1.3.0", + "bytes 1.4.0", "futures-util", "http", "http-body", @@ -598,7 +627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" dependencies = [ "axum", - "bytes 1.3.0", + "bytes 1.4.0", "futures-util", "http", "mime", @@ -614,16 +643,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "addr2line", + "addr2line 0.19.0", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.5.4", - "object 0.29.0", + "miniz_oxide 0.6.2", + "object 0.30.3", "rustc-demangle", ] @@ -637,7 +666,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -646,11 +675,17 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bincode" @@ -671,7 +706,7 @@ dependencies = [ "cexpr", "clang-sys", "clap 2.34.0", - "env_logger", + "env_logger 0.9.3", "lazy_static", "lazycell", "log", @@ -707,18 +742,18 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "blocking" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel", "async-lock", @@ -726,51 +761,52 @@ dependencies = [ "atomic-waker", "fastrand", "futures-lite", + "log", ] [[package]] name = "borsh" -version = "0.9.3" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", - "hashbrown 0.11.2", + "hashbrown 0.13.2", ] [[package]] name = "borsh-derive" -version = "0.9.3" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate", "proc-macro2", - "syn", + "syn 1.0.109", ] [[package]] name = "borsh-derive-internal" -version = "0.9.3" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "borsh-schema-derive-internal" -version = "0.9.3" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -803,45 +839,47 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" dependencies = [ "memchr", + "serde", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" [[package]] name = "bytecheck" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" dependencies = [ "bytecheck_derive", "ptr_meta", + "simdutf8", ] [[package]] name = "bytecheck_derive" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "bytemuck" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -861,9 +899,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "call" @@ -874,7 +912,7 @@ dependencies = [ "client", "collections", "fs", - "futures 0.3.25", + "futures 0.3.28", "gpui", "language", "live_kit_client", @@ -894,7 +932,7 @@ checksum = "e54b86398b5852ddd45784b1d9b196b98beb39171821bad4b8b44534a1e87927" dependencies = [ "cap-primitives", "cap-std", - "io-lifetimes", + "io-lifetimes 0.5.3", "winapi 0.3.9", ] @@ -905,13 +943,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb8fca3e81fae1d91a36e9784ca22a39ef623702b5f7904d89dc31f10184a178" dependencies = [ "ambient-authority", - "errno", + "errno 0.2.8", "fs-set-times", "io-extras", - "io-lifetimes", + "io-lifetimes 0.5.3", "ipnet", "maybe-owned", - "rustix", + "rustix 0.33.7", "winapi 0.3.9", "winapi-util", "winx", @@ -935,9 +973,9 @@ checksum = "2247568946095c7765ad2b441a56caffc08027734c634a6d5edda648f04e32eb" dependencies = [ "cap-primitives", "io-extras", - "io-lifetimes", + "io-lifetimes 0.5.3", "ipnet", - "rustix", + "rustix 0.33.7", ] [[package]] @@ -948,7 +986,7 @@ checksum = "c50472b6ebc302af0401fa3fb939694cd8ff00e0d4c9182001e434fc822ab83a" dependencies = [ "cap-primitives", "once_cell", - "rustix", + "rustix 0.33.7", "winx", ] @@ -960,9 +998,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.77" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -990,9 +1028,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -1006,9 +1044,9 @@ dependencies = [ [[package]] name = "chunked_transfer" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" +checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" [[package]] name = "cipher" @@ -1021,9 +1059,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -1047,9 +1085,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags", @@ -1064,15 +1102,15 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1089,7 +1127,7 @@ name = "cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 3.2.23", + "clap 3.2.25", "core-foundation", "core-services", "dirs 3.0.2", @@ -1108,7 +1146,7 @@ dependencies = [ "async-tungstenite", "collections", "db", - "futures 0.3.25", + "futures 0.3.28", "gpui", "image", "lazy_static", @@ -1125,11 +1163,11 @@ dependencies = [ "sum_tree", "tempfile", "thiserror", - "time 0.3.17", + "time 0.3.21", "tiny_http", "url", "util", - "uuid 1.2.2", + "uuid 1.3.2", ] [[package]] @@ -1141,9 +1179,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.49" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" dependencies = [ "cc", ] @@ -1195,18 +1233,18 @@ dependencies = [ "async-tungstenite", "axum", "axum-extra", - "base64", + "base64 0.13.1", "call", - "clap 3.2.23", + "clap 3.2.25", "client", "collections", "ctor", "dashmap", "editor", - "env_logger", + "env_logger 0.9.3", "envy", "fs", - "futures 0.3.25", + "futures 0.3.28", "git", "gpui", "hyper", @@ -1236,7 +1274,7 @@ dependencies = [ "sha-1 0.9.8", "sqlx", "theme", - "time 0.3.17", + "time 0.3.21", "tokio", "tokio-tungstenite", "toml", @@ -1263,7 +1301,7 @@ dependencies = [ "context_menu", "editor", "feedback", - "futures 0.3.25", + "futures 0.3.28", "fuzzy", "gpui", "log", @@ -1299,7 +1337,7 @@ dependencies = [ "collections", "ctor", "editor", - "env_logger", + "env_logger 0.9.3", "fuzzy", "gpui", "picker", @@ -1313,11 +1351,11 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ - "crossbeam-utils 0.8.14", + "crossbeam-utils 0.8.15", ] [[package]] @@ -1342,7 +1380,7 @@ dependencies = [ "collections", "context_menu", "fs", - "futures 0.3.25", + "futures 0.3.28", "gpui", "language", "log", @@ -1366,7 +1404,7 @@ dependencies = [ "context_menu", "copilot", "editor", - "futures 0.3.25", + "futures 0.3.28", "gpui", "settings", "smol", @@ -1445,9 +1483,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -1472,7 +1510,7 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.26.2", "log", "regalloc2", "smallvec", @@ -1550,18 +1588,18 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "crc32fast" @@ -1584,35 +1622,35 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.14", + "crossbeam-utils 0.8.15", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.14", + "crossbeam-utils 0.8.15", ] [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", - "crossbeam-utils 0.8.14", - "memoffset 0.7.1", + "crossbeam-utils 0.8.15", + "memoffset 0.8.0", "scopeguard", ] @@ -1623,7 +1661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.14", + "crossbeam-utils 0.8.15", ] [[package]] @@ -1639,9 +1677,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if 1.0.0", ] @@ -1673,7 +1711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1693,9 +1731,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.59+curl-7.86.0" +version = "0.4.61+curl-8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cfce34829f448b08f55b7db6d0009e23e2e86a34e8c2b366269bf5799b4a407" +checksum = "14d05c10f541ae6f3bc5b3d923c20001f47db7d5f0b2bc6ad16490133842db79" dependencies = [ "cc", "libc", @@ -1709,9 +1747,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -1721,9 +1759,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -1731,24 +1769,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.15", ] [[package]] name = "cxxbridge-flags" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -1761,7 +1799,7 @@ dependencies = [ "hashbrown 0.12.3", "lock_api", "once_cell", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.7", ] [[package]] @@ -1780,7 +1818,7 @@ dependencies = [ "anyhow", "async-trait", "collections", - "env_logger", + "env_logger 0.9.3", "gpui", "indoc", "lazy_static", @@ -1864,7 +1902,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", "subtle", ] @@ -1931,9 +1969,9 @@ dependencies = [ [[package]] name = "dotenvy" -version = "0.15.6" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "drag_and_drop" @@ -1957,15 +1995,15 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "editor" version = "0.1.0" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "anyhow", "client", "clock", @@ -1975,8 +2013,8 @@ dependencies = [ "ctor", "db", "drag_and_drop", - "env_logger", - "futures 0.3.25", + "env_logger 0.9.3", + "futures 0.3.28", "fuzzy", "git", "glob", @@ -2008,7 +2046,7 @@ dependencies = [ "tree-sitter-html", "tree-sitter-javascript", "tree-sitter-rust", - "tree-sitter-typescript 0.20.2", + "tree-sitter-typescript 0.20.2 (git+https://github.com/tree-sitter/tree-sitter-typescript?rev=5d20856f34315b068c41edaee2ac8a100081d259)", "unindent", "util", "workspace", @@ -2016,15 +2054,15 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if 1.0.0", ] @@ -2042,6 +2080,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal 0.4.7", + "log", + "regex", + "termcolor", +] + [[package]] name = "envy" version = "0.4.2" @@ -2053,9 +2104,9 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.3.23" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" +checksum = "4f2b0c2380453a92ea8b6c8e5f64ecaafccddde8ceab55ff7a8ac1029f894569" dependencies = [ "serde", ] @@ -2071,6 +2122,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -2093,9 +2155,9 @@ dependencies = [ [[package]] name = "euclid" -version = "0.22.7" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" dependencies = [ "num-traits", ] @@ -2124,9 +2186,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -2138,7 +2200,7 @@ dependencies = [ "anyhow", "client", "editor", - "futures 0.3.25", + "futures 0.3.28", "gpui", "human_bytes", "isahc", @@ -2162,11 +2224,11 @@ dependencies = [ [[package]] name = "file-per-thread-logger" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e16290574b39ee41c71aeb90ae960c504ebaf1e2a1c87bd52aa56ed6e1a02f" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger", + "env_logger 0.10.0", "log", ] @@ -2176,7 +2238,7 @@ version = "0.1.0" dependencies = [ "ctor", "editor", - "env_logger", + "env_logger 0.9.3", "fuzzy", "gpui", "menu", @@ -2192,14 +2254,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", - "windows-sys 0.42.0", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", ] [[package]] @@ -2210,12 +2272,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide 0.6.2", + "miniz_oxide 0.7.1", ] [[package]] @@ -2239,7 +2301,7 @@ dependencies = [ "futures-core", "futures-sink", "pin-project", - "spin 0.9.4", + "spin 0.9.8", ] [[package]] @@ -2336,7 +2398,7 @@ dependencies = [ "async-trait", "collections", "fsevent", - "futures 0.3.25", + "futures 0.3.28", "git2", "gpui", "lazy_static", @@ -2360,8 +2422,8 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df62ee66ee2d532ea8d567b5a3f0d03ecd64636b98bad5be1e93dcc918b92aa" dependencies = [ - "io-lifetimes", - "rustix", + "io-lifetimes 0.5.3", + "rustix 0.33.7", "winapi 0.3.9", ] @@ -2414,9 +2476,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -2429,9 +2491,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -2439,15 +2501,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -2467,15 +2529,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand", "futures-core", @@ -2488,32 +2550,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures 0.1.31", "futures-channel", @@ -2548,9 +2610,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -2569,9 +2631,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2599,6 +2661,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "git" version = "0.1.0" @@ -2607,7 +2675,7 @@ dependencies = [ "async-trait", "clock", "collections", - "futures 0.3.25", + "futures 0.3.28", "git2", "lazy_static", "log", @@ -2640,11 +2708,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -2693,11 +2761,11 @@ dependencies = [ "core-text", "ctor", "dhat", - "env_logger", + "env_logger 0.9.3", "etagere", "font-kit", "foreign-types", - "futures 0.3.25", + "futures 0.3.28", "gpui_macros", "image", "itertools", @@ -2726,11 +2794,11 @@ dependencies = [ "smol", "sqlez", "sum_tree", - "time 0.3.17", + "time 0.3.21", "tiny-skia", "usvg", "util", - "uuid 1.2.2", + "uuid 1.3.2", "waker-fn", ] @@ -2740,16 +2808,16 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "h2" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "fnv", "futures-core", "futures-sink", @@ -2758,7 +2826,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tracing", ] @@ -2768,7 +2836,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash", + "ahash 0.7.6", ] [[package]] @@ -2777,7 +2845,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", ] [[package]] @@ -2795,9 +2872,9 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ - "base64", + "base64 0.13.1", "bitflags", - "bytes 1.3.0", + "bytes 1.4.0", "headers-core", "http", "httpdate", @@ -2825,9 +2902,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" dependencies = [ "unicode-segmentation", ] @@ -2850,6 +2927,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -2886,11 +2969,11 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "fnv", "itoa", ] @@ -2901,7 +2984,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "http", "pin-project-lite 0.2.9", ] @@ -2926,9 +3009,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "human_bytes" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b528196c838e8b3da8b665e08c30958a6f2ede91d79f2ffcd0d4664b9c64eb" +checksum = "27e2b089f28ad15597b48d8c0a8fe94eeb1c1cb26ca99b6f66ac9582ae10c5e6" [[package]] name = "humantime" @@ -2938,11 +3021,11 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "futures-channel", "futures-core", "futures-util", @@ -2978,7 +3061,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "hyper", "native-tls", "tokio", @@ -2987,16 +3070,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi 0.3.9", + "windows", ] [[package]] @@ -3021,11 +3104,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ - "crossbeam-utils 0.8.14", "globset", "lazy_static", "log", @@ -3058,9 +3140,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg 1.1.0", "hashbrown 0.12.3", @@ -3069,9 +3151,9 @@ dependencies = [ [[package]] name = "indoc" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" +checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" [[package]] name = "install_cli" @@ -3099,7 +3181,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c937cc9891c12eaa8c63ad347e4a288364b1328b924886970b47a14ab8f8f8" dependencies = [ - "io-lifetimes", + "io-lifetimes 0.5.3", "winapi 0.3.9", ] @@ -3113,6 +3195,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -3143,9 +3236,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" @@ -3154,11 +3247,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c89a757e762896bdbdfadf2860d0f8b0cea5e363d8cf3e7bdfeb63d1d976352" dependencies = [ "hermit-abi 0.2.6", - "io-lifetimes", - "rustix", + "io-lifetimes 0.5.3", + "rustix 0.33.7", "winapi 0.3.9", ] +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes 1.0.10", + "rustix 0.37.19", + "windows-sys 0.48.0", +] + [[package]] name = "isahc" version = "1.7.2" @@ -3167,7 +3272,7 @@ checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" dependencies = [ "async-channel", "castaway", - "crossbeam-utils 0.8.14", + "crossbeam-utils 0.8.15", "curl", "curl-sys", "encoding_rs", @@ -3197,9 +3302,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "ittapi-rs" @@ -3212,9 +3317,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -3246,9 +3351,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" dependencies = [ "wasm-bindgen", ] @@ -3265,7 +3370,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" dependencies = [ - "base64", + "base64 0.13.1", "crypto-common", "digest 0.10.6", "hmac 0.12.1", @@ -3313,9 +3418,9 @@ dependencies = [ "clock", "collections", "ctor", - "env_logger", + "env_logger 0.9.3", "fs", - "futures 0.3.25", + "futures 0.3.28", "fuzzy", "git", "gpui", @@ -3347,7 +3452,7 @@ dependencies = [ "tree-sitter-python", "tree-sitter-ruby", "tree-sitter-rust", - "tree-sitter-typescript 0.20.1", + "tree-sitter-typescript 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicase", "unindent", "util", @@ -3393,15 +3498,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libgit2-sys" -version = "0.14.0+1.5.0" +version = "0.14.2+1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", @@ -3448,9 +3553,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "libc", @@ -3469,9 +3574,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] @@ -3488,6 +3593,12 @@ version = "0.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" +[[package]] +name = "linux-raw-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" + [[package]] name = "lipsum" version = "0.8.2" @@ -3507,13 +3618,13 @@ dependencies = [ "async-trait", "block", "byteorder", - "bytes 1.3.0", + "bytes 1.4.0", "cocoa", "collections", "core-foundation", "core-graphics", "foreign-types", - "futures 0.3.25", + "futures 0.3.28", "gpui", "hmac 0.12.1", "jwt", @@ -3538,7 +3649,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "futures 0.3.25", + "futures 0.3.28", "hmac 0.12.1", "jwt", "log", @@ -3580,8 +3691,8 @@ dependencies = [ "async-pipe", "collections", "ctor", - "env_logger", - "futures 0.3.25", + "env_logger 0.9.3", + "futures 0.3.28", "gpui", "log", "lsp-types", @@ -3615,7 +3726,7 @@ dependencies = [ "anyhow", "collections", "editor", - "futures 0.3.25", + "futures 0.3.28", "gpui", "language", "lsp", @@ -3657,9 +3768,9 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" @@ -3695,7 +3806,7 @@ dependencies = [ "anyhow", "bindgen", "block", - "bytes 1.3.0", + "bytes 1.4.0", "core-foundation", "foreign-types", "metal", @@ -3737,9 +3848,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg 1.1.0", ] @@ -3767,9 +3878,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" @@ -3798,18 +3909,18 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] @@ -3845,14 +3956,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3981,7 +4092,7 @@ dependencies = [ "anyhow", "async-compression", "async-tar", - "futures 0.3.25", + "futures 0.3.28", "gpui", "parking_lot 0.11.2", "serde", @@ -3993,9 +4104,9 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -4012,9 +4123,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi 0.3.9", ] @@ -4102,11 +4213,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi 0.2.6", "libc", ] @@ -4116,13 +4227,13 @@ version = "0.5.0" source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" dependencies = [ "async-trait", - "futures 0.3.25", + "futures 0.3.28", "log", "parity-tokio-ipc", "rmp", "rmpv", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.8", ] [[package]] @@ -4158,18 +4269,18 @@ dependencies = [ [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opaque-debug" @@ -4179,9 +4290,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.43" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020433887e44c27ff16365eaa2d380547a94544ad509aff6eb5b6e3e0b27b376" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -4194,13 +4305,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -4211,11 +4322,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.78" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07d5c8cb6e57b3a3612064d7b18b117912b4ce70955c2504d4b741c9e244b132" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg 1.1.0", "cc", "libc", "pkg-config", @@ -4233,15 +4343,15 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "ouroboros" -version = "0.15.5" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbb50b356159620db6ac971c6d5c9ab788c9cc38a6f49619fca2a27acb062ca" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" dependencies = [ "aliasable", "ouroboros_macro", @@ -4249,15 +4359,15 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.15.5" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0d9d1a6191c4f391f87219d1ea42b23f09ee84d64763cd05ee6ea88d9f384d" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4298,7 +4408,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" dependencies = [ - "futures 0.3.25", + "futures 0.3.28", "libc", "log", "rand 0.7.3", @@ -4308,9 +4418,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -4320,7 +4430,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", ] [[package]] @@ -4330,34 +4440,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.7", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4373,9 +4483,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pathfinder_color" @@ -4426,7 +4536,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" dependencies = [ - "base64", + "base64 0.13.1", "once_cell", "regex", ] @@ -4439,9 +4549,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" dependencies = [ "thiserror", "ucd-trie", @@ -4449,9 +4559,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", "indexmap", @@ -4463,7 +4573,7 @@ version = "0.1.0" dependencies = [ "ctor", "editor", - "env_logger", + "env_logger 0.9.3", "gpui", "menu", "parking_lot 0.11.2", @@ -4497,7 +4607,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4520,22 +4630,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plist" -version = "1.3.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" dependencies = [ - "base64", + "base64 0.21.0", "indexmap", "line-wrap", + "quick-xml", "serde", - "time 0.3.17", - "xml-rs", + "time 0.3.21", ] [[package]] @@ -4557,7 +4667,7 @@ dependencies = [ "quote", "serde", "serde_derive", - "syn", + "syn 1.0.109", ] [[package]] @@ -4590,16 +4700,18 @@ dependencies = [ [[package]] name = "polling" -version = "2.5.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg 1.1.0", + "bitflags", "cfg-if 1.0.0", + "concurrent-queue", "libc", "log", - "wepoll-ffi", - "windows-sys 0.42.0", + "pin-project-lite 0.2.9", + "windows-sys 0.48.0", ] [[package]] @@ -4616,7 +4728,7 @@ checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" dependencies = [ "atomic", "crossbeam-queue", - "futures 0.3.25", + "futures 0.3.28", "log", "parking_lot 0.12.1", "pin-project", @@ -4661,7 +4773,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -4678,9 +4790,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -4700,7 +4812,7 @@ dependencies = [ name = "project" version = "0.1.0" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "anyhow", "async-trait", "backtrace", @@ -4710,10 +4822,10 @@ dependencies = [ "copilot", "ctor", "db", - "env_logger", + "env_logger 0.9.3", "fs", "fsevent", - "futures 0.3.25", + "futures 0.3.28", "fuzzy", "git", "glob", @@ -4754,7 +4866,7 @@ dependencies = [ "context_menu", "drag_and_drop", "editor", - "futures 0.3.25", + "futures 0.3.28", "gpui", "menu", "postage", @@ -4773,7 +4885,7 @@ version = "0.1.0" dependencies = [ "anyhow", "editor", - "futures 0.3.25", + "futures 0.3.28", "fuzzy", "gpui", "language", @@ -4810,7 +4922,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "prost-derive 0.8.0", ] @@ -4820,7 +4932,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "prost-derive 0.9.0", ] @@ -4830,7 +4942,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "heck 0.3.3", "itertools", "lazy_static", @@ -4854,7 +4966,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4867,7 +4979,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4876,7 +4988,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "prost 0.8.0", ] @@ -4886,7 +4998,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "prost 0.9.0", ] @@ -4922,7 +5034,7 @@ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -4936,11 +5048,20 @@ dependencies = [ "unicase", ] +[[package]] +name = "quick-xml" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +dependencies = [ + "memchr", +] + [[package]] name = "quote" -version = "1.0.21" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] @@ -5032,7 +5153,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -5046,24 +5167,23 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.8", "crossbeam-deque", - "crossbeam-utils 0.8.14", + "crossbeam-utils 0.8.15", "num_cpus", ] @@ -5110,14 +5230,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", - "redox_syscall", + "getrandom 0.2.9", + "redox_syscall 0.2.16", "thiserror", ] @@ -5135,13 +5264,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.1", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -5150,14 +5279,20 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "region" @@ -5182,21 +5317,21 @@ dependencies = [ [[package]] name = "rend" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" dependencies = [ "bytecheck", ] [[package]] name = "reqwest" -version = "0.11.13" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" dependencies = [ - "base64", - "bytes 1.3.0", + "base64 0.21.0", + "bytes 1.4.0", "encoding_rs", "futures-core", "futures-util", @@ -5244,9 +5379,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.34" +version = "0.8.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" dependencies = [ "bytemuck", ] @@ -5268,9 +5403,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.39" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" dependencies = [ "bytecheck", "hashbrown 0.12.3", @@ -5282,13 +5417,13 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.39" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -5342,12 +5477,12 @@ dependencies = [ "anyhow", "async-lock", "async-tungstenite", - "base64", + "base64 0.13.1", "clock", "collections", "ctor", - "env_logger", - "futures 0.3.25", + "env_logger 0.9.3", + "futures 0.3.28", "gpui", "parking_lot 0.11.2", "prost 0.8.0", @@ -5386,9 +5521,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "6.4.2" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1" +checksum = "1b68543d5527e158213414a92832d2aab11a84d2571a5eb021ebe22c43aab066" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -5397,22 +5532,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.3.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d" +checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn", + "syn 1.0.109", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "7.3.0" +version = "7.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731" dependencies = [ "globset", "sha2 0.10.6", @@ -5421,15 +5556,15 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.27.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c321ee4e17d2b7abe12b5d20c1231db708dd36185c8a21e9de5fed6da4dbe9" +checksum = "26bd36b60561ee1fb5ec2817f198b6fd09fa571c897a5e86d1487cfc2b096dfc" dependencies = [ "arrayvec 0.7.2", "borsh", "bytecheck", "byteorder", - "bytes 1.3.0", + "bytes 1.4.0", "num-traits", "rand 0.8.5", "rkyv", @@ -5439,9 +5574,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -5465,22 +5600,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "938a344304321a9da4973b9ff4f9f8db9caf4597dfd9dda6a60b523340a0fff0" dependencies = [ "bitflags", - "errno", - "io-lifetimes", + "errno 0.2.8", + "io-lifetimes 0.5.3", "itoa", "libc", - "linux-raw-sys", + "linux-raw-sys 0.0.42", "once_cell", "winapi 0.3.9", ] +[[package]] +name = "rustix" +version = "0.37.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +dependencies = [ + "bitflags", + "errno 0.3.1", + "io-lifetimes 1.0.10", + "libc", + "linux-raw-sys 0.3.7", + "windows-sys 0.48.0", +] + [[package]] name = "rustls" version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64", + "base64 0.13.1", "log", "ring", "sct 0.6.1", @@ -5489,9 +5638,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -5501,18 +5650,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64", + "base64 0.21.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "rustybuzz" @@ -5532,9 +5681,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "safe_arch" @@ -5571,19 +5720,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "schemars" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "dyn-clone", "schemars_derive", @@ -5593,14 +5741,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 1.0.109", ] [[package]] @@ -5617,9 +5765,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "scrypt" @@ -5663,7 +5811,7 @@ dependencies = [ "async-stream", "async-trait", "chrono", - "futures 0.3.25", + "futures 0.3.28", "futures-util", "log", "ouroboros", @@ -5676,10 +5824,10 @@ dependencies = [ "serde_json", "sqlx", "thiserror", - "time 0.3.17", + "time 0.3.21", "tracing", "url", - "uuid 1.2.2", + "uuid 1.3.2", ] [[package]] @@ -5691,7 +5839,7 @@ dependencies = [ "heck 0.3.3", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -5704,8 +5852,8 @@ dependencies = [ "rust_decimal", "sea-query-derive", "serde_json", - "time 0.3.17", - "uuid 1.2.2", + "time 0.3.21", + "uuid 1.3.2", ] [[package]] @@ -5719,8 +5867,8 @@ dependencies = [ "sea-query", "serde_json", "sqlx", - "time 0.3.17", - "uuid 1.2.2", + "time 0.3.21", + "uuid 1.3.2", ] [[package]] @@ -5732,7 +5880,7 @@ dependencies = [ "heck 0.3.3", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "thiserror", ] @@ -5755,7 +5903,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -5771,7 +5919,7 @@ dependencies = [ "anyhow", "collections", "editor", - "futures 0.3.25", + "futures 0.3.28", "glob", "gpui", "language", @@ -5793,9 +5941,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -5806,9 +5954,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -5840,22 +5988,22 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" [[package]] name = "serde" -version = "1.0.148" +version = "1.0.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" +checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.148" +version = "1.0.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" +checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -5866,23 +6014,23 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "serde_fmt" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2963a69a2b3918c1dc75a45a18bd3fcd1120e31d3f59deb1b2f9b5d5ffb8baa4" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" dependencies = [ "serde", ] [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "indexmap", "itoa", @@ -5892,13 +6040,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -5954,7 +6102,7 @@ dependencies = [ "assets", "collections", "fs", - "futures 0.3.25", + "futures 0.3.28", "glob", "gpui", "json_comments", @@ -5965,6 +6113,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "smallvec", "sqlez", "staff_mode", "theme", @@ -6060,9 +6209,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", @@ -6082,13 +6231,19 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "similar" version = "1.3.0" @@ -6135,18 +6290,18 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg 1.1.0", ] [[package]] name = "slice-group-by" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "sluice" @@ -6202,9 +6357,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi 0.3.9", @@ -6218,9 +6373,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.4" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] @@ -6236,14 +6391,14 @@ name = "sqlez" version = "0.1.0" dependencies = [ "anyhow", - "futures 0.3.25", + "futures 0.3.28", "indoc", "lazy_static", "libsqlite3-sys", "parking_lot 0.11.2", "smol", "thread_local", - "uuid 1.2.2", + "uuid 1.3.2", ] [[package]] @@ -6255,14 +6410,14 @@ dependencies = [ "quote", "sqlez", "sqlformat", - "syn", + "syn 1.0.109", ] [[package]] name = "sqlformat" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" dependencies = [ "itertools", "nom", @@ -6271,9 +6426,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788841def501aabde58d3666fcea11351ec3962e6ea75dbcd05c84a71d68bcd1" +checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" dependencies = [ "sqlx-core", "sqlx-macros", @@ -6281,16 +6436,16 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" +checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ - "ahash", + "ahash 0.7.6", "atoi", - "base64", + "base64 0.13.1", "bitflags", "byteorder", - "bytes 1.3.0", + "bytes 1.4.0", "chrono", "crc", "crossbeam-queue", @@ -6321,7 +6476,7 @@ dependencies = [ "percent-encoding", "rand 0.8.5", "rust_decimal", - "rustls 0.20.7", + "rustls 0.20.8", "rustls-pemfile", "serde", "serde_json", @@ -6332,23 +6487,23 @@ dependencies = [ "sqlx-rt", "stringprep", "thiserror", - "time 0.3.17", + "time 0.3.21", "tokio-stream", "url", - "uuid 1.2.2", - "webpki-roots 0.22.5", + "uuid 1.3.2", + "webpki-roots 0.22.6", "whoami", ] [[package]] name = "sqlx-macros" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" +checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" dependencies = [ "dotenvy", "either", - "heck 0.4.0", + "heck 0.4.1", "once_cell", "proc-macro2", "quote", @@ -6356,15 +6511,15 @@ dependencies = [ "sha2 0.10.6", "sqlx-core", "sqlx-rt", - "syn", + "syn 1.0.109", "url", ] [[package]] name = "sqlx-rt" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" +checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" dependencies = [ "once_cell", "tokio", @@ -6425,7 +6580,7 @@ version = "0.1.0" dependencies = [ "arrayvec 0.7.2", "ctor", - "env_logger", + "env_logger 0.9.3", "log", "rand 0.8.5", ] @@ -6467,9 +6622,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.105" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -6477,23 +6632,22 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" - -[[package]] -name = "synstructure" -version = "0.12.6" +name = "syn" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", - "syn", - "unicode-xid", + "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sys-info" version = "0.9.1" @@ -6506,14 +6660,14 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.27.3" +version = "0.27.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1620f9573034c573376acc550f3b9a2be96daeb08abb3c12c8523e1cee06e80f" +checksum = "a902e9050fca0a5d6877550b769abd2bd1ce8c04634b941dbe2809735e1a1e33" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", "libc", - "ntapi 0.4.0", + "ntapi 0.4.1", "once_cell", "rayon", "winapi 0.3.9", @@ -6529,17 +6683,17 @@ dependencies = [ "bitflags", "cap-fs-ext", "cap-std", - "io-lifetimes", - "rustix", + "io-lifetimes 0.5.3", + "rustix 0.33.7", "winapi 0.3.9", "winx", ] [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tempdir" @@ -6553,16 +6707,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi 0.3.9", + "redox_syscall 0.3.5", + "rustix 0.37.19", + "windows-sys 0.45.0", ] [[package]] @@ -6582,7 +6735,7 @@ dependencies = [ "anyhow", "db", "dirs 4.0.0", - "futures 0.3.25", + "futures 0.3.28", "gpui", "itertools", "lazy_static", @@ -6612,7 +6765,7 @@ dependencies = [ "db", "dirs 4.0.0", "editor", - "futures 0.3.25", + "futures 0.3.28", "gpui", "itertools", "language", @@ -6645,7 +6798,7 @@ dependencies = [ "collections", "ctor", "digest 0.9.0", - "env_logger", + "env_logger 0.9.3", "fs", "gpui", "lazy_static", @@ -6722,22 +6875,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -6748,10 +6901,11 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if 1.0.0", "once_cell", ] @@ -6779,9 +6933,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" dependencies = [ "itoa", "serde", @@ -6791,15 +6945,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" dependencies = [ "time-core", ] @@ -6842,28 +6996,27 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.22.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg 1.1.0", - "bytes 1.3.0", + "bytes 1.4.0", "libc", - "memchr", - "mio 0.8.5", + "mio 0.8.6", "num_cpus", "parking_lot 0.12.1", "pin-project-lite 0.2.9", "signal-hook-registry", "socket2", "tokio-macros", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] @@ -6889,20 +7042,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -6914,16 +7067,16 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.7", + "rustls 0.20.8", "tokio", "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -6948,7 +7101,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "futures-core", "futures-sink", "log", @@ -6958,11 +7111,11 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ - "bytes 1.3.0", + "bytes 1.4.0", "futures-core", "futures-io", "futures-sink", @@ -6973,9 +7126,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] @@ -6988,8 +7141,8 @@ checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" dependencies = [ "async-stream", "async-trait", - "base64", - "bytes 1.3.0", + "base64 0.13.1", + "bytes 1.4.0", "futures-core", "futures-util", "h2", @@ -7025,7 +7178,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tower-layer", "tower-service", "tracing", @@ -7038,7 +7191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ "bitflags", - "bytes 1.3.0", + "bytes 1.4.0", "futures-core", "futures-util", "http", @@ -7077,13 +7230,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -7129,9 +7282,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -7331,9 +7484,9 @@ dependencies = [ [[package]] name = "tree-sitter-typescript" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8ed0ecb931cdff13c6a13f45ccd615156e2779d9ffb0395864e05505e6e86d" +checksum = "079c695c32d39ad089101c66393aeaca30e967fba3486a91f573d2f0e12d290a" dependencies = [ "cc", "tree-sitter", @@ -7359,9 +7512,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ttf-parser" @@ -7381,9 +7534,9 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" dependencies = [ - "base64", + "base64 0.13.1", "byteorder", - "bytes 1.3.0", + "bytes 1.4.0", "http", "httparse", "log", @@ -7400,9 +7553,9 @@ version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ - "base64", + "base64 0.13.1", "byteorder", - "bytes 1.3.0", + "bytes 1.4.0", "http", "httparse", "log", @@ -7415,9 +7568,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" @@ -7436,9 +7589,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-bidi-mirroring" @@ -7460,9 +7613,9 @@ checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -7481,9 +7634,9 @@ checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-vo" @@ -7497,12 +7650,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "unicode_categories" version = "0.1.1" @@ -7511,9 +7658,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "unindent" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112" +checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" [[package]] name = "untrusted" @@ -7545,7 +7692,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef8352f317d8f9a918ba5154797fb2a93e2730244041cf7d5be35148266adfa5" dependencies = [ - "base64", + "base64 0.13.1", "data-url", "flate2", "fontdb", @@ -7574,9 +7721,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "util" @@ -7585,7 +7732,7 @@ dependencies = [ "anyhow", "backtrace", "dirs 3.0.2", - "futures 0.3.25", + "futures 0.3.28", "git2", "isahc", "lazy_static", @@ -7610,16 +7757,16 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] name = "uuid" -version = "1.2.2" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", "serde", ] @@ -7718,12 +7865,11 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi 0.3.9", "winapi-util", ] @@ -7769,10 +7915,10 @@ dependencies = [ "cap-time-ext", "fs-set-times", "io-extras", - "io-lifetimes", - "is-terminal", + "io-lifetimes 0.5.3", + "is-terminal 0.1.0", "lazy_static", - "rustix", + "rustix 0.33.7", "system-interface", "tracing", "wasi-common", @@ -7790,7 +7936,7 @@ dependencies = [ "cap-rand", "cap-std", "io-extras", - "rustix", + "rustix 0.33.7", "thiserror", "tracing", "wiggle", @@ -7799,9 +7945,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -7809,24 +7955,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.15", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -7836,9 +7982,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7846,28 +7992,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" [[package]] name = "wasm-encoder" -version = "0.20.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +checksum = "d05d0b6fcd0aeb98adf16e7975331b3c17222aa815148f5b976370ce589d80ef" dependencies = [ "leb128", ] @@ -7922,12 +8068,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1df23c642e1376892f3b72f311596976979cbf8b85469680cdd3a8a063d12a2" dependencies = [ "anyhow", - "base64", + "base64 0.13.1", "bincode", "directories-next", "file-per-thread-logger", "log", - "rustix", + "rustix 0.33.7", "serde", "sha2 0.9.9", "toml", @@ -7947,7 +8093,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli", + "gimli 0.26.2", "log", "more-asserts", "object 0.28.4", @@ -7965,7 +8111,7 @@ checksum = "839d2820e4b830f4b9e7aa08d4c0acabf4a5036105d639f6dfa1c6891c73bdc6" dependencies = [ "anyhow", "cranelift-entity", - "gimli", + "gimli 0.26.2", "indexmap", "log", "more-asserts", @@ -7984,7 +8130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3248be3c4911233535356025f6562193614a40155ee9094bb6a2b43f0dc82803" dependencies = [ "cc", - "rustix", + "rustix 0.33.7", "winapi 0.3.9", ] @@ -7994,18 +8140,18 @@ version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef0a0bcbfa18b946d890078ba0e1bc76bcc53eccfb40806c0020ec29dcd1bd49" dependencies = [ - "addr2line", + "addr2line 0.17.0", "anyhow", "bincode", "cfg-if 1.0.0", "cpp_demangle", - "gimli", + "gimli 0.26.2", "ittapi-rs", "log", "object 0.28.4", "region", "rustc-demangle", - "rustix", + "rustix 0.33.7", "serde", "target-lexicon", "thiserror", @@ -8023,7 +8169,7 @@ checksum = "4f4779d976206c458edd643d1ac622b6c37e4a0800a8b1d25dfbf245ac2f2cac" dependencies = [ "lazy_static", "object 0.28.4", - "rustix", + "rustix 0.33.7", ] [[package]] @@ -8045,7 +8191,7 @@ dependencies = [ "more-asserts", "rand 0.8.5", "region", - "rustix", + "rustix 0.33.7", "thiserror", "wasmtime-environ", "wasmtime-fiber", @@ -8089,9 +8235,9 @@ dependencies = [ [[package]] name = "wast" -version = "50.0.0" +version = "57.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" +checksum = "6eb0f5ed17ac4421193c7477da05892c2edafd67f9639e3c11a82086416662dc" dependencies = [ "leb128", "memchr", @@ -8101,18 +8247,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.52" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" +checksum = "ab9ab0d87337c3be2bb6fc5cd331c4ba9fd6bcb4ee85048a0dd59ed9ecf92e53" dependencies = [ - "wast 50.0.0", + "wast 57.0.0", ] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" dependencies = [ "js-sys", "wasm-bindgen", @@ -8149,9 +8295,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki 0.22.0", ] @@ -8182,20 +8328,11 @@ dependencies = [ "workspace", ] -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -8204,11 +8341,10 @@ dependencies = [ [[package]] name = "whoami" -version = "1.2.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" dependencies = [ - "bumpalo", "wasm-bindgen", "web-sys", ] @@ -8235,11 +8371,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63a1dccd6b3fbd9a27417f5d30ce9aa3ee9cf529aad453abbf88a49c5d605b79" dependencies = [ "anyhow", - "heck 0.4.0", + "heck 0.4.1", "proc-macro2", "quote", "shellexpand", - "syn", + "syn 1.0.109", "witx", ] @@ -8251,7 +8387,7 @@ checksum = "f1c368d57d9560c34deaa67e06b0953ccf65edb906c525e5a2c866c849b48ec2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wiggle-generate", ] @@ -8299,16 +8435,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.36.1" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows-targets 0.48.0", ] [[package]] @@ -8317,86 +8449,146 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" @@ -8414,7 +8606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d5973cb8cd94a77d03ad7e23bbe14889cb29805da1cec0e4aff75e21aebded" dependencies = [ "bitflags", - "io-lifetimes", + "io-lifetimes 0.5.3", "winapi 0.3.9", ] @@ -8445,7 +8637,7 @@ version = "0.1.0" dependencies = [ "anyhow", "assets", - "async-recursion 1.0.0", + "async-recursion 1.0.4", "bincode", "call", "client", @@ -8453,9 +8645,9 @@ dependencies = [ "context_menu", "db", "drag_and_drop", - "env_logger", + "env_logger 0.9.3", "fs", - "futures 0.3.25", + "futures 0.3.28", "gpui", "indoc", "install_cli", @@ -8474,7 +8666,7 @@ dependencies = [ "terminal", "theme", "util", - "uuid 1.2.2", + "uuid 1.3.2", ] [[package]] @@ -8496,12 +8688,6 @@ dependencies = [ "libc", ] -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" - [[package]] name = "xmlparser" version = "0.13.5" @@ -8558,12 +8744,12 @@ dependencies = [ "db", "diagnostics", "editor", - "env_logger", + "env_logger 0.9.3", "feedback", "file_finder", "fs", "fsevent", - "futures 0.3.25", + "futures 0.3.28", "fuzzy", "go_to_line", "gpui", @@ -8631,13 +8817,13 @@ dependencies = [ "tree-sitter-rust", "tree-sitter-scheme", "tree-sitter-toml", - "tree-sitter-typescript 0.20.2", + "tree-sitter-typescript 0.20.2 (git+https://github.com/tree-sitter/tree-sitter-typescript?rev=5d20856f34315b068c41edaee2ac8a100081d259)", "tree-sitter-yaml", "unindent", "url", "urlencoding", "util", - "uuid 1.2.2", + "uuid 1.3.2", "vim", "welcome", "workspace", @@ -8654,14 +8840,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", - "synstructure", + "syn 2.0.15", ] [[package]] @@ -8685,10 +8870,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.4+zstd.1.5.2" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa202f2ef00074143e219d15b62ffc317d17cc33909feac471c044087cad7b0" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 95661039608cd36cc531ecdd346fd96f15e9a277..b38aa7c42d9e9f2d6b5357a0bc31502ca009907c 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -31,6 +31,7 @@ schemars = "0.8" serde.workspace = true serde_derive.workspace = true serde_json.workspace = true +smallvec.workspace = true toml = "0.5" tree-sitter = "*" tree-sitter-json = "*" diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 7ba52fcb5e52e64e1c82f6a0e9f465f1bf8ecc9b..9475d0205f57fb5d24faf5eb54c9295525f07730 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,5 +1,6 @@ mod keymap_file; pub mod settings_file; +pub mod settings_store; pub mod watched_json; use anyhow::{bail, Result}; diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs new file mode 100644 index 0000000000000000000000000000000000000000..61f689c50b9df03c03653b27c0e691aecbe20197 --- /dev/null +++ b/crates/settings/src/settings_store.rs @@ -0,0 +1,608 @@ +use anyhow::{anyhow, Result}; +use collections::{hash_map, BTreeMap, HashMap, HashSet}; +use schemars::JsonSchema; +use serde::{de::DeserializeOwned, Serialize}; +use serde_json::value::RawValue; +use smallvec::SmallVec; +use std::{ + any::{type_name, Any, TypeId}, + cmp::Ordering, + fmt::Debug, + mem, + path::Path, + sync::Arc, +}; +use util::{merge_non_null_json_value_into, ResultExt as _}; + +/// A value that can be defined as a user setting. +/// +/// Settings can be loaded from a combination of multiple JSON files. +pub trait Setting: 'static + Debug { + /// The name of a field within the JSON file from which this setting should + /// be deserialized. If this is `None`, then the setting will be deserialized + /// from the root object. + const FIELD_NAME: Option<&'static str> = None; + + /// The type that is stored in an individual JSON file. + type FileContent: DeserializeOwned + JsonSchema; + + /// The logic for combining together values from one or more JSON files into the + /// final value for this setting. + /// + /// The user values are ordered from least specific (the global settings file) + /// to most specific (the innermost local settings file). + fn load(default_value: &Self::FileContent, user_values: &[&Self::FileContent]) -> Self; + + fn load_via_json_merge( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + ) -> Self + where + Self: DeserializeOwned, + Self::FileContent: Serialize, + { + let mut merged = serde_json::Value::Null; + for value in [default_value].iter().chain(user_values) { + merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); + } + serde_json::from_value(merged).unwrap() + } +} + +/// A set of strongly-typed setting values defined via multiple JSON files. +#[derive(Default)] +pub struct SettingsStore { + setting_keys: Vec<(Option<&'static str>, TypeId)>, + setting_values: HashMap>, + default_deserialized_settings: DeserializedSettingMap, + user_deserialized_settings: Option, + local_deserialized_settings: BTreeMap, DeserializedSettingMap>, + changed_setting_types: HashSet, +} + +#[derive(Debug)] +struct SettingValue { + global_value: Option, + local_values: Vec<(Arc, T)>, +} + +trait AnySettingValue: Debug { + fn setting_type_name(&self) -> &'static str; + fn deserialize_setting(&self, json: &str) -> Result; + fn load_setting( + &self, + default_value: &DeserializedSetting, + custom: &[&DeserializedSetting], + ) -> Box; + fn value_for_path(&self, path: Option<&Path>) -> &dyn Any; + fn set_global_value(&mut self, value: Box); + fn set_local_value(&mut self, path: Arc, value: Box); +} + +struct DeserializedSetting(Box); + +type DeserializedSettingMap = HashMap; + +impl SettingsStore { + /// Add a new type of setting to the store. + /// + /// This should be done before any settings are loaded. + pub fn register_setting(&mut self) { + let type_id = TypeId::of::(); + + let entry = self.setting_values.entry(type_id); + if matches!(entry, hash_map::Entry::Occupied(_)) { + panic!("duplicate setting type: {}", type_name::()); + } + entry.or_insert(Box::new(SettingValue:: { + global_value: None, + local_values: Vec::new(), + })); + + match self + .setting_keys + .binary_search_by_key(&T::FIELD_NAME, |e| e.0) + { + Ok(ix) | Err(ix) => self.setting_keys.insert(ix, (T::FIELD_NAME, type_id)), + } + } + + /// Get the value of a setting. + /// + /// Panics if settings have not yet been loaded, or there is no default + /// value for this setting. + pub fn get(&self, path: Option<&Path>) -> &T { + self.setting_values + .get(&TypeId::of::()) + .unwrap() + .value_for_path(path) + .downcast_ref::() + .unwrap() + } + + /// Set the default settings via a JSON string. + /// + /// The string should contain a JSON object with a default value for every setting. + pub fn set_default_settings(&mut self, default_settings_content: &str) -> Result<()> { + self.default_deserialized_settings = self.load_setting_map(default_settings_content)?; + if self.default_deserialized_settings.len() != self.setting_keys.len() { + return Err(anyhow!( + "default settings file is missing fields: {:?}", + self.setting_keys + .iter() + .filter(|(_, type_id)| !self + .default_deserialized_settings + .contains_key(type_id)) + .map(|(name, _)| *name) + .collect::>() + )); + } + self.recompute_values(false, None, None); + Ok(()) + } + + /// Set the user settings via a JSON string. + pub fn set_user_settings(&mut self, user_settings_content: &str) -> Result<()> { + let user_settings = self.load_setting_map(user_settings_content)?; + let old_user_settings = + mem::replace(&mut self.user_deserialized_settings, Some(user_settings)); + self.recompute_values(true, None, old_user_settings); + Ok(()) + } + + /// Add or remove a set of local settings via a JSON string. + pub fn set_local_settings( + &mut self, + path: Arc, + settings_content: Option<&str>, + ) -> Result<()> { + let removed_map = if let Some(settings_content) = settings_content { + self.local_deserialized_settings + .insert(path.clone(), self.load_setting_map(settings_content)?); + None + } else { + self.local_deserialized_settings.remove(&path) + }; + self.recompute_values(true, Some(&path), removed_map); + Ok(()) + } + + fn recompute_values( + &mut self, + user_settings_changed: bool, + changed_local_path: Option<&Path>, + old_settings_map: Option, + ) { + // Identify all of the setting types that have changed. + let new_settings_map = if let Some(changed_path) = changed_local_path { + &self.local_deserialized_settings.get(changed_path).unwrap() + } else if user_settings_changed { + self.user_deserialized_settings.as_ref().unwrap() + } else { + &self.default_deserialized_settings + }; + self.changed_setting_types.clear(); + self.changed_setting_types.extend(new_settings_map.keys()); + if let Some(previous_settings_map) = old_settings_map { + self.changed_setting_types + .extend(previous_settings_map.keys()); + } + + // Reload the global and local values for every changed setting. + let mut user_values_stack = Vec::<&DeserializedSetting>::new(); + for setting_type_id in self.changed_setting_types.iter() { + let setting_value = self.setting_values.get_mut(setting_type_id).unwrap(); + + // Build the prioritized list of deserialized values to pass to the setting's + // load function. + user_values_stack.clear(); + if let Some(user_settings) = &self.user_deserialized_settings { + if let Some(user_value) = user_settings.get(setting_type_id) { + user_values_stack.push(&user_value); + } + } + + // If the global settings file changed, reload the global value for the field. + if changed_local_path.is_none() { + let global_value = setting_value.load_setting( + &self.default_deserialized_settings[setting_type_id], + &user_values_stack, + ); + setting_value.set_global_value(global_value); + } + + // Reload the local values for the setting. + let user_value_stack_len = user_values_stack.len(); + for (path, deserialized_values) in &self.local_deserialized_settings { + // If a local settings file changed, then avoid recomputing local + // settings for any path outside of that directory. + if changed_local_path.map_or(false, |changed_local_path| { + !path.starts_with(changed_local_path) + }) { + continue; + } + + // Ignore recomputing settings for any path that hasn't customized that setting. + let Some(deserialized_value) = deserialized_values.get(setting_type_id) else { + continue; + }; + + // Build a stack of all of the local values for that setting. + user_values_stack.truncate(user_value_stack_len); + for (preceding_path, preceding_deserialized_values) in + &self.local_deserialized_settings + { + if preceding_path >= path { + break; + } + if !path.starts_with(preceding_path) { + continue; + } + + if let Some(preceding_deserialized_value) = + preceding_deserialized_values.get(setting_type_id) + { + user_values_stack.push(&*preceding_deserialized_value); + } + } + user_values_stack.push(&*deserialized_value); + + // Load the local value for the field. + let local_value = setting_value.load_setting( + &self.default_deserialized_settings[setting_type_id], + &user_values_stack, + ); + setting_value.set_local_value(path.clone(), local_value); + } + } + } + + /// Deserialize the given JSON string into a map keyed by setting type. + /// + /// Returns an error if the string doesn't contain a valid JSON object. + fn load_setting_map(&self, json: &str) -> Result { + let mut map = DeserializedSettingMap::default(); + let settings_content_by_key: BTreeMap<&str, &RawValue> = serde_json::from_str(json)?; + let mut setting_types_by_key = self.setting_keys.iter().peekable(); + + // Load all of the fields that don't have a key. + while let Some((setting_key, setting_type_id)) = setting_types_by_key.peek() { + if setting_key.is_some() { + break; + } + setting_types_by_key.next(); + if let Some(deserialized_value) = self + .setting_values + .get(setting_type_id) + .unwrap() + .deserialize_setting(json) + .log_err() + { + map.insert(*setting_type_id, deserialized_value); + } + } + + // For each key in the file, load all of the settings that belong to that key. + for (key, key_content) in settings_content_by_key { + while let Some((setting_key, setting_type_id)) = setting_types_by_key.peek() { + let setting_key = setting_key.expect("setting names are ordered"); + match setting_key.cmp(key) { + Ordering::Less => { + setting_types_by_key.next(); + continue; + } + Ordering::Greater => break, + Ordering::Equal => { + if let Some(deserialized_value) = self + .setting_values + .get(setting_type_id) + .unwrap() + .deserialize_setting(key_content.get()) + .log_err() + { + map.insert(*setting_type_id, deserialized_value); + } + setting_types_by_key.next(); + } + } + } + } + Ok(map) + } +} + +impl AnySettingValue for SettingValue { + fn setting_type_name(&self) -> &'static str { + type_name::() + } + + fn load_setting( + &self, + default_value: &DeserializedSetting, + user_values: &[&DeserializedSetting], + ) -> Box { + let default_value = default_value.0.downcast_ref::().unwrap(); + let values: SmallVec<[&T::FileContent; 6]> = user_values + .iter() + .map(|value| value.0.downcast_ref().unwrap()) + .collect(); + Box::new(T::load(default_value, &values)) + } + + fn deserialize_setting(&self, json: &str) -> Result { + let value = serde_json::from_str::(json)?; + Ok(DeserializedSetting(Box::new(value))) + } + + fn value_for_path(&self, path: Option<&Path>) -> &dyn Any { + if let Some(path) = path { + for (settings_path, value) in self.local_values.iter().rev() { + if path.starts_with(&settings_path) { + return value; + } + } + } + self.global_value.as_ref().unwrap() + } + + fn set_global_value(&mut self, value: Box) { + self.global_value = Some(*value.downcast().unwrap()); + } + + fn set_local_value(&mut self, path: Arc, value: Box) { + let value = *value.downcast().unwrap(); + match self.local_values.binary_search_by_key(&&path, |e| &e.0) { + Ok(ix) => self.local_values[ix].1 = value, + Err(ix) => self.local_values.insert(ix, (path, value)), + } + } +} + +impl Debug for SettingsStore { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return f + .debug_struct("SettingsStore") + .field( + "setting_value_sets_by_type", + &self + .setting_values + .values() + .map(|set| (set.setting_type_name(), set)) + .collect::>(), + ) + .finish_non_exhaustive(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_derive::Deserialize; + + #[test] + fn test_settings_store() { + let mut store = SettingsStore::default(); + store.register_setting::(); + store.register_setting::(); + store.register_setting::(); + + // error - missing required field in default settings + store + .set_default_settings( + r#"{ + "user": { + "name": "John Doe", + "age": 30, + "staff": false + } + }"#, + ) + .unwrap_err(); + + // error - type error in default settings + store + .set_default_settings( + r#"{ + "turbo": "the-wrong-type", + "user": { + "name": "John Doe", + "age": 30, + "staff": false + } + }"#, + ) + .unwrap_err(); + + // valid default settings. + store + .set_default_settings( + r#"{ + "turbo": false, + "user": { + "name": "John Doe", + "age": 30, + "staff": false + } + }"#, + ) + .unwrap(); + + assert_eq!(store.get::(None), &TurboSetting(false)); + assert_eq!( + store.get::(None), + &UserSettings { + name: "John Doe".to_string(), + age: 30, + staff: false, + } + ); + assert_eq!( + store.get::(None), + &MultiKeySettings { + key1: String::new(), + key2: String::new(), + } + ); + + store + .set_user_settings( + r#"{ + "turbo": true, + "user": { "age": 31 }, + "key1": "a" + }"#, + ) + .unwrap(); + + assert_eq!(store.get::(None), &TurboSetting(true)); + assert_eq!( + store.get::(None), + &UserSettings { + name: "John Doe".to_string(), + age: 31, + staff: false + } + ); + + store + .set_local_settings( + Path::new("/root1").into(), + Some(r#"{ "user": { "staff": true } }"#), + ) + .unwrap(); + store + .set_local_settings( + Path::new("/root1/subdir").into(), + Some(r#"{ "user": { "name": "Jane Doe" } }"#), + ) + .unwrap(); + + store + .set_local_settings( + Path::new("/root2").into(), + Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#), + ) + .unwrap(); + + assert_eq!( + store.get::(Some(Path::new("/root1/something"))), + &UserSettings { + name: "John Doe".to_string(), + age: 31, + staff: true + } + ); + assert_eq!( + store.get::(Some(Path::new("/root1/subdir/something"))), + &UserSettings { + name: "Jane Doe".to_string(), + age: 31, + staff: true + } + ); + assert_eq!( + store.get::(Some(Path::new("/root2/something"))), + &UserSettings { + name: "John Doe".to_string(), + age: 42, + staff: false + } + ); + assert_eq!( + store.get::(Some(Path::new("/root2/something"))), + &MultiKeySettings { + key1: "a".to_string(), + key2: "b".to_string(), + } + ); + } + + #[derive(Debug, PartialEq, Deserialize)] + struct UserSettings { + name: String, + age: u32, + staff: bool, + } + + #[derive(Serialize, Deserialize, JsonSchema)] + struct UserSettingsJson { + name: Option, + age: Option, + staff: Option, + } + + impl Setting for UserSettings { + const FIELD_NAME: Option<&'static str> = Some("user"); + type FileContent = UserSettingsJson; + + fn load(default_value: &UserSettingsJson, user_values: &[&UserSettingsJson]) -> Self { + Self::load_via_json_merge(default_value, user_values) + } + } + + #[derive(Debug, Deserialize, PartialEq)] + struct TurboSetting(bool); + + impl Setting for TurboSetting { + const FIELD_NAME: Option<&'static str> = Some("turbo"); + type FileContent = Option; + + fn load(default_value: &Option, user_values: &[&Option]) -> Self { + Self::load_via_json_merge(default_value, user_values) + } + } + + #[derive(Clone, Debug, PartialEq, Deserialize)] + struct MultiKeySettings { + #[serde(default)] + key1: String, + #[serde(default)] + key2: String, + } + + #[derive(Serialize, Deserialize, JsonSchema)] + struct MultiKeySettingsJson { + key1: Option, + key2: Option, + } + + impl Setting for MultiKeySettings { + type FileContent = MultiKeySettingsJson; + + fn load( + default_value: &MultiKeySettingsJson, + user_values: &[&MultiKeySettingsJson], + ) -> Self { + Self::load_via_json_merge(default_value, user_values) + } + } + + #[derive(Debug, Deserialize)] + struct JournalSettings { + pub path: String, + pub hour_format: HourFormat, + } + + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] + #[serde(rename_all = "snake_case")] + enum HourFormat { + Hour12, + Hour24, + } + + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] + struct JournalSettingsJson { + pub path: Option, + pub hour_format: Option, + } + + impl Setting for JournalSettings { + const FIELD_NAME: Option<&'static str> = Some("journal"); + + type FileContent = JournalSettingsJson; + + fn load(default_value: &JournalSettingsJson, user_values: &[&JournalSettingsJson]) -> Self { + Self::load_via_json_merge(default_value, user_values) + } + } +} diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 903b0eec59741c2830309ec025ebcb57ede12cf4..fafd9d5d3bae3ce4c873d3a0e4439cbe6dd100ab 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -93,6 +93,27 @@ pub fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json: } } +pub fn merge_non_null_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) { + use serde_json::Value; + if let Value::Object(source_object) = source { + let target_object = if let Value::Object(target) = target { + target + } else { + *target = Value::Object(Default::default()); + target.as_object_mut().unwrap() + }; + for (key, value) in source_object { + if let Some(target) = target_object.get_mut(&key) { + merge_non_null_json_value_into(value, target); + } else if !value.is_null() { + target_object.insert(key.clone(), value); + } + } + } else if !source.is_null() { + *target = source + } +} + pub trait ResultExt { type Ok; From 24e06334d00f9823336dbb321615e365a07538da Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 8 May 2023 14:05:38 -0700 Subject: [PATCH 02/25] Allow registering additional settings after loading global settings --- crates/settings/src/settings_store.rs | 226 +++++++++++++++----------- 1 file changed, 133 insertions(+), 93 deletions(-) diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 61f689c50b9df03c03653b27c0e691aecbe20197..7c8c2a75228e423b3aaccb379d519a958afc52c7 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,12 +1,10 @@ use anyhow::{anyhow, Result}; use collections::{hash_map, BTreeMap, HashMap, HashSet}; use schemars::JsonSchema; -use serde::{de::DeserializeOwned, Serialize}; -use serde_json::value::RawValue; +use serde::{de::DeserializeOwned, Deserialize as _, Serialize}; use smallvec::SmallVec; use std::{ any::{type_name, Any, TypeId}, - cmp::Ordering, fmt::Debug, mem, path::Path, @@ -18,13 +16,13 @@ use util::{merge_non_null_json_value_into, ResultExt as _}; /// /// Settings can be loaded from a combination of multiple JSON files. pub trait Setting: 'static + Debug { - /// The name of a field within the JSON file from which this setting should + /// The name of a key within the JSON file from which this setting should /// be deserialized. If this is `None`, then the setting will be deserialized /// from the root object. - const FIELD_NAME: Option<&'static str> = None; + const KEY: Option<&'static str> = None; /// The type that is stored in an individual JSON file. - type FileContent: DeserializeOwned + JsonSchema; + type FileContent: Serialize + DeserializeOwned + JsonSchema; /// The logic for combining together values from one or more JSON files into the /// final value for this setting. @@ -52,9 +50,8 @@ pub trait Setting: 'static + Debug { /// A set of strongly-typed setting values defined via multiple JSON files. #[derive(Default)] pub struct SettingsStore { - setting_keys: Vec<(Option<&'static str>, TypeId)>, setting_values: HashMap>, - default_deserialized_settings: DeserializedSettingMap, + default_deserialized_settings: Option, user_deserialized_settings: Option, local_deserialized_settings: BTreeMap, DeserializedSettingMap>, changed_setting_types: HashSet, @@ -67,8 +64,9 @@ struct SettingValue { } trait AnySettingValue: Debug { + fn key(&self) -> Option<&'static str>; fn setting_type_name(&self) -> &'static str; - fn deserialize_setting(&self, json: &str) -> Result; + fn deserialize_setting(&self, json: &serde_json::Value) -> Result; fn load_setting( &self, default_value: &DeserializedSetting, @@ -81,29 +79,40 @@ trait AnySettingValue: Debug { struct DeserializedSetting(Box); -type DeserializedSettingMap = HashMap; +struct DeserializedSettingMap { + untyped: serde_json::Value, + typed: HashMap, +} impl SettingsStore { /// Add a new type of setting to the store. - /// - /// This should be done before any settings are loaded. pub fn register_setting(&mut self) { - let type_id = TypeId::of::(); + let setting_type_id = TypeId::of::(); - let entry = self.setting_values.entry(type_id); + let entry = self.setting_values.entry(setting_type_id); if matches!(entry, hash_map::Entry::Occupied(_)) { panic!("duplicate setting type: {}", type_name::()); } - entry.or_insert(Box::new(SettingValue:: { + let setting_value = entry.or_insert(Box::new(SettingValue:: { global_value: None, local_values: Vec::new(), })); - match self - .setting_keys - .binary_search_by_key(&T::FIELD_NAME, |e| e.0) - { - Ok(ix) | Err(ix) => self.setting_keys.insert(ix, (T::FIELD_NAME, type_id)), + if let Some(default_settings) = self.default_deserialized_settings.as_mut() { + Self::load_setting_in_map(setting_type_id, setting_value, default_settings); + + let mut user_values_stack = Vec::new(); + if let Some(user_settings) = self.user_deserialized_settings.as_mut() { + Self::load_setting_in_map(setting_type_id, setting_value, user_settings); + if let Some(user_value) = user_settings.typed.get(&setting_type_id) { + user_values_stack = vec![user_value]; + } + } + if let Some(default_deserialized_value) = default_settings.typed.get(&setting_type_id) { + setting_value.set_global_value( + setting_value.load_setting(default_deserialized_value, &user_values_stack), + ); + } } } @@ -124,19 +133,18 @@ impl SettingsStore { /// /// The string should contain a JSON object with a default value for every setting. pub fn set_default_settings(&mut self, default_settings_content: &str) -> Result<()> { - self.default_deserialized_settings = self.load_setting_map(default_settings_content)?; - if self.default_deserialized_settings.len() != self.setting_keys.len() { + let deserialized_setting_map = self.load_setting_map(default_settings_content)?; + if deserialized_setting_map.typed.len() != self.setting_values.len() { return Err(anyhow!( "default settings file is missing fields: {:?}", - self.setting_keys + self.setting_values .iter() - .filter(|(_, type_id)| !self - .default_deserialized_settings - .contains_key(type_id)) + .filter(|(type_id, _)| !deserialized_setting_map.typed.contains_key(type_id)) .map(|(name, _)| *name) .collect::>() )); } + self.default_deserialized_settings = Some(deserialized_setting_map); self.recompute_values(false, None, None); Ok(()) } @@ -175,17 +183,17 @@ impl SettingsStore { ) { // Identify all of the setting types that have changed. let new_settings_map = if let Some(changed_path) = changed_local_path { - &self.local_deserialized_settings.get(changed_path).unwrap() + self.local_deserialized_settings.get(changed_path) } else if user_settings_changed { - self.user_deserialized_settings.as_ref().unwrap() + self.user_deserialized_settings.as_ref() } else { - &self.default_deserialized_settings + self.default_deserialized_settings.as_ref() }; self.changed_setting_types.clear(); - self.changed_setting_types.extend(new_settings_map.keys()); - if let Some(previous_settings_map) = old_settings_map { - self.changed_setting_types - .extend(previous_settings_map.keys()); + for map in [old_settings_map.as_ref(), new_settings_map] { + if let Some(map) = map { + self.changed_setting_types.extend(map.typed.keys()); + } } // Reload the global and local values for every changed setting. @@ -197,18 +205,26 @@ impl SettingsStore { // load function. user_values_stack.clear(); if let Some(user_settings) = &self.user_deserialized_settings { - if let Some(user_value) = user_settings.get(setting_type_id) { + if let Some(user_value) = user_settings.typed.get(setting_type_id) { user_values_stack.push(&user_value); } } + let default_deserialized_value = if let Some(value) = self + .default_deserialized_settings + .as_ref() + .and_then(|map| map.typed.get(setting_type_id)) + { + value + } else { + continue; + }; + // If the global settings file changed, reload the global value for the field. if changed_local_path.is_none() { - let global_value = setting_value.load_setting( - &self.default_deserialized_settings[setting_type_id], - &user_values_stack, + setting_value.set_global_value( + setting_value.load_setting(default_deserialized_value, &user_values_stack), ); - setting_value.set_global_value(global_value); } // Reload the local values for the setting. @@ -223,7 +239,7 @@ impl SettingsStore { } // Ignore recomputing settings for any path that hasn't customized that setting. - let Some(deserialized_value) = deserialized_values.get(setting_type_id) else { + let Some(deserialized_value) = deserialized_values.typed.get(setting_type_id) else { continue; }; @@ -240,7 +256,7 @@ impl SettingsStore { } if let Some(preceding_deserialized_value) = - preceding_deserialized_values.get(setting_type_id) + preceding_deserialized_values.typed.get(setting_type_id) { user_values_stack.push(&*preceding_deserialized_value); } @@ -248,11 +264,10 @@ impl SettingsStore { user_values_stack.push(&*deserialized_value); // Load the local value for the field. - let local_value = setting_value.load_setting( - &self.default_deserialized_settings[setting_type_id], - &user_values_stack, + setting_value.set_local_value( + path.clone(), + setting_value.load_setting(default_deserialized_value, &user_values_stack), ); - setting_value.set_local_value(path.clone(), local_value); } } } @@ -261,57 +276,42 @@ impl SettingsStore { /// /// Returns an error if the string doesn't contain a valid JSON object. fn load_setting_map(&self, json: &str) -> Result { - let mut map = DeserializedSettingMap::default(); - let settings_content_by_key: BTreeMap<&str, &RawValue> = serde_json::from_str(json)?; - let mut setting_types_by_key = self.setting_keys.iter().peekable(); - - // Load all of the fields that don't have a key. - while let Some((setting_key, setting_type_id)) = setting_types_by_key.peek() { - if setting_key.is_some() { - break; - } - setting_types_by_key.next(); - if let Some(deserialized_value) = self - .setting_values - .get(setting_type_id) - .unwrap() - .deserialize_setting(json) - .log_err() - { - map.insert(*setting_type_id, deserialized_value); - } + let mut map = DeserializedSettingMap { + typed: HashMap::default(), + untyped: serde_json::from_str(json)?, + }; + for (setting_type_id, setting_value) in self.setting_values.iter() { + Self::load_setting_in_map(*setting_type_id, setting_value, &mut map); } + Ok(map) + } - // For each key in the file, load all of the settings that belong to that key. - for (key, key_content) in settings_content_by_key { - while let Some((setting_key, setting_type_id)) = setting_types_by_key.peek() { - let setting_key = setting_key.expect("setting names are ordered"); - match setting_key.cmp(key) { - Ordering::Less => { - setting_types_by_key.next(); - continue; - } - Ordering::Greater => break, - Ordering::Equal => { - if let Some(deserialized_value) = self - .setting_values - .get(setting_type_id) - .unwrap() - .deserialize_setting(key_content.get()) - .log_err() - { - map.insert(*setting_type_id, deserialized_value); - } - setting_types_by_key.next(); - } - } + fn load_setting_in_map( + setting_type_id: TypeId, + setting_value: &Box, + map: &mut DeserializedSettingMap, + ) { + let value = if let Some(setting_key) = setting_value.key() { + if let Some(value) = map.untyped.get(setting_key) { + value + } else { + return; } + } else { + &map.untyped + }; + + if let Some(deserialized_value) = setting_value.deserialize_setting(&value).log_err() { + map.typed.insert(setting_type_id, deserialized_value); } - Ok(map) } } impl AnySettingValue for SettingValue { + fn key(&self) -> Option<&'static str> { + T::KEY + } + fn setting_type_name(&self) -> &'static str { type_name::() } @@ -329,8 +329,8 @@ impl AnySettingValue for SettingValue { Box::new(T::load(default_value, &values)) } - fn deserialize_setting(&self, json: &str) -> Result { - let value = serde_json::from_str::(json)?; + fn deserialize_setting(&self, json: &serde_json::Value) -> Result { + let value = T::FileContent::deserialize(json)?; Ok(DeserializedSetting(Box::new(value))) } @@ -380,7 +380,7 @@ mod tests { use serde_derive::Deserialize; #[test] - fn test_settings_store() { + fn test_settings_store_basic() { let mut store = SettingsStore::default(); store.register_setting::(); store.register_setting::(); @@ -517,6 +517,46 @@ mod tests { ); } + #[test] + fn test_setting_store_load_before_register() { + let mut store = SettingsStore::default(); + store + .set_default_settings( + r#"{ + "turbo": true, + "user": { + "name": "John Doe", + "age": 30, + "staff": false + }, + "key1": "x + }"#, + ) + .unwrap(); + store.set_user_settings(r#"{ "turbo": false }"#).unwrap(); + store.register_setting::(); + store.register_setting::(); + + assert_eq!(store.get::(None), &TurboSetting(false)); + assert_eq!( + store.get::(None), + &UserSettings { + name: "John Doe".to_string(), + age: 30, + staff: false, + } + ); + + store.register_setting::(); + assert_eq!( + store.get::(None), + &MultiKeySettings { + key1: "x".into(), + key2: String::new(), + } + ); + } + #[derive(Debug, PartialEq, Deserialize)] struct UserSettings { name: String, @@ -532,7 +572,7 @@ mod tests { } impl Setting for UserSettings { - const FIELD_NAME: Option<&'static str> = Some("user"); + const KEY: Option<&'static str> = Some("user"); type FileContent = UserSettingsJson; fn load(default_value: &UserSettingsJson, user_values: &[&UserSettingsJson]) -> Self { @@ -544,7 +584,7 @@ mod tests { struct TurboSetting(bool); impl Setting for TurboSetting { - const FIELD_NAME: Option<&'static str> = Some("turbo"); + const KEY: Option<&'static str> = Some("turbo"); type FileContent = Option; fn load(default_value: &Option, user_values: &[&Option]) -> Self { @@ -597,7 +637,7 @@ mod tests { } impl Setting for JournalSettings { - const FIELD_NAME: Option<&'static str> = Some("journal"); + const KEY: Option<&'static str> = Some("journal"); type FileContent = JournalSettingsJson; From 316f791a775194dab121e61732469623deb25774 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 8 May 2023 17:52:52 -0700 Subject: [PATCH 03/25] Add generic update method to SettingsStore --- crates/settings/src/settings_store.rs | 416 +++++++++++++++++++++++++- 1 file changed, 408 insertions(+), 8 deletions(-) diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 7c8c2a75228e423b3aaccb379d519a958afc52c7..5191d768ea59933e016ddded445e961210b4e7c8 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,5 +1,6 @@ use anyhow::{anyhow, Result}; use collections::{hash_map, BTreeMap, HashMap, HashSet}; +use lazy_static::lazy_static; use schemars::JsonSchema; use serde::{de::DeserializeOwned, Deserialize as _, Serialize}; use smallvec::SmallVec; @@ -7,10 +8,12 @@ use std::{ any::{type_name, Any, TypeId}, fmt::Debug, mem, + ops::Range, path::Path, + str, sync::Arc, }; -use util::{merge_non_null_json_value_into, ResultExt as _}; +use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _}; /// A value that can be defined as a user setting. /// @@ -22,7 +25,7 @@ pub trait Setting: 'static + Debug { const KEY: Option<&'static str> = None; /// The type that is stored in an individual JSON file. - type FileContent: Serialize + DeserializeOwned + JsonSchema; + type FileContent: Clone + Serialize + DeserializeOwned + JsonSchema; /// The logic for combining together values from one or more JSON files into the /// final value for this setting. @@ -37,7 +40,6 @@ pub trait Setting: 'static + Debug { ) -> Self where Self: DeserializeOwned, - Self::FileContent: Serialize, { let mut merged = serde_json::Value::Null; for value in [default_value].iter().chain(user_values) { @@ -55,6 +57,7 @@ pub struct SettingsStore { user_deserialized_settings: Option, local_deserialized_settings: BTreeMap, DeserializedSettingMap>, changed_setting_types: HashSet, + tab_size_callback: Option<(TypeId, Box Option>)>, } #[derive(Debug)] @@ -129,6 +132,81 @@ impl SettingsStore { .unwrap() } + /// Update the value of a setting. + /// + /// Returns a list of edits to apply to the JSON file. + pub fn update( + &self, + text: &str, + update: impl Fn(&mut T::FileContent), + ) -> Vec<(Range, String)> { + let setting_type_id = TypeId::of::(); + let old_content = self + .user_deserialized_settings + .as_ref() + .unwrap() + .typed + .get(&setting_type_id) + .unwrap() + .0 + .downcast_ref::() + .unwrap() + .clone(); + let mut new_content = old_content.clone(); + update(&mut new_content); + + let mut parser = tree_sitter::Parser::new(); + parser.set_language(tree_sitter_json::language()).unwrap(); + let tree = parser.parse(text, None).unwrap(); + + let old_value = &serde_json::to_value(old_content).unwrap(); + let new_value = &serde_json::to_value(new_content).unwrap(); + + let mut key_path = Vec::new(); + if let Some(key) = T::KEY { + key_path.push(key); + } + + let mut edits = Vec::new(); + let tab_size = self.json_tab_size(); + update_value_in_json_text( + &text, + &tree, + &mut key_path, + tab_size, + &old_value, + &new_value, + &mut edits, + ); + edits.sort_unstable_by_key(|e| e.0.start); + return edits; + } + + /// Configure the tab sized when updating JSON files. + pub fn set_json_tab_size_callback( + &mut self, + get_tab_size: fn(&T) -> Option, + ) { + self.tab_size_callback = Some(( + TypeId::of::(), + Box::new(move |value| get_tab_size(value.downcast_ref::().unwrap())), + )); + } + + fn json_tab_size(&self) -> usize { + const DEFAULT_JSON_TAB_SIZE: usize = 2; + + if let Some((setting_type_id, callback)) = &self.tab_size_callback { + let setting_value = self.setting_values.get(setting_type_id).unwrap(); + let value = setting_value.value_for_path(None); + if let Some(value) = callback(value) { + return value; + } + } + + DEFAULT_JSON_TAB_SIZE + } + /// Set the default settings via a JSON string. /// /// The string should contain a JSON object with a default value for every setting. @@ -277,8 +355,8 @@ impl SettingsStore { /// Returns an error if the string doesn't contain a valid JSON object. fn load_setting_map(&self, json: &str) -> Result { let mut map = DeserializedSettingMap { + untyped: parse_json_with_comments(json)?, typed: HashMap::default(), - untyped: serde_json::from_str(json)?, }; for (setting_type_id, setting_value) in self.setting_values.iter() { Self::load_setting_in_map(*setting_type_id, setting_value, &mut map); @@ -374,10 +452,231 @@ impl Debug for SettingsStore { } } +fn update_value_in_json_text<'a>( + text: &str, + syntax_tree: &tree_sitter::Tree, + key_path: &mut Vec<&'a str>, + tab_size: usize, + old_value: &'a serde_json::Value, + new_value: &'a serde_json::Value, + edits: &mut Vec<(Range, String)>, +) { + // If the old and new values are both objects, then compare them key by key, + // preserving the comments and formatting of the unchanged parts. Otherwise, + // replace the old value with the new value. + if let (serde_json::Value::Object(old_object), serde_json::Value::Object(new_object)) = + (old_value, new_value) + { + for (key, old_sub_value) in old_object.iter() { + key_path.push(key); + let new_sub_value = new_object.get(key).unwrap_or(&serde_json::Value::Null); + update_value_in_json_text( + text, + syntax_tree, + key_path, + tab_size, + old_sub_value, + new_sub_value, + edits, + ); + key_path.pop(); + } + for (key, new_sub_value) in new_object.iter() { + key_path.push(key); + if !old_object.contains_key(key) { + update_value_in_json_text( + text, + syntax_tree, + key_path, + tab_size, + &serde_json::Value::Null, + new_sub_value, + edits, + ); + } + key_path.pop(); + } + } else if old_value != new_value { + let (range, replacement) = + replace_value_in_json_text(text, syntax_tree, &key_path, tab_size, &new_value); + edits.push((range, replacement)); + } +} + +lazy_static! { + static ref PAIR_QUERY: tree_sitter::Query = tree_sitter::Query::new( + tree_sitter_json::language(), + "(pair key: (string) @key value: (_) @value)", + ) + .unwrap(); +} + +fn replace_value_in_json_text( + text: &str, + syntax_tree: &tree_sitter::Tree, + key_path: &[&str], + tab_size: usize, + new_value: impl Serialize, +) -> (Range, String) { + const LANGUAGE_OVERRIDES: &'static str = "language_overrides"; + const LANGUAGES: &'static str = "languages"; + + let mut cursor = tree_sitter::QueryCursor::new(); + + let has_language_overrides = text.contains(LANGUAGE_OVERRIDES); + + let mut depth = 0; + let mut last_value_range = 0..0; + let mut first_key_start = None; + let mut existing_value_range = 0..text.len(); + let matches = cursor.matches(&PAIR_QUERY, syntax_tree.root_node(), text.as_bytes()); + for mat in matches { + if mat.captures.len() != 2 { + continue; + } + + let key_range = mat.captures[0].node.byte_range(); + let value_range = mat.captures[1].node.byte_range(); + + // Don't enter sub objects until we find an exact + // match for the current keypath + if last_value_range.contains_inclusive(&value_range) { + continue; + } + + last_value_range = value_range.clone(); + + if key_range.start > existing_value_range.end { + break; + } + + first_key_start.get_or_insert_with(|| key_range.start); + + let found_key = text + .get(key_range.clone()) + .map(|key_text| { + if key_path[depth] == LANGUAGES && has_language_overrides { + return key_text == format!("\"{}\"", LANGUAGE_OVERRIDES); + } else { + return key_text == format!("\"{}\"", key_path[depth]); + } + }) + .unwrap_or(false); + + if found_key { + existing_value_range = value_range; + // Reset last value range when increasing in depth + last_value_range = existing_value_range.start..existing_value_range.start; + depth += 1; + + if depth == key_path.len() { + break; + } else { + first_key_start = None; + } + } + } + + // We found the exact key we want, insert the new value + if depth == key_path.len() { + let new_val = to_pretty_json(&new_value, tab_size, tab_size * depth); + (existing_value_range, new_val) + } else { + // We have key paths, construct the sub objects + let new_key = if has_language_overrides && key_path[depth] == LANGUAGES { + LANGUAGE_OVERRIDES + } else { + key_path[depth] + }; + + // We don't have the key, construct the nested objects + let mut new_value = serde_json::to_value(new_value).unwrap(); + for key in key_path[(depth + 1)..].iter().rev() { + if has_language_overrides && key == &LANGUAGES { + new_value = serde_json::json!({ LANGUAGE_OVERRIDES.to_string(): new_value }); + } else { + new_value = serde_json::json!({ key.to_string(): new_value }); + } + } + + if let Some(first_key_start) = first_key_start { + let mut row = 0; + let mut column = 0; + for (ix, char) in text.char_indices() { + if ix == first_key_start { + break; + } + if char == '\n' { + row += 1; + column = 0; + } else { + column += char.len_utf8(); + } + } + + if row > 0 { + // depth is 0 based, but division needs to be 1 based. + let new_val = to_pretty_json(&new_value, column / (depth + 1), column); + let space = ' '; + let content = format!("\"{new_key}\": {new_val},\n{space:width$}", width = column); + (first_key_start..first_key_start, content) + } else { + let new_val = serde_json::to_string(&new_value).unwrap(); + let mut content = format!(r#""{new_key}": {new_val},"#); + content.push(' '); + (first_key_start..first_key_start, content) + } + } else { + new_value = serde_json::json!({ new_key.to_string(): new_value }); + let indent_prefix_len = 4 * depth; + let mut new_val = to_pretty_json(&new_value, 4, indent_prefix_len); + if depth == 0 { + new_val.push('\n'); + } + + (existing_value_range, new_val) + } + } +} + +fn to_pretty_json(value: &impl Serialize, indent_size: usize, indent_prefix_len: usize) -> String { + const SPACES: [u8; 32] = [b' '; 32]; + + debug_assert!(indent_size <= SPACES.len()); + debug_assert!(indent_prefix_len <= SPACES.len()); + + let mut output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter( + &mut output, + serde_json::ser::PrettyFormatter::with_indent(&SPACES[0..indent_size.min(SPACES.len())]), + ); + + value.serialize(&mut ser).unwrap(); + let text = String::from_utf8(output).unwrap(); + + let mut adjusted_text = String::new(); + for (i, line) in text.split('\n').enumerate() { + if i > 0 { + adjusted_text.push_str(str::from_utf8(&SPACES[0..indent_prefix_len]).unwrap()); + } + adjusted_text.push_str(line); + adjusted_text.push('\n'); + } + adjusted_text.pop(); + adjusted_text +} + +fn parse_json_with_comments(content: &str) -> Result { + Ok(serde_json::from_reader( + json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()), + )?) +} + #[cfg(test)] mod tests { use super::*; use serde_derive::Deserialize; + use unindent::Unindent; #[test] fn test_settings_store_basic() { @@ -518,7 +817,7 @@ mod tests { } #[test] - fn test_setting_store_load_before_register() { + fn test_setting_store_assign_json_before_register() { let mut store = SettingsStore::default(); store .set_default_settings( @@ -529,7 +828,7 @@ mod tests { "age": 30, "staff": false }, - "key1": "x + "key1": "x" }"#, ) .unwrap(); @@ -557,6 +856,86 @@ mod tests { ); } + #[test] + fn test_setting_store_update() { + let mut store = SettingsStore::default(); + store.register_setting::(); + store.register_setting::(); + + // entries added and updated + check_settings_update::( + &mut store, + r#"{ + "languages": { + "JSON": { + "is_enabled": true + } + } + }"# + .unindent(), + |settings| { + settings.languages.get_mut("JSON").unwrap().is_enabled = false; + settings + .languages + .insert("Rust".into(), LanguageSettingEntry { is_enabled: true }); + }, + r#"{ + "languages": { + "Rust": { + "is_enabled": true + }, + "JSON": { + "is_enabled": false + } + } + }"# + .unindent(), + ); + + // weird formatting + check_settings_update::( + &mut store, + r#"{ + "user": { "age": 36, "name": "Max", "staff": true } + }"# + .unindent(), + |settings| settings.age = Some(37), + r#"{ + "user": { "age": 37, "name": "Max", "staff": true } + }"# + .unindent(), + ); + + // no content + check_settings_update::( + &mut store, + r#""#.unindent(), + |settings| settings.age = Some(37), + r#"{ + "user": { + "age": 37 + } + } + "# + .unindent(), + ); + } + + fn check_settings_update( + store: &mut SettingsStore, + old_json: String, + update: fn(&mut T::FileContent), + expected_new_json: String, + ) { + store.set_user_settings(&old_json).ok(); + let edits = store.update::(&old_json, update); + let mut new_json = old_json; + for (range, replacement) in edits.into_iter().rev() { + new_json.replace_range(range, &replacement); + } + pretty_assertions::assert_eq!(new_json, expected_new_json); + } + #[derive(Debug, PartialEq, Deserialize)] struct UserSettings { name: String, @@ -564,7 +943,7 @@ mod tests { staff: bool, } - #[derive(Serialize, Deserialize, JsonSchema)] + #[derive(Clone, Serialize, Deserialize, JsonSchema)] struct UserSettingsJson { name: Option, age: Option, @@ -600,7 +979,7 @@ mod tests { key2: String, } - #[derive(Serialize, Deserialize, JsonSchema)] + #[derive(Clone, Serialize, Deserialize, JsonSchema)] struct MultiKeySettingsJson { key1: Option, key2: Option, @@ -645,4 +1024,25 @@ mod tests { Self::load_via_json_merge(default_value, user_values) } } + + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] + struct LanguageSettings { + #[serde(default)] + languages: HashMap, + } + + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] + struct LanguageSettingEntry { + is_enabled: bool, + } + + impl Setting for LanguageSettings { + const KEY: Option<&'static str> = None; + + type FileContent = Self; + + fn load(default_value: &Self, user_values: &[&Self]) -> Self { + Self::load_via_json_merge(default_value, user_values) + } + } } From 9a6a2d9d2719660b3b5719f311f0643d0a8bbe26 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 9 May 2023 18:14:42 -0700 Subject: [PATCH 04/25] Start using the SettingsStore in the app --- Cargo.lock | 3 + crates/copilot_button/Cargo.toml | 1 + crates/copilot_button/src/copilot_button.rs | 66 +- crates/settings/Cargo.toml | 3 +- crates/settings/src/keymap_file.rs | 2 +- crates/settings/src/settings.rs | 794 +++----------------- crates/settings/src/settings_file.rs | 360 +++++---- crates/settings/src/settings_store.rs | 209 ++++-- crates/theme/Cargo.toml | 3 + crates/theme/src/theme_registry.rs | 15 +- crates/theme_selector/Cargo.toml | 1 + crates/theme_selector/src/theme_selector.rs | 16 +- crates/welcome/Cargo.toml | 1 + crates/welcome/src/base_keymap_picker.rs | 19 +- crates/welcome/src/welcome.rs | 24 +- crates/zed/src/main.rs | 58 +- crates/zed/src/zed.rs | 2 +- 17 files changed, 529 insertions(+), 1048 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02370dc98b0bd984911e38bf033cfc5e16d1ea53..266b0e6d414bf771436786e7fe25de3d4ca0c1ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1404,6 +1404,7 @@ dependencies = [ "context_menu", "copilot", "editor", + "fs", "futures 0.3.28", "gpui", "settings", @@ -6847,6 +6848,7 @@ name = "theme_selector" version = "0.1.0" dependencies = [ "editor", + "fs", "fuzzy", "gpui", "log", @@ -8315,6 +8317,7 @@ dependencies = [ "anyhow", "db", "editor", + "fs", "fuzzy", "gpui", "install_cli", diff --git a/crates/copilot_button/Cargo.toml b/crates/copilot_button/Cargo.toml index 2d42b192d95bdfdf528a96140289c4e28709e3b8..77937e8bd0ddbd39889e7e2aa433d07fbde7c55b 100644 --- a/crates/copilot_button/Cargo.toml +++ b/crates/copilot_button/Cargo.toml @@ -12,6 +12,7 @@ doctest = false assets = { path = "../assets" } copilot = { path = "../copilot" } editor = { path = "../editor" } +fs = { path = "../fs" } context_menu = { path = "../context_menu" } gpui = { path = "../gpui" } settings = { path = "../settings" } diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 4b0c9b494a1dc4f6a05455522f95e0301b88b794..fec48f1f34468d9cdbe9662acf6c29f65cd1841b 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -2,13 +2,14 @@ use anyhow::Result; use context_menu::{ContextMenu, ContextMenuItem}; use copilot::{Copilot, SignOut, Status}; use editor::{scroll::autoscroll::Autoscroll, Editor}; +use fs::Fs; use gpui::{ elements::*, platform::{CursorStyle, MouseButton}, AnyElement, AppContext, AsyncAppContext, Element, Entity, MouseState, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; -use settings::{settings_file::SettingsFile, Settings}; +use settings::{update_settings_file, Settings, SettingsStore}; use std::{path::Path, sync::Arc}; use util::{paths, ResultExt}; use workspace::{ @@ -26,6 +27,7 @@ pub struct CopilotButton { editor_enabled: Option, language: Option>, path: Option>, + fs: Arc, } impl Entity for CopilotButton { @@ -143,7 +145,7 @@ impl View for CopilotButton { } impl CopilotButton { - pub fn new(cx: &mut ViewContext) -> Self { + pub fn new(fs: Arc, cx: &mut ViewContext) -> Self { let button_view_id = cx.view_id(); let menu = cx.add_view(|cx| { let mut menu = ContextMenu::new(button_view_id, cx); @@ -164,17 +166,19 @@ impl CopilotButton { editor_enabled: None, language: None, path: None, + fs, } } pub fn deploy_copilot_start_menu(&mut self, cx: &mut ViewContext) { let mut menu_options = Vec::with_capacity(2); + let fs = self.fs.clone(); menu_options.push(ContextMenuItem::handler("Sign In", |cx| { initiate_sign_in(cx) })); - menu_options.push(ContextMenuItem::handler("Disable Copilot", |cx| { - hide_copilot(cx) + menu_options.push(ContextMenuItem::handler("Disable Copilot", move |cx| { + hide_copilot(fs.clone(), cx) })); self.popup_menu.update(cx, |menu, cx| { @@ -189,10 +193,12 @@ impl CopilotButton { pub fn deploy_copilot_menu(&mut self, cx: &mut ViewContext) { let settings = cx.global::(); + let fs = self.fs.clone(); let mut menu_options = Vec::with_capacity(8); if let Some(language) = self.language.clone() { + let fs = fs.clone(); let language_enabled = settings.copilot_enabled_for_language(Some(language.as_ref())); menu_options.push(ContextMenuItem::handler( format!( @@ -200,7 +206,7 @@ impl CopilotButton { if language_enabled { "Hide" } else { "Show" }, language ), - move |cx| toggle_copilot_for_language(language.clone(), cx), + move |cx| toggle_copilot_for_language(language.clone(), fs.clone(), cx), )); } @@ -235,7 +241,7 @@ impl CopilotButton { } else { "Show Suggestions for All Files" }, - |cx| toggle_copilot_globally(cx), + move |cx| toggle_copilot_globally(fs.clone(), cx), )); menu_options.push(ContextMenuItem::Separator); @@ -322,25 +328,27 @@ async fn configure_disabled_globs( settings_editor.downgrade().update(&mut cx, |item, cx| { let text = item.buffer().read(cx).snapshot(cx).text(); - let edits = SettingsFile::update_unsaved(&text, cx, |file| { - let copilot = file.copilot.get_or_insert_with(Default::default); - let globs = copilot.disabled_globs.get_or_insert_with(|| { - cx.global::() - .copilot - .disabled_globs - .clone() - .iter() - .map(|glob| glob.as_str().to_string()) - .collect::>() + let edits = cx + .global::() + .update::(&text, |file| { + let copilot = file.copilot.get_or_insert_with(Default::default); + let globs = copilot.disabled_globs.get_or_insert_with(|| { + cx.global::() + .copilot + .disabled_globs + .clone() + .iter() + .map(|glob| glob.as_str().to_string()) + .collect::>() + }); + + if let Some(path_to_disable) = &path_to_disable { + globs.push(path_to_disable.to_string_lossy().into_owned()); + } else { + globs.clear(); + } }); - if let Some(path_to_disable) = &path_to_disable { - globs.push(path_to_disable.to_string_lossy().into_owned()); - } else { - globs.clear(); - } - }); - if !edits.is_empty() { item.change_selections(Some(Autoscroll::newest()), cx, |selections| { selections.select_ranges(edits.iter().map(|e| e.0.clone())); @@ -356,19 +364,19 @@ async fn configure_disabled_globs( anyhow::Ok(()) } -fn toggle_copilot_globally(cx: &mut AppContext) { +fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { let show_copilot_suggestions = cx.global::().show_copilot_suggestions(None, None); - SettingsFile::update(cx, move |file_contents| { + update_settings_file(fs, cx, move |file_contents| { file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) }); } -fn toggle_copilot_for_language(language: Arc, cx: &mut AppContext) { +fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { let show_copilot_suggestions = cx .global::() .show_copilot_suggestions(Some(&language), None); - SettingsFile::update(cx, move |file_contents| { + update_settings_file(fs, cx, move |file_contents| { file_contents.languages.insert( language, settings::EditorSettings { @@ -379,8 +387,8 @@ fn toggle_copilot_for_language(language: Arc, cx: &mut AppContext) { }); } -fn hide_copilot(cx: &mut AppContext) { - SettingsFile::update(cx, move |file_contents| { +fn hide_copilot(fs: Arc, cx: &mut AppContext) { + update_settings_file(fs, cx, move |file_contents| { file_contents.features.copilot = Some(false) }); } diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index b38aa7c42d9e9f2d6b5357a0bc31502ca009907c..6d6d303a820332f4f95304f94836d7bee327aff1 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -9,7 +9,7 @@ path = "src/settings.rs" doctest = false [features] -test-support = [] +test-support = ["theme/test-support", "gpui/test-support", "fs/test-support"] [dependencies] assets = { path = "../assets" } @@ -39,6 +39,7 @@ tree-sitter-json = "*" [dev-dependencies] gpui = { path = "../gpui", features = ["test-support"] } fs = { path = "../fs", features = ["test-support"] } +theme = { path = "../theme", features = ["test-support"] } pretty_assertions = "1.3.0" unindent.workspace = true diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index a45a53145edfca3371af9b135a882664e191ec6e..e0b3f547f9ce79818579f0b0f9a38b2dec82f48c 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -1,4 +1,4 @@ -use crate::{parse_json_with_comments, Settings}; +use crate::{settings_store::parse_json_with_comments, Settings}; use anyhow::{Context, Result}; use assets::Assets; use collections::BTreeMap; diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 9475d0205f57fb5d24faf5eb54c9295525f07730..c3911f9254a555067e615a06a1a1dd6ebe6c4a09 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,34 +1,31 @@ mod keymap_file; -pub mod settings_file; -pub mod settings_store; -pub mod watched_json; +mod settings_file; +mod settings_store; -use anyhow::{bail, Result}; +use anyhow::bail; use gpui::{ font_cache::{FamilyId, FontCache}, - fonts, AssetSource, + fonts, AppContext, AssetSource, }; -use lazy_static::lazy_static; use schemars::{ gen::{SchemaGenerator, SchemaSettings}, schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec}, JsonSchema, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use serde_json::Value; +use settings_store::Setting; use sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; -use std::{ - borrow::Cow, collections::HashMap, num::NonZeroU32, ops::Range, path::Path, str, sync::Arc, -}; +use std::{borrow::Cow, collections::HashMap, num::NonZeroU32, path::Path, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; -use tree_sitter::{Query, Tree}; -use util::{RangeExt, ResultExt as _}; +use util::ResultExt as _; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; -pub use watched_json::watch_files; +pub use settings_file::*; +pub use settings_store::SettingsStore; pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; @@ -69,6 +66,92 @@ pub struct Settings { pub base_keymap: BaseKeymap, } +impl Setting for Settings { + type FileContent = SettingsFileContent; + + fn load( + defaults: &Self::FileContent, + user_values: &[&Self::FileContent], + cx: &AppContext, + ) -> Self { + let buffer_font_features = defaults.buffer_font_features.clone().unwrap(); + let themes = cx.global::>(); + + let mut this = Self { + buffer_font_family: cx + .font_cache() + .load_family( + &[defaults.buffer_font_family.as_ref().unwrap()], + &buffer_font_features, + ) + .unwrap(), + buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(), + buffer_font_features, + buffer_font_size: defaults.buffer_font_size.unwrap(), + active_pane_magnification: defaults.active_pane_magnification.unwrap(), + default_buffer_font_size: defaults.buffer_font_size.unwrap(), + confirm_quit: defaults.confirm_quit.unwrap(), + cursor_blink: defaults.cursor_blink.unwrap(), + hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), + show_completions_on_input: defaults.show_completions_on_input.unwrap(), + show_call_status_icon: defaults.show_call_status_icon.unwrap(), + vim_mode: defaults.vim_mode.unwrap(), + autosave: defaults.autosave.unwrap(), + default_dock_anchor: defaults.default_dock_anchor.unwrap(), + editor_defaults: EditorSettings { + tab_size: defaults.editor.tab_size, + hard_tabs: defaults.editor.hard_tabs, + soft_wrap: defaults.editor.soft_wrap, + preferred_line_length: defaults.editor.preferred_line_length, + remove_trailing_whitespace_on_save: defaults + .editor + .remove_trailing_whitespace_on_save, + ensure_final_newline_on_save: defaults.editor.ensure_final_newline_on_save, + format_on_save: defaults.editor.format_on_save.clone(), + formatter: defaults.editor.formatter.clone(), + enable_language_server: defaults.editor.enable_language_server, + show_copilot_suggestions: defaults.editor.show_copilot_suggestions, + show_whitespaces: defaults.editor.show_whitespaces, + }, + editor_overrides: Default::default(), + copilot: CopilotSettings { + disabled_globs: defaults + .copilot + .clone() + .unwrap() + .disabled_globs + .unwrap() + .into_iter() + .map(|s| glob::Pattern::new(&s).unwrap()) + .collect(), + }, + git: defaults.git.unwrap(), + git_overrides: Default::default(), + journal_defaults: defaults.journal.clone(), + journal_overrides: Default::default(), + terminal_defaults: defaults.terminal.clone(), + terminal_overrides: Default::default(), + language_defaults: defaults.languages.clone(), + language_overrides: Default::default(), + lsp: defaults.lsp.clone(), + theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), + telemetry_defaults: defaults.telemetry, + telemetry_overrides: Default::default(), + auto_update: defaults.auto_update.unwrap(), + base_keymap: Default::default(), + features: Features { + copilot: defaults.features.copilot.unwrap(), + }, + }; + + for value in user_values.into_iter().copied().cloned() { + this.set_user_settings(value, themes.as_ref(), cx.font_cache()); + } + + this + } +} + #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] pub enum BaseKeymap { #[default] @@ -477,7 +560,7 @@ impl Settings { value } - let defaults: SettingsFileContent = parse_json_with_comments( + let defaults: SettingsFileContent = settings_store::parse_json_with_comments( str::from_utf8(assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap().as_ref()).unwrap(), ) .unwrap(); @@ -914,686 +997,3 @@ fn merge(target: &mut T, value: Option) { *target = value; } } - -pub fn parse_json_with_comments(content: &str) -> Result { - Ok(serde_json::from_reader( - json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()), - )?) -} - -lazy_static! { - static ref PAIR_QUERY: Query = Query::new( - tree_sitter_json::language(), - " - (pair - key: (string) @key - value: (_) @value) - ", - ) - .unwrap(); -} - -fn update_object_in_settings_file<'a>( - old_object: &'a serde_json::Map, - new_object: &'a serde_json::Map, - text: &str, - syntax_tree: &Tree, - tab_size: usize, - key_path: &mut Vec<&'a str>, - edits: &mut Vec<(Range, String)>, -) { - for (key, old_value) in old_object.iter() { - key_path.push(key); - let new_value = new_object.get(key).unwrap_or(&Value::Null); - - // If the old and new values are both objects, then compare them key by key, - // preserving the comments and formatting of the unchanged parts. Otherwise, - // replace the old value with the new value. - if let (Value::Object(old_sub_object), Value::Object(new_sub_object)) = - (old_value, new_value) - { - update_object_in_settings_file( - old_sub_object, - new_sub_object, - text, - syntax_tree, - tab_size, - key_path, - edits, - ) - } else if old_value != new_value { - let (range, replacement) = - update_key_in_settings_file(text, syntax_tree, &key_path, tab_size, &new_value); - edits.push((range, replacement)); - } - - key_path.pop(); - } -} - -fn update_key_in_settings_file( - text: &str, - syntax_tree: &Tree, - key_path: &[&str], - tab_size: usize, - new_value: impl Serialize, -) -> (Range, String) { - const LANGUAGE_OVERRIDES: &'static str = "language_overrides"; - const LANGUAGES: &'static str = "languages"; - - let mut cursor = tree_sitter::QueryCursor::new(); - - let has_language_overrides = text.contains(LANGUAGE_OVERRIDES); - - let mut depth = 0; - let mut last_value_range = 0..0; - let mut first_key_start = None; - let mut existing_value_range = 0..text.len(); - let matches = cursor.matches(&PAIR_QUERY, syntax_tree.root_node(), text.as_bytes()); - for mat in matches { - if mat.captures.len() != 2 { - continue; - } - - let key_range = mat.captures[0].node.byte_range(); - let value_range = mat.captures[1].node.byte_range(); - - // Don't enter sub objects until we find an exact - // match for the current keypath - if last_value_range.contains_inclusive(&value_range) { - continue; - } - - last_value_range = value_range.clone(); - - if key_range.start > existing_value_range.end { - break; - } - - first_key_start.get_or_insert_with(|| key_range.start); - - let found_key = text - .get(key_range.clone()) - .map(|key_text| { - if key_path[depth] == LANGUAGES && has_language_overrides { - return key_text == format!("\"{}\"", LANGUAGE_OVERRIDES); - } else { - return key_text == format!("\"{}\"", key_path[depth]); - } - }) - .unwrap_or(false); - - if found_key { - existing_value_range = value_range; - // Reset last value range when increasing in depth - last_value_range = existing_value_range.start..existing_value_range.start; - depth += 1; - - if depth == key_path.len() { - break; - } else { - first_key_start = None; - } - } - } - - // We found the exact key we want, insert the new value - if depth == key_path.len() { - let new_val = to_pretty_json(&new_value, tab_size, tab_size * depth); - (existing_value_range, new_val) - } else { - // We have key paths, construct the sub objects - let new_key = if has_language_overrides && key_path[depth] == LANGUAGES { - LANGUAGE_OVERRIDES - } else { - key_path[depth] - }; - - // We don't have the key, construct the nested objects - let mut new_value = serde_json::to_value(new_value).unwrap(); - for key in key_path[(depth + 1)..].iter().rev() { - if has_language_overrides && key == &LANGUAGES { - new_value = serde_json::json!({ LANGUAGE_OVERRIDES.to_string(): new_value }); - } else { - new_value = serde_json::json!({ key.to_string(): new_value }); - } - } - - if let Some(first_key_start) = first_key_start { - let mut row = 0; - let mut column = 0; - for (ix, char) in text.char_indices() { - if ix == first_key_start { - break; - } - if char == '\n' { - row += 1; - column = 0; - } else { - column += char.len_utf8(); - } - } - - if row > 0 { - // depth is 0 based, but division needs to be 1 based. - let new_val = to_pretty_json(&new_value, column / (depth + 1), column); - let space = ' '; - let content = format!("\"{new_key}\": {new_val},\n{space:width$}", width = column); - (first_key_start..first_key_start, content) - } else { - let new_val = serde_json::to_string(&new_value).unwrap(); - let mut content = format!(r#""{new_key}": {new_val},"#); - content.push(' '); - (first_key_start..first_key_start, content) - } - } else { - new_value = serde_json::json!({ new_key.to_string(): new_value }); - let indent_prefix_len = 4 * depth; - let mut new_val = to_pretty_json(&new_value, 4, indent_prefix_len); - if depth == 0 { - new_val.push('\n'); - } - - (existing_value_range, new_val) - } - } -} - -fn to_pretty_json(value: &impl Serialize, indent_size: usize, indent_prefix_len: usize) -> String { - const SPACES: [u8; 32] = [b' '; 32]; - - debug_assert!(indent_size <= SPACES.len()); - debug_assert!(indent_prefix_len <= SPACES.len()); - - let mut output = Vec::new(); - let mut ser = serde_json::Serializer::with_formatter( - &mut output, - serde_json::ser::PrettyFormatter::with_indent(&SPACES[0..indent_size.min(SPACES.len())]), - ); - - value.serialize(&mut ser).unwrap(); - let text = String::from_utf8(output).unwrap(); - - let mut adjusted_text = String::new(); - for (i, line) in text.split('\n').enumerate() { - if i > 0 { - adjusted_text.push_str(str::from_utf8(&SPACES[0..indent_prefix_len]).unwrap()); - } - adjusted_text.push_str(line); - adjusted_text.push('\n'); - } - adjusted_text.pop(); - adjusted_text -} - -/// Update the settings file with the given callback. -/// -/// Returns a new JSON string and the offset where the first edit occurred. -fn update_settings_file( - text: &str, - mut old_file_content: SettingsFileContent, - tab_size: NonZeroU32, - update: impl FnOnce(&mut SettingsFileContent), -) -> Vec<(Range, String)> { - let mut new_file_content = old_file_content.clone(); - update(&mut new_file_content); - - if new_file_content.languages.len() != old_file_content.languages.len() { - for language in new_file_content.languages.keys() { - old_file_content - .languages - .entry(language.clone()) - .or_default(); - } - for language in old_file_content.languages.keys() { - new_file_content - .languages - .entry(language.clone()) - .or_default(); - } - } - - let mut parser = tree_sitter::Parser::new(); - parser.set_language(tree_sitter_json::language()).unwrap(); - let tree = parser.parse(text, None).unwrap(); - - let old_object = to_json_object(old_file_content); - let new_object = to_json_object(new_file_content); - let mut key_path = Vec::new(); - let mut edits = Vec::new(); - update_object_in_settings_file( - &old_object, - &new_object, - &text, - &tree, - tab_size.get() as usize, - &mut key_path, - &mut edits, - ); - edits.sort_unstable_by_key(|e| e.0.start); - return edits; -} - -fn to_json_object(settings_file: SettingsFileContent) -> serde_json::Map { - let tmp = serde_json::to_value(settings_file).unwrap(); - match tmp { - Value::Object(map) => map, - _ => unreachable!("SettingsFileContent represents a JSON map"), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use unindent::Unindent; - - fn assert_new_settings( - old_json: String, - update: fn(&mut SettingsFileContent), - expected_new_json: String, - ) { - let old_content: SettingsFileContent = serde_json::from_str(&old_json).unwrap_or_default(); - let edits = update_settings_file(&old_json, old_content, 4.try_into().unwrap(), update); - let mut new_json = old_json; - for (range, replacement) in edits.into_iter().rev() { - new_json.replace_range(range, &replacement); - } - pretty_assertions::assert_eq!(new_json, expected_new_json); - } - - #[test] - fn test_update_language_overrides_copilot() { - assert_new_settings( - r#" - { - "language_overrides": { - "JSON": { - "show_copilot_suggestions": false - } - } - } - "# - .unindent(), - |settings| { - settings.languages.insert( - "Rust".into(), - EditorSettings { - show_copilot_suggestions: Some(true), - ..Default::default() - }, - ); - }, - r#" - { - "language_overrides": { - "Rust": { - "show_copilot_suggestions": true - }, - "JSON": { - "show_copilot_suggestions": false - } - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_copilot_globs() { - assert_new_settings( - r#" - { - } - "# - .unindent(), - |settings| { - settings.copilot = Some(CopilotSettingsContent { - disabled_globs: Some(vec![]), - }); - }, - r#" - { - "copilot": { - "disabled_globs": [] - } - } - "# - .unindent(), - ); - - assert_new_settings( - r#" - { - "copilot": { - "disabled_globs": [ - "**/*.json" - ] - } - } - "# - .unindent(), - |settings| { - settings - .copilot - .get_or_insert(Default::default()) - .disabled_globs - .as_mut() - .unwrap() - .push(".env".into()); - }, - r#" - { - "copilot": { - "disabled_globs": [ - "**/*.json", - ".env" - ] - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_copilot() { - assert_new_settings( - r#" - { - "languages": { - "JSON": { - "show_copilot_suggestions": false - } - } - } - "# - .unindent(), - |settings| { - settings.editor.show_copilot_suggestions = Some(true); - }, - r#" - { - "show_copilot_suggestions": true, - "languages": { - "JSON": { - "show_copilot_suggestions": false - } - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_language_copilot() { - assert_new_settings( - r#" - { - "languages": { - "JSON": { - "show_copilot_suggestions": false - } - } - } - "# - .unindent(), - |settings| { - settings.languages.insert( - "Rust".into(), - EditorSettings { - show_copilot_suggestions: Some(true), - ..Default::default() - }, - ); - }, - r#" - { - "languages": { - "Rust": { - "show_copilot_suggestions": true - }, - "JSON": { - "show_copilot_suggestions": false - } - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_telemetry_setting_multiple_fields() { - assert_new_settings( - r#" - { - "telemetry": { - "metrics": false, - "diagnostics": false - } - } - "# - .unindent(), - |settings| { - settings.telemetry.set_diagnostics(true); - settings.telemetry.set_metrics(true); - }, - r#" - { - "telemetry": { - "metrics": true, - "diagnostics": true - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_telemetry_setting_weird_formatting() { - assert_new_settings( - r#"{ - "telemetry": { "metrics": false, "diagnostics": true } - }"# - .unindent(), - |settings| settings.telemetry.set_diagnostics(false), - r#"{ - "telemetry": { "metrics": false, "diagnostics": false } - }"# - .unindent(), - ); - } - - #[test] - fn test_update_telemetry_setting_other_fields() { - assert_new_settings( - r#" - { - "telemetry": { - "metrics": false, - "diagnostics": true - } - } - "# - .unindent(), - |settings| settings.telemetry.set_diagnostics(false), - r#" - { - "telemetry": { - "metrics": false, - "diagnostics": false - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_telemetry_setting_empty_telemetry() { - assert_new_settings( - r#" - { - "telemetry": {} - } - "# - .unindent(), - |settings| settings.telemetry.set_diagnostics(false), - r#" - { - "telemetry": { - "diagnostics": false - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_telemetry_setting_pre_existing() { - assert_new_settings( - r#" - { - "telemetry": { - "diagnostics": true - } - } - "# - .unindent(), - |settings| settings.telemetry.set_diagnostics(false), - r#" - { - "telemetry": { - "diagnostics": false - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_telemetry_setting() { - assert_new_settings( - "{}".into(), - |settings| settings.telemetry.set_diagnostics(true), - r#" - { - "telemetry": { - "diagnostics": true - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_update_object_empty_doc() { - assert_new_settings( - "".into(), - |settings| settings.telemetry.set_diagnostics(true), - r#" - { - "telemetry": { - "diagnostics": true - } - } - "# - .unindent(), - ); - } - - #[test] - fn test_write_theme_into_settings_with_theme() { - assert_new_settings( - r#" - { - "theme": "One Dark" - } - "# - .unindent(), - |settings| settings.theme = Some("summerfruit-light".to_string()), - r#" - { - "theme": "summerfruit-light" - } - "# - .unindent(), - ); - } - - #[test] - fn test_write_theme_into_empty_settings() { - assert_new_settings( - r#" - { - } - "# - .unindent(), - |settings| settings.theme = Some("summerfruit-light".to_string()), - r#" - { - "theme": "summerfruit-light" - } - "# - .unindent(), - ); - } - - #[test] - fn write_key_no_document() { - assert_new_settings( - "".to_string(), - |settings| settings.theme = Some("summerfruit-light".to_string()), - r#" - { - "theme": "summerfruit-light" - } - "# - .unindent(), - ); - } - - #[test] - fn test_write_theme_into_single_line_settings_without_theme() { - assert_new_settings( - r#"{ "a": "", "ok": true }"#.to_string(), - |settings| settings.theme = Some("summerfruit-light".to_string()), - r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#.to_string(), - ); - } - - #[test] - fn test_write_theme_pre_object_whitespace() { - assert_new_settings( - r#" { "a": "", "ok": true }"#.to_string(), - |settings| settings.theme = Some("summerfruit-light".to_string()), - r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#.unindent(), - ); - } - - #[test] - fn test_write_theme_into_multi_line_settings_without_theme() { - assert_new_settings( - r#" - { - "a": "b" - } - "# - .unindent(), - |settings| settings.theme = Some("summerfruit-light".to_string()), - r#" - { - "theme": "summerfruit-light", - "a": "b" - } - "# - .unindent(), - ); - } -} diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 6402a07f5e713f7e4d0ad821ab1120a684bd00a5..17294235ec5d3dbbcaa471ec249b79aaad9ae5f9 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,88 +1,181 @@ -use crate::{update_settings_file, watched_json::WatchedJsonFile, Settings, SettingsFileContent}; +use crate::{ + settings_store::parse_json_with_comments, settings_store::SettingsStore, KeymapFileContent, + Settings, SettingsFileContent, DEFAULT_SETTINGS_ASSET_PATH, +}; use anyhow::Result; use assets::Assets; use fs::Fs; -use gpui::AppContext; -use std::{io::ErrorKind, ops::Range, path::Path, sync::Arc}; - -// TODO: Switch SettingsFile to open a worktree and buffer for synchronization -// And instant updates in the Zed editor -#[derive(Clone)] -pub struct SettingsFile { - path: &'static Path, - settings_file_content: WatchedJsonFile, +use futures::{channel::mpsc, StreamExt}; +use gpui::{executor::Background, AppContext, AssetSource}; +use std::{borrow::Cow, io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration}; +use util::{paths, ResultExt}; + +pub fn default_settings() -> Cow<'static, str> { + match Assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap() { + Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), + Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()), + } +} + +#[cfg(any(test, feature = "test-support"))] +pub fn test_settings() -> String { + let mut value = + parse_json_with_comments::(default_settings().as_ref()).unwrap(); + util::merge_non_null_json_value_into( + serde_json::json!({ + "buffer_font_family": "Courier", + "buffer_font_features": {}, + "default_buffer_font_size": 14, + "preferred_line_length": 80, + "theme": theme::EMPTY_THEME_NAME, + }), + &mut value, + ); + serde_json::to_string(&value).unwrap() +} + +pub fn watch_config_file( + executor: Arc, fs: Arc, + path: PathBuf, +) -> mpsc::UnboundedReceiver { + let (tx, rx) = mpsc::unbounded(); + executor + .spawn(async move { + let events = fs.watch(&path, Duration::from_millis(100)).await; + futures::pin_mut!(events); + loop { + if let Ok(contents) = fs.load(&path).await { + if !tx.unbounded_send(contents).is_ok() { + break; + } + } + if events.next().await.is_none() { + break; + } + } + }) + .detach(); + rx +} + +pub fn handle_keymap_file_changes( + mut user_keymap_file_rx: mpsc::UnboundedReceiver, + cx: &mut AppContext, +) { + cx.spawn(move |mut cx| async move { + let mut settings_subscription = None; + while let Some(user_keymap_content) = user_keymap_file_rx.next().await { + if let Ok(keymap_content) = + parse_json_with_comments::(&user_keymap_content) + { + cx.update(|cx| { + cx.clear_bindings(); + KeymapFileContent::load_defaults(cx); + keymap_content.clone().add_to_cx(cx).log_err(); + }); + + let mut old_base_keymap = cx.read(|cx| cx.global::().base_keymap.clone()); + drop(settings_subscription); + settings_subscription = Some(cx.update(|cx| { + cx.observe_global::(move |cx| { + let settings = cx.global::(); + if settings.base_keymap != old_base_keymap { + old_base_keymap = settings.base_keymap.clone(); + + cx.clear_bindings(); + KeymapFileContent::load_defaults(cx); + keymap_content.clone().add_to_cx(cx).log_err(); + } + }) + .detach(); + })); + } + } + }) + .detach(); } -impl SettingsFile { - pub fn new( - path: &'static Path, - settings_file_content: WatchedJsonFile, - fs: Arc, - ) -> Self { - SettingsFile { - path, - settings_file_content, - fs, +pub fn handle_settings_file_changes( + mut user_settings_file_rx: mpsc::UnboundedReceiver, + cx: &mut AppContext, +) { + let user_settings_content = cx.background().block(user_settings_file_rx.next()).unwrap(); + cx.update_global::(|store, cx| { + store + .set_user_settings(&user_settings_content, cx) + .log_err(); + + // TODO - remove the Settings global, use the SettingsStore instead. + store.register_setting::(cx); + cx.set_global(store.get::(None).clone()); + }); + cx.spawn(move |mut cx| async move { + while let Some(user_settings_content) = user_settings_file_rx.next().await { + cx.update(|cx| { + cx.update_global::(|store, cx| { + store + .set_user_settings(&user_settings_content, cx) + .log_err(); + + // TODO - remove the Settings global, use the SettingsStore instead. + cx.set_global(store.get::(None).clone()); + }); + }); } - } + }) + .detach(); +} - async fn load_settings(path: &Path, fs: &Arc) -> Result { - match fs.load(path).await { - result @ Ok(_) => result, - Err(err) => { - if let Some(e) = err.downcast_ref::() { - if e.kind() == ErrorKind::NotFound { - return Ok(Settings::initial_user_settings_content(&Assets).to_string()); - } +async fn load_settings(fs: &Arc) -> Result { + match fs.load(&paths::SETTINGS).await { + result @ Ok(_) => result, + Err(err) => { + if let Some(e) = err.downcast_ref::() { + if e.kind() == ErrorKind::NotFound { + return Ok(Settings::initial_user_settings_content(&Assets).to_string()); } - return Err(err); } + return Err(err); } } +} - pub fn update_unsaved( - text: &str, - cx: &AppContext, - update: impl FnOnce(&mut SettingsFileContent), - ) -> Vec<(Range, String)> { - let this = cx.global::(); - let tab_size = cx.global::().tab_size(Some("JSON")); - let current_file_content = this.settings_file_content.current(); - update_settings_file(&text, current_file_content, tab_size, update) - } +pub fn update_settings_file( + fs: Arc, + cx: &mut AppContext, + update: impl 'static + Send + FnOnce(&mut SettingsFileContent), +) { + cx.spawn(|cx| async move { + let old_text = cx + .background() + .spawn({ + let fs = fs.clone(); + async move { load_settings(&fs).await } + }) + .await?; - pub fn update( - cx: &mut AppContext, - update: impl 'static + Send + FnOnce(&mut SettingsFileContent), - ) { - let this = cx.global::(); - let tab_size = cx.global::().tab_size(Some("JSON")); - let current_file_content = this.settings_file_content.current(); - let fs = this.fs.clone(); - let path = this.path.clone(); + let edits = cx.read(|cx| { + cx.global::() + .update::(&old_text, update) + }); + + let mut new_text = old_text; + for (range, replacement) in edits.into_iter().rev() { + new_text.replace_range(range, &replacement); + } cx.background() - .spawn(async move { - let old_text = SettingsFile::load_settings(path, &fs).await?; - let edits = update_settings_file(&old_text, current_file_content, tab_size, update); - let mut new_text = old_text; - for (range, replacement) in edits.into_iter().rev() { - new_text.replace_range(range, &replacement); - } - fs.atomic_write(path.to_path_buf(), new_text).await?; - anyhow::Ok(()) - }) - .detach_and_log_err(cx) - } + .spawn(async move { fs.atomic_write(paths::SETTINGS.clone(), new_text).await }) + .await?; + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } #[cfg(test)] mod tests { use super::*; - use crate::{ - watch_files, watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap, - }; use fs::FakeFs; use gpui::{actions, elements::*, Action, Entity, TestAppContext, View, ViewContext}; use theme::ThemeRegistry; @@ -107,7 +200,6 @@ mod tests { async fn test_base_keymap(cx: &mut gpui::TestAppContext) { let executor = cx.background(); let fs = FakeFs::new(executor.clone()); - let font_cache = cx.font_cache(); actions!(test, [A, B]); // From the Atom keymap @@ -145,25 +237,26 @@ mod tests { .await .unwrap(); - let settings_file = - WatchedJsonFile::new(fs.clone(), &executor, "/settings.json".as_ref()).await; - let keymaps_file = - WatchedJsonFile::new(fs.clone(), &executor, "/keymap.json".as_ref()).await; - - let default_settings = cx.read(Settings::test); - cx.update(|cx| { + let mut store = SettingsStore::default(); + store.set_default_settings(&test_settings(), cx).unwrap(); + cx.set_global(store); + cx.set_global(ThemeRegistry::new(Assets, cx.font_cache().clone())); cx.add_global_action(|_: &A, _cx| {}); cx.add_global_action(|_: &B, _cx| {}); cx.add_global_action(|_: &ActivatePreviousPane, _cx| {}); cx.add_global_action(|_: &ActivatePrevItem, _cx| {}); - watch_files( - default_settings, - settings_file, - ThemeRegistry::new((), font_cache), - keymaps_file, - cx, - ) + + let settings_rx = watch_config_file( + executor.clone(), + fs.clone(), + PathBuf::from("/settings.json"), + ); + let keymap_rx = + watch_config_file(executor.clone(), fs.clone(), PathBuf::from("/keymap.json")); + + handle_keymap_file_changes(keymap_rx, cx); + handle_settings_file_changes(settings_rx, cx); }); cx.foreground().run_until_parked(); @@ -255,113 +348,4 @@ mod tests { ); } } - - #[gpui::test] - async fn test_watch_settings_files(cx: &mut gpui::TestAppContext) { - let executor = cx.background(); - let fs = FakeFs::new(executor.clone()); - let font_cache = cx.font_cache(); - - fs.save( - "/settings.json".as_ref(), - &r#" - { - "buffer_font_size": 24, - "soft_wrap": "editor_width", - "tab_size": 8, - "language_overrides": { - "Markdown": { - "tab_size": 2, - "preferred_line_length": 100, - "soft_wrap": "preferred_line_length" - } - } - } - "# - .into(), - Default::default(), - ) - .await - .unwrap(); - - let source = WatchedJsonFile::new(fs.clone(), &executor, "/settings.json".as_ref()).await; - - let default_settings = cx.read(Settings::test).with_language_defaults( - "JavaScript", - EditorSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ); - cx.update(|cx| { - watch_settings_file( - default_settings.clone(), - source, - ThemeRegistry::new((), font_cache), - cx, - ) - }); - - cx.foreground().run_until_parked(); - let settings = cx.read(|cx| cx.global::().clone()); - assert_eq!(settings.buffer_font_size, 24.0); - - assert_eq!(settings.soft_wrap(None), SoftWrap::EditorWidth); - assert_eq!( - settings.soft_wrap(Some("Markdown")), - SoftWrap::PreferredLineLength - ); - assert_eq!( - settings.soft_wrap(Some("JavaScript")), - SoftWrap::EditorWidth - ); - - assert_eq!(settings.preferred_line_length(None), 80); - assert_eq!(settings.preferred_line_length(Some("Markdown")), 100); - assert_eq!(settings.preferred_line_length(Some("JavaScript")), 80); - - assert_eq!(settings.tab_size(None).get(), 8); - assert_eq!(settings.tab_size(Some("Markdown")).get(), 2); - assert_eq!(settings.tab_size(Some("JavaScript")).get(), 8); - - fs.save( - "/settings.json".as_ref(), - &"(garbage)".into(), - Default::default(), - ) - .await - .unwrap(); - // fs.remove_file("/settings.json".as_ref(), Default::default()) - // .await - // .unwrap(); - - cx.foreground().run_until_parked(); - let settings = cx.read(|cx| cx.global::().clone()); - assert_eq!(settings.buffer_font_size, 24.0); - - assert_eq!(settings.soft_wrap(None), SoftWrap::EditorWidth); - assert_eq!( - settings.soft_wrap(Some("Markdown")), - SoftWrap::PreferredLineLength - ); - assert_eq!( - settings.soft_wrap(Some("JavaScript")), - SoftWrap::EditorWidth - ); - - assert_eq!(settings.preferred_line_length(None), 80); - assert_eq!(settings.preferred_line_length(Some("Markdown")), 100); - assert_eq!(settings.preferred_line_length(Some("JavaScript")), 80); - - assert_eq!(settings.tab_size(None).get(), 8); - assert_eq!(settings.tab_size(Some("Markdown")).get(), 2); - assert_eq!(settings.tab_size(Some("JavaScript")).get(), 8); - - fs.remove_file("/settings.json".as_ref(), Default::default()) - .await - .unwrap(); - cx.foreground().run_until_parked(); - let settings = cx.read(|cx| cx.global::().clone()); - assert_eq!(settings.buffer_font_size, default_settings.buffer_font_size); - } } diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 5191d768ea59933e016ddded445e961210b4e7c8..394d457d3d9c73a71cf5309350b88e82914d879e 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,5 +1,6 @@ use anyhow::{anyhow, Result}; use collections::{hash_map, BTreeMap, HashMap, HashSet}; +use gpui::AppContext; use lazy_static::lazy_static; use schemars::JsonSchema; use serde::{de::DeserializeOwned, Deserialize as _, Serialize}; @@ -18,7 +19,7 @@ use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _}; /// A value that can be defined as a user setting. /// /// Settings can be loaded from a combination of multiple JSON files. -pub trait Setting: 'static + Debug { +pub trait Setting: 'static { /// The name of a key within the JSON file from which this setting should /// be deserialized. If this is `None`, then the setting will be deserialized /// from the root object. @@ -32,7 +33,11 @@ pub trait Setting: 'static + Debug { /// /// The user values are ordered from least specific (the global settings file) /// to most specific (the innermost local settings file). - fn load(default_value: &Self::FileContent, user_values: &[&Self::FileContent]) -> Self; + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + cx: &AppContext, + ) -> Self; fn load_via_json_merge( default_value: &Self::FileContent, @@ -66,7 +71,7 @@ struct SettingValue { local_values: Vec<(Arc, T)>, } -trait AnySettingValue: Debug { +trait AnySettingValue { fn key(&self) -> Option<&'static str>; fn setting_type_name(&self) -> &'static str; fn deserialize_setting(&self, json: &serde_json::Value) -> Result; @@ -74,6 +79,7 @@ trait AnySettingValue: Debug { &self, default_value: &DeserializedSetting, custom: &[&DeserializedSetting], + cx: &AppContext, ) -> Box; fn value_for_path(&self, path: Option<&Path>) -> &dyn Any; fn set_global_value(&mut self, value: Box); @@ -89,7 +95,7 @@ struct DeserializedSettingMap { impl SettingsStore { /// Add a new type of setting to the store. - pub fn register_setting(&mut self) { + pub fn register_setting(&mut self, cx: &AppContext) { let setting_type_id = TypeId::of::(); let entry = self.setting_values.entry(setting_type_id); @@ -112,24 +118,26 @@ impl SettingsStore { } } if let Some(default_deserialized_value) = default_settings.typed.get(&setting_type_id) { - setting_value.set_global_value( - setting_value.load_setting(default_deserialized_value, &user_values_stack), - ); + setting_value.set_global_value(setting_value.load_setting( + default_deserialized_value, + &user_values_stack, + cx, + )); } } } /// Get the value of a setting. /// - /// Panics if settings have not yet been loaded, or there is no default + /// Panics if the given setting type has not been registered, or if there is no /// value for this setting. pub fn get(&self, path: Option<&Path>) -> &T { self.setting_values .get(&TypeId::of::()) - .unwrap() + .expect("unregistered setting type") .value_for_path(path) .downcast_ref::() - .unwrap() + .expect("no default value for setting type") } /// Update the value of a setting. @@ -138,7 +146,7 @@ impl SettingsStore { pub fn update( &self, text: &str, - update: impl Fn(&mut T::FileContent), + update: impl FnOnce(&mut T::FileContent), ) -> Vec<(Range, String)> { let setting_type_id = TypeId::of::(); let old_content = self @@ -210,7 +218,11 @@ impl SettingsStore { /// Set the default settings via a JSON string. /// /// The string should contain a JSON object with a default value for every setting. - pub fn set_default_settings(&mut self, default_settings_content: &str) -> Result<()> { + pub fn set_default_settings( + &mut self, + default_settings_content: &str, + cx: &mut AppContext, + ) -> Result<()> { let deserialized_setting_map = self.load_setting_map(default_settings_content)?; if deserialized_setting_map.typed.len() != self.setting_values.len() { return Err(anyhow!( @@ -223,16 +235,20 @@ impl SettingsStore { )); } self.default_deserialized_settings = Some(deserialized_setting_map); - self.recompute_values(false, None, None); + self.recompute_values(false, None, None, cx); Ok(()) } /// Set the user settings via a JSON string. - pub fn set_user_settings(&mut self, user_settings_content: &str) -> Result<()> { + pub fn set_user_settings( + &mut self, + user_settings_content: &str, + cx: &mut AppContext, + ) -> Result<()> { let user_settings = self.load_setting_map(user_settings_content)?; let old_user_settings = mem::replace(&mut self.user_deserialized_settings, Some(user_settings)); - self.recompute_values(true, None, old_user_settings); + self.recompute_values(true, None, old_user_settings, cx); Ok(()) } @@ -241,6 +257,7 @@ impl SettingsStore { &mut self, path: Arc, settings_content: Option<&str>, + cx: &mut AppContext, ) -> Result<()> { let removed_map = if let Some(settings_content) = settings_content { self.local_deserialized_settings @@ -249,7 +266,7 @@ impl SettingsStore { } else { self.local_deserialized_settings.remove(&path) }; - self.recompute_values(true, Some(&path), removed_map); + self.recompute_values(true, Some(&path), removed_map, cx); Ok(()) } @@ -258,6 +275,7 @@ impl SettingsStore { user_settings_changed: bool, changed_local_path: Option<&Path>, old_settings_map: Option, + cx: &AppContext, ) { // Identify all of the setting types that have changed. let new_settings_map = if let Some(changed_path) = changed_local_path { @@ -300,9 +318,11 @@ impl SettingsStore { // If the global settings file changed, reload the global value for the field. if changed_local_path.is_none() { - setting_value.set_global_value( - setting_value.load_setting(default_deserialized_value, &user_values_stack), - ); + setting_value.set_global_value(setting_value.load_setting( + default_deserialized_value, + &user_values_stack, + cx, + )); } // Reload the local values for the setting. @@ -344,7 +364,7 @@ impl SettingsStore { // Load the local value for the field. setting_value.set_local_value( path.clone(), - setting_value.load_setting(default_deserialized_value, &user_values_stack), + setting_value.load_setting(default_deserialized_value, &user_values_stack, cx), ); } } @@ -398,13 +418,14 @@ impl AnySettingValue for SettingValue { &self, default_value: &DeserializedSetting, user_values: &[&DeserializedSetting], + cx: &AppContext, ) -> Box { let default_value = default_value.0.downcast_ref::().unwrap(); let values: SmallVec<[&T::FileContent; 6]> = user_values .iter() .map(|value| value.0.downcast_ref().unwrap()) .collect(); - Box::new(T::load(default_value, &values)) + Box::new(T::load(default_value, &values, cx)) } fn deserialize_setting(&self, json: &serde_json::Value) -> Result { @@ -420,7 +441,9 @@ impl AnySettingValue for SettingValue { } } } - self.global_value.as_ref().unwrap() + self.global_value + .as_ref() + .expect("no default value for setting") } fn set_global_value(&mut self, value: Box) { @@ -436,21 +459,21 @@ impl AnySettingValue for SettingValue { } } -impl Debug for SettingsStore { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - return f - .debug_struct("SettingsStore") - .field( - "setting_value_sets_by_type", - &self - .setting_values - .values() - .map(|set| (set.setting_type_name(), set)) - .collect::>(), - ) - .finish_non_exhaustive(); - } -} +// impl Debug for SettingsStore { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// return f +// .debug_struct("SettingsStore") +// .field( +// "setting_value_sets_by_type", +// &self +// .setting_values +// .values() +// .map(|set| (set.setting_type_name(), set)) +// .collect::>(), +// ) +// .finish_non_exhaustive(); +// } +// } fn update_value_in_json_text<'a>( text: &str, @@ -503,14 +526,6 @@ fn update_value_in_json_text<'a>( } } -lazy_static! { - static ref PAIR_QUERY: tree_sitter::Query = tree_sitter::Query::new( - tree_sitter_json::language(), - "(pair key: (string) @key value: (_) @value)", - ) - .unwrap(); -} - fn replace_value_in_json_text( text: &str, syntax_tree: &tree_sitter::Tree, @@ -521,6 +536,14 @@ fn replace_value_in_json_text( const LANGUAGE_OVERRIDES: &'static str = "language_overrides"; const LANGUAGES: &'static str = "languages"; + lazy_static! { + static ref PAIR_QUERY: tree_sitter::Query = tree_sitter::Query::new( + tree_sitter_json::language(), + "(pair key: (string) @key value: (_) @value)", + ) + .unwrap(); + } + let mut cursor = tree_sitter::QueryCursor::new(); let has_language_overrides = text.contains(LANGUAGE_OVERRIDES); @@ -666,7 +689,7 @@ fn to_pretty_json(value: &impl Serialize, indent_size: usize, indent_prefix_len: adjusted_text } -fn parse_json_with_comments(content: &str) -> Result { +pub fn parse_json_with_comments(content: &str) -> Result { Ok(serde_json::from_reader( json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()), )?) @@ -678,12 +701,12 @@ mod tests { use serde_derive::Deserialize; use unindent::Unindent; - #[test] - fn test_settings_store_basic() { + #[gpui::test] + fn test_settings_store_basic(cx: &mut AppContext) { let mut store = SettingsStore::default(); - store.register_setting::(); - store.register_setting::(); - store.register_setting::(); + store.register_setting::(cx); + store.register_setting::(cx); + store.register_setting::(cx); // error - missing required field in default settings store @@ -695,6 +718,7 @@ mod tests { "staff": false } }"#, + cx, ) .unwrap_err(); @@ -709,6 +733,7 @@ mod tests { "staff": false } }"#, + cx, ) .unwrap_err(); @@ -723,6 +748,7 @@ mod tests { "staff": false } }"#, + cx, ) .unwrap(); @@ -750,6 +776,7 @@ mod tests { "user": { "age": 31 }, "key1": "a" }"#, + cx, ) .unwrap(); @@ -767,12 +794,14 @@ mod tests { .set_local_settings( Path::new("/root1").into(), Some(r#"{ "user": { "staff": true } }"#), + cx, ) .unwrap(); store .set_local_settings( Path::new("/root1/subdir").into(), Some(r#"{ "user": { "name": "Jane Doe" } }"#), + cx, ) .unwrap(); @@ -780,6 +809,7 @@ mod tests { .set_local_settings( Path::new("/root2").into(), Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#), + cx, ) .unwrap(); @@ -816,8 +846,8 @@ mod tests { ); } - #[test] - fn test_setting_store_assign_json_before_register() { + #[gpui::test] + fn test_setting_store_assign_json_before_register(cx: &mut AppContext) { let mut store = SettingsStore::default(); store .set_default_settings( @@ -830,11 +860,14 @@ mod tests { }, "key1": "x" }"#, + cx, ) .unwrap(); - store.set_user_settings(r#"{ "turbo": false }"#).unwrap(); - store.register_setting::(); - store.register_setting::(); + store + .set_user_settings(r#"{ "turbo": false }"#, cx) + .unwrap(); + store.register_setting::(cx); + store.register_setting::(cx); assert_eq!(store.get::(None), &TurboSetting(false)); assert_eq!( @@ -846,7 +879,7 @@ mod tests { } ); - store.register_setting::(); + store.register_setting::(cx); assert_eq!( store.get::(None), &MultiKeySettings { @@ -856,11 +889,12 @@ mod tests { ); } - #[test] - fn test_setting_store_update() { + #[gpui::test] + fn test_setting_store_update(cx: &mut AppContext) { let mut store = SettingsStore::default(); - store.register_setting::(); - store.register_setting::(); + store.register_setting::(cx); + store.register_setting::(cx); + store.register_setting::(cx); // entries added and updated check_settings_update::( @@ -890,6 +924,7 @@ mod tests { } }"# .unindent(), + cx, ); // weird formatting @@ -904,6 +939,33 @@ mod tests { "user": { "age": 37, "name": "Max", "staff": true } }"# .unindent(), + cx, + ); + + // single-line formatting, other keys + check_settings_update::( + &mut store, + r#"{ "one": 1, "two": 2 }"#.unindent(), + |settings| settings.key1 = Some("x".into()), + r#"{ "key1": "x", "one": 1, "two": 2 }"#.unindent(), + cx, + ); + + // empty object + check_settings_update::( + &mut store, + r#"{ + "user": {} + }"# + .unindent(), + |settings| settings.age = Some(37), + r#"{ + "user": { + "age": 37 + } + }"# + .unindent(), + cx, ); // no content @@ -918,6 +980,7 @@ mod tests { } "# .unindent(), + cx, ); } @@ -926,8 +989,9 @@ mod tests { old_json: String, update: fn(&mut T::FileContent), expected_new_json: String, + cx: &mut AppContext, ) { - store.set_user_settings(&old_json).ok(); + store.set_user_settings(&old_json, cx).ok(); let edits = store.update::(&old_json, update); let mut new_json = old_json; for (range, replacement) in edits.into_iter().rev() { @@ -954,7 +1018,11 @@ mod tests { const KEY: Option<&'static str> = Some("user"); type FileContent = UserSettingsJson; - fn load(default_value: &UserSettingsJson, user_values: &[&UserSettingsJson]) -> Self { + fn load( + default_value: &UserSettingsJson, + user_values: &[&UserSettingsJson], + _: &AppContext, + ) -> Self { Self::load_via_json_merge(default_value, user_values) } } @@ -966,7 +1034,11 @@ mod tests { const KEY: Option<&'static str> = Some("turbo"); type FileContent = Option; - fn load(default_value: &Option, user_values: &[&Option]) -> Self { + fn load( + default_value: &Option, + user_values: &[&Option], + _: &AppContext, + ) -> Self { Self::load_via_json_merge(default_value, user_values) } } @@ -991,6 +1063,7 @@ mod tests { fn load( default_value: &MultiKeySettingsJson, user_values: &[&MultiKeySettingsJson], + _: &AppContext, ) -> Self { Self::load_via_json_merge(default_value, user_values) } @@ -1020,7 +1093,11 @@ mod tests { type FileContent = JournalSettingsJson; - fn load(default_value: &JournalSettingsJson, user_values: &[&JournalSettingsJson]) -> Self { + fn load( + default_value: &JournalSettingsJson, + user_values: &[&JournalSettingsJson], + _: &AppContext, + ) -> Self { Self::load_via_json_merge(default_value, user_values) } } @@ -1041,7 +1118,7 @@ mod tests { type FileContent = Self; - fn load(default_value: &Self, user_values: &[&Self]) -> Self { + fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Self { Self::load_via_json_merge(default_value, user_values) } } diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml index 67a28397e25ad31eb4185b8c0085dbe53eef1862..d37ac3465b5be7505a836d8796b1299388935da4 100644 --- a/crates/theme/Cargo.toml +++ b/crates/theme/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" publish = false +[features] +test-support = ["gpui/test-support"] + [lib] path = "src/theme.rs" doctest = false diff --git a/crates/theme/src/theme_registry.rs b/crates/theme/src/theme_registry.rs index f9f89b7adcf6ac1c251ebc6fe3c360230a03b212..2bcdb4528ce41de0ee8214072fa9ed7769eece3b 100644 --- a/crates/theme/src/theme_registry.rs +++ b/crates/theme/src/theme_registry.rs @@ -20,15 +20,26 @@ pub struct ThemeRegistry { next_theme_id: AtomicUsize, } +#[cfg(any(test, feature = "test-support"))] +pub const EMPTY_THEME_NAME: &'static str = "empty-theme"; + impl ThemeRegistry { pub fn new(source: impl AssetSource, font_cache: Arc) -> Arc { - Arc::new(Self { + let this = Arc::new(Self { assets: Box::new(source), themes: Default::default(), theme_data: Default::default(), next_theme_id: Default::default(), font_cache, - }) + }); + + #[cfg(any(test, feature = "test-support"))] + this.themes.lock().insert( + EMPTY_THEME_NAME.to_string(), + gpui::fonts::with_font_cache(this.font_cache.clone(), || Arc::new(Theme::default())), + ); + + this } pub fn list(&self, staff: bool) -> impl Iterator + '_ { diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index a404e43f2927e274e6efe76f4079e5e3513728e1..ac3a85d89a48a777cd852366b588bb8d05fa45a5 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -11,6 +11,7 @@ doctest = false [dependencies] editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } +fs = { path = "../fs" } gpui = { path = "../gpui" } picker = { path = "../picker" } theme = { path = "../theme" } diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 21332114e22bf81bf9147568c471bc0e3d77316b..a35e546891ed218c2228cf9a3882d74aa9d8cdb3 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -1,7 +1,8 @@ +use fs::Fs; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{actions, elements::*, AnyElement, AppContext, Element, MouseState, ViewContext}; use picker::{Picker, PickerDelegate, PickerEvent}; -use settings::{settings_file::SettingsFile, Settings}; +use settings::{update_settings_file, Settings}; use staff_mode::StaffMode; use std::sync::Arc; use theme::{Theme, ThemeMeta, ThemeRegistry}; @@ -18,7 +19,8 @@ pub fn init(cx: &mut AppContext) { pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { workspace.toggle_modal(cx, |workspace, cx| { let themes = workspace.app_state().themes.clone(); - cx.add_view(|cx| ThemeSelector::new(ThemeSelectorDelegate::new(themes, cx), cx)) + let fs = workspace.app_state().fs.clone(); + cx.add_view(|cx| ThemeSelector::new(ThemeSelectorDelegate::new(fs, themes, cx), cx)) }); } @@ -40,6 +42,7 @@ pub fn reload(themes: Arc, cx: &mut AppContext) { pub type ThemeSelector = Picker; pub struct ThemeSelectorDelegate { + fs: Arc, registry: Arc, theme_data: Vec, matches: Vec, @@ -49,7 +52,11 @@ pub struct ThemeSelectorDelegate { } impl ThemeSelectorDelegate { - fn new(registry: Arc, cx: &mut ViewContext) -> Self { + fn new( + fs: Arc, + registry: Arc, + cx: &mut ViewContext, + ) -> Self { let settings = cx.global::(); let original_theme = settings.theme.clone(); @@ -68,6 +75,7 @@ impl ThemeSelectorDelegate { }) .collect(); let mut this = Self { + fs, registry, theme_data: theme_names, matches, @@ -121,7 +129,7 @@ impl PickerDelegate for ThemeSelectorDelegate { self.selection_completed = true; let theme_name = cx.global::().theme.meta.name.clone(); - SettingsFile::update(cx, |settings_content| { + update_settings_file(self.fs.clone(), cx, |settings_content| { settings_content.theme = Some(theme_name); }); diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index d35cced642322c09bda9a933329434b71c03dfe4..696a5c5e4e2d7ac48837b3e426b1f0e5daa3aad1 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -14,6 +14,7 @@ test-support = [] anyhow.workspace = true log.workspace = true editor = { path = "../editor" } +fs = { path = "../fs" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } db = { path = "../db" } diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index 260c279e181a8677f08124024ac3f7db537e8eed..c0e9e0a38d0c30e916e6b3c5ef82711e3d0f96b0 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ actions, @@ -7,7 +5,9 @@ use gpui::{ AppContext, Task, ViewContext, }; use picker::{Picker, PickerDelegate, PickerEvent}; -use settings::{settings_file::SettingsFile, BaseKeymap, Settings}; +use project::Fs; +use settings::{update_settings_file, BaseKeymap, Settings}; +use std::sync::Arc; use util::ResultExt; use workspace::Workspace; @@ -23,8 +23,9 @@ pub fn toggle( _: &ToggleBaseKeymapSelector, cx: &mut ViewContext, ) { - workspace.toggle_modal(cx, |_, cx| { - cx.add_view(|cx| BaseKeymapSelector::new(BaseKeymapSelectorDelegate::new(cx), cx)) + workspace.toggle_modal(cx, |workspace, cx| { + let fs = workspace.app_state().fs.clone(); + cx.add_view(|cx| BaseKeymapSelector::new(BaseKeymapSelectorDelegate::new(fs, cx), cx)) }); } @@ -33,10 +34,11 @@ pub type BaseKeymapSelector = Picker; pub struct BaseKeymapSelectorDelegate { matches: Vec, selected_index: usize, + fs: Arc, } impl BaseKeymapSelectorDelegate { - fn new(cx: &mut ViewContext) -> Self { + fn new(fs: Arc, cx: &mut ViewContext) -> Self { let base = cx.global::().base_keymap; let selected_index = BaseKeymap::OPTIONS .iter() @@ -45,6 +47,7 @@ impl BaseKeymapSelectorDelegate { Self { matches: Vec::new(), selected_index, + fs, } } } @@ -119,7 +122,9 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { fn confirm(&mut self, cx: &mut ViewContext) { if let Some(selection) = self.matches.get(self.selected_index) { let base_keymap = BaseKeymap::from_names(&selection.string); - SettingsFile::update(cx, move |settings| settings.base_keymap = Some(base_keymap)); + update_settings_file(self.fs.clone(), cx, move |settings| { + settings.base_keymap = Some(base_keymap) + }); } cx.emit(PickerEvent::Dismiss); } diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index a3d91adc910f658ab2bfd3b9af063ae4bc81142b..c2e65dc524017074eab3fe2cac7612c6c94427a9 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -7,7 +7,7 @@ use gpui::{ elements::{Flex, Label, ParentElement}, AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, WeakViewHandle, }; -use settings::{settings_file::SettingsFile, Settings}; +use settings::{update_settings_file, Settings}; use workspace::{ item::Item, open_new, sidebar::SidebarSide, AppState, PaneBackdrop, Welcome, Workspace, @@ -169,10 +169,13 @@ impl View for WelcomePage { metrics, 0, cx, - |_, checked, cx| { - SettingsFile::update(cx, move |file| { - file.telemetry.set_metrics(checked) - }) + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file(fs, cx, move |file| { + file.telemetry.set_metrics(checked) + }) + } }, ) .contained() @@ -185,10 +188,13 @@ impl View for WelcomePage { diagnostics, 0, cx, - |_, checked, cx| { - SettingsFile::update(cx, move |file| { - file.telemetry.set_diagnostics(checked) - }) + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file(fs, cx, move |file| { + file.telemetry.set_diagnostics(checked) + }) + } }, ) .contained() diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f498078b520f5fe497fb87f8a7f04a0ca4025460..3d43109e6b59ec2d2027fe93ac6d635cb2784774 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -24,8 +24,8 @@ use parking_lot::Mutex; use project::Fs; use serde::{Deserialize, Serialize}; use settings::{ - self, settings_file::SettingsFile, KeymapFileContent, Settings, SettingsFileContent, - WorkingDirectory, + default_settings, handle_keymap_file_changes, handle_settings_file_changes, watch_config_file, + Settings, SettingsStore, WorkingDirectory, }; use simplelog::ConfigBuilder; use smol::process::Command; @@ -37,6 +37,7 @@ use std::{ os::unix::prelude::OsStrExt, panic, path::PathBuf, + str, sync::{Arc, Weak}, thread, time::Duration, @@ -46,7 +47,6 @@ use util::http::{self, HttpClient}; use welcome::{show_welcome_experience, FIRST_OPEN}; use fs::RealFs; -use settings::watched_json::WatchedJsonFile; #[cfg(debug_assertions)] use staff_mode::StaffMode; use theme::ThemeRegistry; @@ -75,10 +75,11 @@ fn main() { load_embedded_fonts(&app); let fs = Arc::new(RealFs); - let themes = ThemeRegistry::new(Assets, app.font_cache()); - let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes); - let config_files = load_config_files(&app, fs.clone()); + let user_settings_file_rx = + watch_config_file(app.background(), fs.clone(), paths::SETTINGS.clone()); + let user_keymap_file_rx = + watch_config_file(app.background(), fs.clone(), paths::KEYMAP.clone()); let login_shell_env_loaded = if stdout_is_a_pty() { Task::ready(()) @@ -126,26 +127,18 @@ fn main() { app.run(move |cx| { cx.set_global(*RELEASE_CHANNEL); + cx.set_global(themes.clone()); #[cfg(debug_assertions)] cx.set_global(StaffMode(true)); - let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap(); - - //Setup settings global before binding actions - cx.set_global(SettingsFile::new( - &paths::SETTINGS, - settings_file_content.clone(), - fs.clone(), - )); - - settings::watch_files( - default_settings, - settings_file_content, - themes.clone(), - keymap_file, - cx, - ); + let mut store = SettingsStore::default(); + store + .set_default_settings(default_settings().as_ref(), cx) + .unwrap(); + cx.set_global(store); + handle_settings_file_changes(user_settings_file_rx, cx); + handle_keymap_file_changes(user_keymap_file_rx, cx); if !stdout_is_a_pty() { upload_previous_panics(http.clone(), cx); @@ -585,27 +578,6 @@ async fn watch_themes( None } -fn load_config_files( - app: &App, - fs: Arc, -) -> oneshot::Receiver<( - WatchedJsonFile, - WatchedJsonFile, -)> { - let executor = app.background(); - let (tx, rx) = oneshot::channel(); - executor - .clone() - .spawn(async move { - let settings_file = - WatchedJsonFile::new(fs.clone(), &executor, paths::SETTINGS.clone()).await; - let keymap_file = WatchedJsonFile::new(fs, &executor, paths::KEYMAP.clone()).await; - tx.send((settings_file, keymap_file)).ok() - }) - .detach(); - rx -} - fn connect_to_cli( server_name: &str, ) -> Result<(mpsc::Receiver, IpcSender)> { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f687237bd22dd599a3d313bf3d79bdfa0aa64399..26d2b50e0d211bf73a346e03e84defdee9e33ada 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -323,7 +323,7 @@ pub fn initialize_workspace( }); let toggle_terminal = cx.add_view(|cx| TerminalButton::new(workspace_handle.clone(), cx)); - let copilot = cx.add_view(|cx| copilot_button::CopilotButton::new(cx)); + let copilot = cx.add_view(|cx| copilot_button::CopilotButton::new(app_state.fs.clone(), cx)); let diagnostic_summary = cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx)); let activity_indicator = From b6b2c5d1d1604178d0ff2cb108a0843b15f45782 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 May 2023 09:56:58 -0700 Subject: [PATCH 05/25] Generalize settings JSON schema logic to work w/ arbitrary setting types --- crates/settings/src/settings.rs | 139 +++++++++++++------------- crates/settings/src/settings_store.rs | 99 +++++++++++++++++- crates/zed/src/languages/json.rs | 16 ++- 3 files changed, 179 insertions(+), 75 deletions(-) diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index c3911f9254a555067e615a06a1a1dd6ebe6c4a09..3bc7eb81dfb3a84c082a8fbab6caa773ecf3ce94 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -8,7 +8,7 @@ use gpui::{ fonts, AppContext, AssetSource, }; use schemars::{ - gen::{SchemaGenerator, SchemaSettings}, + gen::SchemaGenerator, schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec}, JsonSchema, }; @@ -25,7 +25,7 @@ use util::ResultExt as _; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; pub use settings_file::*; -pub use settings_store::SettingsStore; +pub use settings_store::{SettingsJsonSchemaParams, SettingsStore}; pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; @@ -150,6 +150,75 @@ impl Setting for Settings { this } + + fn json_schema( + generator: &mut SchemaGenerator, + params: &SettingsJsonSchemaParams, + ) -> schemars::schema::RootSchema { + let mut root_schema = generator.root_schema_for::(); + + // Create a schema for a theme name. + let theme_name_schema = SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + enum_values: Some( + params + .theme_names + .iter() + .cloned() + .map(Value::String) + .collect(), + ), + ..Default::default() + }; + + // Create a schema for a 'languages overrides' object, associating editor + // settings with specific langauges. + assert!(root_schema.definitions.contains_key("EditorSettings")); + + let languages_object_schema = SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), + object: Some(Box::new(ObjectValidation { + properties: params + .language_names + .iter() + .map(|name| { + ( + name.clone(), + Schema::new_ref("#/definitions/EditorSettings".into()), + ) + }) + .collect(), + ..Default::default() + })), + ..Default::default() + }; + + // Add these new schemas as definitions, and modify properties of the root + // schema to reference them. + root_schema.definitions.extend([ + ("ThemeName".into(), theme_name_schema.into()), + ("Languages".into(), languages_object_schema.into()), + ]); + let root_schema_object = &mut root_schema.schema.object.as_mut().unwrap(); + + root_schema_object.properties.extend([ + ( + "theme".to_owned(), + Schema::new_ref("#/definitions/ThemeName".into()), + ), + ( + "languages".to_owned(), + Schema::new_ref("#/definitions/Languages".into()), + ), + // For backward compatibility + ( + "language_overrides".to_owned(), + Schema::new_ref("#/definitions/Languages".into()), + ), + ]); + + root_schema + } } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] @@ -926,72 +995,6 @@ impl Settings { } } -pub fn settings_file_json_schema( - theme_names: Vec, - language_names: &[String], -) -> serde_json::Value { - let settings = SchemaSettings::draft07().with(|settings| { - settings.option_add_null_type = false; - }); - let generator = SchemaGenerator::new(settings); - - let mut root_schema = generator.into_root_schema_for::(); - - // Create a schema for a theme name. - let theme_name_schema = SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - enum_values: Some(theme_names.into_iter().map(Value::String).collect()), - ..Default::default() - }; - - // Create a schema for a 'languages overrides' object, associating editor - // settings with specific langauges. - assert!(root_schema.definitions.contains_key("EditorSettings")); - - let languages_object_schema = SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), - object: Some(Box::new(ObjectValidation { - properties: language_names - .iter() - .map(|name| { - ( - name.clone(), - Schema::new_ref("#/definitions/EditorSettings".into()), - ) - }) - .collect(), - ..Default::default() - })), - ..Default::default() - }; - - // Add these new schemas as definitions, and modify properties of the root - // schema to reference them. - root_schema.definitions.extend([ - ("ThemeName".into(), theme_name_schema.into()), - ("Languages".into(), languages_object_schema.into()), - ]); - let root_schema_object = &mut root_schema.schema.object.as_mut().unwrap(); - - root_schema_object.properties.extend([ - ( - "theme".to_owned(), - Schema::new_ref("#/definitions/ThemeName".into()), - ), - ( - "languages".to_owned(), - Schema::new_ref("#/definitions/Languages".into()), - ), - // For backward compatibility - ( - "language_overrides".to_owned(), - Schema::new_ref("#/definitions/Languages".into()), - ), - ]); - - serde_json::to_value(root_schema).unwrap() -} - fn merge(target: &mut T, value: Option) { if let Some(value) = value { *target = value; diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 394d457d3d9c73a71cf5309350b88e82914d879e..887b4eef11e17a5e83fe662e97e440303a7e193e 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; -use collections::{hash_map, BTreeMap, HashMap, HashSet}; +use collections::{btree_map, hash_map, BTreeMap, HashMap, HashSet}; use gpui::AppContext; use lazy_static::lazy_static; -use schemars::JsonSchema; +use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema}; use serde::{de::DeserializeOwned, Deserialize as _, Serialize}; use smallvec::SmallVec; use std::{ @@ -39,6 +39,10 @@ pub trait Setting: 'static { cx: &AppContext, ) -> Self; + fn json_schema(generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams) -> RootSchema { + generator.root_schema_for::() + } + fn load_via_json_merge( default_value: &Self::FileContent, user_values: &[&Self::FileContent], @@ -54,6 +58,11 @@ pub trait Setting: 'static { } } +pub struct SettingsJsonSchemaParams<'a> { + pub theme_names: &'a [String], + pub language_names: &'a [String], +} + /// A set of strongly-typed setting values defined via multiple JSON files. #[derive(Default)] pub struct SettingsStore { @@ -84,6 +93,11 @@ trait AnySettingValue { fn value_for_path(&self, path: Option<&Path>) -> &dyn Any; fn set_global_value(&mut self, value: Box); fn set_local_value(&mut self, path: Arc, value: Box); + fn json_schema( + &self, + generator: &mut SchemaGenerator, + _: &SettingsJsonSchemaParams, + ) -> RootSchema; } struct DeserializedSetting(Box); @@ -270,6 +284,79 @@ impl SettingsStore { Ok(()) } + pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams) -> serde_json::Value { + use schemars::{ + gen::SchemaSettings, + schema::{Schema, SchemaObject}, + }; + + let settings = SchemaSettings::draft07().with(|settings| { + settings.option_add_null_type = false; + }); + let mut generator = SchemaGenerator::new(settings); + let mut combined_schema = RootSchema::default(); + + for setting_value in self.setting_values.values() { + let setting_schema = setting_value.json_schema(&mut generator, schema_params); + combined_schema + .definitions + .extend(setting_schema.definitions); + + let target_schema = if let Some(key) = setting_value.key() { + let key_schema = combined_schema + .schema + .object() + .properties + .entry(key.to_string()) + .or_insert_with(|| Schema::Object(SchemaObject::default())); + if let Schema::Object(key_schema) = key_schema { + key_schema + } else { + continue; + } + } else { + &mut combined_schema.schema + }; + + merge_schema(target_schema, setting_schema.schema); + } + + fn merge_schema(target: &mut SchemaObject, source: SchemaObject) { + if let Some(source) = source.object { + let target_properties = &mut target.object().properties; + for (key, value) in source.properties { + match target_properties.entry(key) { + btree_map::Entry::Vacant(e) => { + e.insert(value); + } + btree_map::Entry::Occupied(e) => { + if let (Schema::Object(target), Schema::Object(src)) = + (e.into_mut(), value) + { + merge_schema(target, src); + } + } + } + } + } + + overwrite(&mut target.instance_type, source.instance_type); + overwrite(&mut target.string, source.string); + overwrite(&mut target.number, source.number); + overwrite(&mut target.reference, source.reference); + overwrite(&mut target.array, source.array); + overwrite(&mut target.enum_values, source.enum_values); + + fn overwrite(target: &mut Option, source: Option) { + if let Some(source) = source { + *target = Some(source); + } + } + } + + serde_json::to_value(&combined_schema).unwrap() + } + fn recompute_values( &mut self, user_settings_changed: bool, @@ -457,6 +544,14 @@ impl AnySettingValue for SettingValue { Err(ix) => self.local_values.insert(ix, (path, value)), } } + + fn json_schema( + &self, + generator: &mut SchemaGenerator, + params: &SettingsJsonSchemaParams, + ) -> RootSchema { + T::json_schema(generator, params) + } } // impl Debug for SettingsStore { diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index d87d36abfef7139033d8f9526e877d2ba4efb826..8ea07c626d121b9e9f7ef1618cedc7b029683139 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -6,7 +6,7 @@ use gpui::AppContext; use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter}; use node_runtime::NodeRuntime; use serde_json::json; -use settings::{keymap_file_json_schema, settings_file_json_schema}; +use settings::{keymap_file_json_schema, SettingsJsonSchemaParams, SettingsStore}; use smol::fs; use staff_mode::StaffMode; use std::{ @@ -128,12 +128,18 @@ impl LspAdapter for JsonLspAdapter { cx: &mut AppContext, ) -> Option> { let action_names = cx.all_action_names().collect::>(); - let theme_names = self + let theme_names = &self .themes .list(**cx.default_global::()) .map(|meta| meta.name) - .collect(); - let language_names = self.languages.language_names(); + .collect::>(); + let language_names = &self.languages.language_names(); + let settings_schema = cx + .global::() + .json_schema(&SettingsJsonSchemaParams { + theme_names, + language_names, + }); Some( future::ready(serde_json::json!({ "json": { @@ -143,7 +149,7 @@ impl LspAdapter for JsonLspAdapter { "schemas": [ { "fileMatch": [schema_file_match(&paths::SETTINGS)], - "schema": settings_file_json_schema(theme_names, &language_names), + "schema": settings_schema, }, { "fileMatch": [schema_file_match(&paths::KEYMAP)], From 926d7b356d585c5631a459dfed103c405b30b9a5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 May 2023 10:25:49 -0700 Subject: [PATCH 06/25] Define auto_update setting in the auto_update crate --- crates/auto_update/src/auto_update.rs | 29 +++++++++++++++++++++------ crates/settings/src/settings.rs | 10 +-------- crates/settings/src/settings_file.rs | 21 +++++++++++++++++-- crates/settings/src/settings_store.rs | 11 ++++++++++ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 68d3776e1c8dd410fa86c4f5c3fadb86097c4599..2e00a204baf78e6101dd2675b871550701321cc2 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -10,7 +10,7 @@ use gpui::{ use isahc::AsyncBody; use serde::Deserialize; use serde_derive::Serialize; -use settings::Settings; +use settings::{Setting, Settings, SettingsStore}; use smol::{fs::File, io::AsyncReadExt, process::Command}; use std::{ffi::OsString, sync::Arc, time::Duration}; use update_notification::UpdateNotification; @@ -58,18 +58,35 @@ impl Entity for AutoUpdater { type Event = (); } +struct AutoUpdateSetting(bool); + +impl Setting for AutoUpdateSetting { + const KEY: Option<&'static str> = Some("auto_update"); + + type FileContent = Option; + + fn load(default_value: &Option, user_values: &[&Option], _: &AppContext) -> Self { + Self( + Self::json_merge(default_value, user_values) + .unwrap() + .unwrap(), + ) + } +} + pub fn init(http_client: Arc, server_url: String, cx: &mut AppContext) { + settings::register_setting::(cx); + if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) { let auto_updater = cx.add_model(|cx| { let updater = AutoUpdater::new(version, http_client, server_url); - let mut update_subscription = cx - .global::() - .auto_update + let mut update_subscription = settings::get_setting::(None, cx) + .0 .then(|| updater.start_polling(cx)); - cx.observe_global::(move |updater, cx| { - if cx.global::().auto_update { + cx.observe_global::(move |updater, cx| { + if settings::get_setting::(None, cx).0 { if update_subscription.is_none() { update_subscription = Some(updater.start_polling(cx)) } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 3bc7eb81dfb3a84c082a8fbab6caa773ecf3ce94..6428e4100d1888a0d6254ba7ec812d77286f5531 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -14,7 +14,6 @@ use schemars::{ }; use serde::{Deserialize, Serialize}; use serde_json::Value; -use settings_store::Setting; use sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, @@ -25,7 +24,7 @@ use util::ResultExt as _; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; pub use settings_file::*; -pub use settings_store::{SettingsJsonSchemaParams, SettingsStore}; +pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; @@ -62,7 +61,6 @@ pub struct Settings { pub theme: Arc, pub telemetry_defaults: TelemetrySettings, pub telemetry_overrides: TelemetrySettings, - pub auto_update: bool, pub base_keymap: BaseKeymap, } @@ -137,7 +135,6 @@ impl Setting for Settings { theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), telemetry_defaults: defaults.telemetry, telemetry_overrides: Default::default(), - auto_update: defaults.auto_update.unwrap(), base_keymap: Default::default(), features: Features { copilot: defaults.features.copilot.unwrap(), @@ -576,8 +573,6 @@ pub struct SettingsFileContent { #[serde(default)] pub telemetry: TelemetrySettings, #[serde(default)] - pub auto_update: Option, - #[serde(default)] pub base_keymap: Option, #[serde(default)] pub features: FeaturesContent, @@ -695,7 +690,6 @@ impl Settings { theme: themes.get(&defaults.theme.unwrap()).unwrap(), telemetry_defaults: defaults.telemetry, telemetry_overrides: Default::default(), - auto_update: defaults.auto_update.unwrap(), base_keymap: Default::default(), features: Features { copilot: defaults.features.copilot.unwrap(), @@ -770,7 +764,6 @@ impl Settings { self.language_overrides = data.languages; self.telemetry_overrides = data.telemetry; self.lsp = data.lsp; - merge(&mut self.auto_update, data.auto_update); } pub fn with_language_defaults( @@ -980,7 +973,6 @@ impl Settings { metrics: Some(true), }, telemetry_overrides: Default::default(), - auto_update: true, base_keymap: Default::default(), features: Features { copilot: true }, } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 17294235ec5d3dbbcaa471ec249b79aaad9ae5f9..a07560307f97e59bd1e47417d4facd2fcac0e9f9 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,15 +1,32 @@ use crate::{ settings_store::parse_json_with_comments, settings_store::SettingsStore, KeymapFileContent, - Settings, SettingsFileContent, DEFAULT_SETTINGS_ASSET_PATH, + Setting, Settings, SettingsFileContent, DEFAULT_SETTINGS_ASSET_PATH, }; use anyhow::Result; use assets::Assets; use fs::Fs; use futures::{channel::mpsc, StreamExt}; use gpui::{executor::Background, AppContext, AssetSource}; -use std::{borrow::Cow, io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration}; +use std::{ + borrow::Cow, + io::ErrorKind, + path::{Path, PathBuf}, + str, + sync::Arc, + time::Duration, +}; use util::{paths, ResultExt}; +pub fn register_setting(cx: &mut AppContext) { + cx.update_global::(|store, cx| { + store.register_setting::(cx); + }); +} + +pub fn get_setting<'a, T: Setting>(path: Option<&Path>, cx: &'a AppContext) -> &'a T { + cx.global::().get(path) +} + pub fn default_settings() -> Cow<'static, str> { match Assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap() { Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 887b4eef11e17a5e83fe662e97e440303a7e193e..38f9d09df00285267ad6c65a33ec6cb2107978db 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -43,6 +43,17 @@ pub trait Setting: 'static { generator.root_schema_for::() } + fn json_merge( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + ) -> Result { + let mut merged = serde_json::Value::Null; + for value in [default_value].iter().chain(user_values) { + merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); + } + Ok(serde_json::from_value(merged)?) + } + fn load_via_json_merge( default_value: &Self::FileContent, user_values: &[&Self::FileContent], From 9b06be2aa27146ee39fdbb86facdb189380e8639 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 May 2023 11:06:55 -0700 Subject: [PATCH 07/25] Define vim_mode setting in vim crate --- crates/editor/src/editor.rs | 12 +++++++--- crates/settings/src/settings.rs | 9 ++----- crates/settings/src/settings_store.rs | 26 +++++++++++++++++++- crates/vim/src/test/vim_test_context.rs | 12 +++++----- crates/vim/src/vim.rs | 32 +++++++++++++++++++++---- 5 files changed, 70 insertions(+), 21 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2bb9869e6defcd623272fe94e8c312d05559694e..964b98450baf521b0609b37833c694644184a0c3 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -70,7 +70,7 @@ use scroll::{ }; use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsStore}; use smallvec::SmallVec; use snippet::Snippet; use std::{ @@ -6868,6 +6868,12 @@ impl Editor { .as_singleton() .and_then(|b| b.read(cx).file()), ) { + let vim_mode = cx + .global::() + .untyped_user_settings() + .get("vim_mode") + == Some(&serde_json::Value::Bool(true)); + let settings = cx.global::(); let extension = Path::new(file.file_name(cx)) @@ -6880,12 +6886,12 @@ impl Editor { "save" => "save editor", _ => name, }, - json!({ "File Extension": extension, "Vim Mode": settings.vim_mode, "In Clickhouse": true }), + json!({ "File Extension": extension, "Vim Mode": vim_mode, "In Clickhouse": true }), settings.telemetry(), ); let event = ClickhouseEvent::Editor { file_extension: extension.map(ToString::to_string), - vim_mode: settings.vim_mode, + vim_mode, operation: name, copilot_enabled: settings.features.copilot, copilot_enabled_for_language: settings.show_copilot_suggestions( diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 6428e4100d1888a0d6254ba7ec812d77286f5531..5284d3a69a86ec30de24f88e32762c19bda71ed2 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -43,7 +43,6 @@ pub struct Settings { pub hover_popover_enabled: bool, pub show_completions_on_input: bool, pub show_call_status_icon: bool, - pub vim_mode: bool, pub autosave: Autosave, pub default_dock_anchor: DockAnchor, pub editor_defaults: EditorSettings, @@ -65,6 +64,8 @@ pub struct Settings { } impl Setting for Settings { + const KEY: Option<&'static str> = None; + type FileContent = SettingsFileContent; fn load( @@ -93,7 +94,6 @@ impl Setting for Settings { hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), show_completions_on_input: defaults.show_completions_on_input.unwrap(), show_call_status_icon: defaults.show_call_status_icon.unwrap(), - vim_mode: defaults.vim_mode.unwrap(), autosave: defaults.autosave.unwrap(), default_dock_anchor: defaults.default_dock_anchor.unwrap(), editor_defaults: EditorSettings { @@ -550,8 +550,6 @@ pub struct SettingsFileContent { #[serde(default)] pub show_call_status_icon: Option, #[serde(default)] - pub vim_mode: Option, - #[serde(default)] pub autosave: Option, #[serde(default)] pub default_dock_anchor: Option, @@ -647,7 +645,6 @@ impl Settings { hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), show_completions_on_input: defaults.show_completions_on_input.unwrap(), show_call_status_icon: defaults.show_call_status_icon.unwrap(), - vim_mode: defaults.vim_mode.unwrap(), autosave: defaults.autosave.unwrap(), default_dock_anchor: defaults.default_dock_anchor.unwrap(), editor_defaults: EditorSettings { @@ -741,7 +738,6 @@ impl Settings { &mut self.show_completions_on_input, data.show_completions_on_input, ); - merge(&mut self.vim_mode, data.vim_mode); merge(&mut self.autosave, data.autosave); merge(&mut self.default_dock_anchor, data.default_dock_anchor); merge(&mut self.base_keymap, data.base_keymap); @@ -940,7 +936,6 @@ impl Settings { hover_popover_enabled: true, show_completions_on_input: true, show_call_status_icon: true, - vim_mode: false, autosave: Autosave::Off, default_dock_anchor: DockAnchor::Bottom, editor_defaults: EditorSettings { diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 38f9d09df00285267ad6c65a33ec6cb2107978db..d986ea06832dfc4e5226f7fdcb8f8b047e2ebd7a 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -23,7 +23,7 @@ pub trait Setting: 'static { /// The name of a key within the JSON file from which this setting should /// be deserialized. If this is `None`, then the setting will be deserialized /// from the root object. - const KEY: Option<&'static str> = None; + const KEY: Option<&'static str>; /// The type that is stored in an individual JSON file. type FileContent: Clone + Serialize + DeserializeOwned + JsonSchema; @@ -165,6 +165,28 @@ impl SettingsStore { .expect("no default value for setting type") } + /// Get the user's settings as a raw JSON value. + /// + /// This is only for debugging and reporting. For user-facing functionality, + /// use the typed setting interface. + pub fn untyped_user_settings(&self) -> &serde_json::Value { + self.user_deserialized_settings + .as_ref() + .map_or(&serde_json::Value::Null, |s| &s.untyped) + } + + /// Override the global value for a particular setting. + /// + /// This is only for tests. Normally, settings are only loaded from + /// JSON files. + #[cfg(any(test, feature = "test-support"))] + pub fn replace_value(&mut self, value: T) { + self.setting_values + .get_mut(&TypeId::of::()) + .expect("unregistered setting type") + .set_global_value(Box::new(value)) + } + /// Update the value of a setting. /// /// Returns a list of edits to apply to the JSON file. @@ -1164,6 +1186,8 @@ mod tests { } impl Setting for MultiKeySettings { + const KEY: Option<&'static str> = None; + type FileContent = MultiKeySettingsJson; fn load( diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index 69227a0e45e2cbb8c0d67b5bb2e4de9634cb7a5b..69e432079a8034a14edf062a41b4368a3337c84e 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -18,8 +18,8 @@ impl<'a> VimTestContext<'a> { pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> { let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await; cx.update(|cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.vim_mode = enabled; + cx.update_global(|store: &mut SettingsStore, _| { + store.replace_value(VimModeSetting(enabled)); }); search::init(cx); crate::init(cx); @@ -52,16 +52,16 @@ impl<'a> VimTestContext<'a> { pub fn enable_vim(&mut self) { self.cx.update(|cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.vim_mode = true; + cx.update_global(|store: &mut SettingsStore, _| { + store.replace_value(VimModeSetting(true)) }); }) } pub fn disable_vim(&mut self) { self.cx.update(|cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.vim_mode = false; + cx.update_global(|store: &mut SettingsStore, _| { + store.replace_value(VimModeSetting(false)) }); }) } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index cc686f851f21c7019c76a43840cea70a8d6f32de..65c46e81cfa59c8abf321792f75f95313fc87600 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -22,11 +22,13 @@ use language::CursorShape; use motion::Motion; use normal::normal_replace; use serde::Deserialize; -use settings::Settings; +use settings::{Setting, SettingsStore}; use state::{Mode, Operator, VimState}; use visual::visual_replace; use workspace::{self, Workspace}; +struct VimModeSetting(bool); + #[derive(Clone, Deserialize, PartialEq)] pub struct SwitchMode(pub Mode); @@ -40,6 +42,8 @@ actions!(vim, [Tab, Enter]); impl_actions!(vim, [Number, SwitchMode, PushOperator]); pub fn init(cx: &mut AppContext) { + settings::register_setting::(cx); + editor_events::init(cx); normal::init(cx); visual::init(cx); @@ -91,11 +95,11 @@ pub fn init(cx: &mut AppContext) { filter.filtered_namespaces.insert("vim"); }); cx.update_default_global(|vim: &mut Vim, cx: &mut AppContext| { - vim.set_enabled(cx.global::().vim_mode, cx) + vim.set_enabled(settings::get_setting::(None, cx).0, cx) }); - cx.observe_global::(|cx| { + cx.observe_global::(|cx| { cx.update_default_global(|vim: &mut Vim, cx: &mut AppContext| { - vim.set_enabled(cx.global::().vim_mode, cx) + vim.set_enabled(settings::get_setting::(None, cx).0, cx) }); }) .detach(); @@ -330,6 +334,26 @@ impl Vim { } } +impl Setting for VimModeSetting { + const KEY: Option<&'static str> = Some("vim_mode"); + + type FileContent = Option; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &AppContext, + ) -> Self { + Self( + user_values + .first() + .map(|e| **e) + .flatten() + .unwrap_or(default_value.unwrap()), + ) + } +} + fn local_selections_changed(newest_empty: bool, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| { if vim.enabled && vim.state.mode == Mode::Normal && !newest_empty { From aa6ea920e2b8fe4e38c09b489aab9d7065c264ce Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 May 2023 12:17:52 -0700 Subject: [PATCH 08/25] Define telemetry settings in the client crate --- Cargo.lock | 2 + Cargo.toml | 1 + crates/auto_update/src/auto_update.rs | 6 +- crates/client/Cargo.toml | 1 + crates/client/src/client.rs | 61 ++++++++++++++++----- crates/client/src/telemetry.rs | 11 ++-- crates/client/src/user.rs | 13 +++-- crates/copilot_button/src/copilot_button.rs | 6 +- crates/editor/src/editor.rs | 8 +-- crates/settings/Cargo.toml | 2 +- crates/settings/src/settings.rs | 59 -------------------- crates/settings/src/settings_file.rs | 11 ++-- crates/theme_selector/src/theme_selector.rs | 2 +- crates/welcome/Cargo.toml | 1 + crates/welcome/src/base_keymap_picker.rs | 2 +- crates/welcome/src/welcome.rs | 26 +++++---- crates/zed/src/main.rs | 8 +-- 17 files changed, 100 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 266b0e6d414bf771436786e7fe25de3d4ca0c1ad..baba47b216468567d8fc0a838a447b16c4c14855 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1155,6 +1155,7 @@ dependencies = [ "postage", "rand 0.8.5", "rpc", + "schemars", "serde", "serde_derive", "settings", @@ -8315,6 +8316,7 @@ name = "welcome" version = "0.1.0" dependencies = [ "anyhow", + "client", "db", "editor", "fs", diff --git a/Cargo.toml b/Cargo.toml index 15df687d41d3a5933250bb4d2cd66a7ab0ed4075..0a73b878f89e5dd8dba8d67810e85024b0d72163 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ parking_lot = { version = "0.11.1" } postage = { version = "0.5", features = ["futures-traits"] } rand = { version = "0.8.5" } regex = { version = "1.5" } +schemars = { version = "0.8" } serde = { version = "1.0", features = ["derive", "rc"] } serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 2e00a204baf78e6101dd2675b871550701321cc2..54f3f93b009e6b7e0f5ab706d8cff748af03212d 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -1,7 +1,7 @@ mod update_notification; use anyhow::{anyhow, Context, Result}; -use client::{Client, ZED_APP_PATH, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN}; +use client::{Client, TelemetrySettings, ZED_APP_PATH, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN}; use db::kvp::KEY_VALUE_STORE; use gpui::{ actions, platform::AppVersion, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, @@ -10,7 +10,7 @@ use gpui::{ use isahc::AsyncBody; use serde::Deserialize; use serde_derive::Serialize; -use settings::{Setting, Settings, SettingsStore}; +use settings::{Setting, SettingsStore}; use smol::{fs::File, io::AsyncReadExt, process::Command}; use std::{ffi::OsString, sync::Arc, time::Duration}; use update_notification::UpdateNotification; @@ -279,7 +279,7 @@ impl AutoUpdater { let release_channel = cx .has_global::() .then(|| cx.global::().display_name()); - let telemetry = cx.global::().telemetry().metrics(); + let telemetry = settings::get_setting::(None, cx).metrics; (installation_id, release_channel, telemetry) }); diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 99c492d638e525e63316da7868e3780241178c21..3ecc51598696cd9ec5965c35d346bda069418086 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -31,6 +31,7 @@ log.workspace = true parking_lot.workspace = true postage.workspace = true rand.workspace = true +schemars.workspace = true smol.workspace = true thiserror.workspace = true time.workspace = true diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 18a0f32ed6de61de2ce668207f194bf7b1a03e3c..9d24254b401141bddc8891a2ec45751ab844ee9d 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -15,19 +15,17 @@ use futures::{ TryStreamExt, }; use gpui::{ - actions, - platform::AppVersion, - serde_json::{self}, - AnyModelHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, - ModelHandle, Task, View, ViewContext, WeakViewHandle, + actions, platform::AppVersion, serde_json, AnyModelHandle, AnyWeakModelHandle, + AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, ModelHandle, Task, View, ViewContext, + WeakViewHandle, }; use lazy_static::lazy_static; use parking_lot::RwLock; use postage::watch; use rand::prelude::*; use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage}; -use serde::Deserialize; -use settings::Settings; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use std::{ any::TypeId, collections::HashMap, @@ -73,6 +71,8 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5); actions!(client, [SignIn, SignOut]); pub fn init(client: Arc, cx: &mut AppContext) { + settings::register_setting::(cx); + cx.add_global_action({ let client = client.clone(); move |_: &SignIn, cx| { @@ -326,6 +326,41 @@ impl Drop for PendingEntitySubscription { } } +#[derive(Copy, Clone)] +pub struct TelemetrySettings { + pub diagnostics: bool, + pub metrics: bool, +} + +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct TelemetrySettingsContent { + pub diagnostics: Option, + pub metrics: Option, +} + +impl settings::Setting for TelemetrySettings { + const KEY: Option<&'static str> = Some("telemetry"); + + type FileContent = TelemetrySettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &AppContext, + ) -> Self { + Self { + diagnostics: user_values + .first() + .and_then(|v| v.diagnostics) + .unwrap_or(default_value.diagnostics.unwrap()), + metrics: user_values + .first() + .and_then(|v| v.metrics) + .unwrap_or(default_value.metrics.unwrap()), + } + } +} + impl Client { pub fn new(http: Arc, cx: &AppContext) -> Arc { Arc::new(Self { @@ -447,9 +482,7 @@ impl Client { })); } Status::SignedOut | Status::UpgradeRequired => { - let telemetry_settings = cx.read(|cx| cx.global::().telemetry()); - self.telemetry - .set_authenticated_user_info(None, false, telemetry_settings); + cx.read(|cx| self.telemetry.set_authenticated_user_info(None, false, cx)); state._reconnect_task.take(); } _ => {} @@ -740,7 +773,7 @@ impl Client { self.telemetry().report_mixpanel_event( "read credentials from keychain", Default::default(), - cx.global::().telemetry(), + *settings::get_setting::(None, cx), ); }); } @@ -1033,7 +1066,9 @@ impl Client { let executor = cx.background(); let telemetry = self.telemetry.clone(); let http = self.http.clone(); - let metrics_enabled = cx.read(|cx| cx.global::().telemetry()); + + let telemetry_settings = + cx.read(|cx| *settings::get_setting::(None, cx)); executor.clone().spawn(async move { // Generate a pair of asymmetric encryption keys. The public key will be used by the @@ -1120,7 +1155,7 @@ impl Client { telemetry.report_mixpanel_event( "authenticate with browser", Default::default(), - metrics_enabled, + telemetry_settings, ); Ok(Credentials { diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 7151dcd7bb0606efc59a5dec68669a4d7273d651..5c8f208137da320626cb819370e0fa82e3c10a25 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -1,4 +1,4 @@ -use crate::{ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL}; +use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL}; use db::kvp::KEY_VALUE_STORE; use gpui::{ executor::Background, @@ -9,7 +9,6 @@ use lazy_static::lazy_static; use parking_lot::Mutex; use serde::Serialize; use serde_json::json; -use settings::TelemetrySettings; use std::{ io::Write, mem, @@ -241,9 +240,9 @@ impl Telemetry { self: &Arc, metrics_id: Option, is_staff: bool, - telemetry_settings: TelemetrySettings, + cx: &AppContext, ) { - if !telemetry_settings.metrics() { + if !settings::get_setting::(None, cx).metrics { return; } @@ -285,7 +284,7 @@ impl Telemetry { event: ClickhouseEvent, telemetry_settings: TelemetrySettings, ) { - if !telemetry_settings.metrics() { + if !telemetry_settings.metrics { return; } @@ -321,7 +320,7 @@ impl Telemetry { properties: Value, telemetry_settings: TelemetrySettings, ) { - if !telemetry_settings.metrics() { + if !telemetry_settings.metrics { return; } diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 6b3aa7e442876c94f1968925d7c48c6e9b155fdf..4c2721ffebbfd78e88c5d7fc656d1ab0ca1a1c79 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -5,7 +5,6 @@ use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task}; use postage::{sink::Sink, watch}; use rpc::proto::{RequestMessage, UsersResponse}; -use settings::Settings; use staff_mode::StaffMode; use std::sync::{Arc, Weak}; use util::http::HttpClient; @@ -144,11 +143,13 @@ impl UserStore { let fetch_metrics_id = client.request(proto::GetPrivateUserInfo {}).log_err(); let (user, info) = futures::join!(fetch_user, fetch_metrics_id); - client.telemetry.set_authenticated_user_info( - info.as_ref().map(|info| info.metrics_id.clone()), - info.as_ref().map(|info| info.staff).unwrap_or(false), - cx.read(|cx| cx.global::().telemetry()), - ); + cx.read(|cx| { + client.telemetry.set_authenticated_user_info( + info.as_ref().map(|info| info.metrics_id.clone()), + info.as_ref().map(|info| info.staff).unwrap_or(false), + cx, + ) + }); cx.update(|cx| { cx.update_default_global(|staff_mode: &mut StaffMode, _| { diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index fec48f1f34468d9cdbe9662acf6c29f65cd1841b..61c84bc517242550283a769e8bb6bb3fab3879c7 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -366,7 +366,7 @@ async fn configure_disabled_globs( fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { let show_copilot_suggestions = cx.global::().show_copilot_suggestions(None, None); - update_settings_file(fs, cx, move |file_contents| { + update_settings_file::(fs, cx, move |file_contents| { file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) }); } @@ -376,7 +376,7 @@ fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut App .global::() .show_copilot_suggestions(Some(&language), None); - update_settings_file(fs, cx, move |file_contents| { + update_settings_file::(fs, cx, move |file_contents| { file_contents.languages.insert( language, settings::EditorSettings { @@ -388,7 +388,7 @@ fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut App } fn hide_copilot(fs: Arc, cx: &mut AppContext) { - update_settings_file(fs, cx, move |file_contents| { + update_settings_file::(fs, cx, move |file_contents| { file_contents.features.copilot = Some(false) }); } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 964b98450baf521b0609b37833c694644184a0c3..abea2d8f3a9b8596dde3e697eed67cb88740809a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -22,7 +22,7 @@ pub mod test; use aho_corasick::AhoCorasick; use anyhow::{anyhow, Result}; use blink_manager::BlinkManager; -use client::ClickhouseEvent; +use client::{ClickhouseEvent, TelemetrySettings}; use clock::ReplicaId; use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque}; use copilot::Copilot; @@ -6873,7 +6873,7 @@ impl Editor { .untyped_user_settings() .get("vim_mode") == Some(&serde_json::Value::Bool(true)); - + let telemetry_settings = *settings::get_setting::(None, cx); let settings = cx.global::(); let extension = Path::new(file.file_name(cx)) @@ -6887,7 +6887,7 @@ impl Editor { _ => name, }, json!({ "File Extension": extension, "Vim Mode": vim_mode, "In Clickhouse": true }), - settings.telemetry(), + telemetry_settings, ); let event = ClickhouseEvent::Editor { file_extension: extension.map(ToString::to_string), @@ -6903,7 +6903,7 @@ impl Editor { .as_deref(), ), }; - telemetry.report_clickhouse_event(event, settings.telemetry()) + telemetry.report_clickhouse_event(event, telemetry_settings) } } diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 6d6d303a820332f4f95304f94836d7bee327aff1..ba9bc38b46f181c8cbbec2b8fb5f2c548db69947 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -27,7 +27,7 @@ glob.workspace = true json_comments = "0.2" lazy_static.workspace = true postage.workspace = true -schemars = "0.8" +schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 5284d3a69a86ec30de24f88e32762c19bda71ed2..82a4148c0dd57b61a6d8f91f6965f793394bae5e 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -58,8 +58,6 @@ pub struct Settings { pub language_overrides: HashMap, EditorSettings>, pub lsp: HashMap, LspSettings>, pub theme: Arc, - pub telemetry_defaults: TelemetrySettings, - pub telemetry_overrides: TelemetrySettings, pub base_keymap: BaseKeymap, } @@ -133,8 +131,6 @@ impl Setting for Settings { language_overrides: Default::default(), lsp: defaults.lsp.clone(), theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), - telemetry_defaults: defaults.telemetry, - telemetry_overrides: Default::default(), base_keymap: Default::default(), features: Features { copilot: defaults.features.copilot.unwrap(), @@ -260,30 +256,6 @@ impl BaseKeymap { } } -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -pub struct TelemetrySettings { - diagnostics: Option, - metrics: Option, -} - -impl TelemetrySettings { - pub fn metrics(&self) -> bool { - self.metrics.unwrap() - } - - pub fn diagnostics(&self) -> bool { - self.diagnostics.unwrap() - } - - pub fn set_metrics(&mut self, value: bool) { - self.metrics = Some(value); - } - - pub fn set_diagnostics(&mut self, value: bool) { - self.diagnostics = Some(value); - } -} - #[derive(Clone, Debug, Default)] pub struct CopilotSettings { pub disabled_globs: Vec, @@ -569,8 +541,6 @@ pub struct SettingsFileContent { #[serde(default)] pub theme: Option, #[serde(default)] - pub telemetry: TelemetrySettings, - #[serde(default)] pub base_keymap: Option, #[serde(default)] pub features: FeaturesContent, @@ -685,8 +655,6 @@ impl Settings { language_overrides: Default::default(), lsp: defaults.lsp.clone(), theme: themes.get(&defaults.theme.unwrap()).unwrap(), - telemetry_defaults: defaults.telemetry, - telemetry_overrides: Default::default(), base_keymap: Default::default(), features: Features { copilot: defaults.features.copilot.unwrap(), @@ -758,7 +726,6 @@ impl Settings { self.terminal_overrides.copy_on_select = data.terminal.copy_on_select; self.terminal_overrides = data.terminal; self.language_overrides = data.languages; - self.telemetry_overrides = data.telemetry; self.lsp = data.lsp; } @@ -869,27 +836,6 @@ impl Settings { }) } - pub fn telemetry(&self) -> TelemetrySettings { - TelemetrySettings { - diagnostics: Some(self.telemetry_diagnostics()), - metrics: Some(self.telemetry_metrics()), - } - } - - pub fn telemetry_diagnostics(&self) -> bool { - self.telemetry_overrides - .diagnostics - .or(self.telemetry_defaults.diagnostics) - .expect("missing default") - } - - pub fn telemetry_metrics(&self) -> bool { - self.telemetry_overrides - .metrics - .or(self.telemetry_defaults.metrics) - .expect("missing default") - } - fn terminal_setting(&self, f: F) -> R where F: Fn(&TerminalSettings) -> Option, @@ -963,11 +909,6 @@ impl Settings { language_overrides: Default::default(), lsp: Default::default(), theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default), - telemetry_defaults: TelemetrySettings { - diagnostics: Some(true), - metrics: Some(true), - }, - telemetry_overrides: Default::default(), base_keymap: Default::default(), features: Features { copilot: true }, } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index a07560307f97e59bd1e47417d4facd2fcac0e9f9..990ccf02490f4cb6072d4d40d096386cf5cffc8b 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,6 +1,6 @@ use crate::{ settings_store::parse_json_with_comments, settings_store::SettingsStore, KeymapFileContent, - Setting, Settings, SettingsFileContent, DEFAULT_SETTINGS_ASSET_PATH, + Setting, Settings, DEFAULT_SETTINGS_ASSET_PATH, }; use anyhow::Result; use assets::Assets; @@ -158,10 +158,10 @@ async fn load_settings(fs: &Arc) -> Result { } } -pub fn update_settings_file( +pub fn update_settings_file( fs: Arc, cx: &mut AppContext, - update: impl 'static + Send + FnOnce(&mut SettingsFileContent), + update: impl 'static + Send + FnOnce(&mut T::FileContent), ) { cx.spawn(|cx| async move { let old_text = cx @@ -172,10 +172,7 @@ pub fn update_settings_file( }) .await?; - let edits = cx.read(|cx| { - cx.global::() - .update::(&old_text, update) - }); + let edits = cx.read(|cx| cx.global::().update::(&old_text, update)); let mut new_text = old_text; for (range, replacement) in edits.into_iter().rev() { diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index a35e546891ed218c2228cf9a3882d74aa9d8cdb3..27c5a9ef4ed66444d7b463bf50c807d29b2e65e9 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -129,7 +129,7 @@ impl PickerDelegate for ThemeSelectorDelegate { self.selection_completed = true; let theme_name = cx.global::().theme.meta.name.clone(); - update_settings_file(self.fs.clone(), cx, |settings_content| { + update_settings_file::(self.fs.clone(), cx, |settings_content| { settings_content.theme = Some(theme_name); }); diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index 696a5c5e4e2d7ac48837b3e426b1f0e5daa3aad1..82cd4ca35a6cf86bac7ea40bc820a720238042b7 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -13,6 +13,7 @@ test-support = [] [dependencies] anyhow.workspace = true log.workspace = true +client = { path = "../client" } editor = { path = "../editor" } fs = { path = "../fs" } fuzzy = { path = "../fuzzy" } diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index c0e9e0a38d0c30e916e6b3c5ef82711e3d0f96b0..24600d5b82424d6cf565f060828e02bcdb8f7b4e 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -122,7 +122,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { fn confirm(&mut self, cx: &mut ViewContext) { if let Some(selection) = self.matches.get(self.selected_index) { let base_keymap = BaseKeymap::from_names(&selection.string); - update_settings_file(self.fs.clone(), cx, move |settings| { + update_settings_file::(self.fs.clone(), cx, move |settings| { settings.base_keymap = Some(base_keymap) }); } diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index c2e65dc524017074eab3fe2cac7612c6c94427a9..6b8fe7312dd03179c9e98701be93bab365bcb5cc 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -2,6 +2,7 @@ mod base_keymap_picker; use std::{borrow::Cow, sync::Arc}; +use client::TelemetrySettings; use db::kvp::KEY_VALUE_STORE; use gpui::{ elements::{Flex, Label, ParentElement}, @@ -63,10 +64,7 @@ impl View for WelcomePage { let width = theme.welcome.page_width; - let (diagnostics, metrics) = { - let telemetry = settings.telemetry(); - (telemetry.diagnostics(), telemetry.metrics()) - }; + let telemetry_settings = *settings::get_setting::(None, cx); enum Metrics {} enum Diagnostics {} @@ -166,15 +164,17 @@ impl View for WelcomePage { .with_style(theme.welcome.usage_note.container), ), &theme.welcome.checkbox, - metrics, + telemetry_settings.metrics, 0, cx, |this, checked, cx| { if let Some(workspace) = this.workspace.upgrade(cx) { let fs = workspace.read(cx).app_state().fs.clone(); - update_settings_file(fs, cx, move |file| { - file.telemetry.set_metrics(checked) - }) + update_settings_file::( + fs, + cx, + move |setting| setting.metrics = Some(checked), + ) } }, ) @@ -185,15 +185,17 @@ impl View for WelcomePage { theme::ui::checkbox::( "Send crash reports", &theme.welcome.checkbox, - diagnostics, + telemetry_settings.diagnostics, 0, cx, |this, checked, cx| { if let Some(workspace) = this.workspace.upgrade(cx) { let fs = workspace.read(cx).app_state().fs.clone(); - update_settings_file(fs, cx, move |file| { - file.telemetry.set_diagnostics(checked) - }) + update_settings_file::( + fs, + cx, + move |setting| setting.diagnostics = Some(checked), + ) } }, ) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 3d43109e6b59ec2d2027fe93ac6d635cb2784774..58c68c56d32819b538ba91dbdd957eb1766c3503 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -8,7 +8,7 @@ use cli::{ ipc::{self, IpcSender}, CliRequest, CliResponse, IpcHandshake, }; -use client::{self, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN}; +use client::{self, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN}; use db::kvp::KEY_VALUE_STORE; use editor::Editor; use futures::{ @@ -187,7 +187,7 @@ fn main() { client.telemetry().report_mixpanel_event( "start app", Default::default(), - cx.global::().telemetry(), + *settings::get_setting::(None, cx), ); let app_state = Arc::new(AppState { @@ -407,7 +407,7 @@ fn init_panic_hook(app_version: String) { } fn upload_previous_panics(http: Arc, cx: &mut AppContext) { - let diagnostics_telemetry = cx.global::().telemetry_diagnostics(); + let telemetry_settings = *settings::get_setting::(None, cx); cx.background() .spawn({ @@ -437,7 +437,7 @@ fn upload_previous_panics(http: Arc, cx: &mut AppContext) { continue; }; - if diagnostics_telemetry { + if telemetry_settings.diagnostics { let panic_data_text = smol::fs::read_to_string(&child_path) .await .context("error reading panic file")?; From 68867fe2e117ff0a119b6f57c43864950d5ddcb7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 May 2023 12:48:09 -0700 Subject: [PATCH 09/25] Define journal settings in journal crate --- Cargo.lock | 2 + crates/journal/Cargo.toml | 5 ++- crates/journal/src/journal.rs | 68 ++++++++++++++++++++++++++------- crates/settings/src/settings.rs | 39 ------------------- 4 files changed, 61 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index baba47b216468567d8fc0a838a447b16c4c14855..ff82c5e67b36efeecfef8447404cdf84d7d4c2ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3336,6 +3336,8 @@ dependencies = [ "editor", "gpui", "log", + "schemars", + "serde", "settings", "shellexpand", "util", diff --git a/crates/journal/Cargo.toml b/crates/journal/Cargo.toml index b88e3e093a7df054ccc261e64ca6b5ff17e26e42..c1d9bde89e19bccc1404b8a292543f29185eb87d 100644 --- a/crates/journal/Cargo.toml +++ b/crates/journal/Cargo.toml @@ -13,9 +13,12 @@ editor = { path = "../editor" } gpui = { path = "../gpui" } util = { path = "../util" } workspace = { path = "../workspace" } +settings = { path = "../settings" } + anyhow.workspace = true chrono = "0.4" dirs = "4.0" +serde.workspace = true +schemars.workspace = true log.workspace = true -settings = { path = "../settings" } shellexpand = "2.1.0" diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 4b9622ece9c38089fd1c6c2b83f138279eef94ac..938cd829223f10258b996e5e31e694e8f94c0340 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -1,7 +1,8 @@ use chrono::{Datelike, Local, NaiveTime, Timelike}; use editor::{scroll::autoscroll::Autoscroll, Editor}; use gpui::{actions, AppContext}; -use settings::{HourFormat, Settings}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use std::{ fs::OpenOptions, path::{Path, PathBuf}, @@ -11,13 +12,61 @@ use workspace::AppState; actions!(journal, [NewJournalEntry]); +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct JournalSettings { + pub path: Option, + pub hour_format: Option, +} + +impl Default for JournalSettings { + fn default() -> Self { + Self { + path: Some("~".into()), + hour_format: Some(Default::default()), + } + } +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum HourFormat { + #[default] + Hour12, + Hour24, +} + +impl settings::Setting for JournalSettings { + const KEY: Option<&'static str> = Some("journal"); + + type FileContent = Self; + + fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Self { + Self { + path: Some( + user_values + .first() + .and_then(|s| s.path.clone()) + .unwrap_or(default_value.path.clone().unwrap()), + ), + hour_format: Some( + user_values + .first() + .and_then(|s| s.hour_format.clone()) + .unwrap_or(default_value.hour_format.clone().unwrap()), + ), + } + } +} + pub fn init(app_state: Arc, cx: &mut AppContext) { + settings::register_setting::(cx); + cx.add_global_action(move |_: &NewJournalEntry, cx| new_journal_entry(app_state.clone(), cx)); } pub fn new_journal_entry(app_state: Arc, cx: &mut AppContext) { - let settings = cx.global::(); - let journal_dir = match journal_dir(&settings) { + let settings = settings::get_setting::(None, cx); + let journal_dir = match journal_dir(settings.path.as_ref().unwrap()) { Some(journal_dir) => journal_dir, None => { log::error!("Can't determine journal directory"); @@ -31,8 +80,7 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut AppContext) { .join(format!("{:02}", now.month())); let entry_path = month_dir.join(format!("{:02}.md", now.day())); let now = now.time(); - let hour_format = &settings.journal_overrides.hour_format; - let entry_heading = heading_entry(now, &hour_format); + let entry_heading = heading_entry(now, &settings.hour_format); let create_entry = cx.background().spawn(async move { std::fs::create_dir_all(month_dir)?; @@ -76,14 +124,8 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut AppContext) { .detach_and_log_err(cx); } -fn journal_dir(settings: &Settings) -> Option { - let journal_dir = settings - .journal_overrides - .path - .as_ref() - .unwrap_or(settings.journal_defaults.path.as_ref()?); - - let expanded_journal_dir = shellexpand::full(&journal_dir) //TODO handle this better +fn journal_dir(path: &str) -> Option { + let expanded_journal_dir = shellexpand::full(path) //TODO handle this better .ok() .map(|dir| Path::new(&dir.to_string()).to_path_buf().join("journal")); diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 82a4148c0dd57b61a6d8f91f6965f793394bae5e..b1e716abb86c34fe15dccaa75adfbde136b37c92 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -50,8 +50,6 @@ pub struct Settings { pub git: GitSettings, pub git_overrides: GitSettings, pub copilot: CopilotSettings, - pub journal_defaults: JournalSettings, - pub journal_overrides: JournalSettings, pub terminal_defaults: TerminalSettings, pub terminal_overrides: TerminalSettings, pub language_defaults: HashMap, EditorSettings>, @@ -123,8 +121,6 @@ impl Setting for Settings { }, git: defaults.git.unwrap(), git_overrides: Default::default(), - journal_defaults: defaults.journal.clone(), - journal_overrides: Default::default(), terminal_defaults: defaults.terminal.clone(), terminal_overrides: Default::default(), language_defaults: defaults.languages.clone(), @@ -336,34 +332,6 @@ pub enum Autosave { OnWindowChange, } -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct JournalSettings { - pub path: Option, - pub hour_format: Option, -} - -impl Default for JournalSettings { - fn default() -> Self { - Self { - path: Some("~".into()), - hour_format: Some(Default::default()), - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum HourFormat { - Hour12, - Hour24, -} - -impl Default for HourFormat { - fn default() -> Self { - Self::Hour12 - } -} - #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct TerminalSettings { pub shell: Option, @@ -528,8 +496,6 @@ pub struct SettingsFileContent { #[serde(flatten)] pub editor: EditorSettings, #[serde(default)] - pub journal: JournalSettings, - #[serde(default)] pub terminal: TerminalSettings, #[serde(default)] pub git: Option, @@ -647,8 +613,6 @@ impl Settings { }, git: defaults.git.unwrap(), git_overrides: Default::default(), - journal_defaults: defaults.journal, - journal_overrides: Default::default(), terminal_defaults: defaults.terminal, terminal_overrides: Default::default(), language_defaults: defaults.languages, @@ -721,7 +685,6 @@ impl Settings { } self.editor_overrides = data.editor; self.git_overrides = data.git.unwrap_or_default(); - self.journal_overrides = data.journal; self.terminal_defaults.font_size = data.terminal.font_size; self.terminal_overrides.copy_on_select = data.terminal.copy_on_select; self.terminal_overrides = data.terminal; @@ -899,8 +862,6 @@ impl Settings { }, editor_overrides: Default::default(), copilot: Default::default(), - journal_defaults: Default::default(), - journal_overrides: Default::default(), terminal_defaults: Default::default(), terminal_overrides: Default::default(), git: Default::default(), From cee7edabf94dc3cf0ffa55e9895613224eb2a6aa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 May 2023 16:39:59 -0700 Subject: [PATCH 10/25] Ensure the SettingsStore global is added in tests --- crates/client/src/client.rs | 25 +++++++++++-------- crates/collab/src/tests.rs | 5 +++- .../src/tests/randomized_integration_tests.rs | 7 ++++-- .../src/test/editor_lsp_test_context.rs | 4 +-- crates/gpui/src/app.rs | 11 +++++++- crates/settings/src/settings_store.rs | 23 +++++++++++------ crates/vim/src/test/vim_test_context.rs | 5 ++-- crates/workspace/src/workspace.rs | 7 ++++-- crates/zed/src/main.rs | 2 +- crates/zed/src/zed.rs | 4 +-- 10 files changed, 61 insertions(+), 32 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 9d24254b401141bddc8891a2ec45751ab844ee9d..bcf91520513cb3781a771a4e9b76d944efc7b184 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -70,27 +70,30 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5); actions!(client, [SignIn, SignOut]); -pub fn init(client: Arc, cx: &mut AppContext) { +pub fn init(client: &Arc, cx: &mut AppContext) { + let client = Arc::downgrade(client); settings::register_setting::(cx); cx.add_global_action({ let client = client.clone(); move |_: &SignIn, cx| { - let client = client.clone(); - cx.spawn( - |cx| async move { client.authenticate_and_connect(true, &cx).log_err().await }, - ) - .detach(); + if let Some(client) = client.upgrade() { + cx.spawn( + |cx| async move { client.authenticate_and_connect(true, &cx).log_err().await }, + ) + .detach(); + } } }); cx.add_global_action({ let client = client.clone(); move |_: &SignOut, cx| { - let client = client.clone(); - cx.spawn(|cx| async move { - client.disconnect(&cx); - }) - .detach(); + if let Some(client) = client.upgrade() { + cx.spawn(|cx| async move { + client.disconnect(&cx); + }) + .detach(); + } } }); } diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 64768d1a4795679cfd54a8f7231d2c79488f01f7..50fb9658fe9b8e727a3176cf1c6d60d703d95b7b 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -19,7 +19,7 @@ use gpui::{ use language::LanguageRegistry; use parking_lot::Mutex; use project::{Project, WorktreeId}; -use settings::Settings; +use settings::{Settings, SettingsStore}; use std::{ cell::{Ref, RefCell, RefMut}, env, @@ -102,6 +102,7 @@ impl TestServer { async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient { cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); cx.set_global(Settings::test(cx)); }); @@ -185,6 +186,8 @@ impl TestServer { }) }); + cx.update(|cx| client::init(&client, cx)); + let fs = FakeFs::new(cx.background()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); let app_state = Arc::new(workspace::AppState { diff --git a/crates/collab/src/tests/randomized_integration_tests.rs b/crates/collab/src/tests/randomized_integration_tests.rs index c4326be101306997e6b9def9faad631a500b5690..3a87ced2e1976af067ce28a77893636429b5e1d4 100644 --- a/crates/collab/src/tests/randomized_integration_tests.rs +++ b/crates/collab/src/tests/randomized_integration_tests.rs @@ -20,7 +20,7 @@ use rand::{ prelude::*, }; use serde::{Deserialize, Serialize}; -use settings::Settings; +use settings::{Settings, SettingsStore}; use std::{ env, ops::Range, @@ -148,8 +148,11 @@ async fn test_random_collaboration( for (client, mut cx) in clients { cx.update(|cx| { + let store = cx.remove_global::(); + let settings = cx.remove_global::(); cx.clear_globals(); - cx.set_global(Settings::test(cx)); + cx.set_global(store); + cx.set_global(settings); drop(client); }); } diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index fe9a7909b8538bd7df4d91a97ff058d19cd1b3da..e268e2a0ceb32ca735ea77547298372c0334425e 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -34,13 +34,13 @@ impl<'a> EditorLspTestContext<'a> { ) -> EditorLspTestContext<'a> { use json::json; + let app_state = cx.update(AppState::test); + cx.update(|cx| { crate::init(cx); pane::init(cx); }); - let app_state = cx.update(AppState::test); - let file_name = format!( "file.{}", language diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index d5a7a2f3ab158fc3b91ece1a558231627026399d..5def0bed9d37ec24360cb2fe2f5cba3c6ed2bbcf 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1174,7 +1174,7 @@ impl AppContext { this.notify_global(type_id); result } else { - panic!("No global added for {}", std::any::type_name::()); + panic!("no global added for {}", std::any::type_name::()); } } @@ -1182,6 +1182,15 @@ impl AppContext { self.globals.clear(); } + pub fn remove_global(&mut self) -> T { + *self + .globals + .remove(&TypeId::of::()) + .unwrap_or_else(|| panic!("no global added for {}", std::any::type_name::())) + .downcast() + .unwrap() + } + pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index d986ea06832dfc4e5226f7fdcb8f8b047e2ebd7a..59f7bee10faf164730f7005a9463ca92d54abc8c 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -122,11 +122,11 @@ impl SettingsStore { /// Add a new type of setting to the store. pub fn register_setting(&mut self, cx: &AppContext) { let setting_type_id = TypeId::of::(); - let entry = self.setting_values.entry(setting_type_id); if matches!(entry, hash_map::Entry::Occupied(_)) { - panic!("duplicate setting type: {}", type_name::()); + return; } + let setting_value = entry.or_insert(Box::new(SettingValue:: { global_value: None, local_values: Vec::new(), @@ -142,6 +142,7 @@ impl SettingsStore { user_values_stack = vec![user_value]; } } + if let Some(default_deserialized_value) = default_settings.typed.get(&setting_type_id) { setting_value.set_global_value(setting_value.load_setting( default_deserialized_value, @@ -159,7 +160,7 @@ impl SettingsStore { pub fn get(&self, path: Option<&Path>) -> &T { self.setting_values .get(&TypeId::of::()) - .expect("unregistered setting type") + .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) .value_for_path(path) .downcast_ref::() .expect("no default value for setting type") @@ -175,6 +176,14 @@ impl SettingsStore { .map_or(&serde_json::Value::Null, |s| &s.untyped) } + #[cfg(any(test, feature = "test-support"))] + pub fn test(cx: &AppContext) -> Self { + let mut this = Self::default(); + this.set_default_settings(&crate::test_settings(), cx) + .unwrap(); + this + } + /// Override the global value for a particular setting. /// /// This is only for tests. Normally, settings are only loaded from @@ -183,7 +192,7 @@ impl SettingsStore { pub fn replace_value(&mut self, value: T) { self.setting_values .get_mut(&TypeId::of::()) - .expect("unregistered setting type") + .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) .set_global_value(Box::new(value)) } @@ -268,7 +277,7 @@ impl SettingsStore { pub fn set_default_settings( &mut self, default_settings_content: &str, - cx: &mut AppContext, + cx: &AppContext, ) -> Result<()> { let deserialized_setting_map = self.load_setting_map(default_settings_content)?; if deserialized_setting_map.typed.len() != self.setting_values.len() { @@ -290,7 +299,7 @@ impl SettingsStore { pub fn set_user_settings( &mut self, user_settings_content: &str, - cx: &mut AppContext, + cx: &AppContext, ) -> Result<()> { let user_settings = self.load_setting_map(user_settings_content)?; let old_user_settings = @@ -304,7 +313,7 @@ impl SettingsStore { &mut self, path: Arc, settings_content: Option<&str>, - cx: &mut AppContext, + cx: &AppContext, ) -> Result<()> { let removed_map = if let Some(settings_content) = settings_content { self.local_deserialized_settings diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index 69e432079a8034a14edf062a41b4368a3337c84e..b426cea717248e5cf9ec020961944b28d0e07196 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -18,11 +18,12 @@ impl<'a> VimTestContext<'a> { pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> { let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await; cx.update(|cx| { + search::init(cx); + crate::init(cx); + cx.update_global(|store: &mut SettingsStore, _| { store.replace_value(VimModeSetting(enabled)); }); - search::init(cx); - crate::init(cx); settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap(); }); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 8a62a28c113c59f9b2490860fc3101f3a1cec76f..0e3ce2a7241efefa7e5000b4685e2ee2e66b353c 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -369,8 +369,8 @@ pub struct AppState { impl AppState { #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut AppContext) -> Arc { - let settings = Settings::test(cx); - cx.set_global(settings); + cx.set_global(settings::SettingsStore::test(cx)); + cx.set_global(Settings::test(cx)); let fs = fs::FakeFs::new(cx.background().clone()); let languages = Arc::new(LanguageRegistry::test()); @@ -378,6 +378,9 @@ impl AppState { let client = Client::new(http_client.clone(), cx); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); let themes = ThemeRegistry::new((), cx.font_cache().clone()); + + client::init(&client, cx); + Arc::new(Self { client, themes, diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 58c68c56d32819b538ba91dbdd957eb1766c3503..465219b8a68aff2db3e9bbd5d3e8d9fb7c254a82 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -158,7 +158,7 @@ fn main() { context_menu::init(cx); project::Project::init(&client); - client::init(client.clone(), cx); + client::init(&client, cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 26d2b50e0d211bf73a346e03e84defdee9e33ada..e6322ad8432a2fbb4215bfef092ab031f7d84dd9 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1283,9 +1283,7 @@ mod tests { #[gpui::test] async fn test_pane_actions(cx: &mut TestAppContext) { - init(cx); - - let app_state = cx.update(AppState::test); + let app_state = init(cx); app_state .fs .as_fake() From bc5b78198a74a4dcba5dbea20a7944c0f69a2bc8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 10 May 2023 17:43:10 -0700 Subject: [PATCH 11/25] Define terminal settings in terminal crate --- Cargo.lock | 1 + crates/diagnostics/src/diagnostics.rs | 4 +- crates/editor/src/editor.rs | 6 +- crates/project/src/terminals.rs | 22 +-- crates/settings/src/font_size.rs | 19 +++ crates/settings/src/settings.rs | 149 +----------------- crates/terminal/Cargo.toml | 2 + crates/terminal/src/terminal.rs | 118 ++++++++++++-- crates/terminal_view/src/terminal_element.rs | 76 ++++----- crates/terminal_view/src/terminal_view.rs | 44 ++---- crates/theme_testbench/src/theme_testbench.rs | 2 +- crates/zed/src/main.rs | 12 +- crates/zed/src/zed.rs | 26 +-- 13 files changed, 205 insertions(+), 276 deletions(-) create mode 100644 crates/settings/src/font_size.rs diff --git a/Cargo.lock b/Cargo.lock index ff82c5e67b36efeecfef8447404cdf84d7d4c2ba..748ec5ef49a8fa187019851f6ae07f3cd950c0ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6748,6 +6748,7 @@ dependencies = [ "ordered-float", "procinfo", "rand 0.8.5", + "schemars", "serde", "serde_derive", "settings", diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index d82c653a0924dfd8fc12e3f795f99be7a975756d..b9db439e49e69baf397c5955f4a327a805998a3e 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -682,7 +682,9 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let settings = cx.global::(); let theme = &settings.theme.editor; let style = theme.diagnostic_header.clone(); - let font_size = (style.text_scale_factor * settings.buffer_font_size).round(); + let font_size = (style.text_scale_factor + * settings::font_size_for_setting(settings.buffer_font_size, cx)) + .round(); let icon_width = cx.em_width * style.icon_width_factor; let icon = if diagnostic.severity == DiagnosticSeverity::ERROR { Svg::new("icons/circle_x_mark_12.svg") diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index abea2d8f3a9b8596dde3e697eed67cb88740809a..abd8885d30f190f6b1b37313f1ae62b42b53a48a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7388,7 +7388,7 @@ fn build_style( let font_id = font_cache .select_font(font_family_id, &font_properties) .unwrap(); - let font_size = settings.buffer_font_size; + let font_size = settings::font_size_for_setting(settings.buffer_font_size, cx); EditorStyle { text: TextStyle { color: settings.theme.editor.text_color, @@ -7561,7 +7561,9 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend let settings = cx.global::(); let theme = &settings.theme.editor; let style = diagnostic_style(diagnostic.severity, is_valid, theme); - let font_size = (style.text_scale_factor * settings.buffer_font_size).round(); + let font_size = (style.text_scale_factor + * settings::font_size_for_setting(settings.buffer_font_size, cx)) + .round(); Flex::column() .with_children(highlighted_lines.iter().map(|(line, highlights)| { Label::new( diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 0f3092ca41d757437963a0658c502c87f913d262..26f2215bea40b9ed2717d2bbe0de719dc693e7b8 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -1,10 +1,7 @@ -use std::path::PathBuf; - -use gpui::{ModelContext, ModelHandle, WeakModelHandle}; -use settings::Settings; -use terminal::{Terminal, TerminalBuilder}; - use crate::Project; +use gpui::{ModelContext, ModelHandle, WeakModelHandle}; +use std::path::PathBuf; +use terminal::{Terminal, TerminalBuilder, TerminalSettings}; pub struct Terminals { pub(crate) local_handles: Vec>, @@ -22,17 +19,14 @@ impl Project { "creating terminals as a guest is not supported yet" )); } else { - let settings = cx.global::(); - let shell = settings.terminal_shell(); - let envs = settings.terminal_env(); - let scroll = settings.terminal_scroll(); + let settings = settings::get_setting::(None, cx); let terminal = TerminalBuilder::new( working_directory.clone(), - shell, - envs, - settings.terminal_overrides.blinking.clone(), - scroll, + settings.shell.clone(), + settings.env.clone(), + Some(settings.blinking.clone()), + settings.alternate_scroll, window_id, ) .map(|builder| { diff --git a/crates/settings/src/font_size.rs b/crates/settings/src/font_size.rs new file mode 100644 index 0000000000000000000000000000000000000000..79abfeb5504ebf8e7262cbe76b4c28f869ead9a2 --- /dev/null +++ b/crates/settings/src/font_size.rs @@ -0,0 +1,19 @@ +use gpui::AppContext; + +#[derive(Default)] +pub struct FontSizeDelta(pub f32); + +pub fn adjust_font_size_delta(cx: &mut AppContext, f: fn(&mut f32, cx: &mut AppContext)) { + cx.update_default_global::(|size, cx| { + f(&mut size.0, cx); + }); + cx.refresh_windows(); +} + +pub fn font_size_for_setting(size: f32, cx: &AppContext) -> f32 { + if cx.has_global::() { + size + cx.global::().0 + } else { + size + } +} diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index b1e716abb86c34fe15dccaa75adfbde136b37c92..c476024d629fa65f4f86cf677665f1b2874476b7 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,3 +1,4 @@ +mod font_size; mod keymap_file; mod settings_file; mod settings_store; @@ -22,6 +23,7 @@ use std::{borrow::Cow, collections::HashMap, num::NonZeroU32, path::Path, str, s use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; +pub use font_size::{adjust_font_size_delta, font_size_for_setting}; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; pub use settings_file::*; pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; @@ -35,7 +37,6 @@ pub struct Settings { pub buffer_font_family_name: String, pub buffer_font_features: fonts::Features, pub buffer_font_family: FamilyId, - pub default_buffer_font_size: f32, pub buffer_font_size: f32, pub active_pane_magnification: f32, pub cursor_blink: bool, @@ -50,8 +51,6 @@ pub struct Settings { pub git: GitSettings, pub git_overrides: GitSettings, pub copilot: CopilotSettings, - pub terminal_defaults: TerminalSettings, - pub terminal_overrides: TerminalSettings, pub language_defaults: HashMap, EditorSettings>, pub language_overrides: HashMap, EditorSettings>, pub lsp: HashMap, LspSettings>, @@ -84,7 +83,6 @@ impl Setting for Settings { buffer_font_features, buffer_font_size: defaults.buffer_font_size.unwrap(), active_pane_magnification: defaults.active_pane_magnification.unwrap(), - default_buffer_font_size: defaults.buffer_font_size.unwrap(), confirm_quit: defaults.confirm_quit.unwrap(), cursor_blink: defaults.cursor_blink.unwrap(), hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), @@ -121,8 +119,6 @@ impl Setting for Settings { }, git: defaults.git.unwrap(), git_overrides: Default::default(), - terminal_defaults: defaults.terminal.clone(), - terminal_overrides: Default::default(), language_defaults: defaults.languages.clone(), language_overrides: Default::default(), lsp: defaults.lsp.clone(), @@ -332,104 +328,6 @@ pub enum Autosave { OnWindowChange, } -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -pub struct TerminalSettings { - pub shell: Option, - pub working_directory: Option, - pub font_size: Option, - pub font_family: Option, - pub line_height: Option, - pub font_features: Option, - pub env: Option>, - pub blinking: Option, - pub alternate_scroll: Option, - pub option_as_meta: Option, - pub copy_on_select: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)] -#[serde(rename_all = "snake_case")] -pub enum TerminalLineHeight { - #[default] - Comfortable, - Standard, - Custom(f32), -} - -impl TerminalLineHeight { - fn value(&self) -> f32 { - match self { - TerminalLineHeight::Comfortable => 1.618, - TerminalLineHeight::Standard => 1.3, - TerminalLineHeight::Custom(line_height) => *line_height, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum TerminalBlink { - Off, - TerminalControlled, - On, -} - -impl Default for TerminalBlink { - fn default() -> Self { - TerminalBlink::TerminalControlled - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum Shell { - System, - Program(String), - WithArguments { program: String, args: Vec }, -} - -impl Default for Shell { - fn default() -> Self { - Shell::System - } -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum AlternateScroll { - On, - Off, -} - -impl Default for AlternateScroll { - fn default() -> Self { - AlternateScroll::On - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum WorkingDirectory { - CurrentProjectDirectory, - FirstProjectDirectory, - AlwaysHome, - Always { directory: String }, -} - -impl Default for WorkingDirectory { - fn default() -> Self { - Self::CurrentProjectDirectory - } -} - -impl TerminalSettings { - fn line_height(&self) -> Option { - self.line_height - .to_owned() - .map(|line_height| line_height.value()) - } -} - #[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum DockAnchor { @@ -496,8 +394,6 @@ pub struct SettingsFileContent { #[serde(flatten)] pub editor: EditorSettings, #[serde(default)] - pub terminal: TerminalSettings, - #[serde(default)] pub git: Option, #[serde(default)] #[serde(alias = "language_overrides")] @@ -575,7 +471,6 @@ impl Settings { buffer_font_features, buffer_font_size: defaults.buffer_font_size.unwrap(), active_pane_magnification: defaults.active_pane_magnification.unwrap(), - default_buffer_font_size: defaults.buffer_font_size.unwrap(), confirm_quit: defaults.confirm_quit.unwrap(), cursor_blink: defaults.cursor_blink.unwrap(), hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), @@ -613,8 +508,6 @@ impl Settings { }, git: defaults.git.unwrap(), git_overrides: Default::default(), - terminal_defaults: defaults.terminal, - terminal_overrides: Default::default(), language_defaults: defaults.languages, language_overrides: Default::default(), lsp: defaults.lsp.clone(), @@ -627,7 +520,7 @@ impl Settings { } // Fill out the overrride and etc. settings from the user's settings.json - pub fn set_user_settings( + fn set_user_settings( &mut self, data: SettingsFileContent, theme_registry: &ThemeRegistry, @@ -662,7 +555,6 @@ impl Settings { &mut self.active_pane_magnification, data.active_pane_magnification, ); - merge(&mut self.default_buffer_font_size, data.buffer_font_size); merge(&mut self.cursor_blink, data.cursor_blink); merge(&mut self.confirm_quit, data.confirm_quit); merge(&mut self.hover_popover_enabled, data.hover_popover_enabled); @@ -685,9 +577,6 @@ impl Settings { } self.editor_overrides = data.editor; self.git_overrides = data.git.unwrap_or_default(); - self.terminal_defaults.font_size = data.terminal.font_size; - self.terminal_overrides.copy_on_select = data.terminal.copy_on_select; - self.terminal_overrides = data.terminal; self.language_overrides = data.languages; self.lsp = data.lsp; } @@ -799,35 +688,6 @@ impl Settings { }) } - fn terminal_setting(&self, f: F) -> R - where - F: Fn(&TerminalSettings) -> Option, - { - None.or_else(|| f(&self.terminal_overrides)) - .or_else(|| f(&self.terminal_defaults)) - .expect("missing default") - } - - pub fn terminal_line_height(&self) -> f32 { - self.terminal_setting(|terminal_setting| terminal_setting.line_height()) - } - - pub fn terminal_scroll(&self) -> AlternateScroll { - self.terminal_setting(|terminal_setting| terminal_setting.alternate_scroll.to_owned()) - } - - pub fn terminal_shell(&self) -> Shell { - self.terminal_setting(|terminal_setting| terminal_setting.shell.to_owned()) - } - - pub fn terminal_env(&self) -> HashMap { - self.terminal_setting(|terminal_setting| terminal_setting.env.to_owned()) - } - - pub fn terminal_strategy(&self) -> WorkingDirectory { - self.terminal_setting(|terminal_setting| terminal_setting.working_directory.to_owned()) - } - #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &gpui::AppContext) -> Settings { Settings { @@ -839,7 +699,6 @@ impl Settings { .unwrap(), buffer_font_size: 14., active_pane_magnification: 1., - default_buffer_font_size: 14., confirm_quit: false, cursor_blink: true, hover_popover_enabled: true, @@ -862,8 +721,6 @@ impl Settings { }, editor_overrides: Default::default(), copilot: Default::default(), - terminal_defaults: Default::default(), - terminal_overrides: Default::default(), git: Default::default(), git_overrides: Default::default(), language_defaults: Default::default(), diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 725b102c04591daa5b7ed2abde3f73cc7fb0e723..a2902234c5fc2909b9974de0beba72b2bc973d64 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -15,6 +15,7 @@ settings = { path = "../settings" } db = { path = "../db" } theme = { path = "../theme" } util = { path = "../util" } + alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "a51dbe25d67e84d6ed4261e640d3954fbdd9be45" } procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false } smallvec.workspace = true @@ -27,6 +28,7 @@ dirs = "4.0.0" shellexpand = "2.1.0" libc = "0.2" anyhow.workspace = true +schemars.workspace = true thiserror.workspace = true lazy_static.workspace = true serde.workspace = true diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 25852875c3b75e8fdc468dd3ec27ed1d6959fefd..bf4fc46ee3754e20fcbe5f54de09e63293e7d6f8 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -31,8 +31,9 @@ use mappings::mouse::{ }; use procinfo::LocalProcessInfo; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{AlternateScroll, Settings, Shell, TerminalBlink}; +use settings::Settings; use util::truncate_and_trailoff; use std::{ @@ -48,11 +49,12 @@ use std::{ use thiserror::Error; use gpui::{ + fonts, geometry::vector::{vec2f, Vector2F}, keymap_matcher::Keystroke, platform::{MouseButton, MouseMovedEvent, TouchPhase}, scene::{MouseDown, MouseDrag, MouseScrollWheel, MouseUp}, - ClipboardItem, Entity, ModelContext, Task, + AppContext, ClipboardItem, Entity, ModelContext, Task, }; use crate::mappings::{ @@ -114,6 +116,105 @@ impl EventListener for ZedListener { } } +pub fn init(cx: &mut AppContext) { + settings::register_setting::(cx); +} + +#[derive(Deserialize)] +pub struct TerminalSettings { + pub shell: Shell, + pub working_directory: WorkingDirectory, + pub font_size: Option, + pub font_family: Option, + pub line_height: TerminalLineHeight, + pub font_features: Option, + pub env: HashMap, + pub blinking: TerminalBlink, + pub alternate_scroll: AlternateScroll, + pub option_as_meta: bool, + pub copy_on_select: bool, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +pub struct TerminalSettingsContent { + pub shell: Option, + pub working_directory: Option, + pub font_size: Option, + pub font_family: Option, + pub line_height: Option, + pub font_features: Option, + pub env: Option>, + pub blinking: Option, + pub alternate_scroll: Option, + pub option_as_meta: Option, + pub copy_on_select: Option, +} + +impl settings::Setting for TerminalSettings { + const KEY: Option<&'static str> = Some("terminal"); + + type FileContent = TerminalSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &AppContext, + ) -> Self { + Self::load_via_json_merge(default_value, user_values) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)] +#[serde(rename_all = "snake_case")] +pub enum TerminalLineHeight { + #[default] + Comfortable, + Standard, + Custom(f32), +} + +impl TerminalLineHeight { + pub fn value(&self) -> f32 { + match self { + TerminalLineHeight::Comfortable => 1.618, + TerminalLineHeight::Standard => 1.3, + TerminalLineHeight::Custom(line_height) => *line_height, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum TerminalBlink { + Off, + TerminalControlled, + On, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum Shell { + System, + Program(String), + WithArguments { program: String, args: Vec }, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum AlternateScroll { + On, + Off, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum WorkingDirectory { + CurrentProjectDirectory, + FirstProjectDirectory, + AlwaysHome, + Always { directory: String }, +} + #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct TerminalSize { pub cell_width: f32, @@ -1049,16 +1150,7 @@ impl Terminal { } pub fn mouse_up(&mut self, e: &MouseUp, origin: Vector2F, cx: &mut ModelContext) { - let settings = cx.global::(); - let copy_on_select = settings - .terminal_overrides - .copy_on_select - .unwrap_or_else(|| { - settings - .terminal_defaults - .copy_on_select - .expect("Should be set in defaults") - }); + let setting = settings::get_setting::(None, cx); let position = e.position.sub(origin); if self.mouse_mode(e.shift) { @@ -1072,7 +1164,7 @@ impl Terminal { self.pty_tx.notify(bytes); } } else { - if e.button == MouseButton::Left && copy_on_select { + if e.button == MouseButton::Left && setting.copy_on_select { self.copy(); } diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index ae2342cd97359aa267adf0154fa0e0c800858992..93be64b612aadbfced6922ff4f88e3aee43a6b9a 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -16,7 +16,7 @@ use gpui::{ use itertools::Itertools; use language::CursorShape; use ordered_float::OrderedFloat; -use settings::Settings; +use settings::{font_size_for_setting, Settings}; use terminal::{ alacritty_terminal::{ ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor}, @@ -25,7 +25,7 @@ use terminal::{ term::{cell::Flags, TermMode}, }, mappings::colors::convert_color, - IndexedCell, Terminal, TerminalContent, TerminalSize, + IndexedCell, Terminal, TerminalContent, TerminalSettings, TerminalSize, }; use theme::TerminalStyle; use util::ResultExt; @@ -510,38 +510,50 @@ impl TerminalElement { scene.push_mouse_region(region); } +} + +impl Element for TerminalElement { + type LayoutState = LayoutState; + type PaintState = (); - ///Configures a text style from the current settings. - pub fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle { - let font_family_name = settings - .terminal_overrides + fn layout( + &mut self, + constraint: gpui::SizeConstraint, + view: &mut TerminalView, + cx: &mut LayoutContext, + ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { + let settings = cx.global::(); + let terminal_settings = settings::get_setting::(None, cx); + + //Setup layout information + let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone. + let link_style = settings.theme.editor.link_definition; + let tooltip_style = settings.theme.tooltip.clone(); + + let font_cache = cx.font_cache(); + let font_size = font_size_for_setting( + terminal_settings + .font_size + .unwrap_or(settings.buffer_font_size), + cx, + ); + let font_family_name = terminal_settings .font_family .as_ref() - .or(settings.terminal_defaults.font_family.as_ref()) .unwrap_or(&settings.buffer_font_family_name); - let font_features = settings - .terminal_overrides + let font_features = terminal_settings .font_features .as_ref() - .or(settings.terminal_defaults.font_features.as_ref()) .unwrap_or(&settings.buffer_font_features); - let family_id = font_cache .load_family(&[font_family_name], &font_features) .log_err() .unwrap_or(settings.buffer_font_family); - - let font_size = settings - .terminal_overrides - .font_size - .or(settings.terminal_defaults.font_size) - .unwrap_or(settings.buffer_font_size); - let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); - TextStyle { + let text_style = TextStyle { color: settings.theme.editor.text_color, font_family_id: family_id, font_family_name: font_cache.family_name(family_id).unwrap(), @@ -549,34 +561,12 @@ impl TerminalElement { font_size, font_properties: Default::default(), underline: Default::default(), - } - } -} - -impl Element for TerminalElement { - type LayoutState = LayoutState; - type PaintState = (); - - fn layout( - &mut self, - constraint: gpui::SizeConstraint, - view: &mut TerminalView, - cx: &mut LayoutContext, - ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { - let settings = cx.global::(); - let font_cache = cx.font_cache(); - - //Setup layout information - let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone. - let link_style = settings.theme.editor.link_definition; - let tooltip_style = settings.theme.tooltip.clone(); - - let text_style = TerminalElement::make_text_style(font_cache, settings); + }; let selection_color = settings.theme.editor.selection.selection; let match_color = settings.theme.search.match_background; let gutter; let dimensions = { - let line_height = text_style.font_size * settings.terminal_line_height(); + let line_height = text_style.font_size * terminal_settings.line_height.value(); let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size); gutter = cell_width; diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index dfb2334dc554d6dabc1ea7b5ac0d71d6726daf86..1615387ca2f08e0efc7feaafb0d7e8ce8c67bbf2 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -2,6 +2,7 @@ mod persistence; pub mod terminal_button; pub mod terminal_element; +use crate::{persistence::TERMINAL_DB, terminal_element::TerminalElement}; use context_menu::{ContextMenu, ContextMenuItem}; use dirs::home_dir; use gpui::{ @@ -16,7 +17,6 @@ use gpui::{ }; use project::{LocalWorktree, Project}; use serde::Deserialize; -use settings::{Settings, TerminalBlink, WorkingDirectory}; use smallvec::{smallvec, SmallVec}; use smol::Timer; use std::{ @@ -30,7 +30,7 @@ use terminal::{ index::Point, term::{search::RegexSearch, TermMode}, }, - Event, Terminal, + Event, Terminal, TerminalBlink, WorkingDirectory, }; use util::ResultExt; use workspace::{ @@ -41,7 +41,7 @@ use workspace::{ Pane, ToolbarItemLocation, Workspace, WorkspaceId, }; -use crate::{persistence::TERMINAL_DB, terminal_element::TerminalElement}; +pub use terminal::TerminalSettings; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); @@ -63,6 +63,8 @@ actions!( impl_actions!(terminal, [SendText, SendKeystroke]); pub fn init(cx: &mut AppContext) { + terminal::init(cx); + cx.add_action(TerminalView::deploy); register_deserializable_item::(cx); @@ -101,9 +103,9 @@ impl TerminalView { _: &workspace::NewTerminal, cx: &mut ViewContext, ) { - let strategy = cx.global::().terminal_strategy(); - - let working_directory = get_working_directory(workspace, cx, strategy); + let strategy = settings::get_setting::(None, cx); + let working_directory = + get_working_directory(workspace, cx, strategy.working_directory.clone()); let window_id = cx.window_id(); let terminal = workspace @@ -215,10 +217,7 @@ impl TerminalView { self.terminal.update(cx, |term, cx| { term.try_keystroke( &Keystroke::parse("ctrl-cmd-space").unwrap(), - cx.global::() - .terminal_overrides - .option_as_meta - .unwrap_or(false), + settings::get_setting::(None, cx).option_as_meta, ) }); } @@ -244,16 +243,7 @@ impl TerminalView { return true; } - let setting = { - let settings = cx.global::(); - settings - .terminal_overrides - .blinking - .clone() - .unwrap_or(TerminalBlink::TerminalControlled) - }; - - match setting { + match settings::get_setting::(None, cx).blinking { //If the user requested to never blink, don't blink it. TerminalBlink::Off => true, //If the terminal is controlling it, check terminal mode @@ -346,10 +336,7 @@ impl TerminalView { self.terminal.update(cx, |term, cx| { term.try_keystroke( &keystroke, - cx.global::() - .terminal_overrides - .option_as_meta - .unwrap_or(false), + settings::get_setting::(None, cx).option_as_meta, ); }); } @@ -412,10 +399,7 @@ impl View for TerminalView { self.terminal.update(cx, |term, cx| { term.try_keystroke( &event.keystroke, - cx.global::() - .terminal_overrides - .option_as_meta - .unwrap_or(false), + settings::get_setting::(None, cx).option_as_meta, ) }) } @@ -617,7 +601,9 @@ impl Item for TerminalView { .flatten() .or_else(|| { cx.read(|cx| { - let strategy = cx.global::().terminal_strategy(); + let strategy = settings::get_setting::(None, cx) + .working_directory + .clone(); workspace .upgrade(cx) .map(|workspace| { diff --git a/crates/theme_testbench/src/theme_testbench.rs b/crates/theme_testbench/src/theme_testbench.rs index c18a580d07294ad06bfe8cecf7b367fb27079f8d..125bc523b7f8723e06b81628fd1c9387e0924787 100644 --- a/crates/theme_testbench/src/theme_testbench.rs +++ b/crates/theme_testbench/src/theme_testbench.rs @@ -223,7 +223,7 @@ impl ThemeTestbench { let settings = cx.global::(); let font_cache = cx.font_cache(); let family_id = settings.buffer_font_family; - let font_size = settings.buffer_font_size; + let font_size = settings::font_size_for_setting(settings.buffer_font_size, cx); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 465219b8a68aff2db3e9bbd5d3e8d9fb7c254a82..33de725e1b2b37f4f8e4caa1187a25e16c86db9f 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -25,7 +25,7 @@ use project::Fs; use serde::{Deserialize, Serialize}; use settings::{ default_settings, handle_keymap_file_changes, handle_settings_file_changes, watch_config_file, - Settings, SettingsStore, WorkingDirectory, + Settings, SettingsStore, }; use simplelog::ConfigBuilder; use smol::process::Command; @@ -42,7 +42,7 @@ use std::{ thread, time::Duration, }; -use terminal_view::{get_working_directory, TerminalView}; +use terminal_view::{get_working_directory, TerminalSettings, TerminalView}; use util::http::{self, HttpClient}; use welcome::{show_welcome_experience, FIRST_OPEN}; @@ -722,13 +722,9 @@ pub fn dock_default_item_factory( workspace: &mut Workspace, cx: &mut ViewContext, ) -> Option> { - let strategy = cx - .global::() - .terminal_overrides + let strategy = settings::get_setting::(None, cx) .working_directory - .clone() - .unwrap_or(WorkingDirectory::CurrentProjectDirectory); - + .clone(); let working_directory = get_working_directory(workspace, cx, strategy); let window_id = cx.window_id(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index e6322ad8432a2fbb4215bfef092ab031f7d84dd9..1398369e75992372d942b4c3bb31a37ab9319a09 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -29,7 +29,7 @@ use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; -use settings::{Settings, DEFAULT_SETTINGS_ASSET_PATH}; +use settings::{adjust_font_size_delta, Settings, DEFAULT_SETTINGS_ASSET_PATH}; use std::{borrow::Cow, str, sync::Arc}; use terminal_view::terminal_button::TerminalButton; use util::{channel::ReleaseChannel, paths, ResultExt}; @@ -117,29 +117,17 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { cx.add_global_action(quit); cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| { - cx.update_global::(|settings, cx| { - settings.buffer_font_size = (settings.buffer_font_size + 1.0).max(MIN_FONT_SIZE); - if let Some(terminal_font_size) = settings.terminal_overrides.font_size.as_mut() { - *terminal_font_size = (*terminal_font_size + 1.0).max(MIN_FONT_SIZE); - } - cx.refresh_windows(); - }); + adjust_font_size_delta(cx, |size, _| *size += 1.0) }); cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| { - cx.update_global::(|settings, cx| { - settings.buffer_font_size = (settings.buffer_font_size - 1.0).max(MIN_FONT_SIZE); - if let Some(terminal_font_size) = settings.terminal_overrides.font_size.as_mut() { - *terminal_font_size = (*terminal_font_size - 1.0).max(MIN_FONT_SIZE); + adjust_font_size_delta(cx, |size, cx| { + if cx.global::().buffer_font_size + *size > MIN_FONT_SIZE { + *size -= 1.0; } - cx.refresh_windows(); - }); + }) }); cx.add_global_action(move |_: &ResetBufferFontSize, cx| { - cx.update_global::(|settings, cx| { - settings.buffer_font_size = settings.default_buffer_font_size; - settings.terminal_overrides.font_size = settings.terminal_defaults.font_size; - cx.refresh_windows(); - }); + adjust_font_size_delta(cx, |size, _| *size = 0.0) }); cx.add_global_action(move |_: &install_cli::Install, cx| { cx.spawn(|cx| async move { From 9ae10a5dd9e92d2536f180106dcfd2abb961d498 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 May 2023 14:39:43 -0700 Subject: [PATCH 12/25] Add a better API for updating settings in the SettingsStore in tests --- crates/settings/src/settings_file.rs | 10 +++--- crates/settings/src/settings_store.rs | 43 ++++++++++++++++++------- crates/vim/src/test/vim_test_context.rs | 12 +++---- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 990ccf02490f4cb6072d4d40d096386cf5cffc8b..936f3a7099034b8322fe513a460c41682b50e823 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -172,12 +172,10 @@ pub fn update_settings_file( }) .await?; - let edits = cx.read(|cx| cx.global::().update::(&old_text, update)); - - let mut new_text = old_text; - for (range, replacement) in edits.into_iter().rev() { - new_text.replace_range(range, &replacement); - } + let new_text = cx.read(|cx| { + cx.global::() + .new_text_for_update::(old_text, update) + }); cx.background() .spawn(async move { fs.atomic_write(paths::SETTINGS.clone(), new_text).await }) diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 59f7bee10faf164730f7005a9463ca92d54abc8c..caa77a36035f84d470d1288977fc71e935475b45 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -184,22 +184,43 @@ impl SettingsStore { this } - /// Override the global value for a particular setting. + /// Update the value of a setting in the user's global configuration. /// /// This is only for tests. Normally, settings are only loaded from /// JSON files. #[cfg(any(test, feature = "test-support"))] - pub fn replace_value(&mut self, value: T) { - self.setting_values - .get_mut(&TypeId::of::()) - .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) - .set_global_value(Box::new(value)) + pub fn update_user_settings( + &mut self, + cx: &AppContext, + update: impl FnOnce(&mut T::FileContent), + ) { + let old_text = if let Some(user_settings) = &self.user_deserialized_settings { + serde_json::to_string(&user_settings.untyped).unwrap() + } else { + String::new() + }; + let new_text = self.new_text_for_update::(old_text, update); + self.set_user_settings(&new_text, cx).unwrap(); } - /// Update the value of a setting. - /// - /// Returns a list of edits to apply to the JSON file. - pub fn update( + /// Update the value of a setting in a JSON file, returning the new text + /// for that JSON file. + pub fn new_text_for_update( + &self, + old_text: String, + update: impl FnOnce(&mut T::FileContent), + ) -> String { + let edits = self.edits_for_update::(&old_text, update); + let mut new_text = old_text; + for (range, replacement) in edits.into_iter().rev() { + new_text.replace_range(range, &replacement); + } + new_text + } + + /// Update the value of a setting in a JSON file, returning a list + /// of edits to apply to the JSON file. + pub fn edits_for_update( &self, text: &str, update: impl FnOnce(&mut T::FileContent), @@ -1129,7 +1150,7 @@ mod tests { cx: &mut AppContext, ) { store.set_user_settings(&old_json, cx).ok(); - let edits = store.update::(&old_json, update); + let edits = store.edits_for_update::(&old_json, update); let mut new_json = old_json; for (range, replacement) in edits.into_iter().rev() { new_json.replace_range(range, &replacement); diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index b426cea717248e5cf9ec020961944b28d0e07196..ac86a082354d3eec21bb6747250fa2ecb3dd4bcf 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -21,8 +21,8 @@ impl<'a> VimTestContext<'a> { search::init(cx); crate::init(cx); - cx.update_global(|store: &mut SettingsStore, _| { - store.replace_value(VimModeSetting(enabled)); + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |s| *s = Some(enabled)); }); settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap(); @@ -53,16 +53,16 @@ impl<'a> VimTestContext<'a> { pub fn enable_vim(&mut self) { self.cx.update(|cx| { - cx.update_global(|store: &mut SettingsStore, _| { - store.replace_value(VimModeSetting(true)) + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |s| *s = Some(true)); }); }) } pub fn disable_vim(&mut self) { self.cx.update(|cx| { - cx.update_global(|store: &mut SettingsStore, _| { - store.replace_value(VimModeSetting(false)) + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |s| *s = Some(false)); }); }) } From 39618ae32da38e415f46902bdeeb5c966d8b9a1b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 11 May 2023 14:40:35 -0700 Subject: [PATCH 13/25] Define language settings in the language crate --- Cargo.lock | 8 + crates/auto_update/src/auto_update.rs | 14 +- crates/client/src/client.rs | 17 +- crates/collab/src/tests.rs | 5 +- crates/collab/src/tests/integration_tests.rs | 13 +- crates/command_palette/Cargo.toml | 1 + crates/command_palette/src/command_palette.rs | 20 +- crates/copilot/src/copilot.rs | 88 ++--- crates/copilot_button/Cargo.toml | 1 + crates/copilot_button/src/copilot_button.rs | 103 +++-- crates/diagnostics/src/diagnostics.rs | 15 +- crates/editor/Cargo.toml | 2 + crates/editor/src/display_map.rs | 65 ++-- crates/editor/src/editor.rs | 91 ++--- crates/editor/src/editor_tests.rs | 352 ++++++++++------- crates/editor/src/element.rs | 90 ++--- .../editor/src/highlight_matching_bracket.rs | 4 +- crates/editor/src/hover_popover.rs | 11 +- crates/editor/src/link_go_to_definition.rs | 13 +- crates/editor/src/mouse_context_menu.rs | 5 +- crates/editor/src/movement.rs | 34 +- crates/editor/src/multi_buffer.rs | 23 +- .../src/test/editor_lsp_test_context.rs | 1 + crates/editor/src/test/editor_test_context.rs | 24 +- crates/file_finder/Cargo.toml | 4 +- crates/file_finder/src/file_finder.rs | 35 +- crates/gpui/src/executor.rs | 16 + crates/journal/src/journal.rs | 18 +- crates/language/Cargo.toml | 3 + crates/language/src/buffer.rs | 17 +- crates/language/src/buffer_tests.rs | 73 ++-- crates/language/src/language.rs | 5 + crates/language/src/language_settings.rs | 285 ++++++++++++++ crates/project/src/project.rs | 50 ++- crates/project/src/project_tests.rs | 143 ++++--- crates/project_panel/Cargo.toml | 1 + crates/project_panel/src/project_panel.rs | 27 +- crates/project_symbols/Cargo.toml | 1 + crates/project_symbols/src/project_symbols.rs | 13 +- crates/search/src/buffer_search.rs | 10 +- crates/search/src/project_search.rs | 29 +- crates/settings/src/settings.rs | 302 +-------------- crates/settings/src/settings_file.rs | 4 +- crates/settings/src/settings_store.rs | 362 ++++++++---------- crates/terminal/src/terminal.rs | 2 +- crates/vim/Cargo.toml | 1 + crates/vim/src/test/vim_test_context.rs | 4 +- crates/vim/src/vim.rs | 16 +- crates/workspace/src/workspace.rs | 8 +- crates/zed/src/languages/c.rs | 15 +- crates/zed/src/languages/python.rs | 15 +- crates/zed/src/languages/rust.rs | 15 +- crates/zed/src/languages/yaml.rs | 8 +- crates/zed/src/zed.rs | 25 +- 54 files changed, 1347 insertions(+), 1160 deletions(-) create mode 100644 crates/language/src/language_settings.rs diff --git a/Cargo.lock b/Cargo.lock index 748ec5ef49a8fa187019851f6ae07f3cd950c0ce..f01022e2941218daf48c2955c11240a038ab92c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1341,6 +1341,7 @@ dependencies = [ "env_logger 0.9.3", "fuzzy", "gpui", + "language", "picker", "project", "serde_json", @@ -1408,6 +1409,7 @@ dependencies = [ "fs", "futures 0.3.28", "gpui", + "language", "settings", "smol", "theme", @@ -2034,6 +2036,7 @@ dependencies = [ "pulldown-cmark", "rand 0.8.5", "rpc", + "schemars", "serde", "serde_derive", "settings", @@ -2243,6 +2246,7 @@ dependencies = [ "env_logger 0.9.3", "fuzzy", "gpui", + "language", "menu", "picker", "postage", @@ -3427,6 +3431,7 @@ dependencies = [ "futures 0.3.28", "fuzzy", "git", + "glob", "gpui", "indoc", "lazy_static", @@ -3437,6 +3442,7 @@ dependencies = [ "rand 0.8.5", "regex", "rpc", + "schemars", "serde", "serde_derive", "serde_json", @@ -4872,6 +4878,7 @@ dependencies = [ "editor", "futures 0.3.28", "gpui", + "language", "menu", "postage", "project", @@ -7818,6 +7825,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "vim" version = "0.1.0" dependencies = [ + "anyhow", "assets", "async-compat", "async-trait", diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 54f3f93b009e6b7e0f5ab706d8cff748af03212d..ebfa50aaf844e391598665cfc04eaf65d4ad09d2 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -65,12 +65,14 @@ impl Setting for AutoUpdateSetting { type FileContent = Option; - fn load(default_value: &Option, user_values: &[&Option], _: &AppContext) -> Self { - Self( - Self::json_merge(default_value, user_values) - .unwrap() - .unwrap(), - ) + fn load( + default_value: &Option, + user_values: &[&Option], + _: &AppContext, + ) -> Result { + Ok(Self( + Self::json_merge(default_value, user_values)?.ok_or_else(Self::missing_default)?, + )) } } diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index bcf91520513cb3781a771a4e9b76d944efc7b184..f1aee9540eb7d4065c911a0bd35ae70b178d1a96 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -350,17 +350,18 @@ impl settings::Setting for TelemetrySettings { default_value: &Self::FileContent, user_values: &[&Self::FileContent], _: &AppContext, - ) -> Self { - Self { - diagnostics: user_values - .first() - .and_then(|v| v.diagnostics) - .unwrap_or(default_value.diagnostics.unwrap()), + ) -> Result { + Ok(Self { + diagnostics: user_values.first().and_then(|v| v.diagnostics).unwrap_or( + default_value + .diagnostics + .ok_or_else(Self::missing_default)?, + ), metrics: user_values .first() .and_then(|v| v.metrics) - .unwrap_or(default_value.metrics.unwrap()), - } + .unwrap_or(default_value.metrics.ok_or_else(Self::missing_default)?), + }) } } diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 50fb9658fe9b8e727a3176cf1c6d60d703d95b7b..02e5fa56e41c22ca69bf6412207e8a97c4045aca 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -186,7 +186,10 @@ impl TestServer { }) }); - cx.update(|cx| client::init(&client, cx)); + cx.update(|cx| { + client::init(&client, cx); + language::init(cx); + }); let fs = FakeFs::new(cx.background()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index e3b5b0be7e651eb95daefddaf1e2e9c8e34e0c58..2896c478086c0f02785c8bd63fa714a509e798a4 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -18,6 +18,7 @@ use gpui::{ }; use indoc::indoc; use language::{ + language_settings::{AllLanguageSettings, Formatter}, tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig, OffsetRangeExt, Point, Rope, }; @@ -26,7 +27,7 @@ use lsp::LanguageServerId; use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath}; use rand::prelude::*; use serde_json::json; -use settings::{Formatter, Settings}; +use settings::{SettingsStore}; use std::{ cell::{Cell, RefCell}, env, future, mem, @@ -4219,10 +4220,12 @@ async fn test_formatting_buffer( // Ensure buffer can be formatted using an external command. Notice how the // host's configuration is honored as opposed to using the guest's settings. cx_a.update(|cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.editor_defaults.formatter = Some(Formatter::External { - command: "awk".to_string(), - arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()], + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |file| { + file.defaults.formatter = Some(Formatter::External { + command: "awk".into(), + arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(), + }); }); }); }); diff --git a/crates/command_palette/Cargo.toml b/crates/command_palette/Cargo.toml index 8ad1843cb677eb5ee00eaddd1a4ab2c5fa9c4f10..95ba452c142dc2fa2c615b000984e2d627a1a2e8 100644 --- a/crates/command_palette/Cargo.toml +++ b/crates/command_palette/Cargo.toml @@ -23,6 +23,7 @@ workspace = { path = "../workspace" } [dev-dependencies] gpui = { path = "../gpui", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } +language = { path = "../language", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } serde_json.workspace = true workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 4e0e776000967fd11185e70d3922e49e5c26c6d7..12134f6d6eb0850e49376214fbbc7f13ba3f6b4d 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -294,14 +294,7 @@ mod tests { #[gpui::test] async fn test_command_palette(deterministic: Arc, cx: &mut TestAppContext) { - deterministic.forbid_parking(); - let app_state = cx.update(AppState::test); - - cx.update(|cx| { - editor::init(cx); - workspace::init(app_state.clone(), cx); - init(cx); - }); + let app_state = init_test(cx); let project = Project::test(app_state.fs.clone(), [], cx).await; let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); @@ -369,4 +362,15 @@ mod tests { assert!(palette.delegate().matches.is_empty()) }); } + + fn init_test(cx: &mut TestAppContext) -> Arc { + cx.update(|cx| { + let app_state = AppState::test(cx); + language::init(cx); + editor::init(cx); + workspace::init(app_state.clone(), cx); + init(cx); + app_state + }) + } } diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 9ccd9c445df45388ad64f8942292ae5e03c37564..b966348cd61c95a33476828b875e40605193cefc 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -10,6 +10,7 @@ use gpui::{ actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle, }; use language::{ + language_settings::{all_language_settings, language_settings}, point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16, ToPointUtf16, }; @@ -17,7 +18,7 @@ use log::{debug, error}; use lsp::{LanguageServer, LanguageServerId}; use node_runtime::NodeRuntime; use request::{LogMessage, StatusNotification}; -use settings::Settings; +use settings::SettingsStore; use smol::{fs, io::BufReader, stream::StreamExt}; use std::{ ffi::OsString, @@ -302,56 +303,34 @@ impl Copilot { node_runtime: Arc, cx: &mut ModelContext, ) -> Self { - cx.observe_global::({ - let http = http.clone(); - let node_runtime = node_runtime.clone(); - move |this, cx| { - if cx.global::().features.copilot { - if matches!(this.server, CopilotServer::Disabled) { - let start_task = cx - .spawn({ - let http = http.clone(); - let node_runtime = node_runtime.clone(); - move |this, cx| { - Self::start_language_server(http, node_runtime, this, cx) - } - }) - .shared(); - this.server = CopilotServer::Starting { task: start_task }; - cx.notify(); - } - } else { - this.server = CopilotServer::Disabled; - cx.notify(); - } - } - }) - .detach(); - - if cx.global::().features.copilot { - let start_task = cx - .spawn({ - let http = http.clone(); - let node_runtime = node_runtime.clone(); - move |this, cx| async { - Self::start_language_server(http, node_runtime, this, cx).await - } - }) - .shared(); + let mut this = Self { + http, + node_runtime, + server: CopilotServer::Disabled, + buffers: Default::default(), + }; + this.enable_or_disable_copilot(cx); + cx.observe_global::(move |this, cx| this.enable_or_disable_copilot(cx)) + .detach(); + this + } - Self { - http, - node_runtime, - server: CopilotServer::Starting { task: start_task }, - buffers: Default::default(), + fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext) { + let http = self.http.clone(); + let node_runtime = self.node_runtime.clone(); + if all_language_settings(None, cx).copilot_enabled(None, None) { + if matches!(self.server, CopilotServer::Disabled) { + let start_task = cx + .spawn({ + move |this, cx| Self::start_language_server(http, node_runtime, this, cx) + }) + .shared(); + self.server = CopilotServer::Starting { task: start_task }; + cx.notify(); } } else { - Self { - http, - node_runtime, - server: CopilotServer::Disabled, - buffers: Default::default(), - } + self.server = CopilotServer::Disabled; + cx.notify(); } } @@ -805,13 +784,14 @@ impl Copilot { let snapshot = registered_buffer.report_changes(buffer, cx); let buffer = buffer.read(cx); let uri = registered_buffer.uri.clone(); - let settings = cx.global::(); let position = position.to_point_utf16(buffer); - let language = buffer.language_at(position); - let language_name = language.map(|language| language.name()); - let language_name = language_name.as_deref(); - let tab_size = settings.tab_size(language_name); - let hard_tabs = settings.hard_tabs(language_name); + let settings = language_settings( + None, + buffer.language_at(position).map(|l| l.name()).as_deref(), + cx, + ); + let tab_size = settings.tab_size; + let hard_tabs = settings.hard_tabs; let relative_path = buffer .file() .map(|file| file.path().to_path_buf()) diff --git a/crates/copilot_button/Cargo.toml b/crates/copilot_button/Cargo.toml index 77937e8bd0ddbd39889e7e2aa433d07fbde7c55b..ad3febd68c6146bee0688bcdc32060bb00caeb65 100644 --- a/crates/copilot_button/Cargo.toml +++ b/crates/copilot_button/Cargo.toml @@ -15,6 +15,7 @@ editor = { path = "../editor" } fs = { path = "../fs" } context_menu = { path = "../context_menu" } gpui = { path = "../gpui" } +language = { path = "../language" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 61c84bc517242550283a769e8bb6bb3fab3879c7..d04f1e0e75d8a7765cdd6e3bdd7abb9beae6ac2b 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -9,6 +9,7 @@ use gpui::{ AnyElement, AppContext, AsyncAppContext, Element, Entity, MouseState, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; +use language::language_settings::{self, all_language_settings, AllLanguageSettings}; use settings::{update_settings_file, Settings, SettingsStore}; use std::{path::Path, sync::Arc}; use util::{paths, ResultExt}; @@ -40,12 +41,12 @@ impl View for CopilotButton { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let settings = cx.global::(); - - if !settings.features.copilot { + let all_language_settings = &all_language_settings(None, cx); + if !all_language_settings.copilot.feature_enabled { return Empty::new().into_any(); } + let settings = cx.global::(); let theme = settings.theme.clone(); let active = self.popup_menu.read(cx).visible(); let Some(copilot) = Copilot::global(cx) else { @@ -55,7 +56,7 @@ impl View for CopilotButton { let enabled = self .editor_enabled - .unwrap_or(settings.show_copilot_suggestions(None, None)); + .unwrap_or_else(|| all_language_settings.copilot_enabled(None, None)); Stack::new() .with_child( @@ -192,14 +193,14 @@ impl CopilotButton { } pub fn deploy_copilot_menu(&mut self, cx: &mut ViewContext) { - let settings = cx.global::(); let fs = self.fs.clone(); - let mut menu_options = Vec::with_capacity(8); if let Some(language) = self.language.clone() { let fs = fs.clone(); - let language_enabled = settings.copilot_enabled_for_language(Some(language.as_ref())); + let language_enabled = + language_settings::language_settings(None, Some(language.as_ref()), cx) + .show_copilot_suggestions; menu_options.push(ContextMenuItem::handler( format!( "{} Suggestions for {}", @@ -210,6 +211,8 @@ impl CopilotButton { )); } + let settings = settings::get_setting::(None, cx); + if let Some(path) = self.path.as_ref() { let path_enabled = settings.copilot_enabled_for_path(path); let path = path.clone(); @@ -234,7 +237,7 @@ impl CopilotButton { )); } - let globally_enabled = cx.global::().features.copilot; + let globally_enabled = settings.copilot_enabled(None, None); menu_options.push(ContextMenuItem::handler( if globally_enabled { "Hide Suggestions for All Files" @@ -246,7 +249,7 @@ impl CopilotButton { menu_options.push(ContextMenuItem::Separator); - let icon_style = settings.theme.copilot.out_link_icon.clone(); + let icon_style = cx.global::().theme.copilot.out_link_icon.clone(); menu_options.push(ContextMenuItem::action( move |state: &mut MouseState, style: &theme::ContextMenuItem| { Flex::row() @@ -272,22 +275,19 @@ impl CopilotButton { pub fn update_enabled(&mut self, editor: ViewHandle, cx: &mut ViewContext) { let editor = editor.read(cx); - let snapshot = editor.buffer().read(cx).snapshot(cx); - let settings = cx.global::(); let suggestion_anchor = editor.selections.newest_anchor().start; - let language_name = snapshot .language_at(suggestion_anchor) .map(|language| language.name()); - let path = snapshot - .file_at(suggestion_anchor) - .map(|file| file.path().clone()); + let path = snapshot.file_at(suggestion_anchor).map(|file| file.path()); - self.editor_enabled = - Some(settings.show_copilot_suggestions(language_name.as_deref(), path.as_deref())); + self.editor_enabled = Some( + all_language_settings(None, cx) + .copilot_enabled(language_name.as_deref(), path.map(|p| p.as_ref())), + ); self.language = language_name; - self.path = path; + self.path = path.cloned(); cx.notify() } @@ -328,27 +328,27 @@ async fn configure_disabled_globs( settings_editor.downgrade().update(&mut cx, |item, cx| { let text = item.buffer().read(cx).snapshot(cx).text(); - let edits = cx - .global::() - .update::(&text, |file| { - let copilot = file.copilot.get_or_insert_with(Default::default); - let globs = copilot.disabled_globs.get_or_insert_with(|| { - cx.global::() - .copilot - .disabled_globs - .clone() - .iter() - .map(|glob| glob.as_str().to_string()) - .collect::>() - }); - - if let Some(path_to_disable) = &path_to_disable { - globs.push(path_to_disable.to_string_lossy().into_owned()); - } else { - globs.clear(); - } + let settings = cx.global::(); + let edits = settings.edits_for_update::(&text, |file| { + let copilot = file.copilot.get_or_insert_with(Default::default); + let globs = copilot.disabled_globs.get_or_insert_with(|| { + settings + .get::(None) + .copilot + .disabled_globs + .clone() + .iter() + .map(|glob| glob.as_str().to_string()) + .collect::>() }); + if let Some(path_to_disable) = &path_to_disable { + globs.push(path_to_disable.to_string_lossy().into_owned()); + } else { + globs.clear(); + } + }); + if !edits.is_empty() { item.change_selections(Some(Autoscroll::newest()), cx, |selections| { selections.select_ranges(edits.iter().map(|e| e.0.clone())); @@ -365,31 +365,26 @@ async fn configure_disabled_globs( } fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = cx.global::().show_copilot_suggestions(None, None); - update_settings_file::(fs, cx, move |file_contents| { - file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) + let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None); + update_settings_file::(fs, cx, move |file| { + file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) }); } fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = cx - .global::() - .show_copilot_suggestions(Some(&language), None); - - update_settings_file::(fs, cx, move |file_contents| { - file_contents.languages.insert( - language, - settings::EditorSettings { - show_copilot_suggestions: Some((!show_copilot_suggestions).into()), - ..Default::default() - }, - ); + let show_copilot_suggestions = + all_language_settings(None, cx).copilot_enabled(Some(&language), None); + update_settings_file::(fs, cx, move |file| { + file.languages + .entry(language) + .or_default() + .show_copilot_suggestions = Some(!show_copilot_suggestions); }); } fn hide_copilot(fs: Arc, cx: &mut AppContext) { - update_settings_file::(fs, cx, move |file_contents| { - file_contents.features.copilot = Some(false) + update_settings_file::(fs, cx, move |file| { + file.features.get_or_insert(Default::default()).copilot = Some(false); }); } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index b9db439e49e69baf397c5955f4a327a805998a3e..62e03ca54f6c456a603f697db6dc3f189dfa50c1 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -820,11 +820,13 @@ mod tests { use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped}; use project::FakeFs; use serde_json::json; + use settings::SettingsStore; use unindent::Unindent as _; #[gpui::test] async fn test_diagnostics(cx: &mut TestAppContext) { - Settings::test_async(cx); + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/test", @@ -1227,7 +1229,8 @@ mod tests { #[gpui::test] async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { - Settings::test_async(cx); + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/test", @@ -1491,6 +1494,14 @@ mod tests { }); } + fn init_test(cx: &mut TestAppContext) { + cx.update(|cx| { + cx.set_global(Settings::test(cx)); + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + }); + } + fn editor_blocks(editor: &ViewHandle, cx: &mut WindowContext) -> Vec<(u32, String)> { editor.update(cx, |editor, cx| { let snapshot = editor.snapshot(cx); diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index e4767a12e24945e3390dfdafc37cfe02c5a694d2..fc7bf4b8abad6732ab338e439db6f30ba2f49e83 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -49,6 +49,7 @@ workspace = { path = "../workspace" } aho-corasick = "0.7" anyhow.workspace = true futures.workspace = true +glob.workspace = true indoc = "1.0.4" itertools = "0.10" lazy_static.workspace = true @@ -58,6 +59,7 @@ parking_lot.workspace = true postage.workspace = true pulldown-cmark = { version = "0.9.2", default-features = false } rand = { workspace = true, optional = true } +schemars.workspace = true serde.workspace = true serde_derive.workspace = true smallvec.workspace = true diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index e190ec7717114dd645b46ef650d24ccba4862d92..4c36be682b0d5aa64ce7b5555497a1d9f5e66a5b 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -13,8 +13,9 @@ use gpui::{ fonts::{FontId, HighlightStyle}, Entity, ModelContext, ModelHandle, }; -use language::{OffsetUtf16, Point, Subscription as BufferSubscription}; -use settings::Settings; +use language::{ + language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription, +}; use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc}; pub use suggestion_map::Suggestion; use suggestion_map::SuggestionMap; @@ -276,8 +277,7 @@ impl DisplayMap { .as_singleton() .and_then(|buffer| buffer.read(cx).language()) .map(|language| language.name()); - - cx.global::().tab_size(language_name.as_deref()) + language_settings(None, language_name.as_deref(), cx).tab_size } #[cfg(test)] @@ -844,8 +844,12 @@ pub mod tests { use super::*; use crate::{movement, test::marked_display_snapshot}; use gpui::{color::Color, elements::*, test::observe, AppContext}; - use language::{Buffer, Language, LanguageConfig, SelectionGoal}; + use language::{ + language_settings::{AllLanguageSettings, AllLanguageSettingsContent}, + Buffer, Language, LanguageConfig, SelectionGoal, + }; use rand::{prelude::*, Rng}; + use settings::SettingsStore; use smol::stream::StreamExt; use std::{env, sync::Arc}; use theme::SyntaxTheme; @@ -882,9 +886,7 @@ pub mod tests { log::info!("wrap width: {:?}", wrap_width); cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = NonZeroU32::new(tab_size); - cx.set_global(settings) + init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size)); }); let buffer = cx.update(|cx| { @@ -939,9 +941,11 @@ pub mod tests { tab_size = *tab_sizes.choose(&mut rng).unwrap(); log::info!("setting tab size to {:?}", tab_size); cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = NonZeroU32::new(tab_size); - cx.set_global(settings) + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, |s| { + s.defaults.tab_size = NonZeroU32::new(tab_size); + }); + }); }); } 30..=44 => { @@ -1119,7 +1123,7 @@ pub mod tests { #[gpui::test(retries = 5)] fn test_soft_wraps(cx: &mut AppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); - cx.foreground().forbid_parking(); + init_test(cx, |_| {}); let font_cache = cx.font_cache(); @@ -1131,7 +1135,6 @@ pub mod tests { .unwrap(); let font_size = 12.0; let wrap_width = Some(64.); - cx.set_global(Settings::test(cx)); let text = "one two three four five\nsix seven eight"; let buffer = MultiBuffer::build_simple(text, cx); @@ -1211,7 +1214,8 @@ pub mod tests { #[gpui::test] fn test_text_chunks(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx, |_| {}); + let text = sample_text(6, 6, 'a'); let buffer = MultiBuffer::build_simple(&text, cx); let family_id = cx @@ -1225,6 +1229,7 @@ pub mod tests { let font_size = 14.0; let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx)); + buffer.update(cx, |buffer, cx| { buffer.edit( vec![ @@ -1289,11 +1294,8 @@ pub mod tests { .unwrap(), ); language.set_theme(&theme); - cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.editor_defaults.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); - }); + + cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap()))); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); buffer.condition(cx, |buf, _| !buf.is_parsing()).await; @@ -1382,7 +1384,7 @@ pub mod tests { ); language.set_theme(&theme); - cx.update(|cx| cx.set_global(Settings::test(cx))); + cx.update(|cx| init_test(cx, |_| {})); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); buffer.condition(cx, |buf, _| !buf.is_parsing()).await; @@ -1429,9 +1431,8 @@ pub mod tests { #[gpui::test] async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) { - cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); + cx.update(|cx| init_test(cx, |_| {})); - cx.update(|cx| cx.set_global(Settings::test(cx))); let theme = SyntaxTheme::new(vec![ ("operator".to_string(), Color::red().into()), ("string".to_string(), Color::green().into()), @@ -1510,7 +1511,8 @@ pub mod tests { #[gpui::test] fn test_clip_point(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx, |_| {}); + fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) { let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx); @@ -1559,7 +1561,7 @@ pub mod tests { #[gpui::test] fn test_clip_at_line_ends(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx, |_| {}); fn assert(text: &str, cx: &mut gpui::AppContext) { let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx); @@ -1578,7 +1580,8 @@ pub mod tests { #[gpui::test] fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx, |_| {}); + let text = "✅\t\tα\nβ\t\n🏀β\t\tγ"; let buffer = MultiBuffer::build_simple(text, cx); let font_cache = cx.font_cache(); @@ -1639,7 +1642,8 @@ pub mod tests { #[gpui::test] fn test_max_point(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx, |_| {}); + let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx); let font_cache = cx.font_cache(); let family_id = font_cache @@ -1718,4 +1722,13 @@ pub mod tests { } chunks } + + fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) { + cx.foreground().forbid_parking(); + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, f); + }); + } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index abd8885d30f190f6b1b37313f1ae62b42b53a48a..41087c6eaaee4afc79d785267ca0cb2d4446fcfd 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -51,6 +51,7 @@ pub use items::MAX_TAB_TITLE_LEN; use itertools::Itertools; pub use language::{char_kind, CharKind}; use language::{ + language_settings::{self, all_language_settings}, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal, TransactionId, @@ -436,7 +437,7 @@ pub enum EditorMode { Full, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum SoftWrap { None, EditorWidth, @@ -471,7 +472,7 @@ pub struct Editor { select_larger_syntax_node_stack: Vec]>>, ime_transaction: Option, active_diagnostics: Option, - soft_wrap_mode_override: Option, + soft_wrap_mode_override: Option, get_field_editor_theme: Option>, override_text_style: Option>, project: Option>, @@ -1247,7 +1248,7 @@ impl Editor { let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx)); let soft_wrap_mode_override = - (mode == EditorMode::SingleLine).then(|| settings::SoftWrap::None); + (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None); let mut this = Self { handle: cx.weak_handle(), buffer: buffer.clone(), @@ -3116,17 +3117,12 @@ impl Editor { snapshot: &MultiBufferSnapshot, cx: &mut ViewContext, ) -> bool { - let settings = cx.global::(); - - let path = snapshot.file_at(location).map(|file| file.path()); + let path = snapshot.file_at(location).map(|file| file.path().as_ref()); let language_name = snapshot .language_at(location) .map(|language| language.name()); - if !settings.show_copilot_suggestions(language_name.as_deref(), path.map(|p| p.as_ref())) { - return false; - } - - true + let settings = all_language_settings(None, cx); + settings.copilot_enabled(language_name.as_deref(), path) } fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool { @@ -3427,12 +3423,9 @@ impl Editor { { let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row); - let language_name = buffer - .language_at(line_buffer_range.start) - .map(|language| language.name()); let indent_len = match indent_size.kind { IndentKind::Space => { - cx.global::().tab_size(language_name.as_deref()) + buffer.settings_at(line_buffer_range.start, cx).tab_size } IndentKind::Tab => NonZeroU32::new(1).unwrap(), }; @@ -3544,12 +3537,11 @@ impl Editor { } // Otherwise, insert a hard or soft tab. - let settings = cx.global::(); - let language_name = buffer.language_at(cursor, cx).map(|l| l.name()); - let tab_size = if settings.hard_tabs(language_name.as_deref()) { + let settings = buffer.settings_at(cursor, cx); + let tab_size = if settings.hard_tabs { IndentSize::tab() } else { - let tab_size = settings.tab_size(language_name.as_deref()).get(); + let tab_size = settings.tab_size.get(); let char_column = snapshot .text_for_range(Point::new(cursor.row, 0)..cursor) .flat_map(str::chars) @@ -3602,10 +3594,9 @@ impl Editor { delta_for_start_row: u32, cx: &AppContext, ) -> u32 { - let language_name = buffer.language_at(selection.start, cx).map(|l| l.name()); - let settings = cx.global::(); - let tab_size = settings.tab_size(language_name.as_deref()).get(); - let indent_kind = if settings.hard_tabs(language_name.as_deref()) { + let settings = buffer.settings_at(selection.start, cx); + let tab_size = settings.tab_size.get(); + let indent_kind = if settings.hard_tabs { IndentKind::Tab } else { IndentKind::Space @@ -3674,11 +3665,8 @@ impl Editor { let buffer = self.buffer.read(cx); let snapshot = buffer.snapshot(cx); for selection in &selections { - let language_name = buffer.language_at(selection.start, cx).map(|l| l.name()); - let tab_size = cx - .global::() - .tab_size(language_name.as_deref()) - .get(); + let settings = buffer.settings_at(selection.start, cx); + let tab_size = settings.tab_size.get(); let mut rows = selection.spanned_rows(false, &display_map); // Avoid re-outdenting a row that has already been outdented by a @@ -6439,27 +6427,24 @@ impl Editor { } pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap { - let language_name = self - .buffer - .read(cx) - .as_singleton() - .and_then(|singleton_buffer| singleton_buffer.read(cx).language()) - .map(|l| l.name()); - - let settings = cx.global::(); + let settings = self.buffer.read(cx).settings_at(0, cx); let mode = self .soft_wrap_mode_override - .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref())); + .unwrap_or_else(|| settings.soft_wrap); match mode { - settings::SoftWrap::None => SoftWrap::None, - settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth, - settings::SoftWrap::PreferredLineLength => { - SoftWrap::Column(settings.preferred_line_length(language_name.as_deref())) + language_settings::SoftWrap::None => SoftWrap::None, + language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth, + language_settings::SoftWrap::PreferredLineLength => { + SoftWrap::Column(settings.preferred_line_length) } } } - pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext) { + pub fn set_soft_wrap_mode( + &mut self, + mode: language_settings::SoftWrap, + cx: &mut ViewContext, + ) { self.soft_wrap_mode_override = Some(mode); cx.notify(); } @@ -6474,8 +6459,8 @@ impl Editor { self.soft_wrap_mode_override.take(); } else { let soft_wrap = match self.soft_wrap_mode(cx) { - SoftWrap::None => settings::SoftWrap::EditorWidth, - SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None, + SoftWrap::None => language_settings::SoftWrap::EditorWidth, + SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None, }; self.soft_wrap_mode_override = Some(soft_wrap); } @@ -6874,7 +6859,12 @@ impl Editor { .get("vim_mode") == Some(&serde_json::Value::Bool(true)); let telemetry_settings = *settings::get_setting::(None, cx); - let settings = cx.global::(); + let copilot_enabled = all_language_settings(None, cx).copilot_enabled(None, None); + let copilot_enabled_for_language = self + .buffer + .read(cx) + .settings_at(0, cx) + .show_copilot_suggestions; let extension = Path::new(file.file_name(cx)) .extension() @@ -6893,15 +6883,8 @@ impl Editor { file_extension: extension.map(ToString::to_string), vim_mode, operation: name, - copilot_enabled: settings.features.copilot, - copilot_enabled_for_language: settings.show_copilot_suggestions( - self.language_at(0, cx) - .map(|language| language.name()) - .as_deref(), - self.file_at(0, cx) - .map(|file| file.path().clone()) - .as_deref(), - ), + copilot_enabled, + copilot_enabled_for_language, }; telemetry.report_clickhouse_event(event, telemetry_settings) } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 0f749bde4841ebce9dbee4c012e0e8ff2fab8830..7ee6a52955cdc06fc35c703568ba4d3cbf39647b 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -12,10 +12,12 @@ use gpui::{ serde_json, TestAppContext, }; use indoc::indoc; -use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point}; +use language::{ + language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent}, + BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point, +}; use parking_lot::Mutex; use project::FakeFs; -use settings::EditorSettings; use std::{cell::RefCell, future::Future, rc::Rc, time::Instant}; use unindent::Unindent; use util::{ @@ -29,7 +31,8 @@ use workspace::{ #[gpui::test] fn test_edit_events(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let buffer = cx.add_model(|cx| { let mut buffer = language::Buffer::new(0, "123456", cx); buffer.set_group_interval(Duration::from_secs(1)); @@ -156,7 +159,8 @@ fn test_edit_events(cx: &mut TestAppContext) { #[gpui::test] fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let mut now = Instant::now(); let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval()); @@ -226,7 +230,8 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { #[gpui::test] fn test_ime_composition(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let buffer = cx.add_model(|cx| { let mut buffer = language::Buffer::new(0, "abcde", cx); // Ensure automatic grouping doesn't occur. @@ -328,7 +333,7 @@ fn test_ime_composition(cx: &mut TestAppContext) { #[gpui::test] fn test_selection_with_mouse(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); @@ -395,7 +400,8 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) { #[gpui::test] fn test_canceling_pending_selection(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); build_editor(buffer, cx) @@ -429,6 +435,8 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) { #[gpui::test] fn test_clone(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + let (text, selection_ranges) = marked_text_ranges( indoc! {" one @@ -439,7 +447,6 @@ fn test_clone(cx: &mut TestAppContext) { "}, true, ); - cx.update(|cx| cx.set_global(Settings::test(cx))); let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&text, cx); @@ -487,7 +494,8 @@ fn test_clone(cx: &mut TestAppContext) { #[gpui::test] async fn test_navigation_history(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + cx.set_global(DragAndDrop::::default()); use workspace::item::Item; @@ -600,7 +608,8 @@ async fn test_navigation_history(cx: &mut TestAppContext) { #[gpui::test] fn test_cancel(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); build_editor(buffer, cx) @@ -642,7 +651,8 @@ fn test_cancel(cx: &mut TestAppContext) { #[gpui::test] fn test_fold_action(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple( &" @@ -731,7 +741,8 @@ fn test_fold_action(cx: &mut TestAppContext) { #[gpui::test] fn test_move_cursor(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx)); let (_, view) = cx.add_window(|cx| build_editor(buffer.clone(), cx)); @@ -806,7 +817,8 @@ fn test_move_cursor(cx: &mut TestAppContext) { #[gpui::test] fn test_move_cursor_multibyte(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); build_editor(buffer.clone(), cx) @@ -910,7 +922,8 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) { #[gpui::test] fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); build_editor(buffer.clone(), cx) @@ -959,7 +972,8 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) { #[gpui::test] fn test_beginning_end_of_line(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("abc\n def", cx); build_editor(buffer, cx) @@ -1121,7 +1135,8 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) { #[gpui::test] fn test_prev_next_word_boundary(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); build_editor(buffer, cx) @@ -1172,7 +1187,8 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) { #[gpui::test] fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); build_editor(buffer, cx) @@ -1229,6 +1245,7 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) { #[gpui::test] async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); let mut cx = EditorTestContext::new(cx); let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); @@ -1343,6 +1360,7 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); let mut cx = EditorTestContext::new(cx); cx.set_state("one «two threeˇ» four"); cx.update_editor(|editor, cx| { @@ -1353,7 +1371,8 @@ async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) { #[gpui::test] fn test_delete_to_word_boundary(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("one two three four", cx); build_editor(buffer.clone(), cx) @@ -1388,7 +1407,8 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) { #[gpui::test] fn test_newline(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); build_editor(buffer.clone(), cx) @@ -1410,7 +1430,8 @@ fn test_newline(cx: &mut TestAppContext) { #[gpui::test] fn test_newline_with_old_selections(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple( " @@ -1491,11 +1512,8 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) { #[gpui::test] async fn test_newline_above(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap()); - }); + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(4) }); let language = Arc::new( @@ -1506,8 +1524,9 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) { .with_indents_query(r#"(_ "(" ")" @end) @indent"#) .unwrap(), ); - cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); + let mut cx = EditorTestContext::new(cx); + cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); cx.set_state(indoc! {" const a: ˇA = ( (ˇ @@ -1516,6 +1535,7 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) { )ˇ ˇ);ˇ "}); + cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx)); cx.assert_editor_state(indoc! {" ˇ @@ -1540,11 +1560,8 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_newline_below(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap()); - }); + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(4) }); let language = Arc::new( @@ -1555,8 +1572,9 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) { .with_indents_query(r#"(_ "(" ")" @end) @indent"#) .unwrap(), ); - cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); + let mut cx = EditorTestContext::new(cx); + cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); cx.set_state(indoc! {" const a: ˇA = ( (ˇ @@ -1565,6 +1583,7 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) { )ˇ ˇ);ˇ "}); + cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx)); cx.assert_editor_state(indoc! {" const a: A = ( @@ -1589,7 +1608,8 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) { #[gpui::test] fn test_insert_with_old_selections(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); let mut editor = build_editor(buffer.clone(), cx); @@ -1615,12 +1635,11 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) { #[gpui::test] async fn test_tab(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap()); - }); + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(3) }); + + let mut cx = EditorTestContext::new(cx); cx.set_state(indoc! {" ˇabˇc ˇ🏀ˇ🏀ˇefg @@ -1646,6 +1665,8 @@ async fn test_tab(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); let language = Arc::new( Language::new( @@ -1704,7 +1725,10 @@ async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAp #[gpui::test] async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(4) + }); + let language = Arc::new( Language::new( LanguageConfig::default(), @@ -1713,14 +1737,9 @@ async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) { .with_indents_query(r#"(_ "{" "}" @end) @indent"#) .unwrap(), ); - cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); - - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.tab_size = Some(4.try_into().unwrap()); - }); - }); + let mut cx = EditorTestContext::new(cx); + cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); cx.set_state(indoc! {" fn a() { if b { @@ -1741,6 +1760,10 @@ async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_indent_outdent(cx: &mut gpui::TestAppContext) { + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(4); + }); + let mut cx = EditorTestContext::new(cx); cx.set_state(indoc! {" @@ -1810,13 +1833,12 @@ async fn test_indent_outdent(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.hard_tabs = Some(true); - }); + init_test(cx, |settings| { + settings.defaults.hard_tabs = Some(true); }); + let mut cx = EditorTestContext::new(cx); + // select two ranges on one line cx.set_state(indoc! {" «oneˇ» «twoˇ» @@ -1907,25 +1929,25 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) { #[gpui::test] fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) { - cx.update(|cx| { - cx.set_global( - Settings::test(cx) - .with_language_defaults( - "TOML", - EditorSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ) - .with_language_defaults( - "Rust", - EditorSettings { - tab_size: Some(4.try_into().unwrap()), - ..Default::default() - }, - ), - ); + init_test(cx, |settings| { + settings.languages.extend([ + ( + "TOML".into(), + LanguageSettingsContent { + tab_size: NonZeroU32::new(2), + ..Default::default() + }, + ), + ( + "Rust".into(), + LanguageSettingsContent { + tab_size: NonZeroU32::new(4), + ..Default::default() + }, + ), + ]); }); + let toml_language = Arc::new(Language::new( LanguageConfig { name: "TOML".into(), @@ -2020,6 +2042,8 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) { #[gpui::test] async fn test_backspace(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); // Basic backspace @@ -2067,8 +2091,9 @@ async fn test_backspace(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_delete(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); cx.set_state(indoc! {" onˇe two three fou«rˇ» five six @@ -2095,7 +2120,8 @@ async fn test_delete(cx: &mut gpui::TestAppContext) { #[gpui::test] fn test_delete_line(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); build_editor(buffer, cx) @@ -2119,7 +2145,6 @@ fn test_delete_line(cx: &mut TestAppContext) { ); }); - cx.update(|cx| cx.set_global(Settings::test(cx))); let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); build_editor(buffer, cx) @@ -2139,7 +2164,8 @@ fn test_delete_line(cx: &mut TestAppContext) { #[gpui::test] fn test_duplicate_line(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); build_editor(buffer, cx) @@ -2191,7 +2217,8 @@ fn test_duplicate_line(cx: &mut TestAppContext) { #[gpui::test] fn test_move_line_up_down(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); build_editor(buffer, cx) @@ -2289,7 +2316,8 @@ fn test_move_line_up_down(cx: &mut TestAppContext) { #[gpui::test] fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); build_editor(buffer, cx) @@ -2315,7 +2343,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) { #[gpui::test] fn test_transpose(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); _ = cx .add_window(|cx| { @@ -2417,6 +2445,8 @@ fn test_transpose(cx: &mut TestAppContext) { #[gpui::test] async fn test_clipboard(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six "); @@ -2497,6 +2527,8 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_paste_multiline(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); let language = Arc::new(Language::new( LanguageConfig::default(), @@ -2609,7 +2641,8 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) { #[gpui::test] fn test_select_all(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); build_editor(buffer, cx) @@ -2625,7 +2658,8 @@ fn test_select_all(cx: &mut TestAppContext) { #[gpui::test] fn test_select_line(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); build_editor(buffer, cx) @@ -2671,7 +2705,8 @@ fn test_select_line(cx: &mut TestAppContext) { #[gpui::test] fn test_split_selection_into_lines(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); build_editor(buffer, cx) @@ -2741,7 +2776,8 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) { #[gpui::test] fn test_add_selection_above_below(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, view) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); build_editor(buffer, cx) @@ -2935,6 +2971,8 @@ fn test_add_selection_above_below(cx: &mut TestAppContext) { #[gpui::test] async fn test_select_next(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); cx.set_state("abc\nˇabc abc\ndefabc\nabc"); @@ -2959,7 +2997,8 @@ async fn test_select_next(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let language = Arc::new(Language::new( LanguageConfig::default(), Some(tree_sitter_rust::language()), @@ -3100,7 +3139,8 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let language = Arc::new( Language::new( LanguageConfig { @@ -3160,6 +3200,8 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); let language = Arc::new(Language::new( @@ -3329,6 +3371,8 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); let html_language = Arc::new( @@ -3563,6 +3607,8 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); let rust_language = Arc::new( @@ -3660,7 +3706,8 @@ async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let language = Arc::new(Language::new( LanguageConfig { brackets: BracketPairConfig { @@ -3814,7 +3861,8 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let language = Arc::new(Language::new( LanguageConfig { brackets: BracketPairConfig { @@ -3919,7 +3967,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_snippets(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); let (text, insertion_ranges) = marked_text_ranges( indoc! {" @@ -4027,7 +4075,7 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx, |_| {}); let mut language = Language::new( LanguageConfig { @@ -4111,16 +4159,14 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { assert!(!cx.read(|cx| editor.is_dirty(cx))); // Set rust language override and assert overriden tabsize is sent to language server - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.language_overrides.insert( - "Rust".into(), - EditorSettings { - tab_size: Some(8.try_into().unwrap()), - ..Default::default() - }, - ); - }) + update_test_settings(cx, |settings| { + settings.languages.insert( + "Rust".into(), + LanguageSettingsContent { + tab_size: NonZeroU32::new(8), + ..Default::default() + }, + ); }); let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx)); @@ -4141,7 +4187,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx, |_| {}); let mut language = Language::new( LanguageConfig { @@ -4227,16 +4273,14 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { assert!(!cx.read(|cx| editor.is_dirty(cx))); // Set rust language override and assert overriden tabsize is sent to language server - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.language_overrides.insert( - "Rust".into(), - EditorSettings { - tab_size: Some(8.try_into().unwrap()), - ..Default::default() - }, - ); - }) + update_test_settings(cx, |settings| { + settings.languages.insert( + "Rust".into(), + LanguageSettingsContent { + tab_size: NonZeroU32::new(8), + ..Default::default() + }, + ); }); let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx)); @@ -4257,7 +4301,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx, |_| {}); let mut language = Language::new( LanguageConfig { @@ -4342,7 +4386,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx, |_| {}); let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { @@ -4399,7 +4443,7 @@ async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx, |_| {}); let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { @@ -4514,6 +4558,8 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) #[gpui::test] async fn test_completion(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { completion_provider: Some(lsp::CompletionOptions { @@ -4681,7 +4727,8 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let language = Arc::new(Language::new( LanguageConfig { line_comment: Some("// ".into()), @@ -4764,8 +4811,7 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); let language = Arc::new(Language::new( LanguageConfig { @@ -4778,6 +4824,7 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) let registry = Arc::new(LanguageRegistry::test()); registry.add(language.clone()); + let mut cx = EditorTestContext::new(cx); cx.update_buffer(|buffer, cx| { buffer.set_language_registry(registry); buffer.set_language(Some(language), cx); @@ -4897,6 +4944,8 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) #[gpui::test] async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); let html_language = Arc::new( @@ -5021,7 +5070,8 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { #[gpui::test] fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); @@ -5067,7 +5117,8 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { #[gpui::test] fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let markers = vec![('[', ']').into(), ('(', ')').into()]; let (initial_text, mut excerpt_ranges) = marked_text_ranges_by( indoc! {" @@ -5140,7 +5191,8 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { #[gpui::test] fn test_refresh_selections(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); let mut excerpt1_id = None; let multibuffer = cx.add_model(|cx| { @@ -5224,7 +5276,8 @@ fn test_refresh_selections(cx: &mut TestAppContext) { #[gpui::test] fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); let mut excerpt1_id = None; let multibuffer = cx.add_model(|cx| { @@ -5282,7 +5335,8 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) { #[gpui::test] async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let language = Arc::new( Language::new( LanguageConfig { @@ -5355,7 +5409,8 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { #[gpui::test] fn test_highlighted_ranges(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); build_editor(buffer.clone(), cx) @@ -5437,7 +5492,8 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) { #[gpui::test] async fn test_following(cx: &mut gpui::TestAppContext) { - Settings::test_async(cx); + init_test(cx, |_| {}); + let fs = FakeFs::new(cx.background()); let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; @@ -5576,7 +5632,8 @@ async fn test_following(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { - Settings::test_async(cx); + init_test(cx, |_| {}); + let fs = FakeFs::new(cx.background()); let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); @@ -5805,6 +5862,8 @@ fn test_combine_syntax_and_fuzzy_match_highlights() { #[gpui::test] async fn go_to_hunk(deterministic: Arc, cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx); let diff_base = r#" @@ -5924,6 +5983,8 @@ fn test_split_words() { #[gpui::test] async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await; let mut assert = |before, after| { let _state_context = cx.set_state(before); @@ -5972,6 +6033,8 @@ async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) { #[gpui::test(iterations = 10)] async fn test_copilot(deterministic: Arc, cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let (copilot, copilot_lsp) = Copilot::fake(cx); cx.update(|cx| cx.set_global(copilot)); let mut cx = EditorLspTestContext::new_rust( @@ -6223,6 +6286,8 @@ async fn test_copilot_completion_invalidation( deterministic: Arc, cx: &mut gpui::TestAppContext, ) { + init_test(cx, |_| {}); + let (copilot, copilot_lsp) = Copilot::fake(cx); cx.update(|cx| cx.set_global(copilot)); let mut cx = EditorLspTestContext::new_rust( @@ -6288,11 +6353,10 @@ async fn test_copilot_multibuffer( deterministic: Arc, cx: &mut gpui::TestAppContext, ) { + init_test(cx, |_| {}); + let (copilot, copilot_lsp) = Copilot::fake(cx); - cx.update(|cx| { - cx.set_global(Settings::test(cx)); - cx.set_global(copilot) - }); + cx.update(|cx| cx.set_global(copilot)); let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx)); let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx)); @@ -6392,14 +6456,16 @@ async fn test_copilot_disabled_globs( deterministic: Arc, cx: &mut gpui::TestAppContext, ) { - let (copilot, copilot_lsp) = Copilot::fake(cx); - cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.copilot.disabled_globs = vec![glob::Pattern::new(".env*").unwrap()]; - cx.set_global(settings); - cx.set_global(copilot) + init_test(cx, |settings| { + settings + .copilot + .get_or_insert(Default::default()) + .disabled_globs = Some(vec![".env*".to_string()]); }); + let (copilot, copilot_lsp) = Copilot::fake(cx); + cx.update(|cx| cx.set_global(copilot)); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/test", @@ -6596,3 +6662,27 @@ fn handle_copilot_completion_request( } }); } + +pub(crate) fn update_test_settings( + cx: &mut TestAppContext, + f: impl Fn(&mut AllLanguageSettingsContent), +) { + cx.update(|cx| { + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, f); + }); + }); +} + +pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) { + cx.foreground().forbid_parking(); + + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + cx.set_global(Settings::test(cx)); + language::init(cx); + crate::init(cx); + }); + + update_test_settings(cx, f); +} diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index c1710b73378da41d32489714535db3170b4c1199..8194d51b25e99568bb8e3f932102da4af9ef6990 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -35,9 +35,12 @@ use gpui::{ }; use itertools::Itertools; use json::json; -use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection}; +use language::{ + language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, + Selection, +}; use project::ProjectPath; -use settings::{GitGutter, Settings, ShowWhitespaces}; +use settings::{GitGutter, Settings}; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -708,6 +711,7 @@ impl EditorElement { let scroll_left = scroll_position.x() * max_glyph_width; let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.); let line_end_overshoot = 0.15 * layout.position_map.line_height; + let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces; scene.push_layer(Some(bounds)); @@ -882,9 +886,10 @@ impl EditorElement { content_origin, scroll_left, visible_text_bounds, - cx, + whitespace_setting, &invisible_display_ranges, visible_bounds, + cx, ) } } @@ -1738,9 +1743,10 @@ impl LineWithInvisibles { content_origin: Vector2F, scroll_left: f32, visible_text_bounds: RectF, - cx: &mut ViewContext, + whitespace_setting: ShowWhitespaceSetting, selection_ranges: &[Range], visible_bounds: RectF, + cx: &mut ViewContext, ) { let line_height = layout.position_map.line_height; let line_y = row as f32 * line_height - scroll_top; @@ -1754,7 +1760,6 @@ impl LineWithInvisibles { ); self.draw_invisibles( - cx, &selection_ranges, layout, content_origin, @@ -1764,12 +1769,13 @@ impl LineWithInvisibles { scene, visible_bounds, line_height, + whitespace_setting, + cx, ); } fn draw_invisibles( &self, - cx: &mut ViewContext, selection_ranges: &[Range], layout: &LayoutState, content_origin: Vector2F, @@ -1779,17 +1785,13 @@ impl LineWithInvisibles { scene: &mut SceneBuilder, visible_bounds: RectF, line_height: f32, + whitespace_setting: ShowWhitespaceSetting, + cx: &mut ViewContext, ) { - let settings = cx.global::(); - let allowed_invisibles_regions = match settings - .editor_overrides - .show_whitespaces - .or(settings.editor_defaults.show_whitespaces) - .unwrap_or_default() - { - ShowWhitespaces::None => return, - ShowWhitespaces::Selection => Some(selection_ranges), - ShowWhitespaces::All => None, + let allowed_invisibles_regions = match whitespace_setting { + ShowWhitespaceSetting::None => return, + ShowWhitespaceSetting::Selection => Some(selection_ranges), + ShowWhitespaceSetting::All => None, }; for invisible in &self.invisibles { @@ -2773,17 +2775,19 @@ mod tests { use super::*; use crate::{ display_map::{BlockDisposition, BlockProperties}, + editor_tests::{init_test, update_test_settings}, Editor, MultiBuffer, }; use gpui::TestAppContext; + use language::language_settings; use log::info; - use settings::Settings; use std::{num::NonZeroU32, sync::Arc}; use util::test::sample_text; #[gpui::test] fn test_layout_line_numbers(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); Editor::new(EditorMode::Full, buffer, None, None, cx) @@ -2801,7 +2805,8 @@ mod tests { #[gpui::test] fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx, |_| {}); + let (_, editor) = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("", cx); Editor::new(EditorMode::Full, buffer, None, None, cx) @@ -2861,26 +2866,27 @@ mod tests { #[gpui::test] fn test_all_invisibles_drawing(cx: &mut TestAppContext) { - let tab_size = 4; + const TAB_SIZE: u32 = 4; + let input_text = "\t \t|\t| a b"; let expected_invisibles = vec![ Invisible::Tab { line_start_offset: 0, }, Invisible::Whitespace { - line_offset: tab_size as usize, + line_offset: TAB_SIZE as usize, }, Invisible::Tab { - line_start_offset: tab_size as usize + 1, + line_start_offset: TAB_SIZE as usize + 1, }, Invisible::Tab { - line_start_offset: tab_size as usize * 2 + 1, + line_start_offset: TAB_SIZE as usize * 2 + 1, }, Invisible::Whitespace { - line_offset: tab_size as usize * 3 + 1, + line_offset: TAB_SIZE as usize * 3 + 1, }, Invisible::Whitespace { - line_offset: tab_size as usize * 3 + 3, + line_offset: TAB_SIZE as usize * 3 + 3, }, ]; assert_eq!( @@ -2892,12 +2898,11 @@ mod tests { "Hardcoded expected invisibles differ from the actual ones in '{input_text}'" ); - cx.update(|cx| { - let mut test_settings = Settings::test(cx); - test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All); - test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap()); - cx.set_global(test_settings); + init_test(cx, |s| { + s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); + s.defaults.tab_size = NonZeroU32::new(TAB_SIZE); }); + let actual_invisibles = collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0); @@ -2906,11 +2911,9 @@ mod tests { #[gpui::test] fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) { - cx.update(|cx| { - let mut test_settings = Settings::test(cx); - test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All); - test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(4).unwrap()); - cx.set_global(test_settings); + init_test(cx, |s| { + s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); + s.defaults.tab_size = NonZeroU32::new(4); }); for editor_mode_without_invisibles in [ @@ -2961,19 +2964,18 @@ mod tests { ); info!("Expected invisibles: {expected_invisibles:?}"); + init_test(cx, |_| {}); + // Put the same string with repeating whitespace pattern into editors of various size, // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point. let resize_step = 10.0; let mut editor_width = 200.0; while editor_width <= 1000.0 { - cx.update(|cx| { - let mut test_settings = Settings::test(cx); - test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap()); - test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All); - test_settings.editor_defaults.preferred_line_length = Some(editor_width as u32); - test_settings.editor_defaults.soft_wrap = - Some(settings::SoftWrap::PreferredLineLength); - cx.set_global(test_settings); + update_test_settings(cx, |s| { + s.defaults.tab_size = NonZeroU32::new(tab_size); + s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); + s.defaults.preferred_line_length = Some(editor_width as u32); + s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength); }); let actual_invisibles = @@ -3021,7 +3023,7 @@ mod tests { let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); let (_, layout_state) = editor.update(cx, |editor, cx| { - editor.set_soft_wrap_mode(settings::SoftWrap::EditorWidth, cx); + editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); editor.set_wrap_width(Some(editor_width), cx); let mut new_parents = Default::default(); diff --git a/crates/editor/src/highlight_matching_bracket.rs b/crates/editor/src/highlight_matching_bracket.rs index ce3864f56a2cf228284092719dda7408ecd574f8..a0baf6882fba4952488701ad8b36a25e5566332a 100644 --- a/crates/editor/src/highlight_matching_bracket.rs +++ b/crates/editor/src/highlight_matching_bracket.rs @@ -33,12 +33,14 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon #[cfg(test)] mod tests { use super::*; - use crate::test::editor_lsp_test_context::EditorLspTestContext; + use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext}; use indoc::indoc; use language::{BracketPair, BracketPairConfig, Language, LanguageConfig}; #[gpui::test] async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new( Language::new( LanguageConfig { diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 438c662ed1125209640b6481097f9b34fd0787c6..c40cb0fbe34a6396cf1af01ff82d6c1b50ee2df5 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -694,7 +694,7 @@ impl DiagnosticPopover { #[cfg(test)] mod tests { use super::*; - use crate::test::editor_lsp_test_context::EditorLspTestContext; + use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext}; use gpui::fonts::Weight; use indoc::indoc; use language::{Diagnostic, DiagnosticSet}; @@ -706,6 +706,8 @@ mod tests { #[gpui::test] async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), @@ -773,6 +775,8 @@ mod tests { #[gpui::test] async fn test_keyboard_hover_info_popover(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), @@ -816,6 +820,8 @@ mod tests { #[gpui::test] async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), @@ -882,7 +888,8 @@ mod tests { #[gpui::test] fn test_render_blocks(cx: &mut gpui::TestAppContext) { - Settings::test_async(cx); + init_test(cx, |_| {}); + cx.add_window(|cx| { let editor = Editor::single_line(None, cx); let style = editor.style(cx); diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index b2105c1c8138fd884985a1a4365eb834aee1ba6b..04cf4611f19298c168ad46fa91b3c5ecbdbb993f 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -1,10 +1,9 @@ -use std::ops::Range; - use crate::{Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase}; use gpui::{Task, ViewContext}; use language::{Bias, ToOffset}; use project::LocationLink; use settings::Settings; +use std::ops::Range; use util::TryFutureExt; #[derive(Debug, Default)] @@ -297,6 +296,8 @@ fn go_to_fetched_definition_of_kind( #[cfg(test)] mod tests { + use super::*; + use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext}; use futures::StreamExt; use gpui::{ platform::{self, Modifiers, ModifiersChangedEvent}, @@ -305,12 +306,10 @@ mod tests { use indoc::indoc; use lsp::request::{GotoDefinition, GotoTypeDefinition}; - use crate::test::editor_lsp_test_context::EditorLspTestContext; - - use super::*; - #[gpui::test] async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), @@ -417,6 +416,8 @@ mod tests { #[gpui::test] async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index b892fffbcac3cd0a1e76966a5a8190a89fcf8d2a..8dfdcdff53b77b8bb1dcb41c71104b9901406b94 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -57,13 +57,14 @@ pub fn deploy_context_menu( #[cfg(test)] mod tests { - use crate::test::editor_lsp_test_context::EditorLspTestContext; - use super::*; + use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext}; use indoc::indoc; #[gpui::test] async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 284f0c94bc4e7d080b9ae756ba62747c443d4b41..311616cfe042a38d45899de96501c92ef2d6fca4 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -367,13 +367,15 @@ pub fn split_display_range_by_lines( #[cfg(test)] mod tests { + use settings::{Settings, SettingsStore}; + use super::*; use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer}; - use settings::Settings; #[gpui::test] fn test_previous_word_start(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + fn assert(marked_text: &str, cx: &mut gpui::AppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -400,7 +402,8 @@ mod tests { #[gpui::test] fn test_previous_subword_start(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + fn assert(marked_text: &str, cx: &mut gpui::AppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -434,7 +437,8 @@ mod tests { #[gpui::test] fn test_find_preceding_boundary(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + fn assert( marked_text: &str, cx: &mut gpui::AppContext, @@ -466,7 +470,8 @@ mod tests { #[gpui::test] fn test_next_word_end(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + fn assert(marked_text: &str, cx: &mut gpui::AppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -490,7 +495,8 @@ mod tests { #[gpui::test] fn test_next_subword_end(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + fn assert(marked_text: &str, cx: &mut gpui::AppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -523,7 +529,8 @@ mod tests { #[gpui::test] fn test_find_boundary(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + fn assert( marked_text: &str, cx: &mut gpui::AppContext, @@ -555,7 +562,8 @@ mod tests { #[gpui::test] fn test_surrounding_word(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + fn assert(marked_text: &str, cx: &mut gpui::AppContext) { let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); assert_eq!( @@ -576,7 +584,8 @@ mod tests { #[gpui::test] fn test_move_up_and_down_with_excerpts(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); + let family_id = cx .font_cache() .load_family(&["Helvetica"], &Default::default()) @@ -691,4 +700,11 @@ mod tests { (DisplayPoint::new(7, 2), SelectionGoal::Column(2)), ); } + + fn init_test(cx: &mut gpui::AppContext) { + cx.set_global(SettingsStore::test(cx)); + cx.set_global(Settings::test(cx)); + language::init(cx); + crate::init(cx); + } } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index a2160b47e5d342122b57f964f2070cdc8010684a..1e07bba065c7d4e8f5d345c06290016448cd0e7f 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -9,7 +9,9 @@ use git::diff::DiffHunk; use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; pub use language::Completion; use language::{ - char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape, + char_kind, + language_settings::{language_settings, LanguageSettings}, + AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape, DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped, @@ -1372,6 +1374,15 @@ impl MultiBuffer { .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset)) } + pub fn settings_at<'a, T: ToOffset>( + &self, + point: T, + cx: &'a AppContext, + ) -> &'a LanguageSettings { + let language = self.language_at(point, cx); + language_settings(None, language.map(|l| l.name()).as_deref(), cx) + } + pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle)) { self.buffers .borrow() @@ -2764,6 +2775,16 @@ impl MultiBufferSnapshot { .and_then(|(buffer, offset)| buffer.language_at(offset)) } + pub fn settings_at<'a, T: ToOffset>( + &'a self, + point: T, + cx: &'a AppContext, + ) -> &'a LanguageSettings { + self.point_to_buffer_offset(point) + .map(|(buffer, offset)| buffer.settings_at(offset, cx)) + .unwrap_or_else(|| language_settings(None, None, cx)) + } + pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { self.point_to_buffer_offset(point) .and_then(|(buffer, offset)| buffer.language_scope_at(offset)) diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index e268e2a0ceb32ca735ea77547298372c0334425e..e9aaa3df1b12a37d1eee52610d51e190506b1bcf 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -37,6 +37,7 @@ impl<'a> EditorLspTestContext<'a> { let app_state = cx.update(AppState::test); cx.update(|cx| { + language::init(cx); crate::init(cx); pane::init(cx); }); diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 269a2f4f3cbb0d9b26654429ad0ae7b0f7a1c3b7..ced99a3f23407524df3baae4ed4b0f25fa84b3fd 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -1,19 +1,16 @@ -use std::{ - any::TypeId, - ops::{Deref, DerefMut, Range}, -}; - -use futures::Future; -use indoc::indoc; - use crate::{ display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer, }; +use futures::Future; use gpui::{ keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle, }; +use indoc::indoc; use language::{Buffer, BufferSnapshot}; -use settings::Settings; +use std::{ + any::TypeId, + ops::{Deref, DerefMut, Range}, +}; use util::{ assert_set_eq, test::{generate_marked_text, marked_text_ranges}, @@ -30,15 +27,10 @@ pub struct EditorTestContext<'a> { impl<'a> EditorTestContext<'a> { pub fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> { let (window_id, editor) = cx.update(|cx| { - cx.set_global(Settings::test(cx)); - crate::init(cx); - - let (window_id, editor) = cx.add_window(Default::default(), |cx| { + cx.add_window(Default::default(), |cx| { cx.focus_self(); build_editor(MultiBuffer::build_simple("", cx), cx) - }); - - (window_id, editor) + }) }); Self { diff --git a/crates/file_finder/Cargo.toml b/crates/file_finder/Cargo.toml index 30a4650ad74d033221c43139631b6dc44d5bcafb..024054c0052269f4c3d8b030856f5021433b383b 100644 --- a/crates/file_finder/Cargo.toml +++ b/crates/file_finder/Cargo.toml @@ -23,7 +23,9 @@ postage.workspace = true [dev-dependencies] gpui = { path = "../gpui", features = ["test-support"] } -serde_json.workspace = true +language = { path = "../language", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } + +serde_json.workspace = true ctor.workspace = true env_logger.workspace = true diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index f00430feb7e16d02eebdbcf3a9f8c142243675a9..7d5aff79d090f3dc0c36535d443a15154ac2ef1f 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -270,6 +270,7 @@ impl PickerDelegate for FileFinderDelegate { mod tests { use super::*; use editor::Editor; + use gpui::TestAppContext; use menu::{Confirm, SelectNext}; use serde_json::json; use workspace::{AppState, Workspace}; @@ -283,12 +284,7 @@ mod tests { #[gpui::test] async fn test_matching_paths(cx: &mut gpui::TestAppContext) { - let app_state = cx.update(|cx| { - super::init(cx); - editor::init(cx); - AppState::test(cx) - }); - + let app_state = init_test(cx); app_state .fs .as_fake() @@ -339,7 +335,7 @@ mod tests { #[gpui::test] async fn test_matching_cancellation(cx: &mut gpui::TestAppContext) { - let app_state = cx.update(AppState::test); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -408,7 +404,7 @@ mod tests { #[gpui::test] async fn test_ignored_files(cx: &mut gpui::TestAppContext) { - let app_state = cx.update(AppState::test); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -462,7 +458,7 @@ mod tests { #[gpui::test] async fn test_single_file_worktrees(cx: &mut gpui::TestAppContext) { - let app_state = cx.update(AppState::test); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -516,9 +512,7 @@ mod tests { #[gpui::test] async fn test_multiple_matches_with_same_relative_path(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); - - let app_state = cx.update(AppState::test); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -570,9 +564,7 @@ mod tests { #[gpui::test] async fn test_path_distance_ordering(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); - - let app_state = cx.update(AppState::test); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -622,7 +614,7 @@ mod tests { #[gpui::test] async fn test_search_worktree_without_files(cx: &mut gpui::TestAppContext) { - let app_state = cx.update(AppState::test); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -658,4 +650,15 @@ mod tests { assert_eq!(finder.delegate().matches.len(), 0); }); } + + fn init_test(cx: &mut TestAppContext) -> Arc { + cx.foreground().forbid_parking(); + cx.update(|cx| { + let state = AppState::test(cx); + language::init(cx); + super::init(cx); + editor::init(cx); + state + }) + } } diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 7baffab2d94caa72478d5fc28051fa927b2ff93d..028656a027db012e38041cfa5ed7b5806bf5b432 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -477,6 +477,14 @@ impl Deterministic { state.rng = StdRng::seed_from_u64(state.seed); } + pub fn allow_parking(&self) { + use rand::prelude::*; + + let mut state = self.state.lock(); + state.forbid_parking = false; + state.rng = StdRng::seed_from_u64(state.seed); + } + pub async fn simulate_random_delay(&self) { use rand::prelude::*; use smol::future::yield_now; @@ -698,6 +706,14 @@ impl Foreground { } } + #[cfg(any(test, feature = "test-support"))] + pub fn allow_parking(&self) { + match self { + Self::Deterministic { executor, .. } => executor.allow_parking(), + _ => panic!("this method can only be called on a deterministic executor"), + } + } + #[cfg(any(test, feature = "test-support"))] pub fn advance_clock(&self, duration: Duration) { match self { diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 938cd829223f10258b996e5e31e694e8f94c0340..34af4ed0e460255ca4c6040458c0bedeb4db31f1 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use chrono::{Datelike, Local, NaiveTime, Timelike}; use editor::{scroll::autoscroll::Autoscroll, Editor}; use gpui::{actions, AppContext}; @@ -40,21 +41,8 @@ impl settings::Setting for JournalSettings { type FileContent = Self; - fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Self { - Self { - path: Some( - user_values - .first() - .and_then(|s| s.path.clone()) - .unwrap_or(default_value.path.clone().unwrap()), - ), - hour_format: Some( - user_values - .first() - .and_then(|s| s.hour_format.clone()) - .unwrap_or(default_value.hour_format.clone().unwrap()), - ), - } + fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Result { + Self::load_via_json_merge(default_value, user_values) } } diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 6e03a07bc3b0d1a392cb3d9bd8cc9299510fce1e..5a7644d98e6220d77bda66bcd6d6f35f895b67a6 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -36,16 +36,19 @@ sum_tree = { path = "../sum_tree" } text = { path = "../text" } theme = { path = "../theme" } util = { path = "../util" } + anyhow.workspace = true async-broadcast = "0.4" async-trait.workspace = true futures.workspace = true +glob.workspace = true lazy_static.workspace = true log.workspace = true parking_lot.workspace = true postage.workspace = true rand = { workspace = true, optional = true } regex.workspace = true +schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 43766192eb871ade7c912bf098b525520a973d65..fd1430b1e42454c1783f13755833a36cbd8aaf59 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -5,6 +5,7 @@ pub use crate::{ }; use crate::{ diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, + language_settings::{language_settings, LanguageSettings}, outline::OutlineItem, syntax_map::{ SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxSnapshot, ToTreeSitterPoint, @@ -18,7 +19,6 @@ use futures::FutureExt as _; use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, Task}; use lsp::LanguageServerId; use parking_lot::Mutex; -use settings::Settings; use similar::{ChangeTag, TextDiff}; use smallvec::SmallVec; use smol::future::yield_now; @@ -1827,11 +1827,11 @@ impl BufferSnapshot { pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { let language_name = self.language_at(position).map(|language| language.name()); - let settings = cx.global::(); - if settings.hard_tabs(language_name.as_deref()) { + let settings = language_settings(None, language_name.as_deref(), cx); + if settings.hard_tabs { IndentSize::tab() } else { - IndentSize::spaces(settings.tab_size(language_name.as_deref()).get()) + IndentSize::spaces(settings.tab_size.get()) } } @@ -2146,6 +2146,15 @@ impl BufferSnapshot { .or(self.language.as_ref()) } + pub fn settings_at<'a, D: ToOffset>( + &self, + position: D, + cx: &'a AppContext, + ) -> &'a LanguageSettings { + let language = self.language_at(position); + language_settings(None, language.map(|l| l.name()).as_deref(), cx) + } + pub fn language_scope_at(&self, position: D) -> Option { let offset = position.to_offset(self); diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index eeac1a48186b3c13bf063ab0cdc4eb1ffbe19cdc..be573aa8956e3dc28e7074d17f85160fe5f2d1e9 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1,3 +1,7 @@ +use crate::language_settings::{ + AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent, +}; + use super::*; use clock::ReplicaId; use collections::BTreeMap; @@ -7,7 +11,7 @@ use indoc::indoc; use proto::deserialize_operation; use rand::prelude::*; use regex::RegexBuilder; -use settings::Settings; +use settings::SettingsStore; use std::{ cell::RefCell, env, @@ -36,7 +40,8 @@ fn init_logger() { #[gpui::test] fn test_line_endings(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let mut buffer = Buffer::new(0, "one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx); @@ -862,8 +867,7 @@ fn test_range_for_syntax_ancestor(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_with_soft_tabs(cx: &mut AppContext) { - let settings = Settings::test(cx); - cx.set_global(settings); + init_settings(cx, |_| {}); cx.add_model(|cx| { let text = "fn a() {}"; @@ -903,9 +907,9 @@ fn test_autoindent_with_soft_tabs(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_with_hard_tabs(cx: &mut AppContext) { - let mut settings = Settings::test(cx); - settings.editor_overrides.hard_tabs = Some(true); - cx.set_global(settings); + init_settings(cx, |settings| { + settings.defaults.hard_tabs = Some(true); + }); cx.add_model(|cx| { let text = "fn a() {}"; @@ -945,8 +949,7 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) { - let settings = Settings::test(cx); - cx.set_global(settings); + init_settings(cx, |_| {}); cx.add_model(|cx| { let mut buffer = Buffer::new( @@ -1082,8 +1085,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC #[gpui::test] fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) { - let settings = Settings::test(cx); - cx.set_global(settings); + init_settings(cx, |_| {}); cx.add_model(|cx| { let mut buffer = Buffer::new( @@ -1145,7 +1147,8 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap #[gpui::test] fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let mut buffer = Buffer::new( 0, @@ -1201,7 +1204,8 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let text = "a\nb"; let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); @@ -1217,7 +1221,8 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_multi_line_insertion(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let text = " const a: usize = 1; @@ -1257,7 +1262,8 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_block_mode(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let text = r#" fn a() { @@ -1339,7 +1345,8 @@ fn test_autoindent_block_mode(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let text = r#" fn a() { @@ -1417,7 +1424,8 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex #[gpui::test] fn test_autoindent_language_without_indents_query(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let text = " * one @@ -1460,25 +1468,23 @@ fn test_autoindent_language_without_indents_query(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_with_injected_languages(cx: &mut AppContext) { - cx.set_global({ - let mut settings = Settings::test(cx); - settings.language_overrides.extend([ + init_settings(cx, |settings| { + settings.languages.extend([ ( "HTML".into(), - settings::EditorSettings { + LanguageSettingsContent { tab_size: Some(2.try_into().unwrap()), ..Default::default() }, ), ( "JavaScript".into(), - settings::EditorSettings { + LanguageSettingsContent { tab_size: Some(8.try_into().unwrap()), ..Default::default() }, ), - ]); - settings + ]) }); let html_language = Arc::new( @@ -1574,9 +1580,10 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) { #[gpui::test] fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) { - let mut settings = Settings::test(cx); - settings.editor_defaults.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + init_settings(cx, |settings| { + settings.defaults.tab_size = Some(2.try_into().unwrap()); + }); + cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx); @@ -1617,7 +1624,8 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) { #[gpui::test] fn test_language_config_at(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + init_settings(cx, |_| {}); + cx.add_model(|cx| { let language = Language::new( LanguageConfig { @@ -2199,7 +2207,6 @@ fn assert_bracket_pairs( language: Language, cx: &mut AppContext, ) { - cx.set_global(Settings::test(cx)); let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false); let buffer = cx.add_model(|cx| { Buffer::new(0, expected_text.clone(), cx).with_language(Arc::new(language), cx) @@ -2222,3 +2229,11 @@ fn assert_bracket_pairs( bracket_pairs ); } + +fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) { + cx.set_global(SettingsStore::test(cx)); + crate::init(cx); + cx.update_global::(|settings, cx| { + settings.update_user_settings::(cx, f); + }); +} diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 85c90899521bf521fca7a7eb3bc2176a23b52301..87e4880b99186527acacd24c0a7ff609b0a015db 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1,6 +1,7 @@ mod buffer; mod diagnostic_set; mod highlight_map; +pub mod language_settings; mod outline; pub mod proto; mod syntax_map; @@ -58,6 +59,10 @@ pub use lsp::LanguageServerId; pub use outline::{Outline, OutlineItem}; pub use tree_sitter::{Parser, Tree}; +pub fn init(cx: &mut AppContext) { + language_settings::init(cx); +} + thread_local! { static PARSER: RefCell = RefCell::new(Parser::new()); } diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..80cc81a4c39658edc55702ede88b857cd95dd410 --- /dev/null +++ b/crates/language/src/language_settings.rs @@ -0,0 +1,285 @@ +use anyhow::Result; +use collections::HashMap; +use gpui::AppContext; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::{num::NonZeroU32, path::Path, sync::Arc}; + +pub fn init(cx: &mut AppContext) { + settings::register_setting::(cx); +} + +pub fn language_settings<'a>( + path: Option<&Path>, + language: Option<&str>, + cx: &'a AppContext, +) -> &'a LanguageSettings { + settings::get_setting::(path, cx).language(language) +} + +pub fn all_language_settings<'a>( + path: Option<&Path>, + cx: &'a AppContext, +) -> &'a AllLanguageSettings { + settings::get_setting::(path, cx) +} + +#[derive(Debug, Clone)] +pub struct AllLanguageSettings { + pub copilot: CopilotSettings, + defaults: LanguageSettings, + languages: HashMap, LanguageSettings>, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct LanguageSettings { + pub tab_size: NonZeroU32, + pub hard_tabs: bool, + pub soft_wrap: SoftWrap, + pub preferred_line_length: u32, + pub format_on_save: FormatOnSave, + pub remove_trailing_whitespace_on_save: bool, + pub ensure_final_newline_on_save: bool, + pub formatter: Formatter, + pub enable_language_server: bool, + pub show_copilot_suggestions: bool, + pub show_whitespaces: ShowWhitespaceSetting, +} + +#[derive(Clone, Debug, Default)] +pub struct CopilotSettings { + pub feature_enabled: bool, + pub disabled_globs: Vec, +} + +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct AllLanguageSettingsContent { + #[serde(default)] + pub features: Option, + #[serde(default)] + pub copilot: Option, + #[serde(flatten)] + pub defaults: LanguageSettingsContent, + #[serde(default, alias = "language_overrides")] + pub languages: HashMap, LanguageSettingsContent>, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct LanguageSettingsContent { + #[serde(default)] + pub tab_size: Option, + #[serde(default)] + pub hard_tabs: Option, + #[serde(default)] + pub soft_wrap: Option, + #[serde(default)] + pub preferred_line_length: Option, + #[serde(default)] + pub format_on_save: Option, + #[serde(default)] + pub remove_trailing_whitespace_on_save: Option, + #[serde(default)] + pub ensure_final_newline_on_save: Option, + #[serde(default)] + pub formatter: Option, + #[serde(default)] + pub enable_language_server: Option, + #[serde(default)] + pub show_copilot_suggestions: Option, + #[serde(default)] + pub show_whitespaces: Option, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +pub struct CopilotSettingsContent { + #[serde(default)] + pub disabled_globs: Option>, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct FeaturesContent { + pub copilot: Option, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SoftWrap { + None, + EditorWidth, + PreferredLineLength, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum FormatOnSave { + On, + Off, + LanguageServer, + External { + command: Arc, + arguments: Arc<[String]>, + }, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ShowWhitespaceSetting { + Selection, + None, + All, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum Formatter { + LanguageServer, + External { + command: Arc, + arguments: Arc<[String]>, + }, +} + +impl AllLanguageSettings { + pub fn language<'a>(&'a self, language_name: Option<&str>) -> &'a LanguageSettings { + if let Some(name) = language_name { + if let Some(overrides) = self.languages.get(name) { + return overrides; + } + } + &self.defaults + } + + pub fn copilot_enabled_for_path(&self, path: &Path) -> bool { + !self + .copilot + .disabled_globs + .iter() + .any(|glob| glob.matches_path(path)) + } + + pub fn copilot_enabled(&self, language_name: Option<&str>, path: Option<&Path>) -> bool { + if !self.copilot.feature_enabled { + return false; + } + + if let Some(path) = path { + if !self.copilot_enabled_for_path(path) { + return false; + } + } + + self.language(language_name).show_copilot_suggestions + } +} + +impl settings::Setting for AllLanguageSettings { + const KEY: Option<&'static str> = None; + + type FileContent = AllLanguageSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_settings: &[&Self::FileContent], + _: &AppContext, + ) -> Result { + // A default is provided for all settings. + let mut defaults: LanguageSettings = + serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?; + + let mut languages = HashMap::default(); + for (language_name, settings) in &default_value.languages { + let mut language_settings = defaults.clone(); + merge_settings(&mut language_settings, &settings); + languages.insert(language_name.clone(), language_settings); + } + + let mut copilot_enabled = default_value + .features + .as_ref() + .and_then(|f| f.copilot) + .ok_or_else(Self::missing_default)?; + let mut copilot_globs = default_value + .copilot + .as_ref() + .and_then(|c| c.disabled_globs.as_ref()) + .ok_or_else(Self::missing_default)?; + + for user_settings in user_settings { + if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) { + copilot_enabled = copilot; + } + if let Some(globs) = user_settings + .copilot + .as_ref() + .and_then(|f| f.disabled_globs.as_ref()) + { + copilot_globs = globs; + } + + // A user's global settings override the default global settings and + // all default language-specific settings. + merge_settings(&mut defaults, &user_settings.defaults); + for language_settings in languages.values_mut() { + merge_settings(language_settings, &user_settings.defaults); + } + + // A user's language-specific settings override default language-specific settings. + for (language_name, user_language_settings) in &user_settings.languages { + merge_settings( + languages + .entry(language_name.clone()) + .or_insert_with(|| defaults.clone()), + &user_language_settings, + ); + } + } + + Ok(Self { + copilot: CopilotSettings { + feature_enabled: copilot_enabled, + disabled_globs: copilot_globs + .iter() + .filter_map(|pattern| glob::Pattern::new(pattern).ok()) + .collect(), + }, + defaults, + languages, + }) + } +} + +fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) { + merge(&mut settings.tab_size, src.tab_size); + merge(&mut settings.hard_tabs, src.hard_tabs); + merge(&mut settings.soft_wrap, src.soft_wrap); + merge( + &mut settings.preferred_line_length, + src.preferred_line_length, + ); + merge(&mut settings.formatter, src.formatter.clone()); + merge(&mut settings.format_on_save, src.format_on_save.clone()); + merge( + &mut settings.remove_trailing_whitespace_on_save, + src.remove_trailing_whitespace_on_save, + ); + merge( + &mut settings.ensure_final_newline_on_save, + src.ensure_final_newline_on_save, + ); + merge( + &mut settings.enable_language_server, + src.enable_language_server, + ); + merge( + &mut settings.show_copilot_suggestions, + src.show_copilot_suggestions, + ); + merge(&mut settings.show_whitespaces, src.show_whitespaces); + + fn merge(target: &mut T, value: Option) { + if let Some(value) = value { + *target = value; + } + } +} diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 94872708c4525ce9d5cbc8ace636be918bdbb1d4..5d0b0a8884e1add4b4f0d9561dd4191f713c53f6 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -23,6 +23,7 @@ use gpui::{ ModelHandle, Task, WeakModelHandle, }; use language::{ + language_settings::{all_language_settings, language_settings, FormatOnSave, Formatter}, point_to_lsp, proto::{ deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version, @@ -44,7 +45,7 @@ use postage::watch; use rand::prelude::*; use search::SearchQuery; use serde::Serialize; -use settings::{FormatOnSave, Formatter, Settings}; +use settings::{Settings, SettingsStore}; use sha2::{Digest, Sha256}; use similar::{ChangeTag, TextDiff}; use std::{ @@ -64,9 +65,7 @@ use std::{ }, time::{Duration, Instant, SystemTime}, }; - use terminals::Terminals; - use util::{debug_panic, defer, merge_json_value_into, post_inc, ResultExt, TryFutureExt as _}; pub use fs::*; @@ -454,7 +453,9 @@ impl Project { client_state: None, opened_buffer: watch::channel(), client_subscriptions: Vec::new(), - _subscriptions: vec![cx.observe_global::(Self::on_settings_changed)], + _subscriptions: vec![ + cx.observe_global::(Self::on_settings_changed) + ], _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx), _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx), active_entry: None, @@ -622,7 +623,7 @@ impl Project { } fn on_settings_changed(&mut self, cx: &mut ModelContext) { - let settings = cx.global::(); + let settings = all_language_settings(None, cx); let mut language_servers_to_start = Vec::new(); for buffer in self.opened_buffers.values() { @@ -630,7 +631,10 @@ impl Project { let buffer = buffer.read(cx); if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) { - if settings.enable_language_server(Some(&language.name())) { + if settings + .language(Some(&language.name())) + .enable_language_server + { let worktree = file.worktree.read(cx); language_servers_to_start.push(( worktree.id(), @@ -645,7 +649,10 @@ impl Project { let mut language_servers_to_stop = Vec::new(); for language in self.languages.to_vec() { for lsp_adapter in language.lsp_adapters() { - if !settings.enable_language_server(Some(&language.name())) { + if !settings + .language(Some(&language.name())) + .enable_language_server + { let lsp_name = &lsp_adapter.name; for (worktree_id, started_lsp_name) in self.language_server_ids.keys() { if lsp_name == started_lsp_name { @@ -2178,10 +2185,7 @@ impl Project { language: Arc, cx: &mut ModelContext, ) { - if !cx - .global::() - .enable_language_server(Some(&language.name())) - { + if !language_settings(None, Some(&language.name()), cx).enable_language_server { return; } @@ -3228,24 +3232,18 @@ impl Project { let mut project_transaction = ProjectTransaction::default(); for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { - let ( - format_on_save, - remove_trailing_whitespace, - ensure_final_newline, - formatter, - tab_size, - ) = buffer.read_with(&cx, |buffer, cx| { - let settings = cx.global::(); + let settings = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - ( - settings.format_on_save(language_name.as_deref()), - settings.remove_trailing_whitespace_on_save(language_name.as_deref()), - settings.ensure_final_newline_on_save(language_name.as_deref()), - settings.formatter(language_name.as_deref()), - settings.tab_size(language_name.as_deref()), - ) + language_settings(buffer_abs_path.as_deref(), language_name.as_deref(), cx) + .clone() }); + let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; + let ensure_final_newline = settings.ensure_final_newline_on_save; + let format_on_save = settings.format_on_save.clone(); + let formatter = settings.formatter.clone(); + let tab_size = settings.tab_size; + // First, format buffer's whitespace according to the settings. let trailing_whitespace_diff = if remove_trailing_whitespace { Some( diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 894b27f2ee94d9081f19a68d2385e352a9eee8a9..1cbef629f357fdfdb995458c16854dec8db700ce 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -1,10 +1,9 @@ use crate::{worktree::WorktreeHandle, Event, *}; -use fs::LineEnding; -use fs::{FakeFs, RealFs}; +use fs::{FakeFs, LineEnding, RealFs}; use futures::{future, StreamExt}; -use gpui::AppContext; -use gpui::{executor::Deterministic, test::subscribe}; +use gpui::{executor::Deterministic, test::subscribe, AppContext}; use language::{ + language_settings::{AllLanguageSettings, LanguageSettingsContent}, tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig, OffsetRangeExt, Point, ToPoint, }; @@ -26,6 +25,9 @@ fn init_logger() { #[gpui::test] async fn test_symlinks(cx: &mut gpui::TestAppContext) { + init_test(cx); + cx.foreground().allow_parking(); + let dir = temp_tree(json!({ "root": { "apple": "", @@ -65,7 +67,7 @@ async fn test_managing_language_servers( deterministic: Arc, cx: &mut gpui::TestAppContext, ) { - cx.foreground().forbid_parking(); + init_test(cx); let mut rust_language = Language::new( LanguageConfig { @@ -451,7 +453,7 @@ async fn test_managing_language_servers( #[gpui::test] async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let mut language = Language::new( LanguageConfig { @@ -556,7 +558,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon #[gpui::test] async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -648,7 +650,7 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -719,7 +721,7 @@ async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let progress_token = "the-progress-token"; let mut language = Language::new( @@ -847,7 +849,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let progress_token = "the-progress-token"; let mut language = Language::new( @@ -925,7 +927,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC #[gpui::test] async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let mut language = Language::new( LanguageConfig { @@ -973,11 +975,8 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T } #[gpui::test] -async fn test_toggling_enable_language_server( - deterministic: Arc, - cx: &mut gpui::TestAppContext, -) { - deterministic.forbid_parking(); +async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) { + init_test(cx); let mut rust = Language::new( LanguageConfig { @@ -1051,14 +1050,16 @@ async fn test_toggling_enable_language_server( // Disable Rust language server, ensuring only that server gets stopped. cx.update(|cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.language_overrides.insert( - Arc::from("Rust"), - settings::EditorSettings { - enable_language_server: Some(false), - ..Default::default() - }, - ); + cx.update_global(|settings: &mut SettingsStore, cx| { + settings.update_user_settings::(cx, |settings| { + settings.languages.insert( + Arc::from("Rust"), + LanguageSettingsContent { + enable_language_server: Some(false), + ..Default::default() + }, + ); + }); }) }); fake_rust_server_1 @@ -1068,21 +1069,23 @@ async fn test_toggling_enable_language_server( // Enable Rust and disable JavaScript language servers, ensuring that the // former gets started again and that the latter stops. cx.update(|cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.language_overrides.insert( - Arc::from("Rust"), - settings::EditorSettings { - enable_language_server: Some(true), - ..Default::default() - }, - ); - settings.language_overrides.insert( - Arc::from("JavaScript"), - settings::EditorSettings { - enable_language_server: Some(false), - ..Default::default() - }, - ); + cx.update_global(|settings: &mut SettingsStore, cx| { + settings.update_user_settings::(cx, |settings| { + settings.languages.insert( + Arc::from("Rust"), + LanguageSettingsContent { + enable_language_server: Some(true), + ..Default::default() + }, + ); + settings.languages.insert( + Arc::from("JavaScript"), + LanguageSettingsContent { + enable_language_server: Some(false), + ..Default::default() + }, + ); + }); }) }); let mut fake_rust_server_2 = fake_rust_servers.next().await.unwrap(); @@ -1102,7 +1105,7 @@ async fn test_toggling_enable_language_server( #[gpui::test] async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let mut language = Language::new( LanguageConfig { @@ -1388,7 +1391,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let text = concat!( "let one = ;\n", // @@ -1457,9 +1460,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppContext) { - println!("hello from stdout"); - eprintln!("hello from stderr"); - cx.foreground().forbid_parking(); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree("/dir", json!({ "a.rs": "one two three" })) @@ -1515,7 +1516,7 @@ async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppC #[gpui::test] async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let mut language = Language::new( LanguageConfig { @@ -1673,7 +1674,7 @@ async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_edits_from_lsp_with_edits_on_adjacent_lines(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let text = " use a::b; @@ -1781,7 +1782,7 @@ async fn test_edits_from_lsp_with_edits_on_adjacent_lines(cx: &mut gpui::TestApp #[gpui::test] async fn test_invalid_edits_from_lsp(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let text = " use a::b; @@ -1902,6 +1903,8 @@ fn chunks_with_diagnostics( #[gpui::test(iterations = 10)] async fn test_definition(cx: &mut gpui::TestAppContext) { + init_test(cx); + let mut language = Language::new( LanguageConfig { name: "Rust".into(), @@ -2001,6 +2004,8 @@ async fn test_definition(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) { + init_test(cx); + let mut language = Language::new( LanguageConfig { name: "TypeScript".into(), @@ -2085,6 +2090,8 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) { + init_test(cx); + let mut language = Language::new( LanguageConfig { name: "TypeScript".into(), @@ -2138,6 +2145,8 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) { #[gpui::test(iterations = 10)] async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { + init_test(cx); + let mut language = Language::new( LanguageConfig { name: "TypeScript".into(), @@ -2254,6 +2263,8 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { #[gpui::test(iterations = 10)] async fn test_save_file(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/dir", @@ -2284,6 +2295,8 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/dir", @@ -2313,6 +2326,8 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_save_as(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree("/dir", json!({})).await; @@ -2373,6 +2388,9 @@ async fn test_rescan_and_remote_updates( deterministic: Arc, cx: &mut gpui::TestAppContext, ) { + init_test(cx); + cx.foreground().allow_parking(); + let dir = temp_tree(json!({ "a": { "file1": "", @@ -2529,6 +2547,8 @@ async fn test_buffer_identity_across_renames( deterministic: Arc, cx: &mut gpui::TestAppContext, ) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/dir", @@ -2577,6 +2597,8 @@ async fn test_buffer_identity_across_renames( #[gpui::test] async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/dir", @@ -2621,6 +2643,8 @@ async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/dir", @@ -2765,6 +2789,8 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { + init_test(cx); + let initial_contents = "aaa\nbbbbb\nc\n"; let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -2844,6 +2870,8 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/dir", @@ -2904,7 +2932,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -3146,7 +3174,7 @@ async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_rename(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); + init_test(cx); let mut language = Language::new( LanguageConfig { @@ -3284,6 +3312,8 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_search(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/dir", @@ -3339,6 +3369,8 @@ async fn test_search(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { + init_test(cx); + let search_query = "file"; let fs = FakeFs::new(cx.background()); @@ -3447,6 +3479,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { + init_test(cx); + let search_query = "file"; let fs = FakeFs::new(cx.background()); @@ -3554,6 +3588,8 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContext) { + init_test(cx); + let search_query = "file"; let fs = FakeFs::new(cx.background()); @@ -3680,3 +3716,12 @@ async fn search( }) .collect()) } + +fn init_test(cx: &mut gpui::TestAppContext) { + cx.foreground().forbid_parking(); + + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + }); +} diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index d3b28766752e13e4a28c47363ba4dcbcf5f24ccc..d52ec403fb024e0d3034d5b2c310ffa0a0597db6 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -24,6 +24,7 @@ futures.workspace = true unicase = "2.6" [dev-dependencies] +language = { path = "../language", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 7602ff7db8fefa640a8b0763656140f8f2e28be5..97684d874a7cc0077c06aeabd492c404dd6b80c5 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1360,15 +1360,12 @@ mod tests { use gpui::{TestAppContext, ViewHandle}; use project::FakeFs; use serde_json::json; + use settings::SettingsStore; use std::{collections::HashSet, path::Path}; #[gpui::test] async fn test_visible_list(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); - cx.update(|cx| { - let settings = Settings::test(cx); - cx.set_global(settings); - }); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -1456,11 +1453,7 @@ mod tests { #[gpui::test(iterations = 30)] async fn test_editing_files(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); - cx.update(|cx| { - let settings = Settings::test(cx); - cx.set_global(settings); - }); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -1776,11 +1769,7 @@ mod tests { #[gpui::test] async fn test_copy_paste(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); - cx.update(|cx| { - let settings = Settings::test(cx); - cx.set_global(settings); - }); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -1940,4 +1929,12 @@ mod tests { result } + + fn init_test(cx: &mut TestAppContext) { + cx.foreground().forbid_parking(); + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + }); + } } diff --git a/crates/project_symbols/Cargo.toml b/crates/project_symbols/Cargo.toml index 1c0d3a61658f0e2a36fa773e5f828598620edc6a..be2510c8955efd3e606e10ebe945c29355b0a90a 100644 --- a/crates/project_symbols/Cargo.toml +++ b/crates/project_symbols/Cargo.toml @@ -30,3 +30,4 @@ gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 25828f17cac959659d89770d5f353c0ba93ab81b..3176cad30864b05df7df922ad6a5fb7d80f64baf 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -244,12 +244,12 @@ mod tests { use gpui::{serde_json::json, TestAppContext}; use language::{FakeLspAdapter, Language, LanguageConfig}; use project::FakeFs; + use settings::SettingsStore; use std::{path::Path, sync::Arc}; #[gpui::test] async fn test_project_symbols(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx); let mut language = Language::new( LanguageConfig { @@ -368,6 +368,15 @@ mod tests { }); } + fn init_test(cx: &mut TestAppContext) { + cx.foreground().forbid_parking(); + cx.update(|cx| { + cx.set_global(Settings::test(cx)); + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + }); + } + fn symbol(name: &str, path: impl AsRef) -> lsp::SymbolInformation { #[allow(deprecated)] lsp::SymbolInformation { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index b0af51379d02e7dbae0cbb037cd3b62e5e925b5a..38f3894743fbe3a44d89429ebd6836a7273f6306 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -655,19 +655,11 @@ mod tests { use editor::{DisplayPoint, Editor}; use gpui::{color::Color, test::EmptyView, TestAppContext}; use language::Buffer; - use std::sync::Arc; use unindent::Unindent as _; #[gpui::test] async fn test_search_simple(cx: &mut TestAppContext) { - let fonts = cx.font_cache(); - let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default); - theme.search.match_background = Color::red(); - cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.theme = Arc::new(theme); - cx.set_global(settings) - }); + crate::project_search::tests::init_test(cx); let buffer = cx.add_model(|cx| { Buffer::new( diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 05d27b824c7bed5a0eb010c60e57d936af735880..4dc947e5f6bc539a0f3bfc4a0706b1e46022f550 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1146,25 +1146,18 @@ impl ToolbarItemView for ProjectSearchBar { } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use editor::DisplayPoint; use gpui::{color::Color, executor::Deterministic, TestAppContext}; use project::FakeFs; use serde_json::json; + use settings::SettingsStore; use std::sync::Arc; #[gpui::test] async fn test_project_search(deterministic: Arc, cx: &mut TestAppContext) { - let fonts = cx.font_cache(); - let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default); - theme.search.match_background = Color::red(); - cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.theme = Arc::new(theme); - cx.set_global(settings); - cx.set_global(ActiveSearches::default()); - }); + init_test(cx); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -1279,4 +1272,20 @@ mod tests { ); }); } + + pub fn init_test(cx: &mut TestAppContext) { + let fonts = cx.font_cache(); + let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default); + theme.search.match_background = Color::red(); + + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + cx.set_global(ActiveSearches::default()); + let mut settings = Settings::test(cx); + settings.theme = Arc::new(theme); + cx.set_global(settings); + + language::init(cx); + }); + } } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index c476024d629fa65f4f86cf677665f1b2874476b7..f5e50aaffb8561e15715796487e618875ff73b41 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -3,7 +3,7 @@ mod keymap_file; mod settings_file; mod settings_store; -use anyhow::bail; +use anyhow::{bail, Result}; use gpui::{ font_cache::{FamilyId, FontCache}, fonts, AppContext, AssetSource, @@ -19,7 +19,7 @@ use sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; -use std::{borrow::Cow, collections::HashMap, num::NonZeroU32, path::Path, str, sync::Arc}; +use std::{borrow::Cow, collections::HashMap, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; @@ -33,7 +33,6 @@ pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settin #[derive(Clone)] pub struct Settings { - pub features: Features, pub buffer_font_family_name: String, pub buffer_font_features: fonts::Features, pub buffer_font_family: FamilyId, @@ -46,13 +45,8 @@ pub struct Settings { pub show_call_status_icon: bool, pub autosave: Autosave, pub default_dock_anchor: DockAnchor, - pub editor_defaults: EditorSettings, - pub editor_overrides: EditorSettings, pub git: GitSettings, pub git_overrides: GitSettings, - pub copilot: CopilotSettings, - pub language_defaults: HashMap, EditorSettings>, - pub language_overrides: HashMap, EditorSettings>, pub lsp: HashMap, LspSettings>, pub theme: Arc, pub base_keymap: BaseKeymap, @@ -67,7 +61,7 @@ impl Setting for Settings { defaults: &Self::FileContent, user_values: &[&Self::FileContent], cx: &AppContext, - ) -> Self { + ) -> Result { let buffer_font_features = defaults.buffer_font_features.clone().unwrap(); let themes = cx.global::>(); @@ -90,50 +84,18 @@ impl Setting for Settings { show_call_status_icon: defaults.show_call_status_icon.unwrap(), autosave: defaults.autosave.unwrap(), default_dock_anchor: defaults.default_dock_anchor.unwrap(), - editor_defaults: EditorSettings { - tab_size: defaults.editor.tab_size, - hard_tabs: defaults.editor.hard_tabs, - soft_wrap: defaults.editor.soft_wrap, - preferred_line_length: defaults.editor.preferred_line_length, - remove_trailing_whitespace_on_save: defaults - .editor - .remove_trailing_whitespace_on_save, - ensure_final_newline_on_save: defaults.editor.ensure_final_newline_on_save, - format_on_save: defaults.editor.format_on_save.clone(), - formatter: defaults.editor.formatter.clone(), - enable_language_server: defaults.editor.enable_language_server, - show_copilot_suggestions: defaults.editor.show_copilot_suggestions, - show_whitespaces: defaults.editor.show_whitespaces, - }, - editor_overrides: Default::default(), - copilot: CopilotSettings { - disabled_globs: defaults - .copilot - .clone() - .unwrap() - .disabled_globs - .unwrap() - .into_iter() - .map(|s| glob::Pattern::new(&s).unwrap()) - .collect(), - }, git: defaults.git.unwrap(), git_overrides: Default::default(), - language_defaults: defaults.languages.clone(), - language_overrides: Default::default(), lsp: defaults.lsp.clone(), theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), base_keymap: Default::default(), - features: Features { - copilot: defaults.features.copilot.unwrap(), - }, }; for value in user_values.into_iter().copied().cloned() { this.set_user_settings(value, themes.as_ref(), cx.font_cache()); } - this + Ok(this) } fn json_schema( @@ -247,18 +209,6 @@ impl BaseKeymap { .unwrap_or_default() } } - -#[derive(Clone, Debug, Default)] -pub struct CopilotSettings { - pub disabled_globs: Vec, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -pub struct CopilotSettingsContent { - #[serde(default)] - pub disabled_globs: Option>, -} - #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct GitSettings { pub git_gutter: Option, @@ -273,52 +223,6 @@ pub enum GitGutter { Hide, } -pub struct GitGutterConfig {} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -pub struct EditorSettings { - pub tab_size: Option, - pub hard_tabs: Option, - pub soft_wrap: Option, - pub preferred_line_length: Option, - pub format_on_save: Option, - pub remove_trailing_whitespace_on_save: Option, - pub ensure_final_newline_on_save: Option, - pub formatter: Option, - pub enable_language_server: Option, - pub show_copilot_suggestions: Option, - pub show_whitespaces: Option, -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum SoftWrap { - None, - EditorWidth, - PreferredLineLength, -} -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum FormatOnSave { - On, - Off, - LanguageServer, - External { - command: String, - arguments: Vec, - }, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum Formatter { - LanguageServer, - External { - command: String, - arguments: Vec, - }, -} - #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Autosave { @@ -374,8 +278,6 @@ pub struct SettingsFileContent { #[serde(default)] pub buffer_font_features: Option, #[serde(default)] - pub copilot: Option, - #[serde(default)] pub active_pane_magnification: Option, #[serde(default)] pub cursor_blink: Option, @@ -391,21 +293,14 @@ pub struct SettingsFileContent { pub autosave: Option, #[serde(default)] pub default_dock_anchor: Option, - #[serde(flatten)] - pub editor: EditorSettings, #[serde(default)] pub git: Option, #[serde(default)] - #[serde(alias = "language_overrides")] - pub languages: HashMap, EditorSettings>, - #[serde(default)] pub lsp: HashMap, LspSettings>, #[serde(default)] pub theme: Option, #[serde(default)] pub base_keymap: Option, - #[serde(default)] - pub features: FeaturesContent, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] @@ -414,26 +309,6 @@ pub struct LspSettings { pub initialization_options: Option, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Features { - pub copilot: bool, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct FeaturesContent { - pub copilot: Option, -} - -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ShowWhitespaces { - #[default] - Selection, - None, - All, -} - impl Settings { pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> { match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() { @@ -448,12 +323,6 @@ impl Settings { font_cache: &FontCache, themes: &ThemeRegistry, ) -> Self { - #[track_caller] - fn required(value: Option) -> Option { - assert!(value.is_some(), "missing default setting value"); - value - } - let defaults: SettingsFileContent = settings_store::parse_json_with_comments( str::from_utf8(assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap().as_ref()).unwrap(), ) @@ -478,44 +347,11 @@ impl Settings { show_call_status_icon: defaults.show_call_status_icon.unwrap(), autosave: defaults.autosave.unwrap(), default_dock_anchor: defaults.default_dock_anchor.unwrap(), - editor_defaults: EditorSettings { - tab_size: required(defaults.editor.tab_size), - hard_tabs: required(defaults.editor.hard_tabs), - soft_wrap: required(defaults.editor.soft_wrap), - preferred_line_length: required(defaults.editor.preferred_line_length), - remove_trailing_whitespace_on_save: required( - defaults.editor.remove_trailing_whitespace_on_save, - ), - ensure_final_newline_on_save: required( - defaults.editor.ensure_final_newline_on_save, - ), - format_on_save: required(defaults.editor.format_on_save), - formatter: required(defaults.editor.formatter), - enable_language_server: required(defaults.editor.enable_language_server), - show_copilot_suggestions: required(defaults.editor.show_copilot_suggestions), - show_whitespaces: required(defaults.editor.show_whitespaces), - }, - editor_overrides: Default::default(), - copilot: CopilotSettings { - disabled_globs: defaults - .copilot - .unwrap() - .disabled_globs - .unwrap() - .into_iter() - .map(|s| glob::Pattern::new(&s).unwrap()) - .collect(), - }, git: defaults.git.unwrap(), git_overrides: Default::default(), - language_defaults: defaults.languages, - language_overrides: Default::default(), lsp: defaults.lsp.clone(), theme: themes.get(&defaults.theme.unwrap()).unwrap(), base_keymap: Default::default(), - features: Features { - copilot: defaults.features.copilot.unwrap(), - }, } } @@ -565,121 +401,11 @@ impl Settings { merge(&mut self.autosave, data.autosave); merge(&mut self.default_dock_anchor, data.default_dock_anchor); merge(&mut self.base_keymap, data.base_keymap); - merge(&mut self.features.copilot, data.features.copilot); - - if let Some(copilot) = data.copilot { - if let Some(disabled_globs) = copilot.disabled_globs { - self.copilot.disabled_globs = disabled_globs - .into_iter() - .filter_map(|s| glob::Pattern::new(&s).ok()) - .collect() - } - } - self.editor_overrides = data.editor; + self.git_overrides = data.git.unwrap_or_default(); - self.language_overrides = data.languages; self.lsp = data.lsp; } - pub fn with_language_defaults( - mut self, - language_name: impl Into>, - overrides: EditorSettings, - ) -> Self { - self.language_defaults - .insert(language_name.into(), overrides); - self - } - - pub fn features(&self) -> &Features { - &self.features - } - - pub fn show_copilot_suggestions(&self, language: Option<&str>, path: Option<&Path>) -> bool { - if !self.features.copilot { - return false; - } - - if !self.copilot_enabled_for_language(language) { - return false; - } - - if let Some(path) = path { - if !self.copilot_enabled_for_path(path) { - return false; - } - } - - true - } - - pub fn copilot_enabled_for_path(&self, path: &Path) -> bool { - !self - .copilot - .disabled_globs - .iter() - .any(|glob| glob.matches_path(path)) - } - - pub fn copilot_enabled_for_language(&self, language: Option<&str>) -> bool { - self.language_setting(language, |settings| settings.show_copilot_suggestions) - } - - pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 { - self.language_setting(language, |settings| settings.tab_size) - } - - pub fn show_whitespaces(&self, language: Option<&str>) -> ShowWhitespaces { - self.language_setting(language, |settings| settings.show_whitespaces) - } - - pub fn hard_tabs(&self, language: Option<&str>) -> bool { - self.language_setting(language, |settings| settings.hard_tabs) - } - - pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap { - self.language_setting(language, |settings| settings.soft_wrap) - } - - pub fn preferred_line_length(&self, language: Option<&str>) -> u32 { - self.language_setting(language, |settings| settings.preferred_line_length) - } - - pub fn remove_trailing_whitespace_on_save(&self, language: Option<&str>) -> bool { - self.language_setting(language, |settings| { - settings.remove_trailing_whitespace_on_save.clone() - }) - } - - pub fn ensure_final_newline_on_save(&self, language: Option<&str>) -> bool { - self.language_setting(language, |settings| { - settings.ensure_final_newline_on_save.clone() - }) - } - - pub fn format_on_save(&self, language: Option<&str>) -> FormatOnSave { - self.language_setting(language, |settings| settings.format_on_save.clone()) - } - - pub fn formatter(&self, language: Option<&str>) -> Formatter { - self.language_setting(language, |settings| settings.formatter.clone()) - } - - pub fn enable_language_server(&self, language: Option<&str>) -> bool { - self.language_setting(language, |settings| settings.enable_language_server) - } - - fn language_setting(&self, language: Option<&str>, f: F) -> R - where - F: Fn(&EditorSettings) -> Option, - { - None.or_else(|| language.and_then(|l| self.language_overrides.get(l).and_then(&f))) - .or_else(|| f(&self.editor_overrides)) - .or_else(|| language.and_then(|l| self.language_defaults.get(l).and_then(&f))) - .or_else(|| f(&self.editor_defaults)) - .expect("missing default") - } - pub fn git_gutter(&self) -> GitGutter { self.git_overrides.git_gutter.unwrap_or_else(|| { self.git @@ -706,29 +432,11 @@ impl Settings { show_call_status_icon: true, autosave: Autosave::Off, default_dock_anchor: DockAnchor::Bottom, - editor_defaults: EditorSettings { - tab_size: Some(4.try_into().unwrap()), - hard_tabs: Some(false), - soft_wrap: Some(SoftWrap::None), - preferred_line_length: Some(80), - remove_trailing_whitespace_on_save: Some(true), - ensure_final_newline_on_save: Some(true), - format_on_save: Some(FormatOnSave::On), - formatter: Some(Formatter::LanguageServer), - enable_language_server: Some(true), - show_copilot_suggestions: Some(true), - show_whitespaces: Some(ShowWhitespaces::None), - }, - editor_overrides: Default::default(), - copilot: Default::default(), git: Default::default(), git_overrides: Default::default(), - language_defaults: Default::default(), - language_overrides: Default::default(), lsp: Default::default(), theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default), base_keymap: Default::default(), - features: Features { copilot: true }, } } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 936f3a7099034b8322fe513a460c41682b50e823..070da5d1ea6d440097e9d42db6507f4940d05d5e 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -42,12 +42,12 @@ pub fn test_settings() -> String { serde_json::json!({ "buffer_font_family": "Courier", "buffer_font_features": {}, - "default_buffer_font_size": 14, - "preferred_line_length": 80, + "buffer_font_size": 14, "theme": theme::EMPTY_THEME_NAME, }), &mut value, ); + value.as_object_mut().unwrap().remove("languages"); serde_json::to_string(&value).unwrap() } diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index caa77a36035f84d470d1288977fc71e935475b45..c389a7bf84fefc8d985ed0c6fc4bac2786ef927f 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,5 +1,5 @@ -use anyhow::{anyhow, Result}; -use collections::{btree_map, hash_map, BTreeMap, HashMap, HashSet}; +use anyhow::Result; +use collections::{btree_map, hash_map, BTreeMap, HashMap}; use gpui::AppContext; use lazy_static::lazy_static; use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema}; @@ -8,7 +8,6 @@ use smallvec::SmallVec; use std::{ any::{type_name, Any, TypeId}, fmt::Debug, - mem, ops::Range, path::Path, str, @@ -37,7 +36,9 @@ pub trait Setting: 'static { default_value: &Self::FileContent, user_values: &[&Self::FileContent], cx: &AppContext, - ) -> Self; + ) -> Result + where + Self: Sized; fn json_schema(generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams) -> RootSchema { generator.root_schema_for::() @@ -57,7 +58,7 @@ pub trait Setting: 'static { fn load_via_json_merge( default_value: &Self::FileContent, user_values: &[&Self::FileContent], - ) -> Self + ) -> Result where Self: DeserializeOwned, { @@ -65,7 +66,11 @@ pub trait Setting: 'static { for value in [default_value].iter().chain(user_values) { merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged); } - serde_json::from_value(merged).unwrap() + Ok(serde_json::from_value(merged)?) + } + + fn missing_default() -> anyhow::Error { + anyhow::anyhow!("missing default") } } @@ -78,10 +83,9 @@ pub struct SettingsJsonSchemaParams<'a> { #[derive(Default)] pub struct SettingsStore { setting_values: HashMap>, - default_deserialized_settings: Option, - user_deserialized_settings: Option, - local_deserialized_settings: BTreeMap, DeserializedSettingMap>, - changed_setting_types: HashSet, + default_deserialized_settings: Option, + user_deserialized_settings: Option, + local_deserialized_settings: BTreeMap, serde_json::Value>, tab_size_callback: Option<(TypeId, Box Option>)>, } @@ -98,9 +102,9 @@ trait AnySettingValue { fn load_setting( &self, default_value: &DeserializedSetting, - custom: &[&DeserializedSetting], + custom: &[DeserializedSetting], cx: &AppContext, - ) -> Box; + ) -> Result>; fn value_for_path(&self, path: Option<&Path>) -> &dyn Any; fn set_global_value(&mut self, value: Box); fn set_local_value(&mut self, path: Arc, value: Box); @@ -113,11 +117,6 @@ trait AnySettingValue { struct DeserializedSetting(Box); -struct DeserializedSettingMap { - untyped: serde_json::Value, - typed: HashMap, -} - impl SettingsStore { /// Add a new type of setting to the store. pub fn register_setting(&mut self, cx: &AppContext) { @@ -132,23 +131,27 @@ impl SettingsStore { local_values: Vec::new(), })); - if let Some(default_settings) = self.default_deserialized_settings.as_mut() { - Self::load_setting_in_map(setting_type_id, setting_value, default_settings); + if let Some(default_settings) = &self.default_deserialized_settings { + if let Some(default_settings) = setting_value + .deserialize_setting(default_settings) + .log_err() + { + let mut user_values_stack = Vec::new(); - let mut user_values_stack = Vec::new(); - if let Some(user_settings) = self.user_deserialized_settings.as_mut() { - Self::load_setting_in_map(setting_type_id, setting_value, user_settings); - if let Some(user_value) = user_settings.typed.get(&setting_type_id) { - user_values_stack = vec![user_value]; + if let Some(user_settings) = &self.user_deserialized_settings { + if let Some(user_settings) = + setting_value.deserialize_setting(user_settings).log_err() + { + user_values_stack = vec![user_settings]; + } } - } - if let Some(default_deserialized_value) = default_settings.typed.get(&setting_type_id) { - setting_value.set_global_value(setting_value.load_setting( - default_deserialized_value, - &user_values_stack, - cx, - )); + if let Some(setting) = setting_value + .load_setting(&default_settings, &user_values_stack, cx) + .log_err() + { + setting_value.set_global_value(setting); + } } } } @@ -173,7 +176,7 @@ impl SettingsStore { pub fn untyped_user_settings(&self) -> &serde_json::Value { self.user_deserialized_settings .as_ref() - .map_or(&serde_json::Value::Null, |s| &s.untyped) + .unwrap_or(&serde_json::Value::Null) } #[cfg(any(test, feature = "test-support"))] @@ -181,6 +184,7 @@ impl SettingsStore { let mut this = Self::default(); this.set_default_settings(&crate::test_settings(), cx) .unwrap(); + this.set_user_settings("{}", cx).unwrap(); this } @@ -194,11 +198,11 @@ impl SettingsStore { cx: &AppContext, update: impl FnOnce(&mut T::FileContent), ) { - let old_text = if let Some(user_settings) = &self.user_deserialized_settings { - serde_json::to_string(&user_settings.untyped).unwrap() - } else { - String::new() - }; + if self.user_deserialized_settings.is_none() { + self.set_user_settings("{}", cx).unwrap(); + } + let old_text = + serde_json::to_string(self.user_deserialized_settings.as_ref().unwrap()).unwrap(); let new_text = self.new_text_for_update::(old_text, update); self.set_user_settings(&new_text, cx).unwrap(); } @@ -212,7 +216,7 @@ impl SettingsStore { ) -> String { let edits = self.edits_for_update::(&old_text, update); let mut new_text = old_text; - for (range, replacement) in edits.into_iter().rev() { + for (range, replacement) in edits.into_iter() { new_text.replace_range(range, &replacement); } new_text @@ -226,26 +230,31 @@ impl SettingsStore { update: impl FnOnce(&mut T::FileContent), ) -> Vec<(Range, String)> { let setting_type_id = TypeId::of::(); + let old_content = self - .user_deserialized_settings - .as_ref() - .unwrap() - .typed + .setting_values .get(&setting_type_id) - .unwrap() + .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) + .deserialize_setting( + self.user_deserialized_settings + .as_ref() + .expect("no user settings loaded"), + ) + .unwrap_or_else(|e| { + panic!( + "could not deserialize setting type {} from user settings: {}", + type_name::(), + e + ) + }) .0 - .downcast_ref::() - .unwrap() - .clone(); + .downcast::() + .unwrap(); let mut new_content = old_content.clone(); update(&mut new_content); - let mut parser = tree_sitter::Parser::new(); - parser.set_language(tree_sitter_json::language()).unwrap(); - let tree = parser.parse(text, None).unwrap(); - - let old_value = &serde_json::to_value(old_content).unwrap(); - let new_value = &serde_json::to_value(new_content).unwrap(); + let old_value = &serde_json::to_value(&old_content).unwrap(); + let new_value = serde_json::to_value(new_content).unwrap(); let mut key_path = Vec::new(); if let Some(key) = T::KEY { @@ -254,16 +263,15 @@ impl SettingsStore { let mut edits = Vec::new(); let tab_size = self.json_tab_size(); + let mut text = text.to_string(); update_value_in_json_text( - &text, - &tree, + &mut text, &mut key_path, tab_size, &old_value, &new_value, &mut edits, ); - edits.sort_unstable_by_key(|e| e.0.start); return edits; } @@ -300,19 +308,8 @@ impl SettingsStore { default_settings_content: &str, cx: &AppContext, ) -> Result<()> { - let deserialized_setting_map = self.load_setting_map(default_settings_content)?; - if deserialized_setting_map.typed.len() != self.setting_values.len() { - return Err(anyhow!( - "default settings file is missing fields: {:?}", - self.setting_values - .iter() - .filter(|(type_id, _)| !deserialized_setting_map.typed.contains_key(type_id)) - .map(|(name, _)| *name) - .collect::>() - )); - } - self.default_deserialized_settings = Some(deserialized_setting_map); - self.recompute_values(false, None, None, cx); + self.default_deserialized_settings = Some(serde_json::from_str(default_settings_content)?); + self.recompute_values(None, cx)?; Ok(()) } @@ -322,10 +319,8 @@ impl SettingsStore { user_settings_content: &str, cx: &AppContext, ) -> Result<()> { - let user_settings = self.load_setting_map(user_settings_content)?; - let old_user_settings = - mem::replace(&mut self.user_deserialized_settings, Some(user_settings)); - self.recompute_values(true, None, old_user_settings, cx); + self.user_deserialized_settings = Some(serde_json::from_str(user_settings_content)?); + self.recompute_values(None, cx)?; Ok(()) } @@ -336,14 +331,13 @@ impl SettingsStore { settings_content: Option<&str>, cx: &AppContext, ) -> Result<()> { - let removed_map = if let Some(settings_content) = settings_content { + if let Some(content) = settings_content { self.local_deserialized_settings - .insert(path.clone(), self.load_setting_map(settings_content)?); - None + .insert(path.clone(), serde_json::from_str(content)?); } else { - self.local_deserialized_settings.remove(&path) - }; - self.recompute_values(true, Some(&path), removed_map, cx); + self.local_deserialized_settings.remove(&path); + } + self.recompute_values(Some(&path), cx)?; Ok(()) } @@ -422,136 +416,78 @@ impl SettingsStore { fn recompute_values( &mut self, - user_settings_changed: bool, changed_local_path: Option<&Path>, - old_settings_map: Option, cx: &AppContext, - ) { - // Identify all of the setting types that have changed. - let new_settings_map = if let Some(changed_path) = changed_local_path { - self.local_deserialized_settings.get(changed_path) - } else if user_settings_changed { - self.user_deserialized_settings.as_ref() - } else { - self.default_deserialized_settings.as_ref() - }; - self.changed_setting_types.clear(); - for map in [old_settings_map.as_ref(), new_settings_map] { - if let Some(map) = map { - self.changed_setting_types.extend(map.typed.keys()); - } - } - - // Reload the global and local values for every changed setting. - let mut user_values_stack = Vec::<&DeserializedSetting>::new(); - for setting_type_id in self.changed_setting_types.iter() { - let setting_value = self.setting_values.get_mut(setting_type_id).unwrap(); - - // Build the prioritized list of deserialized values to pass to the setting's - // load function. - user_values_stack.clear(); - if let Some(user_settings) = &self.user_deserialized_settings { - if let Some(user_value) = user_settings.typed.get(setting_type_id) { - user_values_stack.push(&user_value); + ) -> Result<()> { + // Reload the global and local values for every setting. + let mut user_settings_stack = Vec::::new(); + let mut paths_stack = Vec::>::new(); + for setting_value in self.setting_values.values_mut() { + if let Some(default_settings) = &self.default_deserialized_settings { + let default_settings = setting_value.deserialize_setting(default_settings)?; + + user_settings_stack.clear(); + paths_stack.clear(); + + if let Some(user_settings) = &self.user_deserialized_settings { + if let Some(user_settings) = + setting_value.deserialize_setting(user_settings).log_err() + { + user_settings_stack.push(user_settings); + paths_stack.push(None); + } } - } - let default_deserialized_value = if let Some(value) = self - .default_deserialized_settings - .as_ref() - .and_then(|map| map.typed.get(setting_type_id)) - { - value - } else { - continue; - }; - - // If the global settings file changed, reload the global value for the field. - if changed_local_path.is_none() { - setting_value.set_global_value(setting_value.load_setting( - default_deserialized_value, - &user_values_stack, - cx, - )); - } - - // Reload the local values for the setting. - let user_value_stack_len = user_values_stack.len(); - for (path, deserialized_values) in &self.local_deserialized_settings { - // If a local settings file changed, then avoid recomputing local - // settings for any path outside of that directory. - if changed_local_path.map_or(false, |changed_local_path| { - !path.starts_with(changed_local_path) - }) { - continue; + // If the global settings file changed, reload the global value for the field. + if changed_local_path.is_none() { + setting_value.set_global_value(setting_value.load_setting( + &default_settings, + &user_settings_stack, + cx, + )?); } - // Ignore recomputing settings for any path that hasn't customized that setting. - let Some(deserialized_value) = deserialized_values.typed.get(setting_type_id) else { - continue; - }; - - // Build a stack of all of the local values for that setting. - user_values_stack.truncate(user_value_stack_len); - for (preceding_path, preceding_deserialized_values) in - &self.local_deserialized_settings - { - if preceding_path >= path { + // Reload the local values for the setting. + for (path, local_settings) in &self.local_deserialized_settings { + // Build a stack of all of the local values for that setting. + while let Some(prev_path) = paths_stack.last() { + if let Some(prev_path) = prev_path { + if !path.starts_with(prev_path) { + paths_stack.pop(); + user_settings_stack.pop(); + continue; + } + } break; } - if !path.starts_with(preceding_path) { - continue; - } - if let Some(preceding_deserialized_value) = - preceding_deserialized_values.typed.get(setting_type_id) + if let Some(local_settings) = + setting_value.deserialize_setting(&local_settings).log_err() { - user_values_stack.push(&*preceding_deserialized_value); + paths_stack.push(Some(path.as_ref())); + user_settings_stack.push(local_settings); + + // If a local settings file changed, then avoid recomputing local + // settings for any path outside of that directory. + if changed_local_path.map_or(false, |changed_local_path| { + !path.starts_with(changed_local_path) + }) { + continue; + } + + setting_value.set_local_value( + path.clone(), + setting_value.load_setting( + &default_settings, + &user_settings_stack, + cx, + )?, + ); } } - user_values_stack.push(&*deserialized_value); - - // Load the local value for the field. - setting_value.set_local_value( - path.clone(), - setting_value.load_setting(default_deserialized_value, &user_values_stack, cx), - ); } } - } - - /// Deserialize the given JSON string into a map keyed by setting type. - /// - /// Returns an error if the string doesn't contain a valid JSON object. - fn load_setting_map(&self, json: &str) -> Result { - let mut map = DeserializedSettingMap { - untyped: parse_json_with_comments(json)?, - typed: HashMap::default(), - }; - for (setting_type_id, setting_value) in self.setting_values.iter() { - Self::load_setting_in_map(*setting_type_id, setting_value, &mut map); - } - Ok(map) - } - - fn load_setting_in_map( - setting_type_id: TypeId, - setting_value: &Box, - map: &mut DeserializedSettingMap, - ) { - let value = if let Some(setting_key) = setting_value.key() { - if let Some(value) = map.untyped.get(setting_key) { - value - } else { - return; - } - } else { - &map.untyped - }; - - if let Some(deserialized_value) = setting_value.deserialize_setting(&value).log_err() { - map.typed.insert(setting_type_id, deserialized_value); - } + Ok(()) } } @@ -567,18 +503,21 @@ impl AnySettingValue for SettingValue { fn load_setting( &self, default_value: &DeserializedSetting, - user_values: &[&DeserializedSetting], + user_values: &[DeserializedSetting], cx: &AppContext, - ) -> Box { + ) -> Result> { let default_value = default_value.0.downcast_ref::().unwrap(); let values: SmallVec<[&T::FileContent; 6]> = user_values .iter() .map(|value| value.0.downcast_ref().unwrap()) .collect(); - Box::new(T::load(default_value, &values, cx)) + Ok(Box::new(T::load(default_value, &values, cx)?)) } - fn deserialize_setting(&self, json: &serde_json::Value) -> Result { + fn deserialize_setting(&self, mut json: &serde_json::Value) -> Result { + if let Some(key) = T::KEY { + json = json.get(key).unwrap_or(&serde_json::Value::Null); + } let value = T::FileContent::deserialize(json)?; Ok(DeserializedSetting(Box::new(value))) } @@ -593,7 +532,7 @@ impl AnySettingValue for SettingValue { } self.global_value .as_ref() - .expect("no default value for setting") + .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name())) } fn set_global_value(&mut self, value: Box) { @@ -634,8 +573,7 @@ impl AnySettingValue for SettingValue { // } fn update_value_in_json_text<'a>( - text: &str, - syntax_tree: &tree_sitter::Tree, + text: &mut String, key_path: &mut Vec<&'a str>, tab_size: usize, old_value: &'a serde_json::Value, @@ -653,7 +591,6 @@ fn update_value_in_json_text<'a>( let new_sub_value = new_object.get(key).unwrap_or(&serde_json::Value::Null); update_value_in_json_text( text, - syntax_tree, key_path, tab_size, old_sub_value, @@ -667,7 +604,6 @@ fn update_value_in_json_text<'a>( if !old_object.contains_key(key) { update_value_in_json_text( text, - syntax_tree, key_path, tab_size, &serde_json::Value::Null, @@ -679,14 +615,14 @@ fn update_value_in_json_text<'a>( } } else if old_value != new_value { let (range, replacement) = - replace_value_in_json_text(text, syntax_tree, &key_path, tab_size, &new_value); + replace_value_in_json_text(text, &key_path, tab_size, &new_value); + text.replace_range(range.clone(), &replacement); edits.push((range, replacement)); } } fn replace_value_in_json_text( text: &str, - syntax_tree: &tree_sitter::Tree, key_path: &[&str], tab_size: usize, new_value: impl Serialize, @@ -702,6 +638,10 @@ fn replace_value_in_json_text( .unwrap(); } + let mut parser = tree_sitter::Parser::new(); + parser.set_language(tree_sitter_json::language()).unwrap(); + let syntax_tree = parser.parse(text, None).unwrap(); + let mut cursor = tree_sitter::QueryCursor::new(); let has_language_overrides = text.contains(LANGUAGE_OVERRIDES); @@ -1152,7 +1092,7 @@ mod tests { store.set_user_settings(&old_json, cx).ok(); let edits = store.edits_for_update::(&old_json, update); let mut new_json = old_json; - for (range, replacement) in edits.into_iter().rev() { + for (range, replacement) in edits.into_iter() { new_json.replace_range(range, &replacement); } pretty_assertions::assert_eq!(new_json, expected_new_json); @@ -1180,7 +1120,7 @@ mod tests { default_value: &UserSettingsJson, user_values: &[&UserSettingsJson], _: &AppContext, - ) -> Self { + ) -> Result { Self::load_via_json_merge(default_value, user_values) } } @@ -1196,7 +1136,7 @@ mod tests { default_value: &Option, user_values: &[&Option], _: &AppContext, - ) -> Self { + ) -> Result { Self::load_via_json_merge(default_value, user_values) } } @@ -1224,7 +1164,7 @@ mod tests { default_value: &MultiKeySettingsJson, user_values: &[&MultiKeySettingsJson], _: &AppContext, - ) -> Self { + ) -> Result { Self::load_via_json_merge(default_value, user_values) } } @@ -1257,7 +1197,7 @@ mod tests { default_value: &JournalSettingsJson, user_values: &[&JournalSettingsJson], _: &AppContext, - ) -> Self { + ) -> Result { Self::load_via_json_merge(default_value, user_values) } } @@ -1278,7 +1218,7 @@ mod tests { type FileContent = Self; - fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Self { + fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Result { Self::load_via_json_merge(default_value, user_values) } } diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index bf4fc46ee3754e20fcbe5f54de09e63293e7d6f8..cc4580daa123efad169158bbd9ca9e7ab9e19fdf 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -159,7 +159,7 @@ impl settings::Setting for TerminalSettings { default_value: &Self::FileContent, user_values: &[&Self::FileContent], _: &AppContext, - ) -> Self { + ) -> Result { Self::load_via_json_merge(default_value, user_values) } } diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 5f7cf0a5a33c534bfeeaddb74dd5dc0d8b2377df..c34a5b469b40e73cb13bbf84803576c6ba48b643 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -12,6 +12,7 @@ doctest = false neovim = ["nvim-rs", "async-compat", "async-trait", "tokio"] [dependencies] +anyhow.workspace = true serde.workspace = true serde_derive.workspace = true itertools = "0.10" diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index ac86a082354d3eec21bb6747250fa2ecb3dd4bcf..9ac40831d1bf6b47f093f19e4175f784d4d98691 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -17,14 +17,16 @@ pub struct VimTestContext<'a> { impl<'a> VimTestContext<'a> { pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> { let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await; + cx.update(|cx| { search::init(cx); crate::init(cx); + }); + cx.update(|cx| { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| *s = Some(enabled)); }); - settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap(); }); diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 65c46e81cfa59c8abf321792f75f95313fc87600..23a28b354437ee1316970ecbecd1cd44cf38f408 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -10,8 +10,7 @@ mod state; mod utils; mod visual; -use std::sync::Arc; - +use anyhow::Result; use collections::CommandPaletteFilter; use editor::{Bias, Cancel, Editor, EditorMode, Event}; use gpui::{ @@ -24,6 +23,7 @@ use normal::normal_replace; use serde::Deserialize; use settings::{Setting, SettingsStore}; use state::{Mode, Operator, VimState}; +use std::sync::Arc; use visual::visual_replace; use workspace::{self, Workspace}; @@ -343,14 +343,10 @@ impl Setting for VimModeSetting { default_value: &Self::FileContent, user_values: &[&Self::FileContent], _: &AppContext, - ) -> Self { - Self( - user_values - .first() - .map(|e| **e) - .flatten() - .unwrap_or(default_value.unwrap()), - ) + ) -> Result { + Ok(Self(user_values.iter().rev().find_map(|v| **v).unwrap_or( + default_value.ok_or_else(Self::missing_default)?, + ))) } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 0e3ce2a7241efefa7e5000b4685e2ee2e66b353c..768142d7297674084688271dbe6cfd48006b681d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -369,8 +369,12 @@ pub struct AppState { impl AppState { #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut AppContext) -> Arc { - cx.set_global(settings::SettingsStore::test(cx)); - cx.set_global(Settings::test(cx)); + use settings::SettingsStore; + + if !cx.has_global::() { + cx.set_global(SettingsStore::test(cx)); + cx.set_global(Settings::test(cx)); + } let fs = fs::FakeFs::new(cx.background().clone()); let languages = Arc::new(LanguageRegistry::test()); diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 84c5798b07d53bbcbe9dbb930cedb6f80db2c4ad..7e4ddcef19189cb0a5704d43ca78a6164186d1de 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -249,16 +249,21 @@ impl super::LspAdapter for CLspAdapter { #[cfg(test)] mod tests { use gpui::TestAppContext; - use language::{AutoindentMode, Buffer}; - use settings::Settings; + use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; + use settings::SettingsStore; + use std::num::NonZeroU32; #[gpui::test] async fn test_c_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, |s| { + s.defaults.tab_size = NonZeroU32::new(2); + }); + }); }); let language = crate::languages::language("c", tree_sitter_c::language(), None).await; diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index acd31e82059b0d04aa476340e30e87b4c1867a9d..7aaddf5fe8a72d5731322e911b5a5c87473dfdbe 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -170,8 +170,9 @@ impl LspAdapter for PythonLspAdapter { #[cfg(test)] mod tests { use gpui::{ModelContext, TestAppContext}; - use language::{AutoindentMode, Buffer}; - use settings::Settings; + use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; + use settings::SettingsStore; + use std::num::NonZeroU32; #[gpui::test] async fn test_python_autoindent(cx: &mut TestAppContext) { @@ -179,9 +180,13 @@ mod tests { let language = crate::languages::language("python", tree_sitter_python::language(), None).await; cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, |s| { + s.defaults.tab_size = NonZeroU32::new(2); + }); + }); }); cx.add_model(|cx| { diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 92fb5bc3b2739cf8a63a2d3717c23e7642e50963..15700ec80a1bc798d44210ed47d600cc319a1241 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -253,10 +253,13 @@ impl LspAdapter for RustLspAdapter { #[cfg(test)] mod tests { + use std::num::NonZeroU32; + use super::*; use crate::languages::language; use gpui::{color::Color, TestAppContext}; - use settings::Settings; + use language::language_settings::AllLanguageSettings; + use settings::SettingsStore; use theme::SyntaxTheme; #[gpui::test] @@ -435,9 +438,13 @@ mod tests { async fn test_rust_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); cx.update(|cx| { - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + cx.set_global(SettingsStore::test(cx)); + language::init(cx); + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, |s| { + s.defaults.tab_size = NonZeroU32::new(2); + }); + }); }); let language = crate::languages::language("rust", tree_sitter_rust::language(), None).await; diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index fed76cd5b9e399bdfed5a4756617395eb746852b..b62168775c3145afaa317b464dfdcf5a777b5916 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -2,10 +2,11 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::AppContext; -use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; +use language::{ + language_settings::language_settings, LanguageServerBinary, LanguageServerName, LspAdapter, +}; use node_runtime::NodeRuntime; use serde_json::Value; -use settings::Settings; use smol::fs; use std::{ any::Any, @@ -100,14 +101,13 @@ impl LspAdapter for YamlLspAdapter { } fn workspace_configuration(&self, cx: &mut AppContext) -> Option> { - let settings = cx.global::(); Some( future::ready(serde_json::json!({ "yaml": { "keyOrdering": false }, "[yaml]": { - "editor.tabSize": settings.tab_size(Some("YAML")) + "editor.tabSize": language_settings(None, Some("YAML"), cx).tab_size, } })) .boxed(), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1398369e75992372d942b4c3bb31a37ab9319a09..b8ffab4a3b650d063a94e7e34b305e93e1b1972d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -597,7 +597,7 @@ mod tests { #[gpui::test] async fn test_open_paths_action(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -697,7 +697,7 @@ mod tests { #[gpui::test] async fn test_window_edit_state(executor: Arc, cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -777,7 +777,7 @@ mod tests { #[gpui::test] async fn test_new_empty_workspace(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); cx.update(|cx| { open_new(&app_state, cx, |workspace, cx| { Editor::new_file(workspace, &Default::default(), cx) @@ -816,7 +816,7 @@ mod tests { #[gpui::test] async fn test_open_entry(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -929,7 +929,7 @@ mod tests { #[gpui::test] async fn test_open_paths(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs @@ -1099,7 +1099,7 @@ mod tests { #[gpui::test] async fn test_save_conflicting_item(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -1143,7 +1143,7 @@ mod tests { #[gpui::test] async fn test_open_and_save_new_file(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state.fs.create_dir(Path::new("/root")).await.unwrap(); let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; @@ -1232,7 +1232,7 @@ mod tests { #[gpui::test] async fn test_setting_language_when_saving_as_single_file_worktree(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state.fs.create_dir(Path::new("/root")).await.unwrap(); let project = Project::test(app_state.fs.clone(), [], cx).await; @@ -1271,7 +1271,7 @@ mod tests { #[gpui::test] async fn test_pane_actions(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -1345,7 +1345,7 @@ mod tests { #[gpui::test] async fn test_navigation(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -1622,7 +1622,7 @@ mod tests { #[gpui::test] async fn test_reopening_closed_items(cx: &mut TestAppContext) { - let app_state = init(cx); + let app_state = init_test(cx); app_state .fs .as_fake() @@ -1843,7 +1843,7 @@ mod tests { cx.foreground().run_until_parked(); } - fn init(cx: &mut TestAppContext) -> Arc { + fn init_test(cx: &mut TestAppContext) -> Arc { cx.foreground().forbid_parking(); cx.update(|cx| { let mut app_state = AppState::test(cx); @@ -1852,6 +1852,7 @@ mod tests { state.build_window_options = build_window_options; call::init(app_state.client.clone(), app_state.user_store.clone(), cx); workspace::init(app_state.clone(), cx); + language::init(cx); editor::init(cx); pane::init(cx); app_state From 65e3713d4d375a64df3c0584bc3e9ba49b3d521f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 16 May 2023 17:29:21 -0700 Subject: [PATCH 14/25] Define editor settings in editor crate --- Cargo.lock | 1 + crates/client/src/client.rs | 8 +++- crates/collab/src/tests.rs | 10 ++--- crates/collab/src/tests/integration_tests.rs | 45 +++++++++++--------- crates/editor/src/blink_manager.rs | 6 +-- crates/editor/src/editor.rs | 9 +++- crates/editor/src/editor_settings.rs | 34 +++++++++++++++ crates/editor/src/editor_tests.rs | 6 ++- crates/editor/src/hover_popover.rs | 6 +-- crates/project_panel/Cargo.toml | 1 + crates/project_panel/src/project_panel.rs | 1 + crates/search/src/project_search.rs | 2 + crates/settings/src/settings.rs | 18 -------- 13 files changed, 93 insertions(+), 54 deletions(-) create mode 100644 crates/editor/src/editor_settings.rs diff --git a/Cargo.lock b/Cargo.lock index f01022e2941218daf48c2955c11240a038ab92c1..794f5ffd8bb7b27166fd916b35ea069152270576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4873,6 +4873,7 @@ dependencies = [ name = "project_panel" version = "0.1.0" dependencies = [ + "client", "context_menu", "drag_and_drop", "editor", diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index f1aee9540eb7d4065c911a0bd35ae70b178d1a96..041126271d513eb0d5e81c84eea96e3856d621a6 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -70,10 +70,14 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5); actions!(client, [SignIn, SignOut]); -pub fn init(client: &Arc, cx: &mut AppContext) { - let client = Arc::downgrade(client); +pub fn init_settings(cx: &mut AppContext) { settings::register_setting::(cx); +} +pub fn init(client: &Arc, cx: &mut AppContext) { + init_settings(cx); + + let client = Arc::downgrade(client); cx.add_global_action({ let client = client.clone(); move |_: &SignIn, cx| { diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 02e5fa56e41c22ca69bf6412207e8a97c4045aca..a69a08fedff765056c9716ea89ad746e0c7f6396 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -186,11 +186,6 @@ impl TestServer { }) }); - cx.update(|cx| { - client::init(&client, cx); - language::init(cx); - }); - let fs = FakeFs::new(cx.background()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); let app_state = Arc::new(workspace::AppState { @@ -205,8 +200,11 @@ impl TestServer { background_actions: || &[], }); - Project::init(&client); cx.update(|cx| { + Project::init(&client); + client::init(&client, cx); + language::init(cx); + editor::init_settings(cx); workspace::init(app_state.clone(), cx); call::init(client.clone(), user_store.clone(), cx); }); diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 2896c478086c0f02785c8bd63fa714a509e798a4..5d1a915887939e47c851a6c4f5eaf2ef610d7429 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -27,7 +27,7 @@ use lsp::LanguageServerId; use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath}; use rand::prelude::*; use serde_json::json; -use settings::{SettingsStore}; +use settings::SettingsStore; use std::{ cell::{Cell, RefCell}, env, future, mem, @@ -1439,7 +1439,6 @@ async fn test_host_disconnect( cx_b: &mut TestAppContext, cx_c: &mut TestAppContext, ) { - cx_b.update(editor::init); deterministic.forbid_parking(); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; @@ -1449,6 +1448,8 @@ async fn test_host_disconnect( .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; + cx_b.update(editor::init); + client_a .fs .insert_tree( @@ -1546,7 +1547,6 @@ async fn test_project_reconnect( cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, ) { - cx_b.update(editor::init); deterministic.forbid_parking(); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; @@ -1555,6 +1555,8 @@ async fn test_project_reconnect( .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + cx_b.update(editor::init); + client_a .fs .insert_tree( @@ -4992,7 +4994,6 @@ async fn test_collaborating_with_code_actions( cx_b: &mut TestAppContext, ) { deterministic.forbid_parking(); - cx_b.update(editor::init); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -5001,6 +5002,8 @@ async fn test_collaborating_with_code_actions( .await; let active_call_a = cx_a.read(ActiveCall::global); + cx_b.update(editor::init); + // Set up a fake language server. let mut language = Language::new( LanguageConfig { @@ -5205,7 +5208,6 @@ async fn test_collaborating_with_renames( cx_b: &mut TestAppContext, ) { deterministic.forbid_parking(); - cx_b.update(editor::init); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -5214,6 +5216,8 @@ async fn test_collaborating_with_renames( .await; let active_call_a = cx_a.read(ActiveCall::global); + cx_b.update(editor::init); + // Set up a fake language server. let mut language = Language::new( LanguageConfig { @@ -5395,8 +5399,6 @@ async fn test_language_server_statuses( cx_b: &mut TestAppContext, ) { deterministic.forbid_parking(); - - cx_b.update(editor::init); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -5405,6 +5407,8 @@ async fn test_language_server_statuses( .await; let active_call_a = cx_a.read(ActiveCall::global); + cx_b.update(editor::init); + // Set up a fake language server. let mut language = Language::new( LanguageConfig { @@ -6112,8 +6116,6 @@ async fn test_basic_following( cx_d: &mut TestAppContext, ) { deterministic.forbid_parking(); - cx_a.update(editor::init); - cx_b.update(editor::init); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; @@ -6131,6 +6133,9 @@ async fn test_basic_following( let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); + cx_a.update(editor::init); + cx_b.update(editor::init); + client_a .fs .insert_tree( @@ -6709,9 +6714,6 @@ async fn test_following_tab_order( cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, ) { - cx_a.update(editor::init); - cx_b.update(editor::init); - let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -6721,6 +6723,9 @@ async fn test_following_tab_order( let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); + cx_a.update(editor::init); + cx_b.update(editor::init); + client_a .fs .insert_tree( @@ -6831,9 +6836,6 @@ async fn test_peers_following_each_other( cx_b: &mut TestAppContext, ) { deterministic.forbid_parking(); - cx_a.update(editor::init); - cx_b.update(editor::init); - let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -6843,6 +6845,9 @@ async fn test_peers_following_each_other( let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); + cx_a.update(editor::init); + cx_b.update(editor::init); + // Client A shares a project. client_a .fs @@ -7002,8 +7007,6 @@ async fn test_auto_unfollowing( cx_b: &mut TestAppContext, ) { deterministic.forbid_parking(); - cx_a.update(editor::init); - cx_b.update(editor::init); // 2 clients connect to a server. let mut server = TestServer::start(&deterministic).await; @@ -7015,6 +7018,9 @@ async fn test_auto_unfollowing( let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); + cx_a.update(editor::init); + cx_b.update(editor::init); + // Client A shares a project. client_a .fs @@ -7169,8 +7175,6 @@ async fn test_peers_simultaneously_following_each_other( cx_b: &mut TestAppContext, ) { deterministic.forbid_parking(); - cx_a.update(editor::init); - cx_b.update(editor::init); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; @@ -7180,6 +7184,9 @@ async fn test_peers_simultaneously_following_each_other( .await; let active_call_a = cx_a.read(ActiveCall::global); + cx_a.update(editor::init); + cx_b.update(editor::init); + client_a.fs.insert_tree("/a", json!({})).await; let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let workspace_a = client_a.build_workspace(&project_a, cx_a); diff --git a/crates/editor/src/blink_manager.rs b/crates/editor/src/blink_manager.rs index 409b6f9b0344a3f5ee9f109bab82991772a15221..303d0960edd6e6b2ac589b8c4be40aca5a3502cb 100644 --- a/crates/editor/src/blink_manager.rs +++ b/crates/editor/src/blink_manager.rs @@ -1,8 +1,8 @@ -use std::time::Duration; - +use crate::EditorSettings; use gpui::{Entity, ModelContext}; use settings::Settings; use smol::Timer; +use std::time::Duration; pub struct BlinkManager { blink_interval: Duration, @@ -64,7 +64,7 @@ impl BlinkManager { } fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext) { - if cx.global::().cursor_blink { + if settings::get_setting::(None, cx).cursor_blink { if epoch == self.blink_epoch && self.enabled && !self.blinking_paused { self.visible = !self.visible; cx.notify(); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 41087c6eaaee4afc79d785267ca0cb2d4446fcfd..791f67be29f743711dca4b233196855c16d34841 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1,5 +1,6 @@ mod blink_manager; pub mod display_map; +mod editor_settings; mod element; mod git; @@ -28,6 +29,7 @@ use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque}; use copilot::Copilot; pub use display_map::DisplayPoint; use display_map::*; +pub use editor_settings::EditorSettings; pub use element::*; use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; @@ -287,7 +289,12 @@ pub enum Direction { Next, } +pub fn init_settings(cx: &mut AppContext) { + settings::register_setting::(cx); +} + pub fn init(cx: &mut AppContext) { + init_settings(cx); cx.add_action(Editor::new_file); cx.add_action(Editor::cancel); cx.add_action(Editor::newline); @@ -2354,7 +2361,7 @@ impl Editor { } fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext) { - if !cx.global::().show_completions_on_input { + if !settings::get_setting::(None, cx).show_completions_on_input { return; } diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed100d0a016db1fa08da8de0bd9105585478b21c --- /dev/null +++ b/crates/editor/src/editor_settings.rs @@ -0,0 +1,34 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use settings::Setting; + +#[derive(Deserialize)] +pub struct EditorSettings { + pub cursor_blink: bool, + pub hover_popover_enabled: bool, + pub show_completions_on_input: bool, +} + +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct EditorSettingsContent { + pub cursor_blink: Option, + pub hover_popover_enabled: Option, + pub show_completions_on_input: Option, +} + +impl Setting for EditorSettings { + const KEY: Option<&'static str> = None; + + type FileContent = EditorSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result + where + Self: Sized, + { + Self::load_via_json_merge(default_value, user_values) + } +} diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 7ee6a52955cdc06fc35c703568ba4d3cbf39647b..6f40b157a1a7224a1205d963d8bf876bc26ac257 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -4697,8 +4697,10 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { apply_additional_edits.await.unwrap(); cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.show_completions_on_input = false; + cx.update_global::(|settings, cx| { + settings.update_user_settings::(cx, |settings| { + settings.show_completions_on_input = Some(false); + }); }) }); cx.set_state("editorˇ"); diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index c40cb0fbe34a6396cf1af01ff82d6c1b50ee2df5..89626c3ea49a0990232f57bb54eb527777c66268 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1,6 +1,6 @@ use crate::{ - display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSnapshot, - EditorStyle, RangeToAnchorExt, + display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, + EditorSnapshot, EditorStyle, RangeToAnchorExt, }; use futures::FutureExt; use gpui::{ @@ -38,7 +38,7 @@ pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext) { /// The internal hover action dispatches between `show_hover` or `hide_hover` /// depending on whether a point to hover over is provided. pub fn hover_at(editor: &mut Editor, point: Option, cx: &mut ViewContext) { - if cx.global::().hover_popover_enabled { + if settings::get_setting::(None, cx).hover_popover_enabled { if let Some(point) = point { show_hover(editor, point, false, cx); } else { diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index d52ec403fb024e0d3034d5b2c310ffa0a0597db6..6fcdf06d2c736c63ea19cc5bd5ebdd79e7c36061 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -24,6 +24,7 @@ futures.workspace = true unicase = "2.6" [dev-dependencies] +client = { path = "../client", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 97684d874a7cc0077c06aeabd492c404dd6b80c5..d36536f3348d8526359787809dfbf4ec4d81315d 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1935,6 +1935,7 @@ mod tests { cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); language::init(cx); + editor::init_settings(cx); }); } } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 4dc947e5f6bc539a0f3bfc4a0706b1e46022f550..e869efcd5c8b9cc140bd2e45a63cbf3e629a5e02 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1281,11 +1281,13 @@ pub mod tests { cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); cx.set_global(ActiveSearches::default()); + let mut settings = Settings::test(cx); settings.theme = Arc::new(theme); cx.set_global(settings); language::init(cx); + editor::init_settings(cx); }); } } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index f5e50aaffb8561e15715796487e618875ff73b41..438dc0d02e57ca51cb15e003c5b9c25cdd34c9ee 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -38,10 +38,7 @@ pub struct Settings { pub buffer_font_family: FamilyId, pub buffer_font_size: f32, pub active_pane_magnification: f32, - pub cursor_blink: bool, pub confirm_quit: bool, - pub hover_popover_enabled: bool, - pub show_completions_on_input: bool, pub show_call_status_icon: bool, pub autosave: Autosave, pub default_dock_anchor: DockAnchor, @@ -78,9 +75,6 @@ impl Setting for Settings { buffer_font_size: defaults.buffer_font_size.unwrap(), active_pane_magnification: defaults.active_pane_magnification.unwrap(), confirm_quit: defaults.confirm_quit.unwrap(), - cursor_blink: defaults.cursor_blink.unwrap(), - hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), - show_completions_on_input: defaults.show_completions_on_input.unwrap(), show_call_status_icon: defaults.show_call_status_icon.unwrap(), autosave: defaults.autosave.unwrap(), default_dock_anchor: defaults.default_dock_anchor.unwrap(), @@ -341,9 +335,6 @@ impl Settings { buffer_font_size: defaults.buffer_font_size.unwrap(), active_pane_magnification: defaults.active_pane_magnification.unwrap(), confirm_quit: defaults.confirm_quit.unwrap(), - cursor_blink: defaults.cursor_blink.unwrap(), - hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), - show_completions_on_input: defaults.show_completions_on_input.unwrap(), show_call_status_icon: defaults.show_call_status_icon.unwrap(), autosave: defaults.autosave.unwrap(), default_dock_anchor: defaults.default_dock_anchor.unwrap(), @@ -391,13 +382,7 @@ impl Settings { &mut self.active_pane_magnification, data.active_pane_magnification, ); - merge(&mut self.cursor_blink, data.cursor_blink); merge(&mut self.confirm_quit, data.confirm_quit); - merge(&mut self.hover_popover_enabled, data.hover_popover_enabled); - merge( - &mut self.show_completions_on_input, - data.show_completions_on_input, - ); merge(&mut self.autosave, data.autosave); merge(&mut self.default_dock_anchor, data.default_dock_anchor); merge(&mut self.base_keymap, data.base_keymap); @@ -426,9 +411,6 @@ impl Settings { buffer_font_size: 14., active_pane_magnification: 1., confirm_quit: false, - cursor_blink: true, - hover_popover_enabled: true, - show_completions_on_input: true, show_call_status_icon: true, autosave: Autosave::Off, default_dock_anchor: DockAnchor::Bottom, From cbd4771f107b457cb38ff4eb2e6c28465ccb4eda Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 16 May 2023 17:45:04 -0700 Subject: [PATCH 15/25] Define project settings in project crate --- Cargo.lock | 1 + Cargo.toml | 1 + crates/collab/Cargo.toml | 2 +- crates/collab/src/tests.rs | 2 +- crates/editor/src/editor_settings.rs | 5 +-- crates/editor/src/editor_tests.rs | 1 + .../src/test/editor_lsp_test_context.rs | 1 + crates/project/Cargo.toml | 3 +- crates/project/src/project.rs | 14 +++++++-- crates/project/src/project_settings.rs | 31 +++++++++++++++++++ crates/project/src/project_tests.rs | 1 + crates/project_symbols/src/project_symbols.rs | 1 + crates/settings/Cargo.toml | 2 +- crates/settings/src/settings.rs | 15 +-------- crates/theme/Cargo.toml | 2 +- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 2 +- 17 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 crates/project/src/project_settings.rs diff --git a/Cargo.lock b/Cargo.lock index 794f5ffd8bb7b27166fd916b35ea069152270576..e0f70b15c54e4ef005935e328d613006bf19ee1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4852,6 +4852,7 @@ dependencies = [ "rand 0.8.5", "regex", "rpc", + "schemars", "serde", "serde_derive", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 0a73b878f89e5dd8dba8d67810e85024b0d72163..f14e1c73552f766034ee1d98dd1a51fe3a00bf09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ smol = { version = "1.2" } tempdir = { version = "0.3.7" } thiserror = { version = "1.0.29" } time = { version = "0.3", features = ["serde", "serde-well-known"] } +toml = { version = "0.5" } unindent = { version = "0.1.7" } [patch.crates-io] diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index a980fdc13e28ec3b7d535d9d1e16e85efa47e3f6..84e58744b1cb3d9d009493eaa61bae0c4bd33db4 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -51,7 +51,7 @@ tokio = { version = "1", features = ["full"] } tokio-tungstenite = "0.17" tonic = "0.6" tower = "0.4" -toml = "0.5.8" +toml.workspace = true tracing = "0.1.34" tracing-log = "0.1.3" tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] } diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index a69a08fedff765056c9716ea89ad746e0c7f6396..f1593822afd45fe32aece5e0019e57b05efb3c13 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -201,7 +201,7 @@ impl TestServer { }); cx.update(|cx| { - Project::init(&client); + Project::init(&client, cx); client::init(&client, cx); language::init(cx); editor::init_settings(cx); diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index ed100d0a016db1fa08da8de0bd9105585478b21c..2cd1d30e9ebf8f2a8f26223c45981fa45fd8f73a 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -25,10 +25,7 @@ impl Setting for EditorSettings { default_value: &Self::FileContent, user_values: &[&Self::FileContent], _: &gpui::AppContext, - ) -> anyhow::Result - where - Self: Sized, - { + ) -> anyhow::Result { Self::load_via_json_merge(default_value, user_values) } } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 6f40b157a1a7224a1205d963d8bf876bc26ac257..49e878947df4a2b002bc71f6159697802dcc75b1 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -6684,6 +6684,7 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC cx.set_global(Settings::test(cx)); language::init(cx); crate::init(cx); + Project::init_settings(cx); }); update_test_settings(cx, f); diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index e9aaa3df1b12a37d1eee52610d51e190506b1bcf..a5633a9e55fe552e7a6aa1c5540e0916d9b85307 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -40,6 +40,7 @@ impl<'a> EditorLspTestContext<'a> { language::init(cx); crate::init(cx); pane::init(cx); + Project::init_settings(cx); }); let file_name = format!( diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 2b4892aab9414047c4a7a9343b82f8df82298691..83af0afeb38535651de097c35e63ce320777719f 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -50,6 +50,7 @@ parking_lot.workspace = true postage.workspace = true rand.workspace = true regex.workspace = true +schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true @@ -57,7 +58,7 @@ sha2 = "0.10" similar = "1.3" smol.workspace = true thiserror.workspace = true -toml = "0.5" +toml.workspace = true itertools = "0.10" [dev-dependencies] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 5d0b0a8884e1add4b4f0d9561dd4191f713c53f6..b8f55c1aa45b37841865ac83432a70a288545f4e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1,6 +1,7 @@ mod ignore; mod lsp_command; mod lsp_glob_set; +mod project_settings; pub mod search; pub mod terminals; pub mod worktree; @@ -42,6 +43,7 @@ use lsp::{ use lsp_command::*; use lsp_glob_set::LspGlobSet; use postage::watch; +use project_settings::ProjectSettings; use rand::prelude::*; use search::SearchQuery; use serde::Serialize; @@ -385,7 +387,13 @@ impl FormatTrigger { } impl Project { - pub fn init(client: &Arc) { + pub fn init_settings(cx: &mut AppContext) { + settings::register_setting::(cx); + } + + pub fn init(client: &Arc, cx: &mut AppContext) { + Self::init_settings(cx); + client.add_model_message_handler(Self::handle_add_collaborator); client.add_model_message_handler(Self::handle_update_project_collaborator); client.add_model_message_handler(Self::handle_remove_collaborator); @@ -2206,7 +2214,9 @@ impl Project { None => continue, }; - let lsp = &cx.global::().lsp.get(&adapter.name.0); + let lsp = settings::get_setting::(None, cx) + .lsp + .get(&adapter.name.0); let override_options = lsp.map(|s| s.initialization_options.clone()).flatten(); let mut initialization_options = adapter.initialization_options.clone(); diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..92e8cfcca79fc59f17a879c5c7ade0264a6b40bf --- /dev/null +++ b/crates/project/src/project_settings.rs @@ -0,0 +1,31 @@ +use collections::HashMap; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use settings::Setting; +use std::sync::Arc; + +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct ProjectSettings { + #[serde(default)] + pub lsp: HashMap, LspSettings>, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct LspSettings { + pub initialization_options: Option, +} + +impl Setting for ProjectSettings { + const KEY: Option<&'static str> = None; + + type FileContent = Self; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result { + Self::load_via_json_merge(default_value, user_values) + } +} diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 1cbef629f357fdfdb995458c16854dec8db700ce..e7b1a84924eb2f5b25b519177ba9bcd5039eb000 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -3723,5 +3723,6 @@ fn init_test(cx: &mut gpui::TestAppContext) { cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); language::init(cx); + Project::init_settings(cx); }); } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 3176cad30864b05df7df922ad6a5fb7d80f64baf..239a0f98f2e99109eb2041c4cb76e80805cd6539 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -374,6 +374,7 @@ mod tests { cx.set_global(Settings::test(cx)); cx.set_global(SettingsStore::test(cx)); language::init(cx); + Project::init_settings(cx); }); } diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index ba9bc38b46f181c8cbbec2b8fb5f2c548db69947..6d2b4ded91b7ebf983b6791c045bd22123b7c6ee 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -32,7 +32,7 @@ serde.workspace = true serde_derive.workspace = true serde_json.workspace = true smallvec.workspace = true -toml = "0.5" +toml.workspace = true tree-sitter = "*" tree-sitter-json = "*" diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 438dc0d02e57ca51cb15e003c5b9c25cdd34c9ee..b1f171d52ef298eac65700e5f5ffdd92be158823 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -19,7 +19,7 @@ use sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; -use std::{borrow::Cow, collections::HashMap, str, sync::Arc}; +use std::{borrow::Cow, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; @@ -44,7 +44,6 @@ pub struct Settings { pub default_dock_anchor: DockAnchor, pub git: GitSettings, pub git_overrides: GitSettings, - pub lsp: HashMap, LspSettings>, pub theme: Arc, pub base_keymap: BaseKeymap, } @@ -80,7 +79,6 @@ impl Setting for Settings { default_dock_anchor: defaults.default_dock_anchor.unwrap(), git: defaults.git.unwrap(), git_overrides: Default::default(), - lsp: defaults.lsp.clone(), theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), base_keymap: Default::default(), }; @@ -290,19 +288,11 @@ pub struct SettingsFileContent { #[serde(default)] pub git: Option, #[serde(default)] - pub lsp: HashMap, LspSettings>, - #[serde(default)] pub theme: Option, #[serde(default)] pub base_keymap: Option, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct LspSettings { - pub initialization_options: Option, -} - impl Settings { pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> { match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() { @@ -340,7 +330,6 @@ impl Settings { default_dock_anchor: defaults.default_dock_anchor.unwrap(), git: defaults.git.unwrap(), git_overrides: Default::default(), - lsp: defaults.lsp.clone(), theme: themes.get(&defaults.theme.unwrap()).unwrap(), base_keymap: Default::default(), } @@ -388,7 +377,6 @@ impl Settings { merge(&mut self.base_keymap, data.base_keymap); self.git_overrides = data.git.unwrap_or_default(); - self.lsp = data.lsp; } pub fn git_gutter(&self) -> GitGutter { @@ -416,7 +404,6 @@ impl Settings { default_dock_anchor: DockAnchor::Bottom, git: Default::default(), git_overrides: Default::default(), - lsp: Default::default(), theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default), base_keymap: Default::default(), } diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml index d37ac3465b5be7505a836d8796b1299388935da4..dedbf2dd6fb7d7ad909036a8b5c42c6ee239be51 100644 --- a/crates/theme/Cargo.toml +++ b/crates/theme/Cargo.toml @@ -19,4 +19,4 @@ parking_lot.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true -toml = "0.5" +toml.workspace = true diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 70c71cc18e4f60fbed651337605afae6b2f66b8c..d937ce7ffea2c3bc2b38ae82f95e77b0885206dc 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -101,7 +101,7 @@ smol.workspace = true tempdir.workspace = true thiserror.workspace = true tiny_http = "0.8" -toml = "0.5" +toml.workspace = true tree-sitter = "0.20" tree-sitter-c = "0.20.1" tree-sitter-cpp = "0.20.0" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 33de725e1b2b37f4f8e4caa1187a25e16c86db9f..9ce3db7c3497fdccc4dbc3c63d3be9fd9e8cb093 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -157,7 +157,7 @@ fn main() { cx.set_global(client.clone()); context_menu::init(cx); - project::Project::init(&client); + project::Project::init(&client, cx); client::init(&client, cx); command_palette::init(cx); editor::init(cx); From 6403bb86e11cdcbc2055bf704d8c891076deaed3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 16 May 2023 20:25:18 -0700 Subject: [PATCH 16/25] Define workspace settings in workspace crate --- Cargo.lock | 1 + .../collab_ui/src/sharing_status_indicator.rs | 6 +- crates/diagnostics/src/diagnostics.rs | 1 + crates/editor/src/editor_tests.rs | 1 + crates/editor/src/element.rs | 10 +- .../src/test/editor_lsp_test_context.rs | 1 + crates/file_finder/src/file_finder.rs | 1 + crates/project_panel/src/project_panel.rs | 1 + crates/project_symbols/src/project_symbols.rs | 1 + crates/search/src/project_search.rs | 1 + crates/settings/src/settings.rs | 128 +----------------- crates/terminal_view/src/terminal_view.rs | 47 +++---- crates/workspace/Cargo.toml | 1 + crates/workspace/src/dock.rs | 18 +-- crates/workspace/src/item.rs | 17 ++- crates/workspace/src/pane.rs | 38 ++++-- crates/workspace/src/pane_group.rs | 6 +- crates/workspace/src/persistence.rs | 9 +- crates/workspace/src/persistence/model.rs | 9 +- crates/workspace/src/workspace.rs | 92 ++++++++----- crates/workspace/src/workspace_settings.rs | 103 ++++++++++++++ crates/zed/src/zed.rs | 4 +- 22 files changed, 253 insertions(+), 243 deletions(-) create mode 100644 crates/workspace/src/workspace_settings.rs diff --git a/Cargo.lock b/Cargo.lock index e0f70b15c54e4ef005935e328d613006bf19ee1f..216103959cad1936f38fb32ea8a6b9e41b22d103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8676,6 +8676,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "project", + "schemars", "serde", "serde_derive", "serde_json", diff --git a/crates/collab_ui/src/sharing_status_indicator.rs b/crates/collab_ui/src/sharing_status_indicator.rs index 9fbe57af65678c140e2fd360673f6eaa7f1cc6d6..8394036f398ef8fd1bbfc0ce5e237d71af3631f9 100644 --- a/crates/collab_ui/src/sharing_status_indicator.rs +++ b/crates/collab_ui/src/sharing_status_indicator.rs @@ -6,7 +6,7 @@ use gpui::{ platform::{Appearance, MouseButton}, AnyElement, AppContext, Element, Entity, View, ViewContext, }; -use settings::Settings; +use workspace::WorkspaceSettings; pub fn init(cx: &mut AppContext) { let active_call = ActiveCall::global(cx); @@ -15,7 +15,9 @@ pub fn init(cx: &mut AppContext) { cx.observe(&active_call, move |call, cx| { if let Some(room) = call.read(cx).room() { if room.read(cx).is_screen_sharing() { - if status_indicator.is_none() && cx.global::().show_call_status_icon { + if status_indicator.is_none() + && settings::get_setting::(None, cx).show_call_status_icon + { status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator)); } } else if let Some((window_id, _)) = status_indicator.take() { diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 62e03ca54f6c456a603f697db6dc3f189dfa50c1..f7746496aa568908652003ffc9825da7984f0704 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -1499,6 +1499,7 @@ mod tests { cx.set_global(Settings::test(cx)); cx.set_global(SettingsStore::test(cx)); language::init(cx); + workspace::init_settings(cx); }); } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 49e878947df4a2b002bc71f6159697802dcc75b1..cce10eb05e04c9ddb708e432cbdd7654928f3f8a 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -6685,6 +6685,7 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC language::init(cx); crate::init(cx); Project::init_settings(cx); + workspace::init_settings(cx); }); update_test_settings(cx, f); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 8194d51b25e99568bb8e3f932102da4af9ef6990..bc5998cc2948ba0d6f9901f6369e96a9d3539351 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -40,7 +40,7 @@ use language::{ Selection, }; use project::ProjectPath; -use settings::{GitGutter, Settings}; +use settings::Settings; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -50,7 +50,7 @@ use std::{ ops::Range, sync::Arc, }; -use workspace::item::Item; +use workspace::{item::Item, GitGutterSetting, WorkspaceSettings}; enum FoldMarkers {} @@ -550,11 +550,11 @@ impl EditorElement { let scroll_top = scroll_position.y() * line_height; let show_gutter = matches!( - &cx.global::() - .git_overrides + settings::get_setting::(None, cx) + .git .git_gutter .unwrap_or_default(), - GitGutter::TrackedFiles + GitGutterSetting::TrackedFiles ); if show_gutter { diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index a5633a9e55fe552e7a6aa1c5540e0916d9b85307..9f2ab4ffac205108ac57d6d8515ee7b095f40977 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -41,6 +41,7 @@ impl<'a> EditorLspTestContext<'a> { crate::init(cx); pane::init(cx); Project::init_settings(cx); + workspace::init_settings(cx); }); let file_name = format!( diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 7d5aff79d090f3dc0c36535d443a15154ac2ef1f..ad865ec2f732ba77b8c65252ee3de06d3e830d12 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -658,6 +658,7 @@ mod tests { language::init(cx); super::init(cx); editor::init(cx); + workspace::init_settings(cx); state }) } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index d36536f3348d8526359787809dfbf4ec4d81315d..fb82fbfdc4d55d05d7d3ec88537aff90658a356c 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1936,6 +1936,7 @@ mod tests { cx.set_global(SettingsStore::test(cx)); language::init(cx); editor::init_settings(cx); + workspace::init_settings(cx); }); } } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 239a0f98f2e99109eb2041c4cb76e80805cd6539..74edd45c212591a92e9efcfbbd221ca2d5b1444f 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -375,6 +375,7 @@ mod tests { cx.set_global(SettingsStore::test(cx)); language::init(cx); Project::init_settings(cx); + workspace::init_settings(cx); }); } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index e869efcd5c8b9cc140bd2e45a63cbf3e629a5e02..7364431407dfcd26ad9b11f4dcd4c2b12d1c543d 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1288,6 +1288,7 @@ pub mod tests { language::init(cx); editor::init_settings(cx); + workspace::init_settings(cx); }); } } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index b1f171d52ef298eac65700e5f5ffdd92be158823..cd63776f5f0e4058819e06a1326df25b333afd95 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -3,7 +3,7 @@ mod keymap_file; mod settings_file; mod settings_store; -use anyhow::{bail, Result}; +use anyhow::Result; use gpui::{ font_cache::{FamilyId, FontCache}, fonts, AppContext, AssetSource, @@ -15,10 +15,6 @@ use schemars::{ }; use serde::{Deserialize, Serialize}; use serde_json::Value; -use sqlez::{ - bindable::{Bind, Column, StaticColumnCount}, - statement::Statement, -}; use std::{borrow::Cow, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; @@ -37,13 +33,6 @@ pub struct Settings { pub buffer_font_features: fonts::Features, pub buffer_font_family: FamilyId, pub buffer_font_size: f32, - pub active_pane_magnification: f32, - pub confirm_quit: bool, - pub show_call_status_icon: bool, - pub autosave: Autosave, - pub default_dock_anchor: DockAnchor, - pub git: GitSettings, - pub git_overrides: GitSettings, pub theme: Arc, pub base_keymap: BaseKeymap, } @@ -72,13 +61,6 @@ impl Setting for Settings { buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(), buffer_font_features, buffer_font_size: defaults.buffer_font_size.unwrap(), - active_pane_magnification: defaults.active_pane_magnification.unwrap(), - confirm_quit: defaults.confirm_quit.unwrap(), - show_call_status_icon: defaults.show_call_status_icon.unwrap(), - autosave: defaults.autosave.unwrap(), - default_dock_anchor: defaults.default_dock_anchor.unwrap(), - git: defaults.git.unwrap(), - git_overrides: Default::default(), theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), base_keymap: Default::default(), }; @@ -201,65 +183,6 @@ impl BaseKeymap { .unwrap_or_default() } } -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -pub struct GitSettings { - pub git_gutter: Option, - pub gutter_debounce: Option, -} - -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum GitGutter { - #[default] - TrackedFiles, - Hide, -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum Autosave { - Off, - AfterDelay { milliseconds: u64 }, - OnFocusChange, - OnWindowChange, -} - -#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum DockAnchor { - #[default] - Bottom, - Right, - Expanded, -} - -impl StaticColumnCount for DockAnchor {} -impl Bind for DockAnchor { - fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result { - match self { - DockAnchor::Bottom => "Bottom", - DockAnchor::Right => "Right", - DockAnchor::Expanded => "Expanded", - } - .bind(statement, start_index) - } -} - -impl Column for DockAnchor { - fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> { - String::column(statement, start_index).and_then(|(anchor_text, next_index)| { - Ok(( - match anchor_text.as_ref() { - "Bottom" => DockAnchor::Bottom, - "Right" => DockAnchor::Right, - "Expanded" => DockAnchor::Expanded, - _ => bail!("Stored dock anchor is incorrect"), - }, - next_index, - )) - }) - } -} #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct SettingsFileContent { @@ -270,24 +193,6 @@ pub struct SettingsFileContent { #[serde(default)] pub buffer_font_features: Option, #[serde(default)] - pub active_pane_magnification: Option, - #[serde(default)] - pub cursor_blink: Option, - #[serde(default)] - pub confirm_quit: Option, - #[serde(default)] - pub hover_popover_enabled: Option, - #[serde(default)] - pub show_completions_on_input: Option, - #[serde(default)] - pub show_call_status_icon: Option, - #[serde(default)] - pub autosave: Option, - #[serde(default)] - pub default_dock_anchor: Option, - #[serde(default)] - pub git: Option, - #[serde(default)] pub theme: Option, #[serde(default)] pub base_keymap: Option, @@ -323,13 +228,6 @@ impl Settings { buffer_font_family_name: defaults.buffer_font_family.unwrap(), buffer_font_features, buffer_font_size: defaults.buffer_font_size.unwrap(), - active_pane_magnification: defaults.active_pane_magnification.unwrap(), - confirm_quit: defaults.confirm_quit.unwrap(), - show_call_status_icon: defaults.show_call_status_icon.unwrap(), - autosave: defaults.autosave.unwrap(), - default_dock_anchor: defaults.default_dock_anchor.unwrap(), - git: defaults.git.unwrap(), - git_overrides: Default::default(), theme: themes.get(&defaults.theme.unwrap()).unwrap(), base_keymap: Default::default(), } @@ -367,24 +265,7 @@ impl Settings { } merge(&mut self.buffer_font_size, data.buffer_font_size); - merge( - &mut self.active_pane_magnification, - data.active_pane_magnification, - ); - merge(&mut self.confirm_quit, data.confirm_quit); - merge(&mut self.autosave, data.autosave); - merge(&mut self.default_dock_anchor, data.default_dock_anchor); merge(&mut self.base_keymap, data.base_keymap); - - self.git_overrides = data.git.unwrap_or_default(); - } - - pub fn git_gutter(&self) -> GitGutter { - self.git_overrides.git_gutter.unwrap_or_else(|| { - self.git - .git_gutter - .expect("git_gutter should be some by setting setup") - }) } #[cfg(any(test, feature = "test-support"))] @@ -397,13 +278,6 @@ impl Settings { .load_family(&["Monaco"], &Default::default()) .unwrap(), buffer_font_size: 14., - active_pane_magnification: 1., - confirm_quit: false, - show_call_status_icon: true, - autosave: Autosave::Off, - default_dock_anchor: DockAnchor::Bottom, - git: Default::default(), - git_overrides: Default::default(), theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default), base_keymap: Default::default(), } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 1615387ca2f08e0efc7feaafb0d7e8ce8c67bbf2..76bc9b4a3868e9dbe83ba7264d639c6200c39a03 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -787,22 +787,18 @@ fn get_path_from_wt(wt: &LocalWorktree) -> Option { #[cfg(test)] mod tests { - use super::*; use gpui::TestAppContext; use project::{Entry, Project, ProjectPath, Worktree}; - use workspace::AppState; - use std::path::Path; + use workspace::AppState; - ///Working directory calculation tests + // Working directory calculation tests - ///No Worktrees in project -> home_dir() + // No Worktrees in project -> home_dir() #[gpui::test] async fn no_worktree(cx: &mut TestAppContext) { - //Setup variables - let (project, workspace) = blank_workspace(cx).await; - //Test + let (project, workspace) = init_test(cx).await; cx.read(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -818,14 +814,12 @@ mod tests { }); } - ///No active entry, but a worktree, worktree is a file -> home_dir() + // No active entry, but a worktree, worktree is a file -> home_dir() #[gpui::test] async fn no_active_entry_worktree_is_file(cx: &mut TestAppContext) { - //Setup variables + let (project, workspace) = init_test(cx).await; - let (project, workspace) = blank_workspace(cx).await; create_file_wt(project.clone(), "/root.txt", cx).await; - cx.read(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -841,14 +835,12 @@ mod tests { }); } - //No active entry, but a worktree, worktree is a folder -> worktree_folder + // No active entry, but a worktree, worktree is a folder -> worktree_folder #[gpui::test] async fn no_active_entry_worktree_is_dir(cx: &mut TestAppContext) { - //Setup variables - let (project, workspace) = blank_workspace(cx).await; - let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await; + let (project, workspace) = init_test(cx).await; - //Test + let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await; cx.update(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -863,17 +855,15 @@ mod tests { }); } - //Active entry with a work tree, worktree is a file -> home_dir() + // Active entry with a work tree, worktree is a file -> home_dir() #[gpui::test] async fn active_entry_worktree_is_file(cx: &mut TestAppContext) { - //Setup variables + let (project, workspace) = init_test(cx).await; - let (project, workspace) = blank_workspace(cx).await; let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await; let (wt2, entry2) = create_file_wt(project.clone(), "/root2.txt", cx).await; insert_active_entry_for(wt2, entry2, project.clone(), cx); - //Test cx.update(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -887,16 +877,15 @@ mod tests { }); } - //Active entry, with a worktree, worktree is a folder -> worktree_folder + // Active entry, with a worktree, worktree is a folder -> worktree_folder #[gpui::test] async fn active_entry_worktree_is_dir(cx: &mut TestAppContext) { - //Setup variables - let (project, workspace) = blank_workspace(cx).await; + let (project, workspace) = init_test(cx).await; + let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await; let (wt2, entry2) = create_folder_wt(project.clone(), "/root2/", cx).await; insert_active_entry_for(wt2, entry2, project.clone(), cx); - //Test cx.update(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -910,8 +899,8 @@ mod tests { }); } - ///Creates a worktree with 1 file: /root.txt - pub async fn blank_workspace( + /// Creates a worktree with 1 file: /root.txt + pub async fn init_test( cx: &mut TestAppContext, ) -> (ModelHandle, ViewHandle) { let params = cx.update(AppState::test); @@ -922,7 +911,7 @@ mod tests { (project, workspace) } - ///Creates a worktree with 1 folder: /root{suffix}/ + /// Creates a worktree with 1 folder: /root{suffix}/ async fn create_folder_wt( project: ModelHandle, path: impl AsRef, @@ -931,7 +920,7 @@ mod tests { create_wt(project, true, path, cx).await } - ///Creates a worktree with 1 file: /root{suffix}.txt + /// Creates a worktree with 1 file: /root{suffix}.txt async fn create_file_wt( project: ModelHandle, path: impl AsRef, diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 177dc0a292f387fefee9aa5b68c1d0e6e07ac8c3..26797e8d6c3a1a3635936e481229e4705d640270 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -45,6 +45,7 @@ lazy_static.workspace = true log.workspace = true parking_lot.workspace = true postage.workspace = true +schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 7efcb7f9d31dae6c2dc665bfcd6243843e3f443e..51163919761000b114a9606a5b12f1f208be36f1 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -1,5 +1,9 @@ mod toggle_dock_button; +use crate::{ + sidebar::SidebarSide, BackgroundActions, DockAnchor, ItemHandle, Pane, Workspace, + WorkspaceSettings, +}; use collections::HashMap; use gpui::{ actions, @@ -8,10 +12,7 @@ use gpui::{ platform::{CursorStyle, MouseButton}, AnyElement, AppContext, Border, Element, SizeConstraint, ViewContext, ViewHandle, }; -use settings::{DockAnchor, Settings}; use theme::Theme; - -use crate::{sidebar::SidebarSide, BackgroundActions, ItemHandle, Pane, Workspace}; pub use toggle_dock_button::ToggleDockButton; actions!( @@ -171,7 +172,9 @@ impl Dock { background_actions: BackgroundActions, cx: &mut ViewContext, ) -> Self { - let position = DockPosition::Hidden(cx.global::().default_dock_anchor); + let position = DockPosition::Hidden( + settings::get_setting::(None, cx).default_dock_anchor, + ); let workspace = cx.weak_handle(); let pane = cx.add_view(|cx| Pane::new(workspace, Some(position.anchor()), background_actions, cx)); @@ -405,7 +408,6 @@ mod tests { use gpui::{AppContext, BorrowWindowContext, TestAppContext, ViewContext, WindowContext}; use project::{FakeFs, Project}; - use settings::Settings; use theme::ThemeRegistry; use super::*; @@ -417,6 +419,7 @@ mod tests { }, register_deserializable_item, sidebar::Sidebar, + tests::init_test, AppState, ItemHandle, Workspace, }; @@ -429,8 +432,7 @@ mod tests { #[gpui::test] async fn test_dock_workspace_infinite_loop(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); cx.update(|cx| { register_deserializable_item::(cx); @@ -598,7 +600,7 @@ mod tests { impl<'a> DockTestContext<'a> { pub async fn new(cx: &'a mut TestAppContext) -> DockTestContext<'a> { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); cx.update(|cx| init(cx)); diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 43c4544611e9f174db5d8d2350501020eaa6d84e..e8c10c6cd2ec228d11d8d7a0f3ec4953e275a7e3 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -3,6 +3,7 @@ use crate::{ FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId, }; +use crate::{AutosaveSetting, WorkspaceSettings}; use anyhow::Result; use client::{proto, Client}; use gpui::{ @@ -10,7 +11,6 @@ use gpui::{ ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use project::{Project, ProjectEntryId, ProjectPath}; -use settings::{Autosave, Settings}; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -450,8 +450,11 @@ impl ItemHandle for ViewHandle { } ItemEvent::Edit => { - if let Autosave::AfterDelay { milliseconds } = - cx.global::().autosave + let settings = settings::get_setting::(None, cx); + let debounce_delay = settings.git.gutter_debounce; + + if let AutosaveSetting::AfterDelay { milliseconds } = + settings.autosave { let delay = Duration::from_millis(milliseconds); let item = item.clone(); @@ -460,9 +463,6 @@ impl ItemHandle for ViewHandle { }); } - let settings = cx.global::(); - let debounce_delay = settings.git_overrides.gutter_debounce; - let item = item.clone(); if let Some(delay) = debounce_delay { @@ -500,7 +500,10 @@ impl ItemHandle for ViewHandle { })); cx.observe_focus(self, move |workspace, item, focused, cx| { - if !focused && cx.global::().autosave == Autosave::OnFocusChange { + if !focused + && settings::get_setting::(None, cx).autosave + == AutosaveSetting::OnFocusChange + { Pane::autosave_item(&item, workspace.project.clone(), cx) .detach_and_log_err(cx); } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 96320a4baf5c5460030e73efd8276f26c7c6f804..ca454d2c751b57878ffb09497456aea7262c5b9b 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -5,7 +5,8 @@ use crate::{ dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, Dock, ExpandDock}, item::WeakItemHandle, toolbar::Toolbar, - Item, NewFile, NewSearch, NewTerminal, Workspace, + AutosaveSetting, DockAnchor, Item, NewFile, NewSearch, NewTerminal, Workspace, + WorkspaceSettings, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; @@ -29,7 +30,7 @@ use gpui::{ }; use project::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; -use settings::{Autosave, DockAnchor, Settings}; +use settings::Settings; use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc}; use theme::Theme; use util::ResultExt; @@ -1024,8 +1025,8 @@ impl Pane { } else if is_dirty && (can_save || is_singleton) { let will_autosave = cx.read(|cx| { matches!( - cx.global::().autosave, - Autosave::OnFocusChange | Autosave::OnWindowChange + settings::get_setting::(None, cx).autosave, + AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange ) && Self::can_autosave_item(&*item, cx) }); let should_save = if should_prompt_for_save && !will_autosave { @@ -2087,10 +2088,11 @@ mod tests { use crate::item::test::{TestItem, TestProjectItem}; use gpui::{executor::Deterministic, TestAppContext}; use project::FakeFs; + use settings::SettingsStore; #[gpui::test] async fn test_remove_active_empty(cx: &mut TestAppContext) { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2104,7 +2106,7 @@ mod tests { #[gpui::test] async fn test_add_item_with_new_item(cx: &mut TestAppContext) { cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2192,7 +2194,7 @@ mod tests { #[gpui::test] async fn test_add_item_with_existing_item(cx: &mut TestAppContext) { cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2268,7 +2270,7 @@ mod tests { #[gpui::test] async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) { cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2377,7 +2379,7 @@ mod tests { #[gpui::test] async fn test_remove_item_ordering(deterministic: Arc, cx: &mut TestAppContext) { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2424,7 +2426,7 @@ mod tests { #[gpui::test] async fn test_close_inactive_items(deterministic: Arc, cx: &mut TestAppContext) { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2443,7 +2445,7 @@ mod tests { #[gpui::test] async fn test_close_clean_items(deterministic: Arc, cx: &mut TestAppContext) { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2470,7 +2472,7 @@ mod tests { deterministic: Arc, cx: &mut TestAppContext, ) { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2492,7 +2494,7 @@ mod tests { deterministic: Arc, cx: &mut TestAppContext, ) { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2511,7 +2513,7 @@ mod tests { #[gpui::test] async fn test_close_all_items(deterministic: Arc, cx: &mut TestAppContext) { - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -2531,6 +2533,14 @@ mod tests { assert_item_labels(&pane, [], cx); } + fn init_test(cx: &mut TestAppContext) { + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + cx.set_global(settings::Settings::test(cx)); + crate::init_settings(cx); + }); + } + fn add_labeled_item( workspace: &ViewHandle, pane: &ViewHandle, diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 55032b4bc1e79bf40e75c5be5a0a4b7e47f3e836..edc7c617d0098ef16d85014f2b38a14f7afd83ef 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::{AppState, FollowerStatesByLeader, Pane, Workspace}; +use crate::{AppState, FollowerStatesByLeader, Pane, Workspace, WorkspaceSettings}; use anyhow::{anyhow, Result}; use call::{ActiveCall, ParticipantLocation}; use gpui::{ @@ -11,7 +11,6 @@ use gpui::{ }; use project::Project; use serde::Deserialize; -use settings::Settings; use theme::Theme; #[derive(Clone, Debug, Eq, PartialEq)] @@ -380,7 +379,8 @@ impl PaneAxis { .with_children(self.members.iter().enumerate().map(|(ix, member)| { let mut flex = 1.0; if member.contains(active_pane) { - flex = cx.global::().active_pane_magnification; + flex = settings::get_setting::(None, cx) + .active_pane_magnification; } let mut member = member.render( diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index d5d79c3ddd3368ed5960747908a6c88d4edd8094..4ffae0d7e3cacaefbd244ff1af8c80439f44b6f8 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -497,13 +497,10 @@ impl WorkspaceDb { #[cfg(test)] mod tests { - - use std::sync::Arc; - - use db::open_test_db; - use settings::DockAnchor; - use super::*; + use crate::DockAnchor; + use db::open_test_db; + use std::sync::Arc; #[gpui::test] async fn test_next_id_stability() { diff --git a/crates/workspace/src/persistence/model.rs b/crates/workspace/src/persistence/model.rs index a92c369e7a2e47c19fbfbc831a90b6b60edcfcb1..ac1bcf6eedeae11cb9f6fcd05d5c60967553bb13 100644 --- a/crates/workspace/src/persistence/model.rs +++ b/crates/workspace/src/persistence/model.rs @@ -1,5 +1,6 @@ use crate::{ - dock::DockPosition, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId, + dock::DockPosition, DockAnchor, ItemDeserializers, Member, Pane, PaneAxis, Workspace, + WorkspaceId, }; use anyhow::{anyhow, Context, Result}; use async_recursion::async_recursion; @@ -11,7 +12,6 @@ use gpui::{ platform::WindowBounds, AsyncAppContext, Axis, ModelHandle, Task, ViewHandle, WeakViewHandle, }; use project::Project; -use settings::DockAnchor; use std::{ path::{Path, PathBuf}, sync::Arc, @@ -305,10 +305,9 @@ impl Column for DockPosition { #[cfg(test)] mod tests { - use db::sqlez::connection::Connection; - use settings::DockAnchor; - use super::WorkspaceLocation; + use crate::DockAnchor; + use db::sqlez::connection::Connection; #[test] fn test_workspace_round_trips() { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 768142d7297674084688271dbe6cfd48006b681d..dcc4d017dd02136c7ff0e0a5d77c94d4b7ff6c09 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -13,6 +13,7 @@ pub mod shared_screen; pub mod sidebar; mod status_bar; mod toolbar; +mod workspace_settings; use anyhow::{anyhow, Context, Result}; use assets::Assets; @@ -75,7 +76,7 @@ pub use persistence::{ use postage::prelude::Stream; use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; use serde::Deserialize; -use settings::{Autosave, DockAnchor, Settings}; +use settings::Settings; use shared_screen::SharedScreen; use sidebar::{Sidebar, SidebarButtons, SidebarSide, ToggleSidebarItem}; use status_bar::StatusBar; @@ -83,6 +84,7 @@ pub use status_bar::StatusItemView; use theme::{Theme, ThemeRegistry}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; use util::{paths, ResultExt}; +pub use workspace_settings::{AutosaveSetting, DockAnchor, GitGutterSetting, WorkspaceSettings}; lazy_static! { static ref ZED_WINDOW_SIZE: Option = env::var("ZED_WINDOW_SIZE") @@ -183,7 +185,12 @@ pub type WorkspaceId = i64; impl_actions!(workspace, [ActivatePane]); +pub fn init_settings(cx: &mut AppContext) { + settings::register_setting::(cx); +} + pub fn init(app_state: Arc, cx: &mut AppContext) { + init_settings(cx); pane::init(cx); dock::init(cx); notifications::init(cx); @@ -384,6 +391,7 @@ impl AppState { let themes = ThemeRegistry::new((), cx.font_cache().clone()); client::init(&client, cx); + crate::init_settings(cx); Arc::new(Self { client, @@ -672,7 +680,9 @@ impl Workspace { Self::load_from_serialized_workspace(weak_handle, serialized_workspace, cx) }); } else if project.read(cx).is_local() { - if cx.global::().default_dock_anchor != DockAnchor::Expanded { + if settings::get_setting::(None, cx).default_dock_anchor + != DockAnchor::Expanded + { Dock::show(&mut this, false, cx); } } @@ -2406,8 +2416,8 @@ impl Workspace { item.workspace_deactivated(cx); } if matches!( - cx.global::().autosave, - Autosave::OnWindowChange | Autosave::OnFocusChange + settings::get_setting::(None, cx).autosave, + AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange ) { for item in pane.items() { Pane::autosave_item(item.as_ref(), self.project.clone(), cx) @@ -3067,7 +3077,7 @@ pub fn join_remote_project( } pub fn restart(_: &Restart, cx: &mut AppContext) { - let should_confirm = cx.global::().confirm_quit; + let should_confirm = settings::get_setting::(None, cx).confirm_quit; cx.spawn(|mut cx| async move { let mut workspaces = cx .window_ids() @@ -3128,20 +3138,18 @@ fn parse_pixel_position_env_var(value: &str) -> Option { #[cfg(test)] mod tests { - use std::{cell::RefCell, rc::Rc}; - - use crate::item::test::{TestItem, TestItemEvent, TestProjectItem}; - use super::*; + use crate::item::test::{TestItem, TestItemEvent, TestProjectItem}; use fs::FakeFs; use gpui::{executor::Deterministic, TestAppContext}; use project::{Project, ProjectEntryId}; use serde_json::json; + use settings::SettingsStore; + use std::{cell::RefCell, rc::Rc}; #[gpui::test] async fn test_tab_disambiguation(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; @@ -3189,8 +3197,8 @@ mod tests { #[gpui::test] async fn test_tracking_active_path(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree( "/root1", @@ -3293,8 +3301,8 @@ mod tests { #[gpui::test] async fn test_close_window(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); + let fs = FakeFs::new(cx.background()); fs.insert_tree("/root", json!({ "one": "" })).await; @@ -3329,8 +3337,8 @@ mod tests { #[gpui::test] async fn test_close_pane_items(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); + let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; @@ -3436,8 +3444,8 @@ mod tests { #[gpui::test] async fn test_prompting_to_save_only_on_last_item_for_entry(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - Settings::test_async(cx); + init_test(cx); + let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; @@ -3542,9 +3550,8 @@ mod tests { #[gpui::test] async fn test_autosave(deterministic: Arc, cx: &mut gpui::TestAppContext) { - deterministic.forbid_parking(); + init_test(cx); - Settings::test_async(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; @@ -3560,8 +3567,10 @@ mod tests { // Autosave on window change. item.update(cx, |item, cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.autosave = Autosave::OnWindowChange; + cx.update_global(|settings: &mut SettingsStore, cx| { + settings.update_user_settings::(cx, |settings| { + settings.autosave = Some(AutosaveSetting::OnWindowChange); + }) }); item.is_dirty = true; }); @@ -3574,8 +3583,10 @@ mod tests { // Autosave on focus change. item.update(cx, |item, cx| { cx.focus_self(); - cx.update_global(|settings: &mut Settings, _| { - settings.autosave = Autosave::OnFocusChange; + cx.update_global(|settings: &mut SettingsStore, cx| { + settings.update_user_settings::(cx, |settings| { + settings.autosave = Some(AutosaveSetting::OnFocusChange); + }) }); item.is_dirty = true; }); @@ -3598,8 +3609,10 @@ mod tests { // Autosave after delay. item.update(cx, |item, cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.autosave = Autosave::AfterDelay { milliseconds: 500 }; + cx.update_global(|settings: &mut SettingsStore, cx| { + settings.update_user_settings::(cx, |settings| { + settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 }); + }) }); item.is_dirty = true; cx.emit(TestItemEvent::Edit); @@ -3615,8 +3628,10 @@ mod tests { // Autosave on focus change, ensuring closing the tab counts as such. item.update(cx, |item, cx| { - cx.update_global(|settings: &mut Settings, _| { - settings.autosave = Autosave::OnFocusChange; + cx.update_global(|settings: &mut SettingsStore, cx| { + settings.update_user_settings::(cx, |settings| { + settings.autosave = Some(AutosaveSetting::OnFocusChange); + }) }); item.is_dirty = true; }); @@ -3656,12 +3671,9 @@ mod tests { } #[gpui::test] - async fn test_pane_navigation( - deterministic: Arc, - cx: &mut gpui::TestAppContext, - ) { - deterministic.forbid_parking(); - Settings::test_async(cx); + async fn test_pane_navigation(cx: &mut gpui::TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; @@ -3713,4 +3725,14 @@ mod tests { assert!(pane.can_navigate_forward()); }); } + + pub fn init_test(cx: &mut TestAppContext) { + cx.foreground().forbid_parking(); + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + cx.set_global(Settings::test(cx)); + language::init(cx); + crate::init_settings(cx); + }); + } } diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..41e47964910575a06fb0891c5e75d6ced13eefd7 --- /dev/null +++ b/crates/workspace/src/workspace_settings.rs @@ -0,0 +1,103 @@ +use anyhow::bail; +use db::sqlez::{ + bindable::{Bind, Column, StaticColumnCount}, + statement::Statement, +}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use settings::Setting; + +#[derive(Deserialize)] +pub struct WorkspaceSettings { + pub active_pane_magnification: f32, + pub confirm_quit: bool, + pub show_call_status_icon: bool, + pub autosave: AutosaveSetting, + pub default_dock_anchor: DockAnchor, + pub git: GitSettings, +} + +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct WorkspaceSettingsContent { + pub active_pane_magnification: Option, + pub confirm_quit: Option, + pub show_call_status_icon: Option, + pub autosave: Option, + pub default_dock_anchor: Option, + pub git: Option, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum AutosaveSetting { + Off, + AfterDelay { milliseconds: u64 }, + OnFocusChange, + OnWindowChange, +} + +#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum DockAnchor { + #[default] + Bottom, + Right, + Expanded, +} + +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +pub struct GitSettings { + pub git_gutter: Option, + pub gutter_debounce: Option, +} + +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum GitGutterSetting { + #[default] + TrackedFiles, + Hide, +} + +impl StaticColumnCount for DockAnchor {} + +impl Bind for DockAnchor { + fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result { + match self { + DockAnchor::Bottom => "Bottom", + DockAnchor::Right => "Right", + DockAnchor::Expanded => "Expanded", + } + .bind(statement, start_index) + } +} + +impl Column for DockAnchor { + fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> { + String::column(statement, start_index).and_then(|(anchor_text, next_index)| { + Ok(( + match anchor_text.as_ref() { + "Bottom" => DockAnchor::Bottom, + "Right" => DockAnchor::Right, + "Expanded" => DockAnchor::Expanded, + _ => bail!("Stored dock anchor is incorrect"), + }, + next_index, + )) + }) + } +} + +impl Setting for WorkspaceSettings { + const KEY: Option<&'static str> = None; + + type FileContent = WorkspaceSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result { + Self::load_via_json_merge(default_value, user_values) + } +} diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b8ffab4a3b650d063a94e7e34b305e93e1b1972d..923a3d6d9ca6c038edacf9c28facef0d69f9264d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -37,7 +37,7 @@ use uuid::Uuid; pub use workspace; use workspace::{ create_and_open_local_file, open_new, sidebar::SidebarSide, AppState, NewFile, NewWindow, - Workspace, + Workspace, WorkspaceSettings, }; #[derive(Deserialize, Clone, PartialEq)] @@ -367,7 +367,7 @@ pub fn build_window_options( } fn quit(_: &Quit, cx: &mut gpui::AppContext) { - let should_confirm = cx.global::().confirm_quit; + let should_confirm = settings::get_setting::(None, cx).confirm_quit; cx.spawn(|mut cx| async move { let mut workspaces = cx .window_ids() From d2ba18eae9d77774248007cf883361893785653a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 09:26:32 -0700 Subject: [PATCH 17/25] Customize language settings JSON schema in language crate --- crates/language/src/language_settings.rs | 60 +++++++++++++++++++++++- crates/settings/src/settings.rs | 57 +++++----------------- crates/settings/src/settings_store.rs | 7 +-- crates/zed/src/main.rs | 1 + 4 files changed, 77 insertions(+), 48 deletions(-) diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 80cc81a4c39658edc55702ede88b857cd95dd410..55afd2f572161b9dbebe0031d21f79ac29d7a975 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -1,7 +1,10 @@ use anyhow::Result; use collections::HashMap; use gpui::AppContext; -use schemars::JsonSchema; +use schemars::{ + schema::{InstanceType, ObjectValidation, Schema, SchemaObject}, + JsonSchema, +}; use serde::{Deserialize, Serialize}; use std::{num::NonZeroU32, path::Path, sync::Arc}; @@ -247,6 +250,61 @@ impl settings::Setting for AllLanguageSettings { languages, }) } + + fn json_schema( + generator: &mut schemars::gen::SchemaGenerator, + params: &settings::SettingsJsonSchemaParams, + ) -> schemars::schema::RootSchema { + let mut root_schema = generator.root_schema_for::(); + + // Create a schema for a 'languages overrides' object, associating editor + // settings with specific langauges. + assert!(root_schema + .definitions + .contains_key("LanguageSettingsContent")); + + let languages_object_schema = SchemaObject { + instance_type: Some(InstanceType::Object.into()), + object: Some(Box::new(ObjectValidation { + properties: params + .language_names + .iter() + .map(|name| { + ( + name.clone(), + Schema::new_ref("#/definitions/LanguageSettingsContent".into()), + ) + }) + .collect(), + ..Default::default() + })), + ..Default::default() + }; + + root_schema + .definitions + .extend([("Languages".into(), languages_object_schema.into())]); + + root_schema + .schema + .object + .as_mut() + .unwrap() + .properties + .extend([ + ( + "languages".to_owned(), + Schema::new_ref("#/definitions/Languages".into()), + ), + // For backward compatibility + ( + "language_overrides".to_owned(), + Schema::new_ref("#/definitions/Languages".into()), + ), + ]); + + root_schema + } } fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) { diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index cd63776f5f0e4058819e06a1326df25b333afd95..0e4cd4d9228b977eb7b8ec273ca446355abf53e3 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -10,7 +10,7 @@ use gpui::{ }; use schemars::{ gen::SchemaGenerator, - schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec}, + schema::{InstanceType, Schema, SchemaObject}, JsonSchema, }; use serde::{Deserialize, Serialize}; @@ -80,7 +80,7 @@ impl Setting for Settings { // Create a schema for a theme name. let theme_name_schema = SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + instance_type: Some(InstanceType::String.into()), enum_values: Some( params .theme_names @@ -92,51 +92,20 @@ impl Setting for Settings { ..Default::default() }; - // Create a schema for a 'languages overrides' object, associating editor - // settings with specific langauges. - assert!(root_schema.definitions.contains_key("EditorSettings")); - - let languages_object_schema = SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), - object: Some(Box::new(ObjectValidation { - properties: params - .language_names - .iter() - .map(|name| { - ( - name.clone(), - Schema::new_ref("#/definitions/EditorSettings".into()), - ) - }) - .collect(), - ..Default::default() - })), - ..Default::default() - }; - - // Add these new schemas as definitions, and modify properties of the root - // schema to reference them. - root_schema.definitions.extend([ - ("ThemeName".into(), theme_name_schema.into()), - ("Languages".into(), languages_object_schema.into()), - ]); - let root_schema_object = &mut root_schema.schema.object.as_mut().unwrap(); + root_schema + .definitions + .extend([("ThemeName".into(), theme_name_schema.into())]); - root_schema_object.properties.extend([ - ( + root_schema + .schema + .object + .as_mut() + .unwrap() + .properties + .extend([( "theme".to_owned(), Schema::new_ref("#/definitions/ThemeName".into()), - ), - ( - "languages".to_owned(), - Schema::new_ref("#/definitions/Languages".into()), - ), - // For backward compatibility - ( - "language_overrides".to_owned(), - Schema::new_ref("#/definitions/Languages".into()), - ), - ]); + )]); root_schema } diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index c389a7bf84fefc8d985ed0c6fc4bac2786ef927f..cdc765918997113a67698064d7b04122ef8e2254 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -308,7 +308,8 @@ impl SettingsStore { default_settings_content: &str, cx: &AppContext, ) -> Result<()> { - self.default_deserialized_settings = Some(serde_json::from_str(default_settings_content)?); + self.default_deserialized_settings = + Some(parse_json_with_comments(default_settings_content)?); self.recompute_values(None, cx)?; Ok(()) } @@ -319,7 +320,7 @@ impl SettingsStore { user_settings_content: &str, cx: &AppContext, ) -> Result<()> { - self.user_deserialized_settings = Some(serde_json::from_str(user_settings_content)?); + self.user_deserialized_settings = Some(parse_json_with_comments(user_settings_content)?); self.recompute_values(None, cx)?; Ok(()) } @@ -333,7 +334,7 @@ impl SettingsStore { ) -> Result<()> { if let Some(content) = settings_content { self.local_deserialized_settings - .insert(path.clone(), serde_json::from_str(content)?); + .insert(path.clone(), parse_json_with_comments(content)?); } else { self.local_deserialized_settings.remove(&path); } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 9ce3db7c3497fdccc4dbc3c63d3be9fd9e8cb093..3466daebe9a8c8943509a5711d0f2de71d4ca33f 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -160,6 +160,7 @@ fn main() { project::Project::init(&client, cx); client::init(&client, cx); command_palette::init(cx); + language::init(cx); editor::init(cx); go_to_line::init(cx); file_finder::init(cx); From 5c729c0e56a57ee81b84f9e806855216dff09a8a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 11:23:09 -0700 Subject: [PATCH 18/25] Define base keymap setting in welcome crate --- Cargo.lock | 2 + assets/settings/default.json | 9 + crates/settings/src/keymap_file.rs | 20 +- crates/settings/src/settings.rs | 49 ----- crates/settings/src/settings_file.rs | 218 +------------------- crates/vim/src/test/vim_test_context.rs | 2 +- crates/welcome/Cargo.toml | 7 +- crates/welcome/src/base_keymap_picker.rs | 11 +- crates/welcome/src/base_keymap_setting.rs | 65 ++++++ crates/welcome/src/welcome.rs | 10 +- crates/zed/src/main.rs | 7 +- crates/zed/src/zed.rs | 231 +++++++++++++++++++++- 12 files changed, 334 insertions(+), 297 deletions(-) create mode 100644 crates/welcome/src/base_keymap_setting.rs diff --git a/Cargo.lock b/Cargo.lock index c2e0790cf493cbf68463ce1307057cc26513c267..e5922f9e667abdcb1c8975fa46f645e99951959e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8353,6 +8353,8 @@ dependencies = [ "log", "picker", "project", + "schemars", + "serde", "settings", "theme", "theme_selector", diff --git a/assets/settings/default.json b/assets/settings/default.json index 20d07acae05fc26fcb2da87fb39e57cf3c1fcd18..3a80a5d5af7d09a4ed7d3a5001f0b307867c3e93 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1,6 +1,15 @@ { // The name of the Zed theme to use for the UI "theme": "One Dark", + // The name of a base set of key bindings to use. + // This setting can take four values, each named after another + // text editor: + // + // 1. "VSCode" + // 2. "JetBrains" + // 3. "SublimeText" + // 4. "Atom" + "base_keymap": "VSCode", // Features that can be globally enabled or disabled "features": { // Show Copilot icon in status bar diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index e0b3f547f9ce79818579f0b0f9a38b2dec82f48c..0b638da9242b8dbfdfe504cce5fa478a14c53fb0 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -1,4 +1,4 @@ -use crate::{settings_store::parse_json_with_comments, Settings}; +use crate::settings_store::parse_json_with_comments; use anyhow::{Context, Result}; use assets::Assets; use collections::BTreeMap; @@ -41,20 +41,14 @@ impl JsonSchema for KeymapAction { struct ActionWithData(Box, Box); impl KeymapFileContent { - pub fn load_defaults(cx: &mut AppContext) { - for path in ["keymaps/default.json", "keymaps/vim.json"] { - Self::load(path, cx).unwrap(); - } - - if let Some(asset_path) = cx.global::().base_keymap.asset_path() { - Self::load(asset_path, cx).log_err(); - } - } - - pub fn load(asset_path: &str, cx: &mut AppContext) -> Result<()> { + pub fn load_asset(asset_path: &str, cx: &mut AppContext) -> Result<()> { let content = Assets::get(asset_path).unwrap().data; let content_str = std::str::from_utf8(content.as_ref()).unwrap(); - parse_json_with_comments::(content_str)?.add_to_cx(cx) + Self::parse(content_str)?.add_to_cx(cx) + } + + pub fn parse(content: &str) -> Result { + parse_json_with_comments::(content) } pub fn add_to_cx(self, cx: &mut AppContext) -> Result<()> { diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 0e4cd4d9228b977eb7b8ec273ca446355abf53e3..bd1104105fc4b4854da79a5417bee27940d3ba75 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -34,7 +34,6 @@ pub struct Settings { pub buffer_font_family: FamilyId, pub buffer_font_size: f32, pub theme: Arc, - pub base_keymap: BaseKeymap, } impl Setting for Settings { @@ -62,7 +61,6 @@ impl Setting for Settings { buffer_font_features, buffer_font_size: defaults.buffer_font_size.unwrap(), theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), - base_keymap: Default::default(), }; for value in user_values.into_iter().copied().cloned() { @@ -111,48 +109,6 @@ impl Setting for Settings { } } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] -pub enum BaseKeymap { - #[default] - VSCode, - JetBrains, - SublimeText, - Atom, - TextMate, -} - -impl BaseKeymap { - pub const OPTIONS: [(&'static str, Self); 5] = [ - ("VSCode (Default)", Self::VSCode), - ("Atom", Self::Atom), - ("JetBrains", Self::JetBrains), - ("Sublime Text", Self::SublimeText), - ("TextMate", Self::TextMate), - ]; - - pub fn asset_path(&self) -> Option<&'static str> { - match self { - BaseKeymap::JetBrains => Some("keymaps/jetbrains.json"), - BaseKeymap::SublimeText => Some("keymaps/sublime_text.json"), - BaseKeymap::Atom => Some("keymaps/atom.json"), - BaseKeymap::TextMate => Some("keymaps/textmate.json"), - BaseKeymap::VSCode => None, - } - } - - pub fn names() -> impl Iterator { - Self::OPTIONS.iter().map(|(name, _)| *name) - } - - pub fn from_names(option: &str) -> BaseKeymap { - Self::OPTIONS - .iter() - .copied() - .find_map(|(name, value)| (name == option).then(|| value)) - .unwrap_or_default() - } -} - #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct SettingsFileContent { #[serde(default)] @@ -163,8 +119,6 @@ pub struct SettingsFileContent { pub buffer_font_features: Option, #[serde(default)] pub theme: Option, - #[serde(default)] - pub base_keymap: Option, } impl Settings { @@ -198,7 +152,6 @@ impl Settings { buffer_font_features, buffer_font_size: defaults.buffer_font_size.unwrap(), theme: themes.get(&defaults.theme.unwrap()).unwrap(), - base_keymap: Default::default(), } } @@ -234,7 +187,6 @@ impl Settings { } merge(&mut self.buffer_font_size, data.buffer_font_size); - merge(&mut self.base_keymap, data.base_keymap); } #[cfg(any(test, feature = "test-support"))] @@ -248,7 +200,6 @@ impl Settings { .unwrap(), buffer_font_size: 14., theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default), - base_keymap: Default::default(), } } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 070da5d1ea6d440097e9d42db6507f4940d05d5e..cf1787d7c02e0ff90ccebbae74a0473ea72f0e9c 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,6 +1,6 @@ use crate::{ - settings_store::parse_json_with_comments, settings_store::SettingsStore, KeymapFileContent, - Setting, Settings, DEFAULT_SETTINGS_ASSET_PATH, + settings_store::parse_json_with_comments, settings_store::SettingsStore, Setting, Settings, + DEFAULT_SETTINGS_ASSET_PATH, }; use anyhow::Result; use assets::Assets; @@ -76,43 +76,6 @@ pub fn watch_config_file( rx } -pub fn handle_keymap_file_changes( - mut user_keymap_file_rx: mpsc::UnboundedReceiver, - cx: &mut AppContext, -) { - cx.spawn(move |mut cx| async move { - let mut settings_subscription = None; - while let Some(user_keymap_content) = user_keymap_file_rx.next().await { - if let Ok(keymap_content) = - parse_json_with_comments::(&user_keymap_content) - { - cx.update(|cx| { - cx.clear_bindings(); - KeymapFileContent::load_defaults(cx); - keymap_content.clone().add_to_cx(cx).log_err(); - }); - - let mut old_base_keymap = cx.read(|cx| cx.global::().base_keymap.clone()); - drop(settings_subscription); - settings_subscription = Some(cx.update(|cx| { - cx.observe_global::(move |cx| { - let settings = cx.global::(); - if settings.base_keymap != old_base_keymap { - old_base_keymap = settings.base_keymap.clone(); - - cx.clear_bindings(); - KeymapFileContent::load_defaults(cx); - keymap_content.clone().add_to_cx(cx).log_err(); - } - }) - .detach(); - })); - } - } - }) - .detach(); -} - pub fn handle_settings_file_changes( mut user_settings_file_rx: mpsc::UnboundedReceiver, cx: &mut AppContext, @@ -184,180 +147,3 @@ pub fn update_settings_file( }) .detach_and_log_err(cx); } - -#[cfg(test)] -mod tests { - use super::*; - use fs::FakeFs; - use gpui::{actions, elements::*, Action, Entity, TestAppContext, View, ViewContext}; - use theme::ThemeRegistry; - - struct TestView; - - impl Entity for TestView { - type Event = (); - } - - impl View for TestView { - fn ui_name() -> &'static str { - "TestView" - } - - fn render(&mut self, _: &mut ViewContext) -> AnyElement { - Empty::new().into_any() - } - } - - #[gpui::test] - async fn test_base_keymap(cx: &mut gpui::TestAppContext) { - let executor = cx.background(); - let fs = FakeFs::new(executor.clone()); - - actions!(test, [A, B]); - // From the Atom keymap - actions!(workspace, [ActivatePreviousPane]); - // From the JetBrains keymap - actions!(pane, [ActivatePrevItem]); - - fs.save( - "/settings.json".as_ref(), - &r#" - { - "base_keymap": "Atom" - } - "# - .into(), - Default::default(), - ) - .await - .unwrap(); - - fs.save( - "/keymap.json".as_ref(), - &r#" - [ - { - "bindings": { - "backspace": "test::A" - } - } - ] - "# - .into(), - Default::default(), - ) - .await - .unwrap(); - - cx.update(|cx| { - let mut store = SettingsStore::default(); - store.set_default_settings(&test_settings(), cx).unwrap(); - cx.set_global(store); - cx.set_global(ThemeRegistry::new(Assets, cx.font_cache().clone())); - cx.add_global_action(|_: &A, _cx| {}); - cx.add_global_action(|_: &B, _cx| {}); - cx.add_global_action(|_: &ActivatePreviousPane, _cx| {}); - cx.add_global_action(|_: &ActivatePrevItem, _cx| {}); - - let settings_rx = watch_config_file( - executor.clone(), - fs.clone(), - PathBuf::from("/settings.json"), - ); - let keymap_rx = - watch_config_file(executor.clone(), fs.clone(), PathBuf::from("/keymap.json")); - - handle_keymap_file_changes(keymap_rx, cx); - handle_settings_file_changes(settings_rx, cx); - }); - - cx.foreground().run_until_parked(); - - let (window_id, _view) = cx.add_window(|_| TestView); - - // Test loading the keymap base at all - assert_key_bindings_for( - window_id, - cx, - vec![("backspace", &A), ("k", &ActivatePreviousPane)], - line!(), - ); - - // Test modifying the users keymap, while retaining the base keymap - fs.save( - "/keymap.json".as_ref(), - &r#" - [ - { - "bindings": { - "backspace": "test::B" - } - } - ] - "# - .into(), - Default::default(), - ) - .await - .unwrap(); - - cx.foreground().run_until_parked(); - - assert_key_bindings_for( - window_id, - cx, - vec![("backspace", &B), ("k", &ActivatePreviousPane)], - line!(), - ); - - // Test modifying the base, while retaining the users keymap - fs.save( - "/settings.json".as_ref(), - &r#" - { - "base_keymap": "JetBrains" - } - "# - .into(), - Default::default(), - ) - .await - .unwrap(); - - cx.foreground().run_until_parked(); - - assert_key_bindings_for( - window_id, - cx, - vec![("backspace", &B), ("[", &ActivatePrevItem)], - line!(), - ); - } - - fn assert_key_bindings_for<'a>( - window_id: usize, - cx: &TestAppContext, - actions: Vec<(&'static str, &'a dyn Action)>, - line: u32, - ) { - for (key, action) in actions { - // assert that... - assert!( - cx.available_actions(window_id, 0) - .into_iter() - .any(|(_, bound_action, b)| { - // action names match... - bound_action.name() == action.name() - && bound_action.namespace() == action.namespace() - // and key strokes contain the given key - && b.iter() - .any(|binding| binding.keystrokes().iter().any(|k| k.key == key)) - }), - "On {} Failed to find {} with key binding {}", - line, - action.name(), - key - ); - } - } -} diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index 9ac40831d1bf6b47f093f19e4175f784d4d98691..531fbf0bba110847fa4474fbb8b8bde025865a98 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -27,7 +27,7 @@ impl<'a> VimTestContext<'a> { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| *s = Some(enabled)); }); - settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap(); + settings::KeymapFileContent::load_asset("keymaps/vim.json", cx).unwrap(); }); // Setup search toolbars and keypress hook diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index 82cd4ca35a6cf86bac7ea40bc820a720238042b7..65f5151584bea831d1c4744c150b994ea86fa72e 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -11,8 +11,6 @@ path = "src/welcome.rs" test-support = [] [dependencies] -anyhow.workspace = true -log.workspace = true client = { path = "../client" } editor = { path = "../editor" } fs = { path = "../fs" } @@ -27,3 +25,8 @@ theme_selector = { path = "../theme_selector" } util = { path = "../util" } picker = { path = "../picker" } workspace = { path = "../workspace" } + +anyhow.workspace = true +log.workspace = true +schemars.workspace = true +serde.workspace = true diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index 24600d5b82424d6cf565f060828e02bcdb8f7b4e..815de5e6aee4a5d11dcbc24374c42285d677e741 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -1,3 +1,4 @@ +use super::base_keymap_setting::BaseKeymap; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ actions, @@ -6,7 +7,7 @@ use gpui::{ }; use picker::{Picker, PickerDelegate, PickerEvent}; use project::Fs; -use settings::{update_settings_file, BaseKeymap, Settings}; +use settings::{update_settings_file, Settings}; use std::sync::Arc; use util::ResultExt; use workspace::Workspace; @@ -39,10 +40,10 @@ pub struct BaseKeymapSelectorDelegate { impl BaseKeymapSelectorDelegate { fn new(fs: Arc, cx: &mut ViewContext) -> Self { - let base = cx.global::().base_keymap; + let base = settings::get_setting::(None, cx); let selected_index = BaseKeymap::OPTIONS .iter() - .position(|(_, value)| *value == base) + .position(|(_, value)| value == base) .unwrap_or(0); Self { matches: Vec::new(), @@ -122,8 +123,8 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { fn confirm(&mut self, cx: &mut ViewContext) { if let Some(selection) = self.matches.get(self.selected_index) { let base_keymap = BaseKeymap::from_names(&selection.string); - update_settings_file::(self.fs.clone(), cx, move |settings| { - settings.base_keymap = Some(base_keymap) + update_settings_file::(self.fs.clone(), cx, move |setting| { + *setting = Some(base_keymap) }); } cx.emit(PickerEvent::Dismiss); diff --git a/crates/welcome/src/base_keymap_setting.rs b/crates/welcome/src/base_keymap_setting.rs new file mode 100644 index 0000000000000000000000000000000000000000..c5b6171f9b4fb809670d7e17f800f5d23699da61 --- /dev/null +++ b/crates/welcome/src/base_keymap_setting.rs @@ -0,0 +1,65 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use settings::Setting; + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] +pub enum BaseKeymap { + #[default] + VSCode, + JetBrains, + SublimeText, + Atom, + TextMate, +} + +impl BaseKeymap { + pub const OPTIONS: [(&'static str, Self); 5] = [ + ("VSCode (Default)", Self::VSCode), + ("Atom", Self::Atom), + ("JetBrains", Self::JetBrains), + ("Sublime Text", Self::SublimeText), + ("TextMate", Self::TextMate), + ]; + + pub fn asset_path(&self) -> Option<&'static str> { + match self { + BaseKeymap::JetBrains => Some("keymaps/jetbrains.json"), + BaseKeymap::SublimeText => Some("keymaps/sublime_text.json"), + BaseKeymap::Atom => Some("keymaps/atom.json"), + BaseKeymap::TextMate => Some("keymaps/textmate.json"), + BaseKeymap::VSCode => None, + } + } + + pub fn names() -> impl Iterator { + Self::OPTIONS.iter().map(|(name, _)| *name) + } + + pub fn from_names(option: &str) -> BaseKeymap { + Self::OPTIONS + .iter() + .copied() + .find_map(|(name, value)| (name == option).then(|| value)) + .unwrap_or_default() + } +} + +impl Setting for BaseKeymap { + const KEY: Option<&'static str> = Some("base_keymap"); + + type FileContent = Option; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result + where + Self: Sized, + { + Ok(user_values + .first() + .and_then(|v| **v) + .unwrap_or(default_value.unwrap())) + } +} diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 6b8fe7312dd03179c9e98701be93bab365bcb5cc..ca04d5f6dc9a46316a419928017f263acfebf32a 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,7 +1,7 @@ mod base_keymap_picker; +mod base_keymap_setting; -use std::{borrow::Cow, sync::Arc}; - +use crate::base_keymap_picker::ToggleBaseKeymapSelector; use client::TelemetrySettings; use db::kvp::KEY_VALUE_STORE; use gpui::{ @@ -9,17 +9,19 @@ use gpui::{ AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, WeakViewHandle, }; use settings::{update_settings_file, Settings}; - +use std::{borrow::Cow, sync::Arc}; use workspace::{ item::Item, open_new, sidebar::SidebarSide, AppState, PaneBackdrop, Welcome, Workspace, WorkspaceId, }; -use crate::base_keymap_picker::ToggleBaseKeymapSelector; +pub use base_keymap_setting::BaseKeymap; pub const FIRST_OPEN: &str = "first_open"; pub fn init(cx: &mut AppContext) { + settings::register_setting::(cx); + cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); workspace.add_item(Box::new(welcome_page), cx) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index b290460cca610b47e045618edce24f497a0bc9af..def504963348fd36f4bb8a7cedf406e6f6fdc79a 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -24,8 +24,7 @@ use parking_lot::Mutex; use project::Fs; use serde::{Deserialize, Serialize}; use settings::{ - default_settings, handle_keymap_file_changes, handle_settings_file_changes, watch_config_file, - Settings, SettingsStore, + default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore, }; use simplelog::ConfigBuilder; use smol::process::Command; @@ -63,7 +62,9 @@ use workspace::{ dock::FocusDock, item::ItemHandle, notifications::NotifyResultExt, AppState, OpenSettings, Workspace, }; -use zed::{self, build_window_options, initialize_workspace, languages, menus}; +use zed::{ + self, build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus, +}; fn main() { let http = http::client(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 923a3d6d9ca6c038edacf9c28facef0d69f9264d..d6e4f26b675666f2f9e44c8939558f402e9b6b5a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -15,7 +15,7 @@ use anyhow::anyhow; use feedback::{ feedback_info_text::FeedbackInfoText, submit_feedback_button::SubmitFeedbackButton, }; -use futures::StreamExt; +use futures::{channel::mpsc, StreamExt}; use gpui::{ actions, geometry::vector::vec2f, @@ -29,11 +29,14 @@ use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; -use settings::{adjust_font_size_delta, Settings, DEFAULT_SETTINGS_ASSET_PATH}; +use settings::{ + adjust_font_size_delta, KeymapFileContent, Settings, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH, +}; use std::{borrow::Cow, str, sync::Arc}; use terminal_view::terminal_button::TerminalButton; use util::{channel::ReleaseChannel, paths, ResultExt}; use uuid::Uuid; +use welcome::BaseKeymap; pub use workspace; use workspace::{ create_and_open_local_file, open_new, sidebar::SidebarSide, AppState, NewFile, NewWindow, @@ -258,7 +261,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { activity_indicator::init(cx); lsp_log::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); - settings::KeymapFileContent::load_defaults(cx); + load_default_keymap(cx); } pub fn initialize_workspace( @@ -478,6 +481,52 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext) { .detach(); } +pub fn load_default_keymap(cx: &mut AppContext) { + for path in ["keymaps/default.json", "keymaps/vim.json"] { + KeymapFileContent::load_asset(path, cx).unwrap(); + } + + if let Some(asset_path) = settings::get_setting::(None, cx).asset_path() { + KeymapFileContent::load_asset(asset_path, cx).unwrap(); + } +} + +pub fn handle_keymap_file_changes( + mut user_keymap_file_rx: mpsc::UnboundedReceiver, + cx: &mut AppContext, +) { + cx.spawn(move |mut cx| async move { + let mut settings_subscription = None; + while let Some(user_keymap_content) = user_keymap_file_rx.next().await { + if let Ok(keymap_content) = KeymapFileContent::parse(&user_keymap_content) { + cx.update(|cx| { + cx.clear_bindings(); + load_default_keymap(cx); + keymap_content.clone().add_to_cx(cx).log_err(); + }); + + let mut old_base_keymap = + cx.read(|cx| *settings::get_setting::(None, cx)); + drop(settings_subscription); + settings_subscription = Some(cx.update(|cx| { + cx.observe_global::(move |cx| { + let new_base_keymap = *settings::get_setting::(None, cx); + if new_base_keymap != old_base_keymap { + old_base_keymap = new_base_keymap.clone(); + + cx.clear_bindings(); + load_default_keymap(cx); + keymap_content.clone().add_to_cx(cx).log_err(); + } + }) + .detach(); + })); + } + } + }) + .detach(); +} + fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext) { workspace.with_local_workspace(cx, move |workspace, cx| { let app_state = workspace.app_state().clone(); @@ -579,11 +628,16 @@ mod tests { use super::*; use assets::Assets; use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor}; - use gpui::{executor::Deterministic, AppContext, AssetSource, TestAppContext, ViewHandle}; + use fs::{FakeFs, Fs}; + use gpui::{ + elements::Empty, executor::Deterministic, Action, AnyElement, AppContext, AssetSource, + Element, Entity, TestAppContext, View, ViewHandle, + }; use language::LanguageRegistry; use node_runtime::NodeRuntime; use project::{Project, ProjectPath}; use serde_json::json; + use settings::{handle_settings_file_changes, watch_config_file, SettingsStore}; use std::{ collections::HashSet, path::{Path, PathBuf}, @@ -1797,6 +1851,175 @@ mod tests { } } + #[gpui::test] + async fn test_base_keymap(cx: &mut gpui::TestAppContext) { + struct TestView; + + impl Entity for TestView { + type Event = (); + } + + impl View for TestView { + fn ui_name() -> &'static str { + "TestView" + } + + fn render(&mut self, _: &mut ViewContext) -> AnyElement { + Empty::new().into_any() + } + } + + let executor = cx.background(); + let fs = FakeFs::new(executor.clone()); + + actions!(test, [A, B]); + // From the Atom keymap + actions!(workspace, [ActivatePreviousPane]); + // From the JetBrains keymap + actions!(pane, [ActivatePrevItem]); + + fs.save( + "/settings.json".as_ref(), + &r#" + { + "base_keymap": "Atom" + } + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + fs.save( + "/keymap.json".as_ref(), + &r#" + [ + { + "bindings": { + "backspace": "test::A" + } + } + ] + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + cx.set_global(ThemeRegistry::new(Assets, cx.font_cache().clone())); + welcome::init(cx); + + cx.add_global_action(|_: &A, _cx| {}); + cx.add_global_action(|_: &B, _cx| {}); + cx.add_global_action(|_: &ActivatePreviousPane, _cx| {}); + cx.add_global_action(|_: &ActivatePrevItem, _cx| {}); + + let settings_rx = watch_config_file( + executor.clone(), + fs.clone(), + PathBuf::from("/settings.json"), + ); + let keymap_rx = + watch_config_file(executor.clone(), fs.clone(), PathBuf::from("/keymap.json")); + + handle_keymap_file_changes(keymap_rx, cx); + handle_settings_file_changes(settings_rx, cx); + }); + + cx.foreground().run_until_parked(); + + let (window_id, _view) = cx.add_window(|_| TestView); + + // Test loading the keymap base at all + assert_key_bindings_for( + window_id, + cx, + vec![("backspace", &A), ("k", &ActivatePreviousPane)], + line!(), + ); + + // Test modifying the users keymap, while retaining the base keymap + fs.save( + "/keymap.json".as_ref(), + &r#" + [ + { + "bindings": { + "backspace": "test::B" + } + } + ] + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + cx.foreground().run_until_parked(); + + assert_key_bindings_for( + window_id, + cx, + vec![("backspace", &B), ("k", &ActivatePreviousPane)], + line!(), + ); + + // Test modifying the base, while retaining the users keymap + fs.save( + "/settings.json".as_ref(), + &r#" + { + "base_keymap": "JetBrains" + } + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + cx.foreground().run_until_parked(); + + assert_key_bindings_for( + window_id, + cx, + vec![("backspace", &B), ("[", &ActivatePrevItem)], + line!(), + ); + + fn assert_key_bindings_for<'a>( + window_id: usize, + cx: &TestAppContext, + actions: Vec<(&'static str, &'a dyn Action)>, + line: u32, + ) { + for (key, action) in actions { + // assert that... + assert!( + cx.available_actions(window_id, 0) + .into_iter() + .any(|(_, bound_action, b)| { + // action names match... + bound_action.name() == action.name() + && bound_action.namespace() == action.namespace() + // and key strokes contain the given key + && b.iter() + .any(|binding| binding.keystrokes().iter().any(|k| k.key == key)) + }), + "On {} Failed to find {} with key binding {}", + line, + action.name(), + key + ); + } + } + } + #[gpui::test] fn test_bundled_settings_and_themes(cx: &mut AppContext) { cx.platform() From 67a25126d48b173ba8a5dffdff1aaf94002c3781 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 14:44:55 -0700 Subject: [PATCH 19/25] Define theme/ui text style settings in theme crate --- Cargo.lock | 9 +- crates/activity_indicator/Cargo.toml | 2 + .../src/activity_indicator.rs | 8 +- crates/auto_update/src/update_notification.rs | 3 +- crates/breadcrumbs/src/breadcrumbs.rs | 3 +- crates/collab/src/tests.rs | 6 +- .../src/tests/randomized_integration_tests.rs | 4 +- crates/collab_ui/src/collab_titlebar_item.rs | 7 +- crates/collab_ui/src/contact_finder.rs | 3 +- crates/collab_ui/src/contact_list.rs | 5 +- crates/collab_ui/src/contacts_popover.rs | 3 +- .../src/incoming_call_notification.rs | 28 +-- crates/collab_ui/src/notifications.rs | 3 +- .../src/project_shared_notification.rs | 27 +-- crates/command_palette/src/command_palette.rs | 5 +- crates/context_menu/src/context_menu.rs | 5 +- crates/copilot/src/sign_in.rs | 5 +- crates/copilot_button/src/copilot_button.rs | 11 +- crates/diagnostics/Cargo.toml | 1 + crates/diagnostics/src/diagnostics.rs | 38 ++-- crates/diagnostics/src/items.rs | 10 +- crates/editor/src/blink_manager.rs | 6 +- crates/editor/src/display_map/block_map.rs | 13 +- crates/editor/src/display_map/fold_map.rs | 14 +- .../editor/src/display_map/suggestion_map.rs | 10 +- crates/editor/src/display_map/wrap_map.rs | 14 +- crates/editor/src/editor.rs | 22 +- crates/editor/src/editor_tests.rs | 6 +- crates/editor/src/element.rs | 9 +- crates/editor/src/hover_popover.rs | 3 +- crates/editor/src/items.rs | 3 +- crates/editor/src/link_go_to_definition.rs | 3 +- crates/editor/src/movement.rs | 5 +- crates/editor/src/multi_buffer.rs | 6 +- .../src/test/editor_lsp_test_context.rs | 1 + crates/feedback/src/deploy_feedback_button.rs | 3 +- crates/feedback/src/feedback_info_text.rs | 3 +- crates/feedback/src/submit_feedback_button.rs | 3 +- crates/file_finder/src/file_finder.rs | 6 +- crates/go_to_line/Cargo.toml | 1 + crates/go_to_line/src/go_to_line.rs | 3 +- crates/language/src/language_settings.rs | 1 + .../src/active_buffer_language.rs | 3 +- .../src/language_selector.rs | 4 +- crates/lsp_log/src/lsp_log.rs | 3 +- crates/outline/Cargo.toml | 2 + crates/outline/src/outline.rs | 7 +- crates/picker/src/picker.rs | 2 +- crates/project/src/project.rs | 10 +- crates/project_panel/src/project_panel.rs | 13 +- crates/project_symbols/Cargo.toml | 3 + crates/project_symbols/src/project_symbols.rs | 12 +- crates/recent_projects/Cargo.toml | 1 + crates/recent_projects/src/recent_projects.rs | 6 +- crates/search/src/buffer_search.rs | 25 +-- crates/search/src/project_search.rs | 35 ++- crates/settings/Cargo.toml | 8 +- crates/settings/src/settings.rs | 209 +----------------- crates/settings/src/settings_file.rs | 25 +-- crates/settings/src/settings_store.rs | 30 ++- crates/terminal/src/terminal.rs | 3 +- crates/terminal_view/src/terminal_button.rs | 3 +- crates/terminal_view/src/terminal_element.rs | 6 +- crates/terminal_view/src/terminal_view.rs | 1 + crates/theme/Cargo.toml | 15 +- crates/theme/src/theme.rs | 18 +- crates/theme/src/theme_registry.rs | 12 +- crates/theme/src/theme_settings.rs | 136 ++++++++++++ crates/theme_selector/src/theme_selector.rs | 54 ++--- crates/theme_testbench/src/theme_testbench.rs | 7 +- crates/welcome/src/base_keymap_picker.rs | 4 +- crates/welcome/src/welcome.rs | 10 +- crates/workspace/src/dock.rs | 3 - .../workspace/src/dock/toggle_dock_button.rs | 3 +- crates/workspace/src/notifications.rs | 9 +- crates/workspace/src/pane.rs | 17 +- .../src/pane/dragged_item_receiver.rs | 12 +- crates/workspace/src/shared_screen.rs | 3 +- crates/workspace/src/sidebar.rs | 5 +- crates/workspace/src/status_bar.rs | 3 +- crates/workspace/src/toolbar.rs | 5 +- crates/workspace/src/workspace.rs | 17 +- crates/zed/src/languages.rs | 8 +- crates/zed/src/languages/json.rs | 31 +-- crates/zed/src/main.rs | 33 ++- crates/zed/src/zed.rs | 30 ++- 86 files changed, 530 insertions(+), 637 deletions(-) create mode 100644 crates/theme/src/theme_settings.rs diff --git a/Cargo.lock b/Cargo.lock index e5922f9e667abdcb1c8975fa46f645e99951959e..af412a8c1bdec8059c29dc13c4bfd78381af26a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "project", "settings", "smallvec", + "theme", "util", "workspace", ] @@ -2750,6 +2751,7 @@ dependencies = [ "postage", "settings", "text", + "theme", "util", "workspace", ] @@ -4398,6 +4400,7 @@ dependencies = [ "settings", "smol", "text", + "theme", "workspace", ] @@ -4915,6 +4918,7 @@ dependencies = [ "settings", "smol", "text", + "theme", "util", "workspace", ] @@ -5235,6 +5239,7 @@ dependencies = [ "settings", "smol", "text", + "theme", "util", "workspace", ] @@ -6135,7 +6140,6 @@ dependencies = [ "smallvec", "sqlez", "staff_mode", - "theme", "toml", "tree-sitter", "tree-sitter-json 0.19.0", @@ -6863,10 +6867,13 @@ dependencies = [ "gpui", "indexmap", "parking_lot 0.11.2", + "schemars", "serde", "serde_derive", "serde_json", + "settings", "toml", + "util", ] [[package]] diff --git a/crates/activity_indicator/Cargo.toml b/crates/activity_indicator/Cargo.toml index 629aa2c032929d3a6465723b0dab5fe04902c14b..917383234a322f1145e47c218ca0937cae8b339f 100644 --- a/crates/activity_indicator/Cargo.toml +++ b/crates/activity_indicator/Cargo.toml @@ -16,6 +16,8 @@ gpui = { path = "../gpui" } project = { path = "../project" } settings = { path = "../settings" } util = { path = "../util" } +theme = { path = "../theme" } workspace = { path = "../workspace" } + futures.workspace = true smallvec.workspace = true diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index d5ee1364b37100a7e71e4f6365bbee523e630bda..801c8f7172ec40d5c111109482d55e345d037592 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -9,7 +9,6 @@ use gpui::{ }; use language::{LanguageRegistry, LanguageServerBinaryStatus}; use project::{LanguageServerProgress, Project}; -use settings::Settings; use smallvec::SmallVec; use std::{cmp::Reverse, fmt::Write, sync::Arc}; use util::ResultExt; @@ -325,12 +324,7 @@ impl View for ActivityIndicator { } = self.content_to_render(cx); let mut element = MouseEventHandler::::new(0, cx, |state, cx| { - let theme = &cx - .global::() - .theme - .workspace - .status_bar - .lsp_status; + let theme = &theme::current(cx).workspace.status_bar.lsp_status; let style = if state.hovered() && on_click.is_some() { theme.hover.as_ref().unwrap_or(&theme.default) } else { diff --git a/crates/auto_update/src/update_notification.rs b/crates/auto_update/src/update_notification.rs index b48ac2a413b00e2f24a41ba742883019b507d961..6f31df614dbbd0f0761c93487d988ae9168f3d9d 100644 --- a/crates/auto_update/src/update_notification.rs +++ b/crates/auto_update/src/update_notification.rs @@ -5,7 +5,6 @@ use gpui::{ Element, Entity, View, ViewContext, }; use menu::Cancel; -use settings::Settings; use util::channel::ReleaseChannel; use workspace::notifications::Notification; @@ -27,7 +26,7 @@ impl View for UpdateNotification { } fn render(&mut self, cx: &mut gpui::ViewContext) -> gpui::AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let theme = &theme.update_notification; let app_name = cx.global::().display_name(); diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs index f3be60f8de7fb343da80012271b3439524d36d4e..906d70abb738e7267d35a59f501be96809fbf5b1 100644 --- a/crates/breadcrumbs/src/breadcrumbs.rs +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -4,7 +4,6 @@ use gpui::{ }; use itertools::Itertools; use search::ProjectSearchView; -use settings::Settings; use workspace::{ item::{ItemEvent, ItemHandle}, ToolbarItemLocation, ToolbarItemView, Workspace, @@ -50,7 +49,7 @@ impl View for Breadcrumbs { }; let not_editor = active_item.downcast::().is_none(); - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let style = &theme.workspace.breadcrumbs; let breadcrumbs = match active_item.breadcrumbs(&theme, cx) { diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index f1593822afd45fe32aece5e0019e57b05efb3c13..3c571327eb2c7ab4482779de9e3ec5c7936304ef 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -19,7 +19,7 @@ use gpui::{ use language::LanguageRegistry; use parking_lot::Mutex; use project::{Project, WorktreeId}; -use settings::{Settings, SettingsStore}; +use settings::SettingsStore; use std::{ cell::{Ref, RefCell, RefMut}, env, @@ -30,7 +30,6 @@ use std::{ Arc, }, }; -use theme::ThemeRegistry; use util::http::FakeHttpClient; use workspace::Workspace; @@ -103,7 +102,6 @@ impl TestServer { async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient { cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); - cx.set_global(Settings::test(cx)); }); let http = FakeHttpClient::with_404_response(); @@ -192,7 +190,6 @@ impl TestServer { client: client.clone(), user_store: user_store.clone(), languages: Arc::new(LanguageRegistry::test()), - themes: ThemeRegistry::new((), cx.font_cache()), fs: fs.clone(), build_window_options: |_, _, _| Default::default(), initialize_workspace: |_, _, _| unimplemented!(), @@ -201,6 +198,7 @@ impl TestServer { }); cx.update(|cx| { + theme::init((), cx); Project::init(&client, cx); client::init(&client, cx); language::init(cx); diff --git a/crates/collab/src/tests/randomized_integration_tests.rs b/crates/collab/src/tests/randomized_integration_tests.rs index fb0645b1479950fd0b1bcfa57e5b46200c8e0f37..3beff6942aed3b1e3c001bcc9190b206663dbc3b 100644 --- a/crates/collab/src/tests/randomized_integration_tests.rs +++ b/crates/collab/src/tests/randomized_integration_tests.rs @@ -21,7 +21,7 @@ use rand::{ prelude::*, }; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::SettingsStore; use std::{ env, ops::Range, @@ -150,10 +150,8 @@ async fn test_random_collaboration( for (client, mut cx) in clients { cx.update(|cx| { let store = cx.remove_global::(); - let settings = cx.remove_global::(); cx.clear_globals(); cx.set_global(store); - cx.set_global(settings); drop(client); }); } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 7374b166ca6e4eb5b2b7aae5304c556fea5ff526..eb1755a9ff3c25494c93c5e320362d08da45c56b 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -18,7 +18,6 @@ use gpui::{ ViewContext, ViewHandle, WeakViewHandle, }; use project::Project; -use settings::Settings; use std::{ops::Range, sync::Arc}; use theme::{AvatarStyle, Theme}; use util::ResultExt; @@ -70,7 +69,7 @@ impl View for CollabTitlebarItem { }; let project = self.project.read(cx); - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let mut left_container = Flex::row(); let mut right_container = Flex::row().align_children_center(); @@ -298,7 +297,7 @@ impl CollabTitlebarItem { } pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext) { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let avatar_style = theme.workspace.titlebar.leader_avatar.clone(); let item_style = theme.context_menu.item.disabled_style().clone(); self.user_menu.update(cx, |user_menu, cx| { @@ -866,7 +865,7 @@ impl CollabTitlebarItem { ) -> Option> { enum ConnectionStatusButton {} - let theme = &cx.global::().theme.clone(); + let theme = &theme::current(cx).clone(); match status { client::Status::ConnectionError | client::Status::ConnectionLost diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index 8530867f1479dd653a90b974f2840307bd461ecf..b5f2416a5bc51e1d4bbe763082f77ff80b75922d 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -1,7 +1,6 @@ use client::{ContactRequestStatus, User, UserStore}; use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext}; use picker::{Picker, PickerDelegate, PickerEvent}; -use settings::Settings; use std::sync::Arc; use util::TryFutureExt; @@ -98,7 +97,7 @@ impl PickerDelegate for ContactFinderDelegate { selected: bool, cx: &gpui::AppContext, ) -> AnyElement> { - let theme = &cx.global::().theme; + let theme = &theme::current(cx); let user = &self.potential_contacts[ix]; let request_status = self.user_store.read(cx).contact_request_status(user); diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 452867b8c4dd6d74a8ba23b77120f7472dbde7e1..e8dae210c4c5ed196207fefb285c21c5a25bd1e1 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -14,7 +14,6 @@ use gpui::{ use menu::{Confirm, SelectNext, SelectPrev}; use project::Project; use serde::Deserialize; -use settings::Settings; use std::{mem, sync::Arc}; use theme::IconButton; use workspace::Workspace; @@ -192,7 +191,7 @@ impl ContactList { .detach(); let list_state = ListState::::new(0, Orientation::Top, 1000., move |this, ix, cx| { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let is_selected = this.selection == Some(ix); let current_project_id = this.project.read(cx).remote_id(); @@ -1313,7 +1312,7 @@ impl View for ContactList { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { enum AddContact {} - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); Flex::column() .with_child( diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 35734d81f48c132aa41259f47420680977ea4ed8..1d6d1c84c7353faf85556f621fdc8d1b372b444a 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -9,7 +9,6 @@ use gpui::{ }; use picker::PickerEvent; use project::Project; -use settings::Settings; use workspace::Workspace; actions!(contacts_popover, [ToggleContactFinder]); @@ -108,7 +107,7 @@ impl View for ContactsPopover { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let child = match &self.child { Child::ContactList(child) => ChildView::new(child, cx), Child::ContactFinder(child) => ChildView::new(child, cx), diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 35484b33090fb6eacc4cb41de8e233b92a19a16d..ed3e6485552eeb8f78173b47dbd58c1dcafd856e 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -9,7 +9,6 @@ use gpui::{ platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions}, AnyElement, AppContext, Entity, View, ViewContext, }; -use settings::Settings; use util::ResultExt; use workspace::AppState; @@ -26,7 +25,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { if let Some(incoming_call) = incoming_call { const PADDING: f32 = 16.; let window_size = cx.read(|cx| { - let theme = &cx.global::().theme.incoming_call_notification; + let theme = &theme::current(cx).incoming_call_notification; vec2f(theme.window_width, theme.window_height) }); @@ -106,7 +105,7 @@ impl IncomingCallNotification { } fn render_caller(&self, cx: &mut ViewContext) -> AnyElement { - let theme = &cx.global::().theme.incoming_call_notification; + let theme = &theme::current(cx).incoming_call_notification; let default_project = proto::ParticipantProject::default(); let initial_project = self .call @@ -170,10 +169,11 @@ impl IncomingCallNotification { enum Accept {} enum Decline {} + let theme = theme::current(cx); Flex::column() .with_child( - MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.incoming_call_notification; + MouseEventHandler::::new(0, cx, |_, _| { + let theme = &theme.incoming_call_notification; Label::new("Accept", theme.accept_button.text.clone()) .aligned() .contained() @@ -186,8 +186,8 @@ impl IncomingCallNotification { .flex(1., true), ) .with_child( - MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.incoming_call_notification; + MouseEventHandler::::new(0, cx, |_, _| { + let theme = &theme.incoming_call_notification; Label::new("Decline", theme.decline_button.text.clone()) .aligned() .contained() @@ -200,12 +200,7 @@ impl IncomingCallNotification { .flex(1., true), ) .constrained() - .with_width( - cx.global::() - .theme - .incoming_call_notification - .button_width, - ) + .with_width(theme.incoming_call_notification.button_width) .into_any() } } @@ -220,12 +215,7 @@ impl View for IncomingCallNotification { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let background = cx - .global::() - .theme - .incoming_call_notification - .background; - + let background = theme::current(cx).incoming_call_notification.background; Flex::row() .with_child(self.render_caller(cx)) .with_child(self.render_buttons(cx)) diff --git a/crates/collab_ui/src/notifications.rs b/crates/collab_ui/src/notifications.rs index 1dec5a8411205cbb210bb082428e412a91fab897..abeb65b1dcc2d876e45dfa1d69fc3fb86b9d406d 100644 --- a/crates/collab_ui/src/notifications.rs +++ b/crates/collab_ui/src/notifications.rs @@ -4,7 +4,6 @@ use gpui::{ platform::{CursorStyle, MouseButton}, AnyElement, Element, View, ViewContext, }; -use settings::Settings; use std::sync::Arc; enum Dismiss {} @@ -22,7 +21,7 @@ where F: 'static + Fn(&mut V, &mut ViewContext), V: View, { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let theme = &theme.contact_notification; Flex::column() diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 8a41368276dded678f6df7b60732175eaf5c8fd0..155209f47079a2587c2bf10814638445031e7ee1 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -7,7 +7,6 @@ use gpui::{ platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions}, AppContext, Entity, View, ViewContext, }; -use settings::Settings; use std::sync::{Arc, Weak}; use workspace::AppState; @@ -22,7 +21,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { worktree_root_names, } => { const PADDING: f32 = 16.; - let theme = &cx.global::().theme.project_shared_notification; + let theme = &theme::current(cx).project_shared_notification; let window_size = vec2f(theme.window_width, theme.window_height); for screen in cx.platform().screens() { @@ -109,7 +108,7 @@ impl ProjectSharedNotification { } fn render_owner(&self, cx: &mut ViewContext) -> AnyElement { - let theme = &cx.global::().theme.project_shared_notification; + let theme = &theme::current(cx).project_shared_notification; Flex::row() .with_children(self.owner.avatar.clone().map(|avatar| { Image::from_data(avatar) @@ -167,10 +166,11 @@ impl ProjectSharedNotification { enum Open {} enum Dismiss {} + let theme = theme::current(cx); Flex::column() .with_child( - MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.project_shared_notification; + MouseEventHandler::::new(0, cx, |_, _| { + let theme = &theme.project_shared_notification; Label::new("Open", theme.open_button.text.clone()) .aligned() .contained() @@ -181,8 +181,8 @@ impl ProjectSharedNotification { .flex(1., true), ) .with_child( - MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.project_shared_notification; + MouseEventHandler::::new(0, cx, |_, _| { + let theme = &theme.project_shared_notification; Label::new("Dismiss", theme.dismiss_button.text.clone()) .aligned() .contained() @@ -195,12 +195,7 @@ impl ProjectSharedNotification { .flex(1., true), ) .constrained() - .with_width( - cx.global::() - .theme - .project_shared_notification - .button_width, - ) + .with_width(theme.project_shared_notification.button_width) .into_any() } } @@ -215,11 +210,7 @@ impl View for ProjectSharedNotification { } fn render(&mut self, cx: &mut ViewContext) -> gpui::AnyElement { - let background = cx - .global::() - .theme - .project_shared_notification - .background; + let background = theme::current(cx).project_shared_notification.background; Flex::row() .with_child(self.render_owner(cx)) .with_child(self.render_buttons(cx)) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 12134f6d6eb0850e49376214fbbc7f13ba3f6b4d..2ee93a0734dd9b88aae34f07ebda21a8572af513 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -5,7 +5,6 @@ use gpui::{ ViewContext, }; use picker::{Picker, PickerDelegate, PickerEvent}; -use settings::Settings; use std::cmp; use util::ResultExt; use workspace::Workspace; @@ -185,8 +184,7 @@ impl PickerDelegate for CommandPaletteDelegate { ) -> AnyElement> { let mat = &self.matches[ix]; let command = &self.actions[mat.candidate_id]; - let settings = cx.global::(); - let theme = &settings.theme; + let theme = theme::current(cx); let style = theme.picker.item.style_for(mouse_state, selected); let key_style = &theme.command_palette.key.style_for(mouse_state, selected); let keystroke_spacing = theme.command_palette.keystroke_spacing; @@ -366,6 +364,7 @@ mod tests { fn init_test(cx: &mut TestAppContext) -> Arc { cx.update(|cx| { let app_state = AppState::test(cx); + theme::init((), cx); language::init(cx); editor::init(cx); workspace::init(app_state.clone(), cx); diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index f0d477e42f84748550dfaa51f157d69e9bb94d51..fb455fe1d0a76cfe2c0fdcc3fff0ec295fb89df8 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -8,7 +8,6 @@ use gpui::{ View, ViewContext, }; use menu::*; -use settings::Settings; use std::{any::TypeId, borrow::Cow, sync::Arc, time::Duration}; pub fn init(cx: &mut AppContext) { @@ -323,7 +322,7 @@ impl ContextMenu { } fn render_menu_for_measurement(&self, cx: &mut ViewContext) -> impl Element { - let style = cx.global::().theme.context_menu.clone(); + let style = theme::current(cx).context_menu.clone(); Flex::row() .with_child( Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| { @@ -403,7 +402,7 @@ impl ContextMenu { enum Menu {} enum MenuItem {} - let style = cx.global::().theme.context_menu.clone(); + let style = theme::current(cx).context_menu.clone(); MouseEventHandler::::new(0, cx, |_, cx| { Flex::column() diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index da3c96956e613941f96d3e58077236f43876f25e..764a0e4df18c3fd1c67db4a4f3ff2ca330026b02 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -6,7 +6,6 @@ use gpui::{ AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext, ViewHandle, }; -use settings::Settings; use theme::ui::modal; #[derive(PartialEq, Eq, Debug, Clone)] @@ -68,7 +67,7 @@ fn create_copilot_auth_window( cx: &mut AppContext, status: &Status, ) -> ViewHandle { - let window_size = cx.global::().theme.copilot.modal.dimensions(); + let window_size = theme::current(cx).copilot.modal.dimensions(); let window_options = WindowOptions { bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)), titlebar: None, @@ -338,7 +337,7 @@ impl View for CopilotCodeVerification { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { enum ConnectModal {} - let style = cx.global::().theme.clone(); + let style = theme::current(cx).clone(); modal::( "Connect Copilot to Zed", diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index d04f1e0e75d8a7765cdd6e3bdd7abb9beae6ac2b..560506a52938b05ab1375ba527de7af5b3f1cd85 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -10,7 +10,7 @@ use gpui::{ ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use language::language_settings::{self, all_language_settings, AllLanguageSettings}; -use settings::{update_settings_file, Settings, SettingsStore}; +use settings::{update_settings_file, SettingsStore}; use std::{path::Path, sync::Arc}; use util::{paths, ResultExt}; use workspace::{ @@ -46,8 +46,7 @@ impl View for CopilotButton { return Empty::new().into_any(); } - let settings = cx.global::(); - let theme = settings.theme.clone(); + let theme = theme::current(cx).clone(); let active = self.popup_menu.read(cx).visible(); let Some(copilot) = Copilot::global(cx) else { return Empty::new().into_any(); @@ -158,7 +157,7 @@ impl CopilotButton { Copilot::global(cx).map(|copilot| cx.observe(&copilot, |_, _, cx| cx.notify()).detach()); - cx.observe_global::(move |_, cx| cx.notify()) + cx.observe_global::(move |_, cx| cx.notify()) .detach(); Self { @@ -249,7 +248,7 @@ impl CopilotButton { menu_options.push(ContextMenuItem::Separator); - let icon_style = cx.global::().theme.copilot.out_link_icon.clone(); + let icon_style = theme::current(cx).copilot.out_link_icon.clone(); menu_options.push(ContextMenuItem::action( move |state: &mut MouseState, style: &theme::ContextMenuItem| { Flex::row() @@ -316,7 +315,7 @@ async fn configure_disabled_globs( let settings_editor = workspace .update(&mut cx, |_, cx| { create_and_open_local_file(&paths::SETTINGS, cx, || { - Settings::initial_user_settings_content(&assets::Assets) + settings::initial_user_settings_content(&assets::Assets) .as_ref() .into() }) diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 9d455f520e2d9fa08114a50d9740334f8d0c7f11..4e898cca0a523c63ddc834fedfef34f40753efae 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -31,6 +31,7 @@ language = { path = "../language", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } +theme = { path = "../theme", features = ["test-support"] } serde_json.workspace = true unindent.workspace = true diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 4ce1303ba4b8403268f5d1857d95595a639e87c7..458ca51ce35eecdda59eb706e60f1e846d2164c8 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -20,7 +20,6 @@ use language::{ use lsp::LanguageServerId; use project::{DiagnosticSummary, Project, ProjectPath}; use serde_json::json; -use settings::Settings; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -30,6 +29,7 @@ use std::{ path::PathBuf, sync::Arc, }; +use theme::ThemeSettings; use util::TryFutureExt; use workspace::{ item::{BreadcrumbText, Item, ItemEvent, ItemHandle}, @@ -89,7 +89,7 @@ impl View for ProjectDiagnosticsEditor { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { if self.path_states.is_empty() { - let theme = &cx.global::().theme.project_diagnostics; + let theme = &theme::current(cx).project_diagnostics; Label::new("No problems in workspace", theme.empty_message.clone()) .aligned() .contained() @@ -537,7 +537,7 @@ impl Item for ProjectDiagnosticsEditor { render_summary( &self.summary, &style.label.text, - &cx.global::().theme.project_diagnostics, + &theme::current(cx).project_diagnostics, ) } @@ -679,7 +679,7 @@ impl Item for ProjectDiagnosticsEditor { fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let (message, highlights) = highlight_diagnostic_message(Vec::new(), &diagnostic.message); Arc::new(move |cx| { - let settings = cx.global::(); + let settings = settings::get_setting::(None, cx); let theme = &settings.theme.editor; let style = theme.diagnostic_header.clone(); let font_size = (style.text_scale_factor @@ -832,23 +832,23 @@ mod tests { "/test", json!({ "consts.rs": " - const a: i32 = 'a'; - const b: i32 = c; - " + const a: i32 = 'a'; + const b: i32 = c; + " .unindent(), "main.rs": " - fn main() { - let x = vec![]; - let y = vec![]; - a(x); - b(y); - // comment 1 - // comment 2 - c(y); - d(x); - } - " + fn main() { + let x = vec![]; + let y = vec![]; + a(x); + b(y); + // comment 1 + // comment 2 + c(y); + d(x); + } + " .unindent(), }), ) @@ -1496,8 +1496,8 @@ mod tests { fn init_test(cx: &mut TestAppContext) { cx.update(|cx| { - cx.set_global(Settings::test(cx)); cx.set_global(SettingsStore::test(cx)); + theme::init((), cx); language::init(cx); client::init_settings(cx); workspace::init_settings(cx); diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index f0ceacc6194326a221454edeeb07f385421f5054..f84846eae1e28d4a1dcd89172000983a6219f268 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -7,7 +7,6 @@ use gpui::{ }; use language::Diagnostic; use lsp::LanguageServerId; -use settings::Settings; use workspace::{item::ItemHandle, StatusItemView, Workspace}; use crate::ProjectDiagnosticsEditor; @@ -92,13 +91,12 @@ impl View for DiagnosticIndicator { enum Summary {} enum Message {} - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); let in_progress = !self.in_progress_checks.is_empty(); let mut element = Flex::row().with_child( MouseEventHandler::::new(0, cx, |state, cx| { - let style = cx - .global::() - .theme + let theme = theme::current(cx); + let style = theme .workspace .status_bar .diagnostic_summary @@ -184,7 +182,7 @@ impl View for DiagnosticIndicator { .into_any(), ); - let style = &cx.global::().theme.workspace.status_bar; + let style = &theme::current(cx).workspace.status_bar; let item_spacing = style.item_spacing; if in_progress { diff --git a/crates/editor/src/blink_manager.rs b/crates/editor/src/blink_manager.rs index 303d0960edd6e6b2ac589b8c4be40aca5a3502cb..43d1ec6130365415e982003e8e23a62f61667487 100644 --- a/crates/editor/src/blink_manager.rs +++ b/crates/editor/src/blink_manager.rs @@ -1,6 +1,6 @@ use crate::EditorSettings; use gpui::{Entity, ModelContext}; -use settings::Settings; +use settings::SettingsStore; use smol::Timer; use std::time::Duration; @@ -15,8 +15,8 @@ pub struct BlinkManager { impl BlinkManager { pub fn new(blink_interval: Duration, cx: &mut ModelContext) -> Self { - cx.observe_global::(move |this, cx| { - // Make sure we blink the cursors if the setting is re-enabled + // Make sure we blink the cursors if the setting is re-enabled + cx.observe_global::(move |this, cx| { this.blink_cursors(this.blink_epoch, cx) }) .detach(); diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 93e43f876c47316e74f12a3e5ced4d6a3351b233..05ff9886f1d33827776fa55795a4b0b6b26efd64 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -993,7 +993,7 @@ mod tests { use crate::multi_buffer::MultiBuffer; use gpui::{elements::Empty, Element}; use rand::prelude::*; - use settings::Settings; + use settings::SettingsStore; use std::env; use util::RandomCharIter; @@ -1013,7 +1013,7 @@ mod tests { #[gpui::test] fn test_basic_blocks(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); let family_id = cx .font_cache() @@ -1189,7 +1189,7 @@ mod tests { #[gpui::test] fn test_blocks_on_wrapped_lines(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); let family_id = cx .font_cache() @@ -1239,7 +1239,7 @@ mod tests { #[gpui::test(iterations = 100)] fn test_random_blocks(cx: &mut gpui::AppContext, mut rng: StdRng) { - cx.set_global(Settings::test(cx)); + init_test(cx); let operations = env::var("OPERATIONS") .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) @@ -1647,6 +1647,11 @@ mod tests { } } + fn init_test(cx: &mut gpui::AppContext) { + cx.set_global(SettingsStore::test(cx)); + theme::init((), cx); + } + impl TransformBlock { fn as_custom(&self) -> Option<&Block> { match self { diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index bd3cd1a620ada42b93fb5dd1a46151dbb0f8c37b..6ef1ebce1da844d1cd23185d5d246524716e082f 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -1204,7 +1204,7 @@ mod tests { use crate::{MultiBuffer, ToPoint}; use collections::HashSet; use rand::prelude::*; - use settings::Settings; + use settings::SettingsStore; use std::{cmp::Reverse, env, mem, sync::Arc}; use sum_tree::TreeMap; use util::test::sample_text; @@ -1213,7 +1213,7 @@ mod tests { #[gpui::test] fn test_basic_folds(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let buffer_snapshot = buffer.read(cx).snapshot(cx); @@ -1286,7 +1286,7 @@ mod tests { #[gpui::test] fn test_adjacent_folds(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); let buffer = MultiBuffer::build_simple("abcdefghijkl", cx); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let buffer_snapshot = buffer.read(cx).snapshot(cx); @@ -1349,7 +1349,7 @@ mod tests { #[gpui::test] fn test_merging_folds_via_edit(cx: &mut gpui::AppContext) { - cx.set_global(Settings::test(cx)); + init_test(cx); let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let buffer_snapshot = buffer.read(cx).snapshot(cx); @@ -1400,7 +1400,7 @@ mod tests { #[gpui::test(iterations = 100)] fn test_random_folds(cx: &mut gpui::AppContext, mut rng: StdRng) { - cx.set_global(Settings::test(cx)); + init_test(cx); let operations = env::var("OPERATIONS") .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); @@ -1676,6 +1676,10 @@ mod tests { assert_eq!(snapshot.buffer_rows(3).collect::>(), [Some(6)]); } + fn init_test(cx: &mut gpui::AppContext) { + cx.set_global(SettingsStore::test(cx)); + } + impl FoldMap { fn merged_fold_ranges(&self) -> Vec> { let buffer = self.buffer.lock().clone(); diff --git a/crates/editor/src/display_map/suggestion_map.rs b/crates/editor/src/display_map/suggestion_map.rs index f48efc76f41bec4dc9a8847f9446f991227e9428..eac903d0af9c7e9c3d9a4bc184ce842e8d07cf52 100644 --- a/crates/editor/src/display_map/suggestion_map.rs +++ b/crates/editor/src/display_map/suggestion_map.rs @@ -578,7 +578,7 @@ mod tests { use crate::{display_map::fold_map::FoldMap, MultiBuffer}; use gpui::AppContext; use rand::{prelude::StdRng, Rng}; - use settings::Settings; + use settings::SettingsStore; use std::{ env, ops::{Bound, RangeBounds}, @@ -631,7 +631,8 @@ mod tests { #[gpui::test(iterations = 100)] fn test_random_suggestions(cx: &mut AppContext, mut rng: StdRng) { - cx.set_global(Settings::test(cx)); + init_test(cx); + let operations = env::var("OPERATIONS") .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); @@ -834,6 +835,11 @@ mod tests { } } + fn init_test(cx: &mut AppContext) { + cx.set_global(SettingsStore::test(cx)); + theme::init((), cx); + } + impl SuggestionMap { pub fn randomly_mutate( &self, diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index e1de4fce77fa13b132b934025d2b9583ea6b31c1..478eaf4c7e1a16f56e55286b8e438cda9d78b4b1 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -1043,16 +1043,16 @@ mod tests { }; use gpui::test::observe; use rand::prelude::*; - use settings::Settings; + use settings::SettingsStore; use smol::stream::StreamExt; use std::{cmp, env, num::NonZeroU32}; use text::Rope; #[gpui::test(iterations = 100)] async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + init_test(cx); + cx.foreground().set_block_on_ticks(0..=50); - cx.foreground().forbid_parking(); let operations = env::var("OPERATIONS") .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); @@ -1287,6 +1287,14 @@ mod tests { wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty())); } + fn init_test(cx: &mut gpui::TestAppContext) { + cx.foreground().forbid_parking(); + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + theme::init((), cx); + }); + } + fn wrap_text( unwrapped_text: &str, wrap_width: Option, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 915e041100bc87daaf06a8357af20a5bbb96ea51..78844ac332d1c4071a9219a7e5a35b3f43a699a1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -73,7 +73,7 @@ use scroll::{ }; use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::SettingsStore; use smallvec::SmallVec; use snippet::Snippet; use std::{ @@ -88,7 +88,7 @@ use std::{ time::{Duration, Instant}, }; pub use sum_tree::Bias; -use theme::{DiagnosticStyle, Theme}; +use theme::{DiagnosticStyle, Theme, ThemeSettings}; use util::{post_inc, RangeExt, ResultExt, TryFutureExt}; use workspace::{ItemNavHistory, ViewId, Workspace}; @@ -1237,8 +1237,8 @@ impl Editor { ) -> Self { let editor_view_id = cx.view_id(); let display_map = cx.add_model(|cx| { - let settings = cx.global::(); - let style = build_style(&*settings, get_field_editor_theme.as_deref(), None, cx); + let settings = settings::get_setting::(None, cx); + let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx); DisplayMap::new( buffer.clone(), style.text.font_id, @@ -1319,7 +1319,7 @@ impl Editor { cx.subscribe(&buffer, Self::on_buffer_event), cx.observe(&display_map, Self::on_display_map_changed), cx.observe(&blink_manager, |_, _, cx| cx.notify()), - cx.observe_global::(Self::settings_changed), + cx.observe_global::(Self::settings_changed), ], }; @@ -1418,7 +1418,7 @@ impl Editor { fn style(&self, cx: &AppContext) -> EditorStyle { build_style( - cx.global::(), + settings::get_setting::(None, cx), self.get_field_editor_theme.as_deref(), self.override_text_style.as_deref(), cx, @@ -6561,8 +6561,8 @@ impl Editor { let buffer = &snapshot.buffer_snapshot; let start = buffer.anchor_before(0); let end = buffer.anchor_after(buffer.len()); - let theme = cx.global::().theme.as_ref(); - self.background_highlights_in_range(start..end, &snapshot, theme) + let theme = theme::current(cx); + self.background_highlights_in_range(start..end, &snapshot, theme.as_ref()) } fn document_highlights_for_position<'a>( @@ -6985,7 +6985,7 @@ impl Editor { let mut lines = Vec::new(); let mut line: VecDeque = VecDeque::new(); - let theme = &cx.global::().theme.editor.syntax; + let theme = &theme::current(cx).editor.syntax; for chunk in chunks { let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme)); @@ -7407,7 +7407,7 @@ impl View for Editor { } fn build_style( - settings: &Settings, + settings: &ThemeSettings, get_field_editor_theme: Option<&GetFieldEditorTheme>, override_text_style: Option<&OverrideTextStyle>, cx: &AppContext, @@ -7607,7 +7607,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend } Arc::new(move |cx: &mut BlockContext| { - let settings = cx.global::(); + let settings = settings::get_setting::(None, cx); let theme = &settings.theme.editor; let style = diagnostic_style(diagnostic.severity, is_valid, theme); let font_size = (style.text_scale_factor diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index cf72f0286cfe6a217f0caba9660e272f1052cd5c..b61c2a780ffa28027e30e48a01a344bdc4d2a4a7 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5452,7 +5452,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) { let mut highlighted_ranges = editor.background_highlights_in_range( anchor_range(Point::new(3, 4)..Point::new(7, 4)), &snapshot, - cx.global::().theme.as_ref(), + theme::current(cx).as_ref(), ); // Enforce a consistent ordering based on color without relying on the ordering of the // highlight's `TypeId` which is non-deterministic. @@ -5482,7 +5482,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) { editor.background_highlights_in_range( anchor_range(Point::new(5, 6)..Point::new(6, 4)), &snapshot, - cx.global::().theme.as_ref(), + theme::current(cx).as_ref(), ), &[( DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5), @@ -6681,7 +6681,7 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); - cx.set_global(Settings::test(cx)); + theme::init((), cx); client::init_settings(cx); language::init(cx); Project::init_settings(cx); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index bc5998cc2948ba0d6f9901f6369e96a9d3539351..56a4f4ad18005cb142208d32da03dcb858ebde6a 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -40,7 +40,6 @@ use language::{ Selection, }; use project::ProjectPath; -use settings::Settings; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -611,7 +610,7 @@ impl EditorElement { layout: &mut LayoutState, cx: &mut ViewContext, ) { - let diff_style = &cx.global::().theme.editor.diff.clone(); + let diff_style = &theme::current(cx).editor.diff.clone(); let line_height = layout.position_map.line_height; let scroll_position = layout.position_map.snapshot.scroll_position(); @@ -1417,7 +1416,7 @@ impl EditorElement { editor: &mut Editor, cx: &mut LayoutContext, ) -> (f32, Vec) { - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); let scroll_x = snapshot.scroll_anchor.offset.x(); let (fixed_blocks, non_fixed_blocks) = snapshot .blocks_in_range(rows.clone()) @@ -1936,11 +1935,11 @@ impl Element for EditorElement { let is_singleton = editor.is_singleton(cx); let highlighted_rows = editor.highlighted_rows(); - let theme = cx.global::().theme.as_ref(); + let theme = theme::current(cx); let highlighted_ranges = editor.background_highlights_in_range( start_anchor..end_anchor, &snapshot.display_snapshot, - theme, + theme.as_ref(), ); fold_ranges.extend( diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 29d32e4a435baaa661bc3053c3b66b4b2f0b94a4..24f09f108204769dcd0c382c6b4c06c89b5e407c 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -12,7 +12,6 @@ use gpui::{ }; use language::{Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry}; use project::{HoverBlock, HoverBlockKind, Project}; -use settings::Settings; use std::{ops::Range, sync::Arc, time::Duration}; use util::TryFutureExt; @@ -654,7 +653,7 @@ impl DiagnosticPopover { _ => style.hover_popover.container, }; - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); MouseEventHandler::::new(0, cx, |_, _| { text.with_soft_wrap(true) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 9e122cc63d31ce5d79246a37050ffa0c76fbd907..988f263337e16f78af76fad7d3fd4150057cc75d 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -16,7 +16,6 @@ use language::{ }; use project::{FormatTrigger, Item as _, Project, ProjectPath}; use rpc::proto::{self, update_view}; -use settings::Settings; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -1116,7 +1115,7 @@ impl View for CursorPosition { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { if let Some(position) = self.position { - let theme = &cx.global::().theme.workspace.status_bar; + let theme = &theme::current(cx).workspace.status_bar; let mut text = format!( "{}{FILE_ROW_COLUMN_DELIMITER}{}", position.row + 1, diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 04cf4611f19298c168ad46fa91b3c5ecbdbb993f..a52647fb5504b419a1597879de2db585182326bb 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -2,7 +2,6 @@ use crate::{Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase}; use gpui::{Task, ViewContext}; use language::{Bias, ToOffset}; use project::LocationLink; -use settings::Settings; use std::ops::Range; use util::TryFutureExt; @@ -210,7 +209,7 @@ pub fn show_link_definition( }); // Highlight symbol using theme link definition highlight style - let style = cx.global::().theme.editor.link_definition; + let style = theme::current(cx).editor.link_definition; this.highlight_text::( vec![highlight_range], style, diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 311616cfe042a38d45899de96501c92ef2d6fca4..6c9bd6cb4fe1554d9a04a840db78ff8edc88b4c9 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -367,10 +367,9 @@ pub fn split_display_range_by_lines( #[cfg(test)] mod tests { - use settings::{Settings, SettingsStore}; - use super::*; use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer}; + use settings::SettingsStore; #[gpui::test] fn test_previous_word_start(cx: &mut gpui::AppContext) { @@ -703,7 +702,7 @@ mod tests { fn init_test(cx: &mut gpui::AppContext) { cx.set_global(SettingsStore::test(cx)); - cx.set_global(Settings::test(cx)); + theme::init((), cx); language::init(cx); crate::init(cx); } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 1e07bba065c7d4e8f5d345c06290016448cd0e7f..99df8ce796778c2e97e68887b361d83f048bc0ef 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -3806,10 +3806,9 @@ mod tests { use gpui::{AppContext, TestAppContext}; use language::{Buffer, Rope}; use rand::prelude::*; - use settings::Settings; + use settings::SettingsStore; use std::{env, rc::Rc}; use unindent::Unindent; - use util::test::sample_text; #[gpui::test] @@ -5055,7 +5054,8 @@ mod tests { #[gpui::test] fn test_history(cx: &mut AppContext) { - cx.set_global(Settings::test(cx)); + cx.set_global(SettingsStore::test(cx)); + let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx)); let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx)); let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index 9f2ab4ffac205108ac57d6d8515ee7b095f40977..0fe49d4d0462216b28ea9273e9490e96a7bc3a9d 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -37,6 +37,7 @@ impl<'a> EditorLspTestContext<'a> { let app_state = cx.update(AppState::test); cx.update(|cx| { + theme::init((), cx); language::init(cx); crate::init(cx); pane::init(cx); diff --git a/crates/feedback/src/deploy_feedback_button.rs b/crates/feedback/src/deploy_feedback_button.rs index b464d0088707e73be0f71a1c2a333823ce76f13d..9133174475145ee3cc159695f14af99e562de43a 100644 --- a/crates/feedback/src/deploy_feedback_button.rs +++ b/crates/feedback/src/deploy_feedback_button.rs @@ -3,7 +3,6 @@ use gpui::{ platform::{CursorStyle, MouseButton}, Entity, View, ViewContext, WeakViewHandle, }; -use settings::Settings; use workspace::{item::ItemHandle, StatusItemView, Workspace}; use crate::feedback_editor::{FeedbackEditor, GiveFeedback}; @@ -33,7 +32,7 @@ impl View for DeployFeedbackButton { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { let active = self.active; - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { diff --git a/crates/feedback/src/feedback_info_text.rs b/crates/feedback/src/feedback_info_text.rs index 9aee4e0e68b6c2ec4f1d590373559eb97b4b8412..5852cd9a6184449b08eb7844bf7ac6d11b3ca6ee 100644 --- a/crates/feedback/src/feedback_info_text.rs +++ b/crates/feedback/src/feedback_info_text.rs @@ -3,7 +3,6 @@ use gpui::{ platform::{CursorStyle, MouseButton}, AnyElement, Element, Entity, View, ViewContext, ViewHandle, }; -use settings::Settings; use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView}; use crate::{feedback_editor::FeedbackEditor, open_zed_community_repo, OpenZedCommunityRepo}; @@ -30,7 +29,7 @@ impl View for FeedbackInfoText { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); Flex::row() .with_child( diff --git a/crates/feedback/src/submit_feedback_button.rs b/crates/feedback/src/submit_feedback_button.rs index ccd58c3dc97dbcdad2920722a4e4fb0882edbb2f..56bc235570742d90850886b2460322035fa3c2fa 100644 --- a/crates/feedback/src/submit_feedback_button.rs +++ b/crates/feedback/src/submit_feedback_button.rs @@ -5,7 +5,6 @@ use gpui::{ platform::{CursorStyle, MouseButton}, AnyElement, AppContext, Element, Entity, Task, View, ViewContext, ViewHandle, }; -use settings::Settings; use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView}; pub fn init(cx: &mut AppContext) { @@ -46,7 +45,7 @@ impl View for SubmitFeedbackButton { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); enum SubmitFeedbackButton {} MouseEventHandler::::new(0, cx, |state, _| { let style = theme.feedback.submit_button.style_for(state, false); diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 37ab4218a8fdce3eb0feb33c8c84f169dedfe8fd..b318f1d1677ab5a82c4288e945737aa875601629 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -5,7 +5,6 @@ use gpui::{ }; use picker::{Picker, PickerDelegate}; use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId}; -use settings::Settings; use std::{ path::Path, sync::{ @@ -324,8 +323,8 @@ impl PickerDelegate for FileFinderDelegate { cx: &AppContext, ) -> AnyElement> { let path_match = &self.matches[ix]; - let settings = cx.global::(); - let style = settings.theme.picker.item.style_for(mouse_state, selected); + let theme = theme::current(cx); + let style = theme.picker.item.style_for(mouse_state, selected); let (file_name, file_name_positions, full_path, full_path_positions) = self.labels_for_match(path_match); Flex::column() @@ -909,6 +908,7 @@ mod tests { cx.foreground().forbid_parking(); cx.update(|cx| { let state = AppState::test(cx); + theme::init((), cx); language::init(cx); super::init(cx); editor::init(cx); diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index 8f99aa366cbad598d00d5cbe24a6c84ede2deaa5..441f7ef7e40fbc5ea4b7ac6c1965c33090b45b5d 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -16,4 +16,5 @@ settings = { path = "../settings" } text = { path = "../text" } workspace = { path = "../workspace" } postage.workspace = true +theme = { path = "../theme" } util = { path = "../util" } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 967f17b79457596cf58967bd2258bebf97282d59..0b41ee6dca3630450668a7eac69aeea2e99d3400 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -6,7 +6,6 @@ use gpui::{ View, ViewContext, ViewHandle, }; use menu::{Cancel, Confirm}; -use settings::Settings; use text::{Bias, Point}; use util::paths::FILE_ROW_COLUMN_DELIMITER; use workspace::{Modal, Workspace}; @@ -151,7 +150,7 @@ impl View for GoToLine { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = &cx.global::().theme.picker; + let theme = &theme::current(cx).picker; let label = format!( "{}{FILE_ROW_COLUMN_DELIMITER}{} of {} lines", diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 55afd2f572161b9dbebe0031d21f79ac29d7a975..4d33366a7184de3871144a24f7c4f313675fc4c4 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -254,6 +254,7 @@ impl settings::Setting for AllLanguageSettings { fn json_schema( generator: &mut schemars::gen::SchemaGenerator, params: &settings::SettingsJsonSchemaParams, + _: &AppContext, ) -> schemars::schema::RootSchema { let mut root_schema = generator.root_schema_for::(); diff --git a/crates/language_selector/src/active_buffer_language.rs b/crates/language_selector/src/active_buffer_language.rs index 425f4c8dd7f80ea4edc4fa88338a238d8db2c194..2c78b89f31c61fd7193aaf15eb1b90c15b39fdd4 100644 --- a/crates/language_selector/src/active_buffer_language.rs +++ b/crates/language_selector/src/active_buffer_language.rs @@ -4,7 +4,6 @@ use gpui::{ platform::{CursorStyle, MouseButton}, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; -use settings::Settings; use std::sync::Arc; use workspace::{item::ItemHandle, StatusItemView, Workspace}; @@ -55,7 +54,7 @@ impl View for ActiveBufferLanguage { }; MouseEventHandler::::new(0, cx, |state, cx| { - let theme = &cx.global::().theme.workspace.status_bar; + let theme = &theme::current(cx).workspace.status_bar; let style = theme.active_language.style_for(state, false); Label::new(active_language_text, style.text.clone()) .contained() diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index fd43111443373773593573d6189ec407ec165e73..817901cd3a6a3f480a19ba5fb34d11e6c20074ae 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -8,7 +8,6 @@ use gpui::{actions, elements::*, AppContext, ModelHandle, MouseState, ViewContex use language::{Buffer, LanguageRegistry}; use picker::{Picker, PickerDelegate, PickerEvent}; use project::Project; -use settings::Settings; use std::sync::Arc; use util::ResultExt; use workspace::Workspace; @@ -179,8 +178,7 @@ impl PickerDelegate for LanguageSelectorDelegate { selected: bool, cx: &AppContext, ) -> AnyElement> { - let settings = cx.global::(); - let theme = &settings.theme; + let theme = theme::current(cx); let mat = &self.matches[ix]; let style = theme.picker.item.style_for(mouse_state, selected); let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name()); diff --git a/crates/lsp_log/src/lsp_log.rs b/crates/lsp_log/src/lsp_log.rs index 0efcd3afc83d773c15bdb484c6d0f50b7876f106..db41c6ff4d475bf870f28a2fb366ff9dcd199783 100644 --- a/crates/lsp_log/src/lsp_log.rs +++ b/crates/lsp_log/src/lsp_log.rs @@ -13,7 +13,6 @@ use gpui::{ }; use language::{Buffer, LanguageServerId, LanguageServerName}; use project::{Project, WorktreeId}; -use settings::Settings; use std::{borrow::Cow, sync::Arc}; use theme::{ui, Theme}; use workspace::{ @@ -304,7 +303,7 @@ impl View for LspLogToolbarItemView { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let Some(log_view) = self.log_view.as_ref() else { return Empty::new().into_any() }; let project = self.project.read(cx); let log_view = log_view.read(cx); diff --git a/crates/outline/Cargo.toml b/crates/outline/Cargo.toml index 91e5011b2a93082185d7110778f46f2e48b02419..95272b063e798c6cc8a2ea9aa3f7278ea9c1b755 100644 --- a/crates/outline/Cargo.toml +++ b/crates/outline/Cargo.toml @@ -16,7 +16,9 @@ language = { path = "../language" } picker = { path = "../picker" } settings = { path = "../settings" } text = { path = "../text" } +theme = { path = "../theme" } workspace = { path = "../workspace" } + ordered-float.workspace = true postage.workspace = true smol.workspace = true diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 6ecaf370e40b1c846a1775bb27c421468fcf5066..1e364f5fc8f6d9198fba71b6d11429e11e13d3e0 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -10,7 +10,6 @@ use gpui::{ use language::Outline; use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate, PickerEvent}; -use settings::Settings; use std::{ cmp::{self, Reverse}, sync::Arc, @@ -34,7 +33,7 @@ pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext().theme.editor.syntax.as_ref())); + .outline(Some(theme::current(cx).editor.syntax.as_ref())); if let Some(outline) = outline { workspace.toggle_modal(cx, |_, cx| { cx.add_view(|cx| { @@ -204,9 +203,9 @@ impl PickerDelegate for OutlineViewDelegate { selected: bool, cx: &AppContext, ) -> AnyElement> { - let settings = cx.global::(); + let theme = theme::current(cx); + let style = theme.picker.item.style_for(mouse_state, selected); let string_match = &self.matches[ix]; - let style = settings.theme.picker.item.style_for(mouse_state, selected); let outline_item = &self.outline.items[string_match.candidate_id]; Text::new(outline_item.text.clone(), style.label.text.clone()) diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 01749ccf84d72f5cc5907bf66f7d9c75c4d3d06f..69f16e494933ed0a47bc8bf45c08e046b3e55372 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -57,7 +57,7 @@ impl View for Picker { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = (self.theme.lock())(&cx.global::().theme); + let theme = (self.theme.lock())(theme::current(cx).as_ref()); let query = self.query(cx); let match_count = self.delegate.match_count(); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 14359fa288c3f2a65e2fa72a9054b4169bd9dcb8..a487b23716015e2751a75b66a04d0f675275222e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -47,7 +47,7 @@ use project_settings::ProjectSettings; use rand::prelude::*; use search::SearchQuery; use serde::Serialize; -use settings::{Settings, SettingsStore}; +use settings::SettingsStore; use sha2::{Digest, Sha256}; use similar::{ChangeTag, TextDiff}; use std::{ @@ -610,12 +610,6 @@ impl Project { root_paths: impl IntoIterator, cx: &mut gpui::TestAppContext, ) -> ModelHandle { - if !cx.read(|cx| cx.has_global::()) { - cx.update(|cx| { - cx.set_global(Settings::test(cx)); - }); - } - let mut languages = LanguageRegistry::test(); languages.set_executor(cx.background()); let http_client = util::http::FakeHttpClient::with_404_response(); @@ -2137,7 +2131,7 @@ impl Project { let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel(); let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx); - let settings_observation = cx.observe_global::(move |_, _| { + let settings_observation = cx.observe_global::(move |_, _| { *settings_changed_tx.borrow_mut() = (); }); cx.spawn_weak(|this, mut cx| async move { diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 7325e69f3f45c6dc372a5fd8d174d9952a2da15b..683ce8ad06671e2346628bcac46494dbfbfc3d7f 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -20,7 +20,6 @@ use project::{ repository::GitFileStatus, Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId, }; -use settings::Settings; use std::{ cmp::Ordering, collections::{hash_map, HashMap}, @@ -1113,7 +1112,7 @@ impl ProjectPanel { ComponentHost::new(FileName::new( details.filename.clone(), details.git_status, - FileName::style(style.text.clone(), &cx.global::().theme), + FileName::style(style.text.clone(), &theme::current(cx)), )) .contained() .with_margin_left(style.icon_spacing) @@ -1223,7 +1222,7 @@ impl ProjectPanel { let row_container_style = theme.dragged_entry.container; move |_, cx: &mut ViewContext| { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); Self::render_entry_visual_element( &details, None, @@ -1246,7 +1245,7 @@ impl View for ProjectPanel { fn render(&mut self, cx: &mut gpui::ViewContext) -> gpui::AnyElement { enum ProjectPanel {} - let theme = &cx.global::().theme.project_panel; + let theme = &theme::current(cx).project_panel; let mut container_style = theme.container; let padding = std::mem::take(&mut container_style.padding); let last_worktree_root_id = self.last_worktree_root_id; @@ -1265,7 +1264,7 @@ impl View for ProjectPanel { .sum(), cx, move |this, range, items, cx| { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let mut dragged_entry_destination = this.dragged_entry_destination.clone(); this.for_each_visible_entry(range, cx, |id, details, cx| { @@ -1302,8 +1301,7 @@ impl View for ProjectPanel { .with_child( MouseEventHandler::::new(2, cx, { let button_style = theme.open_project_button.clone(); - let context_menu_item_style = - cx.global::().theme.context_menu.item.clone(); + let context_menu_item_style = theme::current(cx).context_menu.item.clone(); move |state, cx| { let button_style = button_style.style_for(state, false).clone(); let context_menu_item = @@ -1952,6 +1950,7 @@ mod tests { cx.foreground().forbid_parking(); cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); + theme::init((), cx); language::init(cx); editor::init_settings(cx); workspace::init_settings(cx); diff --git a/crates/project_symbols/Cargo.toml b/crates/project_symbols/Cargo.toml index be2510c8955efd3e606e10ebe945c29355b0a90a..7e23e42b2633c4af743d2586fea0db3ebafcbe7b 100644 --- a/crates/project_symbols/Cargo.toml +++ b/crates/project_symbols/Cargo.toml @@ -17,7 +17,9 @@ project = { path = "../project" } text = { path = "../text" } settings = { path = "../settings" } workspace = { path = "../workspace" } +theme = { path = "../theme" } util = { path = "../util" } + anyhow.workspace = true ordered-float.workspace = true postage.workspace = true @@ -30,4 +32,5 @@ gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +theme = { path = "../theme", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 74edd45c212591a92e9efcfbbd221ca2d5b1444f..992283df011cdb7eee4f2317e9904cd006216481 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -9,7 +9,6 @@ use gpui::{ use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate, PickerEvent}; use project::{Project, Symbol}; -use settings::Settings; use std::{borrow::Cow, cmp::Reverse, sync::Arc}; use util::ResultExt; use workspace::Workspace; @@ -195,12 +194,13 @@ impl PickerDelegate for ProjectSymbolsDelegate { selected: bool, cx: &AppContext, ) -> AnyElement> { - let string_match = &self.matches[ix]; - let settings = cx.global::(); - let style = &settings.theme.picker.item; + let theme = theme::current(cx); + let style = &theme.picker.item; let current_style = style.style_for(mouse_state, selected); + + let string_match = &self.matches[ix]; let symbol = &self.symbols[string_match.candidate_id]; - let syntax_runs = styled_runs_for_code_label(&symbol.label, &settings.theme.editor.syntax); + let syntax_runs = styled_runs_for_code_label(&symbol.label, &theme.editor.syntax); let mut path = symbol.path.path.to_string_lossy(); if self.show_worktree_root_name { @@ -371,8 +371,8 @@ mod tests { fn init_test(cx: &mut TestAppContext) { cx.foreground().forbid_parking(); cx.update(|cx| { - cx.set_global(Settings::test(cx)); cx.set_global(SettingsStore::test(cx)); + theme::init((), cx); language::init(cx); Project::init_settings(cx); workspace::init_settings(cx); diff --git a/crates/recent_projects/Cargo.toml b/crates/recent_projects/Cargo.toml index 968ae8e9a4d8d2a753fd8ae9af76b9aa11761885..d9e7546f34464a3b611b1f4457a26efad9286e1a 100644 --- a/crates/recent_projects/Cargo.toml +++ b/crates/recent_projects/Cargo.toml @@ -18,6 +18,7 @@ picker = { path = "../picker" } settings = { path = "../settings" } text = { path = "../text" } util = { path = "../util"} +theme = { path = "../theme" } workspace = { path = "../workspace" } ordered-float.workspace = true diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 644e74d87887ffd82124fff90fc7d4eccdd9bac0..a1dc8982c79fcedd36447f5bf116f068701b238f 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -10,7 +10,6 @@ use gpui::{ use highlighted_workspace_location::HighlightedWorkspaceLocation; use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate, PickerEvent}; -use settings::Settings; use std::sync::Arc; use workspace::{ notifications::simple_message_notification::MessageNotification, Workspace, WorkspaceLocation, @@ -173,9 +172,10 @@ impl PickerDelegate for RecentProjectsDelegate { selected: bool, cx: &gpui::AppContext, ) -> AnyElement> { - let settings = cx.global::(); + let theme = theme::current(cx); + let style = theme.picker.item.style_for(mouse_state, selected); + let string_match = &self.matches[ix]; - let style = settings.theme.picker.item.style_for(mouse_state, selected); let highlighted_location = HighlightedWorkspaceLocation::new( &string_match, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 38f3894743fbe3a44d89429ebd6836a7273f6306..87a8b265fb2f774135e5db267a269c3890eaae21 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -13,7 +13,6 @@ use gpui::{ }; use project::search::SearchQuery; use serde::Deserialize; -use settings::Settings; use std::{any::Any, sync::Arc}; use util::ResultExt; use workspace::{ @@ -93,7 +92,7 @@ impl View for BufferSearchBar { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let editor_container = if self.query_contains_error { theme.search.invalid_editor } else { @@ -324,16 +323,12 @@ impl BufferSearchBar { return None; } - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); let is_active = self.is_search_option_enabled(option); Some( MouseEventHandler::::new(option as usize, cx, |state, cx| { - let style = cx - .global::() - .theme - .search - .option_button - .style_for(state, is_active); + let theme = theme::current(cx); + let style = theme.search.option_button.style_for(state, is_active); Label::new(icon, style.text.clone()) .contained() .with_style(style.container) @@ -371,16 +366,12 @@ impl BufferSearchBar { tooltip = "Select Next Match"; } }; - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); enum NavButton {} MouseEventHandler::::new(direction as usize, cx, |state, cx| { - let style = cx - .global::() - .theme - .search - .option_button - .style_for(state, false); + let theme = theme::current(cx); + let style = theme.search.option_button.style_for(state, false); Label::new(icon, style.text.clone()) .contained() .with_style(style.container) @@ -408,7 +399,7 @@ impl BufferSearchBar { cx: &mut ViewContext, ) -> AnyElement { let tooltip = "Dismiss Buffer Search"; - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); enum CloseButton {} MouseEventHandler::::new(0, cx, |state, _| { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 0d020da5706bc4249131244fd98e4489963f9677..17f86c153c9b51edba847f4fafb99a8efda9cbbb 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -17,7 +17,6 @@ use gpui::{ }; use menu::Confirm; use project::{search::SearchQuery, Project}; -use settings::Settings; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -195,7 +194,7 @@ impl View for ProjectSearchView { if model.match_ranges.is_empty() { enum Status {} - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let text = if self.query_editor.read(cx).text(cx).is_empty() { "" } else if model.pending_search.is_some() { @@ -903,16 +902,12 @@ impl ProjectSearchBar { tooltip = "Select Next Match"; } }; - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); enum NavButton {} MouseEventHandler::::new(direction as usize, cx, |state, cx| { - let style = &cx - .global::() - .theme - .search - .option_button - .style_for(state, false); + let theme = theme::current(cx); + let style = theme.search.option_button.style_for(state, false); Label::new(icon, style.text.clone()) .contained() .with_style(style.container) @@ -939,15 +934,11 @@ impl ProjectSearchBar { option: SearchOption, cx: &mut ViewContext, ) -> AnyElement { - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); let is_active = self.is_option_enabled(option, cx); MouseEventHandler::::new(option as usize, cx, |state, cx| { - let style = &cx - .global::() - .theme - .search - .option_button - .style_for(state, is_active); + let theme = theme::current(cx); + let style = theme.search.option_button.style_for(state, is_active); Label::new(icon, style.text.clone()) .contained() .with_style(style.container) @@ -992,7 +983,7 @@ impl View for ProjectSearchBar { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { if let Some(search) = self.active_project_search.as_ref() { let search = search.read(cx); - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let query_container_style = if search.panels_with_errors.contains(&InputPanel::Query) { theme.search.invalid_editor } else { @@ -1154,6 +1145,7 @@ pub mod tests { use serde_json::json; use settings::SettingsStore; use std::sync::Arc; + use theme::ThemeSettings; #[gpui::test] async fn test_project_search(deterministic: Arc, cx: &mut TestAppContext) { @@ -1282,9 +1274,12 @@ pub mod tests { cx.set_global(SettingsStore::test(cx)); cx.set_global(ActiveSearches::default()); - let mut settings = Settings::test(cx); - settings.theme = Arc::new(theme); - cx.set_global(settings); + theme::init((), cx); + cx.update_global::(|store, _| { + let mut settings = store.get::(None).clone(); + settings.theme = Arc::new(theme); + store.override_global(settings) + }); language::init(cx); client::init_settings(cx); diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 6d2b4ded91b7ebf983b6791c045bd22123b7c6ee..2cb6637eadc876f375313f502d97ff89aaa3ee4f 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -9,7 +9,7 @@ path = "src/settings.rs" doctest = false [features] -test-support = ["theme/test-support", "gpui/test-support", "fs/test-support"] +test-support = ["gpui/test-support", "fs/test-support"] [dependencies] assets = { path = "../assets" } @@ -17,12 +17,11 @@ collections = { path = "../collections" } gpui = { path = "../gpui" } sqlez = { path = "../sqlez" } fs = { path = "../fs" } -anyhow.workspace = true -futures.workspace = true -theme = { path = "../theme" } staff_mode = { path = "../staff_mode" } util = { path = "../util" } +anyhow.workspace = true +futures.workspace = true glob.workspace = true json_comments = "0.2" lazy_static.workspace = true @@ -39,7 +38,6 @@ tree-sitter-json = "*" [dev-dependencies] gpui = { path = "../gpui", features = ["test-support"] } fs = { path = "../fs", features = ["test-support"] } -theme = { path = "../theme", features = ["test-support"] } pretty_assertions = "1.3.0" unindent.workspace = true diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index bd1104105fc4b4854da79a5417bee27940d3ba75..e4784d61ac3b9791e91ab68762c0c5b2fadbff27 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -3,23 +3,10 @@ mod keymap_file; mod settings_file; mod settings_store; -use anyhow::Result; -use gpui::{ - font_cache::{FamilyId, FontCache}, - fonts, AppContext, AssetSource, -}; -use schemars::{ - gen::SchemaGenerator, - schema::{InstanceType, Schema, SchemaObject}, - JsonSchema, -}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::{borrow::Cow, str, sync::Arc}; -use theme::{Theme, ThemeRegistry}; -use util::ResultExt as _; +use std::{borrow::Cow, str}; pub use font_size::{adjust_font_size_delta, font_size_for_setting}; +use gpui::AssetSource; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; pub use settings_file::*; pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; @@ -27,193 +14,9 @@ pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; -#[derive(Clone)] -pub struct Settings { - pub buffer_font_family_name: String, - pub buffer_font_features: fonts::Features, - pub buffer_font_family: FamilyId, - pub buffer_font_size: f32, - pub theme: Arc, -} - -impl Setting for Settings { - const KEY: Option<&'static str> = None; - - type FileContent = SettingsFileContent; - - fn load( - defaults: &Self::FileContent, - user_values: &[&Self::FileContent], - cx: &AppContext, - ) -> Result { - let buffer_font_features = defaults.buffer_font_features.clone().unwrap(); - let themes = cx.global::>(); - - let mut this = Self { - buffer_font_family: cx - .font_cache() - .load_family( - &[defaults.buffer_font_family.as_ref().unwrap()], - &buffer_font_features, - ) - .unwrap(), - buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(), - buffer_font_features, - buffer_font_size: defaults.buffer_font_size.unwrap(), - theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), - }; - - for value in user_values.into_iter().copied().cloned() { - this.set_user_settings(value, themes.as_ref(), cx.font_cache()); - } - - Ok(this) - } - - fn json_schema( - generator: &mut SchemaGenerator, - params: &SettingsJsonSchemaParams, - ) -> schemars::schema::RootSchema { - let mut root_schema = generator.root_schema_for::(); - - // Create a schema for a theme name. - let theme_name_schema = SchemaObject { - instance_type: Some(InstanceType::String.into()), - enum_values: Some( - params - .theme_names - .iter() - .cloned() - .map(Value::String) - .collect(), - ), - ..Default::default() - }; - - root_schema - .definitions - .extend([("ThemeName".into(), theme_name_schema.into())]); - - root_schema - .schema - .object - .as_mut() - .unwrap() - .properties - .extend([( - "theme".to_owned(), - Schema::new_ref("#/definitions/ThemeName".into()), - )]); - - root_schema - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -pub struct SettingsFileContent { - #[serde(default)] - pub buffer_font_family: Option, - #[serde(default)] - pub buffer_font_size: Option, - #[serde(default)] - pub buffer_font_features: Option, - #[serde(default)] - pub theme: Option, -} - -impl Settings { - pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> { - match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() { - Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), - Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()), - } - } - - /// Fill out the settings corresponding to the default.json file, overrides will be set later - pub fn defaults( - assets: impl AssetSource, - font_cache: &FontCache, - themes: &ThemeRegistry, - ) -> Self { - let defaults: SettingsFileContent = settings_store::parse_json_with_comments( - str::from_utf8(assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap().as_ref()).unwrap(), - ) - .unwrap(); - - let buffer_font_features = defaults.buffer_font_features.unwrap(); - Self { - buffer_font_family: font_cache - .load_family( - &[defaults.buffer_font_family.as_ref().unwrap()], - &buffer_font_features, - ) - .unwrap(), - buffer_font_family_name: defaults.buffer_font_family.unwrap(), - buffer_font_features, - buffer_font_size: defaults.buffer_font_size.unwrap(), - theme: themes.get(&defaults.theme.unwrap()).unwrap(), - } - } - - // Fill out the overrride and etc. settings from the user's settings.json - fn set_user_settings( - &mut self, - data: SettingsFileContent, - theme_registry: &ThemeRegistry, - font_cache: &FontCache, - ) { - let mut family_changed = false; - if let Some(value) = data.buffer_font_family { - self.buffer_font_family_name = value; - family_changed = true; - } - if let Some(value) = data.buffer_font_features { - self.buffer_font_features = value; - family_changed = true; - } - if family_changed { - if let Some(id) = font_cache - .load_family(&[&self.buffer_font_family_name], &self.buffer_font_features) - .log_err() - { - self.buffer_font_family = id; - } - } - - if let Some(value) = &data.theme { - if let Some(theme) = theme_registry.get(value).log_err() { - self.theme = theme; - } - } - - merge(&mut self.buffer_font_size, data.buffer_font_size); - } - - #[cfg(any(test, feature = "test-support"))] - pub fn test(cx: &gpui::AppContext) -> Settings { - Settings { - buffer_font_family_name: "Monaco".to_string(), - buffer_font_features: Default::default(), - buffer_font_family: cx - .font_cache() - .load_family(&["Monaco"], &Default::default()) - .unwrap(), - buffer_font_size: 14., - theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default), - } - } - - #[cfg(any(test, feature = "test-support"))] - pub fn test_async(cx: &mut gpui::TestAppContext) { - cx.update(|cx| { - let settings = Self::test(cx); - cx.set_global(settings); - }); - } -} - -fn merge(target: &mut T, value: Option) { - if let Some(value) = value { - *target = value; +pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> { + match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() { + Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), + Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()), } } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index cf1787d7c02e0ff90ccebbae74a0473ea72f0e9c..ed2741651249d88b9a18567f04848c33b45c0465 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,7 +1,4 @@ -use crate::{ - settings_store::parse_json_with_comments, settings_store::SettingsStore, Setting, Settings, - DEFAULT_SETTINGS_ASSET_PATH, -}; +use crate::{settings_store::SettingsStore, Setting, DEFAULT_SETTINGS_ASSET_PATH}; use anyhow::Result; use assets::Assets; use fs::Fs; @@ -34,16 +31,21 @@ pub fn default_settings() -> Cow<'static, str> { } } +#[cfg(any(test, feature = "test-support"))] +pub const EMPTY_THEME_NAME: &'static str = "empty-theme"; + #[cfg(any(test, feature = "test-support"))] pub fn test_settings() -> String { - let mut value = - parse_json_with_comments::(default_settings().as_ref()).unwrap(); + let mut value = crate::settings_store::parse_json_with_comments::( + default_settings().as_ref(), + ) + .unwrap(); util::merge_non_null_json_value_into( serde_json::json!({ "buffer_font_family": "Courier", "buffer_font_features": {}, "buffer_font_size": 14, - "theme": theme::EMPTY_THEME_NAME, + "theme": EMPTY_THEME_NAME, }), &mut value, ); @@ -85,10 +87,6 @@ pub fn handle_settings_file_changes( store .set_user_settings(&user_settings_content, cx) .log_err(); - - // TODO - remove the Settings global, use the SettingsStore instead. - store.register_setting::(cx); - cx.set_global(store.get::(None).clone()); }); cx.spawn(move |mut cx| async move { while let Some(user_settings_content) = user_settings_file_rx.next().await { @@ -97,9 +95,6 @@ pub fn handle_settings_file_changes( store .set_user_settings(&user_settings_content, cx) .log_err(); - - // TODO - remove the Settings global, use the SettingsStore instead. - cx.set_global(store.get::(None).clone()); }); }); } @@ -113,7 +108,7 @@ async fn load_settings(fs: &Arc) -> Result { Err(err) => { if let Some(e) = err.downcast_ref::() { if e.kind() == ErrorKind::NotFound { - return Ok(Settings::initial_user_settings_content(&Assets).to_string()); + return Ok(crate::initial_user_settings_content(&Assets).to_string()); } } return Err(err); diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index cdc765918997113a67698064d7b04122ef8e2254..dd81b05434af71680f85f18b644839d7a6b47875 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -40,7 +40,11 @@ pub trait Setting: 'static { where Self: Sized; - fn json_schema(generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams) -> RootSchema { + fn json_schema( + generator: &mut SchemaGenerator, + _: &SettingsJsonSchemaParams, + _: &AppContext, + ) -> RootSchema { generator.root_schema_for::() } @@ -75,7 +79,7 @@ pub trait Setting: 'static { } pub struct SettingsJsonSchemaParams<'a> { - pub theme_names: &'a [String], + pub staff_mode: bool, pub language_names: &'a [String], } @@ -112,6 +116,7 @@ trait AnySettingValue { &self, generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams, + cx: &AppContext, ) -> RootSchema; } @@ -169,6 +174,16 @@ impl SettingsStore { .expect("no default value for setting type") } + /// Override the global value for a setting. + /// + /// The given value will be overwritten if the user settings file changes. + pub fn override_global(&mut self, value: T) { + self.setting_values + .get_mut(&TypeId::of::()) + .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::())) + .set_global_value(Box::new(value)) + } + /// Get the user's settings as a raw JSON value. /// /// This is only for debugging and reporting. For user-facing functionality, @@ -342,7 +357,11 @@ impl SettingsStore { Ok(()) } - pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams) -> serde_json::Value { + pub fn json_schema( + &self, + schema_params: &SettingsJsonSchemaParams, + cx: &AppContext, + ) -> serde_json::Value { use schemars::{ gen::SchemaSettings, schema::{Schema, SchemaObject}, @@ -355,7 +374,7 @@ impl SettingsStore { let mut combined_schema = RootSchema::default(); for setting_value in self.setting_values.values() { - let setting_schema = setting_value.json_schema(&mut generator, schema_params); + let setting_schema = setting_value.json_schema(&mut generator, schema_params, cx); combined_schema .definitions .extend(setting_schema.definitions); @@ -552,8 +571,9 @@ impl AnySettingValue for SettingValue { &self, generator: &mut SchemaGenerator, params: &SettingsJsonSchemaParams, + cx: &AppContext, ) -> RootSchema { - T::json_schema(generator, params) + T::json_schema(generator, params, cx) } } diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index cc4580daa123efad169158bbd9ca9e7ab9e19fdf..f918d91b6d77c22ba98ecc6ac1151c7410533184 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -33,7 +33,6 @@ use mappings::mouse::{ use procinfo::LocalProcessInfo; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Settings; use util::truncate_and_trailoff; use std::{ @@ -700,7 +699,7 @@ impl Terminal { match event { InternalEvent::ColorRequest(index, format) => { let color = term.colors()[*index].unwrap_or_else(|| { - let term_style = &cx.global::().theme.terminal; + let term_style = &theme::current(cx).terminal; to_alac_rgb(get_color_at_index(index, &term_style)) }); self.write_to_pty(format(color)) diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index a92f7285b51d4bad489af8ef929cb8a88ef91074..fcb5e7feb3ae8d5eaa1db8c87229b8c5c31c2e0e 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -5,7 +5,6 @@ use gpui::{ platform::{CursorStyle, MouseButton}, AnyElement, Element, Entity, View, ViewContext, ViewHandle, WeakViewHandle, }; -use settings::Settings; use std::any::TypeId; use workspace::{ dock::{Dock, FocusDock}, @@ -43,7 +42,7 @@ impl View for TerminalButton { let has_terminals = !project.local_terminal_handles().is_empty(); let terminal_count = project.local_terminal_handles().len() as i32; - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); Stack::new() .with_child( diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 93be64b612aadbfced6922ff4f88e3aee43a6b9a..8976a34210493d3892dc101205c4e120d249cd1c 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -16,7 +16,7 @@ use gpui::{ use itertools::Itertools; use language::CursorShape; use ordered_float::OrderedFloat; -use settings::{font_size_for_setting, Settings}; +use settings::font_size_for_setting; use terminal::{ alacritty_terminal::{ ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor}, @@ -27,7 +27,7 @@ use terminal::{ mappings::colors::convert_color, IndexedCell, Terminal, TerminalContent, TerminalSettings, TerminalSize, }; -use theme::TerminalStyle; +use theme::{TerminalStyle, ThemeSettings}; use util::ResultExt; use std::{fmt::Debug, ops::RangeInclusive}; @@ -522,7 +522,7 @@ impl Element for TerminalElement { view: &mut TerminalView, cx: &mut LayoutContext, ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { - let settings = cx.global::(); + let settings = settings::get_setting::(None, cx); let terminal_settings = settings::get_setting::(None, cx); //Setup layout information diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 76bc9b4a3868e9dbe83ba7264d639c6200c39a03..944366e358a3ce13055121e25a2f1a0efee95368 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -904,6 +904,7 @@ mod tests { cx: &mut TestAppContext, ) -> (ModelHandle, ViewHandle) { let params = cx.update(AppState::test); + cx.update(|cx| theme::init((), cx)); let project = Project::test(params.fs.clone(), [], cx).await; let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml index 0cba8d8da22272ab22c70cd37f0502d04a2243ac..b213cc9c1e2ece98c09791f0a005cf3d2b1cf752 100644 --- a/crates/theme/Cargo.toml +++ b/crates/theme/Cargo.toml @@ -5,7 +5,11 @@ edition = "2021" publish = false [features] -test-support = ["gpui/test-support"] +test-support = [ + "gpui/test-support", + "fs/test-support", + "settings/test-support" +] [lib] path = "src/theme.rs" @@ -14,10 +18,19 @@ doctest = false [dependencies] gpui = { path = "../gpui" } fs = { path = "../fs" } +settings = { path = "../settings" } +util = { path = "../util" } + anyhow.workspace = true indexmap = "1.6.2" parking_lot.workspace = true +schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true toml.workspace = true + +[dev-dependencies] +gpui = { path = "../gpui", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } +settings = { path = "../settings", features = ["test-support"] } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 8760ea54ea842776017ef664d94ecf344507c130..ae3bcb765d6bdb21da1d98d18175564fe54c7f80 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -1,19 +1,31 @@ mod theme_registry; +mod theme_settings; +pub mod ui; use gpui::{ color::Color, elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle}, fonts::{HighlightStyle, TextStyle}, - platform, Border, MouseState, + platform, AppContext, AssetSource, Border, MouseState, }; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; use std::{collections::HashMap, sync::Arc}; use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle}; -pub mod ui; +pub use theme_registry::ThemeRegistry; +pub use theme_settings::ThemeSettings; -pub use theme_registry::*; +pub fn current(cx: &AppContext) -> Arc { + settings::get_setting::(None, cx) + .theme + .clone() +} + +pub fn init(source: impl AssetSource, cx: &mut AppContext) { + cx.set_global(ThemeRegistry::new(source, cx.font_cache().clone())); + settings::register_setting::(cx); +} #[derive(Deserialize, Default)] pub struct Theme { diff --git a/crates/theme/src/theme_registry.rs b/crates/theme/src/theme_registry.rs index 2bcdb4528ce41de0ee8214072fa9ed7769eece3b..12ccaf3d519c754d9d8fafee79aafe05300c1d82 100644 --- a/crates/theme/src/theme_registry.rs +++ b/crates/theme/src/theme_registry.rs @@ -20,9 +20,6 @@ pub struct ThemeRegistry { next_theme_id: AtomicUsize, } -#[cfg(any(test, feature = "test-support"))] -pub const EMPTY_THEME_NAME: &'static str = "empty-theme"; - impl ThemeRegistry { pub fn new(source: impl AssetSource, font_cache: Arc) -> Arc { let this = Arc::new(Self { @@ -35,8 +32,13 @@ impl ThemeRegistry { #[cfg(any(test, feature = "test-support"))] this.themes.lock().insert( - EMPTY_THEME_NAME.to_string(), - gpui::fonts::with_font_cache(this.font_cache.clone(), || Arc::new(Theme::default())), + settings::EMPTY_THEME_NAME.to_string(), + gpui::fonts::with_font_cache(this.font_cache.clone(), || { + let mut theme = Theme::default(); + theme.meta.id = this.next_theme_id.fetch_add(1, SeqCst); + theme.meta.name = settings::EMPTY_THEME_NAME.into(); + Arc::new(theme) + }), ); this diff --git a/crates/theme/src/theme_settings.rs b/crates/theme/src/theme_settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..85958e576d856a89bde8354642272cab5e18ad00 --- /dev/null +++ b/crates/theme/src/theme_settings.rs @@ -0,0 +1,136 @@ +use crate::{Theme, ThemeRegistry}; +use anyhow::Result; +use gpui::{font_cache::FamilyId, fonts, AppContext}; +use schemars::{ + gen::SchemaGenerator, + schema::{InstanceType, Schema, SchemaObject}, + JsonSchema, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use settings::SettingsJsonSchemaParams; +use std::sync::Arc; +use util::ResultExt as _; + +#[derive(Clone)] +pub struct ThemeSettings { + pub buffer_font_family_name: String, + pub buffer_font_features: fonts::Features, + pub buffer_font_family: FamilyId, + pub buffer_font_size: f32, + pub theme: Arc, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +pub struct ThemeSettingsContent { + #[serde(default)] + pub buffer_font_family: Option, + #[serde(default)] + pub buffer_font_size: Option, + #[serde(default)] + pub buffer_font_features: Option, + #[serde(default)] + pub theme: Option, +} + +impl settings::Setting for ThemeSettings { + const KEY: Option<&'static str> = None; + + type FileContent = ThemeSettingsContent; + + fn load( + defaults: &Self::FileContent, + user_values: &[&Self::FileContent], + cx: &AppContext, + ) -> Result { + let buffer_font_features = defaults.buffer_font_features.clone().unwrap(); + let themes = cx.global::>(); + + let mut this = Self { + buffer_font_family: cx + .font_cache() + .load_family( + &[defaults.buffer_font_family.as_ref().unwrap()], + &buffer_font_features, + ) + .unwrap(), + buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(), + buffer_font_features, + buffer_font_size: defaults.buffer_font_size.unwrap(), + theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(), + }; + + for value in user_values.into_iter().copied().cloned() { + let font_cache = cx.font_cache(); + let mut family_changed = false; + if let Some(value) = value.buffer_font_family { + this.buffer_font_family_name = value; + family_changed = true; + } + if let Some(value) = value.buffer_font_features { + this.buffer_font_features = value; + family_changed = true; + } + if family_changed { + if let Some(id) = font_cache + .load_family(&[&this.buffer_font_family_name], &this.buffer_font_features) + .log_err() + { + this.buffer_font_family = id; + } + } + + if let Some(value) = &value.theme { + if let Some(theme) = themes.get(value).log_err() { + this.theme = theme; + } + } + + merge(&mut this.buffer_font_size, value.buffer_font_size); + } + + Ok(this) + } + + fn json_schema( + generator: &mut SchemaGenerator, + params: &SettingsJsonSchemaParams, + cx: &AppContext, + ) -> schemars::schema::RootSchema { + let mut root_schema = generator.root_schema_for::(); + let theme_names = cx + .global::>() + .list(params.staff_mode) + .map(|theme| Value::String(theme.name.clone())) + .collect(); + + let theme_name_schema = SchemaObject { + instance_type: Some(InstanceType::String.into()), + enum_values: Some(theme_names), + ..Default::default() + }; + + root_schema + .definitions + .extend([("ThemeName".into(), theme_name_schema.into())]); + + root_schema + .schema + .object + .as_mut() + .unwrap() + .properties + .extend([( + "theme".to_owned(), + Schema::new_ref("#/definitions/ThemeName".into()), + )]); + + root_schema + } +} + +fn merge(target: &mut T, value: Option) { + if let Some(value) = value { + *target = value; + } +} diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 27c5a9ef4ed66444d7b463bf50c807d29b2e65e9..a6c84d1d91a6b149b3ed4950c2127f194040fc4a 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -2,10 +2,10 @@ use fs::Fs; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{actions, elements::*, AnyElement, AppContext, Element, MouseState, ViewContext}; use picker::{Picker, PickerDelegate, PickerEvent}; -use settings::{update_settings_file, Settings}; +use settings::{update_settings_file, SettingsStore}; use staff_mode::StaffMode; use std::sync::Arc; -use theme::{Theme, ThemeMeta, ThemeRegistry}; +use theme::{Theme, ThemeMeta, ThemeRegistry, ThemeSettings}; use util::ResultExt; use workspace::Workspace; @@ -18,17 +18,17 @@ pub fn init(cx: &mut AppContext) { pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { workspace.toggle_modal(cx, |workspace, cx| { - let themes = workspace.app_state().themes.clone(); let fs = workspace.app_state().fs.clone(); - cx.add_view(|cx| ThemeSelector::new(ThemeSelectorDelegate::new(fs, themes, cx), cx)) + cx.add_view(|cx| ThemeSelector::new(ThemeSelectorDelegate::new(fs, cx), cx)) }); } #[cfg(debug_assertions)] -pub fn reload(themes: Arc, cx: &mut AppContext) { - let current_theme_name = cx.global::().theme.meta.name.clone(); - themes.clear(); - match themes.get(¤t_theme_name) { +pub fn reload(cx: &mut AppContext) { + let current_theme_name = theme::current(cx).meta.name.clone(); + let registry = cx.global::>(); + registry.clear(); + match registry.get(¤t_theme_name) { Ok(theme) => { ThemeSelectorDelegate::set_theme(theme, cx); log::info!("reloaded theme {}", current_theme_name); @@ -43,7 +43,6 @@ pub type ThemeSelector = Picker; pub struct ThemeSelectorDelegate { fs: Arc, - registry: Arc, theme_data: Vec, matches: Vec, original_theme: Arc, @@ -52,18 +51,12 @@ pub struct ThemeSelectorDelegate { } impl ThemeSelectorDelegate { - fn new( - fs: Arc, - registry: Arc, - cx: &mut ViewContext, - ) -> Self { - let settings = cx.global::(); - - let original_theme = settings.theme.clone(); + fn new(fs: Arc, cx: &mut ViewContext) -> Self { + let original_theme = theme::current(cx).clone(); - let mut theme_names = registry - .list(**cx.default_global::()) - .collect::>(); + let staff_mode = **cx.default_global::(); + let registry = cx.global::>(); + let mut theme_names = registry.list(staff_mode).collect::>(); theme_names.sort_unstable_by(|a, b| a.is_light.cmp(&b.is_light).then(a.name.cmp(&b.name))); let matches = theme_names .iter() @@ -76,7 +69,6 @@ impl ThemeSelectorDelegate { .collect(); let mut this = Self { fs, - registry, theme_data: theme_names, matches, original_theme: original_theme.clone(), @@ -89,7 +81,8 @@ impl ThemeSelectorDelegate { fn show_selected_theme(&mut self, cx: &mut ViewContext) { if let Some(mat) = self.matches.get(self.selected_index) { - match self.registry.get(&mat.string) { + let registry = cx.global::>(); + match registry.get(&mat.string) { Ok(theme) => { Self::set_theme(theme, cx); } @@ -109,8 +102,10 @@ impl ThemeSelectorDelegate { } fn set_theme(theme: Arc, cx: &mut AppContext) { - cx.update_global::(|settings, cx| { - settings.theme = theme; + cx.update_global::(|store, cx| { + let mut theme_settings = store.get::(None).clone(); + theme_settings.theme = theme; + store.override_global(theme_settings); cx.refresh_windows(); }); } @@ -128,9 +123,9 @@ impl PickerDelegate for ThemeSelectorDelegate { fn confirm(&mut self, cx: &mut ViewContext) { self.selection_completed = true; - let theme_name = cx.global::().theme.meta.name.clone(); - update_settings_file::(self.fs.clone(), cx, |settings_content| { - settings_content.theme = Some(theme_name); + let theme_name = theme::current(cx).meta.name.clone(); + update_settings_file::(self.fs.clone(), cx, |settings| { + settings.theme = Some(theme_name); }); cx.emit(PickerEvent::Dismiss); @@ -212,11 +207,10 @@ impl PickerDelegate for ThemeSelectorDelegate { selected: bool, cx: &AppContext, ) -> AnyElement> { - let settings = cx.global::(); - let theme = &settings.theme; - let theme_match = &self.matches[ix]; + let theme = theme::current(cx); let style = theme.picker.item.style_for(mouse_state, selected); + let theme_match = &self.matches[ix]; Label::new(theme_match.string.clone(), style.label.clone()) .with_highlights(theme_match.positions.clone()) .contained() diff --git a/crates/theme_testbench/src/theme_testbench.rs b/crates/theme_testbench/src/theme_testbench.rs index 125bc523b7f8723e06b81628fd1c9387e0924787..52eca5f8ad3d26a1298778809d1cdd804c51e5e8 100644 --- a/crates/theme_testbench/src/theme_testbench.rs +++ b/crates/theme_testbench/src/theme_testbench.rs @@ -10,8 +10,7 @@ use gpui::{ WeakViewHandle, }; use project::Project; -use settings::Settings; -use theme::{ColorScheme, Layer, Style, StyleSet}; +use theme::{ColorScheme, Layer, Style, StyleSet, ThemeSettings}; use workspace::{item::Item, register_deserializable_item, Pane, Workspace}; actions!(theme, [DeployThemeTestbench]); @@ -220,7 +219,7 @@ impl ThemeTestbench { } fn render_label(text: String, style: &Style, cx: &mut ViewContext) -> Label { - let settings = cx.global::(); + let settings = settings::get_setting::(None, cx); let font_cache = cx.font_cache(); let family_id = settings.buffer_font_family; let font_size = settings::font_size_for_setting(settings.buffer_font_size, cx); @@ -252,7 +251,7 @@ impl View for ThemeTestbench { } fn render(&mut self, cx: &mut gpui::ViewContext) -> AnyElement { - let color_scheme = &cx.global::().theme.clone().color_scheme; + let color_scheme = &theme::current(cx).clone().color_scheme; Flex::row() .with_child( diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index 815de5e6aee4a5d11dcbc24374c42285d677e741..c9dfec9eb060aa1cf941e437896a3647e886c25d 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -7,7 +7,7 @@ use gpui::{ }; use picker::{Picker, PickerDelegate, PickerEvent}; use project::Fs; -use settings::{update_settings_file, Settings}; +use settings::update_settings_file; use std::sync::Arc; use util::ResultExt; use workspace::Workspace; @@ -139,7 +139,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { selected: bool, cx: &gpui::AppContext, ) -> gpui::AnyElement> { - let theme = &cx.global::().theme; + let theme = &theme::current(cx); let keymap_match = &self.matches[ix]; let style = theme.picker.item.style_for(mouse_state, selected); diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index ca04d5f6dc9a46316a419928017f263acfebf32a..29b013156dc4906964192f452b8bad5056b8f418 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -8,7 +8,7 @@ use gpui::{ elements::{Flex, Label, ParentElement}, AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, WeakViewHandle, }; -use settings::{update_settings_file, Settings}; +use settings::{update_settings_file, SettingsStore}; use std::{borrow::Cow, sync::Arc}; use workspace::{ item::Item, open_new, sidebar::SidebarSide, AppState, PaneBackdrop, Welcome, Workspace, @@ -61,9 +61,7 @@ impl View for WelcomePage { fn render(&mut self, cx: &mut gpui::ViewContext) -> AnyElement { let self_handle = cx.handle(); - let settings = cx.global::(); - let theme = settings.theme.clone(); - + let theme = theme::current(cx); let width = theme.welcome.page_width; let telemetry_settings = *settings::get_setting::(None, cx); @@ -224,7 +222,7 @@ impl WelcomePage { pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { WelcomePage { workspace: workspace.weak_handle(), - _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), } } } @@ -260,7 +258,7 @@ impl Item for WelcomePage { ) -> Option { Some(WelcomePage { workspace: self.workspace.clone(), - _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), }) } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 9879aba5c699b8862d4105dc178d9e6365585848..beac87f99c49cd7ed4a4c5adde68b393089f6022 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -408,7 +408,6 @@ mod tests { use gpui::{AppContext, BorrowWindowContext, TestAppContext, ViewContext, WindowContext}; use project::{FakeFs, Project}; - use theme::ThemeRegistry; use super::*; use crate::{ @@ -468,7 +467,6 @@ mod tests { project.clone(), Arc::new(AppState { languages: project.read(cx).languages().clone(), - themes: ThemeRegistry::new((), cx.font_cache().clone()), client: project.read(cx).client(), user_store: project.read(cx).user_store(), fs: project.read(cx).fs().clone(), @@ -615,7 +613,6 @@ mod tests { project.clone(), Arc::new(AppState { languages: project.read(cx).languages().clone(), - themes: ThemeRegistry::new((), cx.font_cache().clone()), client: project.read(cx).client(), user_store: project.read(cx).user_store(), fs: project.read(cx).fs().clone(), diff --git a/crates/workspace/src/dock/toggle_dock_button.rs b/crates/workspace/src/dock/toggle_dock_button.rs index 1fda55b7834967207a0f4463e548c8ad219bf84e..9ab7a8996bc18868c1f1f0f06847aebc06183f56 100644 --- a/crates/workspace/src/dock/toggle_dock_button.rs +++ b/crates/workspace/src/dock/toggle_dock_button.rs @@ -6,7 +6,6 @@ use gpui::{ platform::MouseButton, AnyElement, Element, Entity, View, ViewContext, ViewHandle, WeakViewHandle, }; -use settings::Settings; pub struct ToggleDockButton { workspace: WeakViewHandle, @@ -43,7 +42,7 @@ impl View for ToggleDockButton { let dock_position = workspace.read(cx).dock.position; let dock_pane = workspace.read(cx).dock_pane().clone(); - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let button = MouseEventHandler::::new(0, cx, { let theme = theme.clone(); diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 7881603bbc6c47bc97edc931633d957965949181..21b3be09d06ea6781e70dd12fa91a1c2ace2e152 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -149,6 +149,8 @@ impl Workspace { } pub mod simple_message_notification { + use super::Notification; + use crate::Workspace; use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text}, @@ -158,13 +160,8 @@ pub mod simple_message_notification { }; use menu::Cancel; use serde::Deserialize; - use settings::Settings; use std::{borrow::Cow, sync::Arc}; - use crate::Workspace; - - use super::Notification; - actions!(message_notifications, [CancelMessageNotification]); #[derive(Clone, Default, Deserialize, PartialEq)] @@ -240,7 +237,7 @@ pub mod simple_message_notification { } fn render(&mut self, cx: &mut gpui::ViewContext) -> gpui::AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let theme = &theme.simple_message_notification; enum MessageNotificationTag {} diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ca454d2c751b57878ffb09497456aea7262c5b9b..3717cfc200e224f35e023d0adc0f4f6b6ad974cf 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -30,7 +30,6 @@ use gpui::{ }; use project::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; -use settings::Settings; use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc}; use theme::Theme; use util::ResultExt; @@ -1297,7 +1296,7 @@ impl Pane { } fn render_tabs(&mut self, cx: &mut ViewContext) -> impl Element { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let pane = cx.handle().downgrade(); let autoscroll = if mem::take(&mut self.autoscroll) { @@ -1328,7 +1327,7 @@ impl Pane { let pane = pane.clone(); let detail = detail.clone(); - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let mut tooltip_theme = theme.tooltip.clone(); tooltip_theme.max_text_width = None; let tab_tooltip_text = item.tab_tooltip_text(cx).map(|a| a.to_string()); @@ -1406,7 +1405,7 @@ impl Pane { pane: pane.clone(), }, { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let detail = detail.clone(); move |dragged_item: &DraggedItem, cx: &mut ViewContext| { @@ -1699,7 +1698,7 @@ impl View for Pane { if let Some(active_item) = self.active_item() { Flex::column() .with_child({ - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); let mut stack = Stack::new(); @@ -1765,7 +1764,7 @@ impl View for Pane { .into_any() } else { enum EmptyPane {} - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); dragged_item_receiver::(0, 0, false, None, cx, |_, cx| { self.render_blank_pane(&theme, cx) @@ -1862,7 +1861,7 @@ fn render_tab_bar_button)>( Stack::new() .with_child( MouseEventHandler::::new(index, cx, |mouse_state, cx| { - let theme = &cx.global::().theme.workspace.tab_bar; + let theme = &theme::current(cx).workspace.tab_bar; let style = theme.pane_button.style_for(mouse_state, false); Svg::new(icon) .with_color(style.color) @@ -2024,7 +2023,7 @@ impl Element for PaneBackdrop { view: &mut V, cx: &mut ViewContext, ) -> Self::PaintState { - let background = cx.global::().theme.editor.background; + let background = theme::current(cx).editor.background; let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); @@ -2536,7 +2535,7 @@ mod tests { fn init_test(cx: &mut TestAppContext) { cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); - cx.set_global(settings::Settings::test(cx)); + theme::init((), cx); crate::init_settings(cx); }); } diff --git a/crates/workspace/src/pane/dragged_item_receiver.rs b/crates/workspace/src/pane/dragged_item_receiver.rs index 961205b9ee72e67a32e152bb18cbad58a7c7a64f..532e6bff5c03e6e9565e86f333603c1eacd5d299 100644 --- a/crates/workspace/src/pane/dragged_item_receiver.rs +++ b/crates/workspace/src/pane/dragged_item_receiver.rs @@ -1,3 +1,5 @@ +use super::DraggedItem; +use crate::{Pane, SplitDirection, Workspace}; use drag_and_drop::DragAndDrop; use gpui::{ color::Color, @@ -8,11 +10,6 @@ use gpui::{ AppContext, Element, EventContext, MouseState, Quad, View, ViewContext, WeakViewHandle, }; use project::ProjectEntryId; -use settings::Settings; - -use crate::{Pane, SplitDirection, Workspace}; - -use super::DraggedItem; pub fn dragged_item_receiver( region_id: usize, @@ -225,8 +222,5 @@ fn drop_split_direction( } fn overlay_color(cx: &AppContext) -> Color { - cx.global::() - .theme - .workspace - .drop_target_overlay_color + theme::current(cx).workspace.drop_target_overlay_color } diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs index 5cc54a6a7f4244f09a1a40fa0cea7c592703331d..9a2e0bc5d2ec98e216e18f22bfd0bf709a755473 100644 --- a/crates/workspace/src/shared_screen.rs +++ b/crates/workspace/src/shared_screen.rs @@ -12,7 +12,6 @@ use gpui::{ platform::MouseButton, AppContext, Entity, Task, View, ViewContext, }; -use settings::Settings; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -88,7 +87,7 @@ impl View for SharedScreen { } }) .contained() - .with_style(cx.global::().theme.shared_screen) + .with_style(theme::current(cx).shared_screen) }) .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent()) .into_any() diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 6463ab7d24b9ea6e44ababa2c3768dc7d36b663a..50148fa211dad3a415e59a7381c44f55573c6b18 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -4,7 +4,6 @@ use gpui::{ AppContext, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use serde::Deserialize; -use settings::Settings; use std::rc::Rc; pub trait SidebarItem: View { @@ -192,7 +191,7 @@ impl View for Sidebar { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { if let Some(active_item) = self.active_item() { enum ResizeHandleTag {} - let style = &cx.global::().theme.workspace.sidebar; + let style = &theme::current(cx).workspace.sidebar; ChildView::new(active_item.as_any(), cx) .contained() .with_style(style.container) @@ -231,7 +230,7 @@ impl View for SidebarButtons { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = &cx.global::().theme; + let theme = &theme::current(cx); let tooltip_style = theme.tooltip.clone(); let theme = &theme.workspace.status_bar.sidebar_buttons; let sidebar = self.sidebar.read(cx); diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index b4de6b3575d392497ccba3c18274820bd51fa371..6fc1467566bec3a3c93ff6807a15bf1fec988e67 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -11,7 +11,6 @@ use gpui::{ AnyElement, AnyViewHandle, Entity, LayoutContext, SceneBuilder, SizeConstraint, Subscription, View, ViewContext, ViewHandle, WindowContext, }; -use settings::Settings; pub trait StatusItemView: View { fn set_active_pane_item( @@ -47,7 +46,7 @@ impl View for StatusBar { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = &cx.global::().theme.workspace.status_bar; + let theme = &theme::current(cx).workspace.status_bar; StatusBarElement { left: Flex::row() diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index b2832aa1e810ff7e5fad7e3dc5802de6cf49a8b5..30890ed5d2cefa4ebdadece37e94733087f5112f 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -3,7 +3,6 @@ use gpui::{ elements::*, platform::CursorStyle, platform::MouseButton, Action, AnyElement, AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; -use settings::Settings; pub trait ToolbarItemView: View { fn set_active_pane_item( @@ -68,7 +67,7 @@ impl View for Toolbar { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = &cx.global::().theme.workspace.toolbar; + let theme = &theme::current(cx).workspace.toolbar; let mut primary_left_items = Vec::new(); let mut primary_right_items = Vec::new(); @@ -131,7 +130,7 @@ impl View for Toolbar { let height = theme.height * primary_items_row_count as f32; let nav_button_height = theme.height; let button_style = theme.nav_button; - let tooltip_style = cx.global::().theme.tooltip.clone(); + let tooltip_style = theme::current(cx).tooltip.clone(); Flex::column() .with_child( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d956db10b69d4fd95672df71bdb3ca5b50f4c60b..a94bf3ce1c9e4bb02ffa9c79781078e8f27a8be8 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -76,12 +76,11 @@ pub use persistence::{ use postage::prelude::Stream; use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; use serde::Deserialize; -use settings::Settings; use shared_screen::SharedScreen; use sidebar::{Sidebar, SidebarButtons, SidebarSide, ToggleSidebarItem}; use status_bar::StatusBar; pub use status_bar::StatusItemView; -use theme::{Theme, ThemeRegistry}; +use theme::Theme; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; use util::{async_iife, paths, ResultExt}; pub use workspace_settings::{AutosaveSetting, DockAnchor, GitGutterSetting, WorkspaceSettings}; @@ -276,7 +275,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { cx.add_action( move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { create_and_open_local_file(&paths::SETTINGS, cx, || { - Settings::initial_user_settings_content(&Assets) + settings::initial_user_settings_content(&Assets) .as_ref() .into() }) @@ -361,7 +360,6 @@ pub fn register_deserializable_item(cx: &mut AppContext) { pub struct AppState { pub languages: Arc, - pub themes: Arc, pub client: Arc, pub user_store: ModelHandle, pub fs: Arc, @@ -379,7 +377,6 @@ impl AppState { if !cx.has_global::() { cx.set_global(SettingsStore::test(cx)); - cx.set_global(Settings::test(cx)); } let fs = fs::FakeFs::new(cx.background().clone()); @@ -387,14 +384,13 @@ impl AppState { let http_client = util::http::FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone(), cx); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); - let themes = ThemeRegistry::new((), cx.font_cache().clone()); + theme::init((), cx); client::init(&client, cx); crate::init_settings(cx); Arc::new(Self { client, - themes, fs, languages, user_store, @@ -1992,7 +1988,7 @@ impl Workspace { enum DisconnectedOverlay {} Some( MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme; + let theme = &theme::current(cx); Label::new( "Your connection to the remote project has been lost.", theme.workspace.disconnected_overlay.text.clone(), @@ -2630,7 +2626,6 @@ impl Workspace { pub fn test_new(project: ModelHandle, cx: &mut ViewContext) -> Self { let app_state = Arc::new(AppState { languages: project.read(cx).languages().clone(), - themes: ThemeRegistry::new((), cx.font_cache().clone()), client: project.read(cx).client(), user_store: project.read(cx).user_store(), fs: project.read(cx).fs().clone(), @@ -2776,7 +2771,7 @@ impl View for Workspace { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = cx.global::().theme.clone(); + let theme = theme::current(cx).clone(); Stack::new() .with_child( Flex::column() @@ -3798,7 +3793,7 @@ mod tests { cx.foreground().forbid_parking(); cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); - cx.set_global(Settings::test(cx)); + theme::init((), cx); language::init(cx); crate::init_settings(cx); }); diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 91b58f634b855743e8dd0421b317cbc5efe6b725..1f2b359af1523212d97de8dfd43cf4bccaae6dfb 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -3,7 +3,6 @@ pub use language::*; use node_runtime::NodeRuntime; use rust_embed::RustEmbed; use std::{borrow::Cow, str, sync::Arc}; -use theme::ThemeRegistry; mod c; mod elixir; @@ -32,11 +31,7 @@ mod yaml; #[exclude = "*.rs"] struct LanguageDir; -pub fn init( - languages: Arc, - themes: Arc, - node_runtime: Arc, -) { +pub fn init(languages: Arc, node_runtime: Arc) { fn adapter_arc(adapter: impl LspAdapter) -> Arc { Arc::new(adapter) } @@ -69,7 +64,6 @@ pub fn init( vec![adapter_arc(json::JsonLspAdapter::new( node_runtime.clone(), languages.clone(), - themes.clone(), ))], ), ("markdown", tree_sitter_markdown::language(), vec![]), diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 8ea07c626d121b9e9f7ef1618cedc7b029683139..1fb1a5a941c78dbe7378dce83e9bfe7aeb4707d4 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -16,7 +16,6 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use theme::ThemeRegistry; use util::http::HttpClient; use util::{paths, ResultExt}; @@ -30,20 +29,11 @@ fn server_binary_arguments(server_path: &Path) -> Vec { pub struct JsonLspAdapter { node: Arc, languages: Arc, - themes: Arc, } impl JsonLspAdapter { - pub fn new( - node: Arc, - languages: Arc, - themes: Arc, - ) -> Self { - JsonLspAdapter { - node, - languages, - themes, - } + pub fn new(node: Arc, languages: Arc) -> Self { + JsonLspAdapter { node, languages } } } @@ -128,18 +118,15 @@ impl LspAdapter for JsonLspAdapter { cx: &mut AppContext, ) -> Option> { let action_names = cx.all_action_names().collect::>(); - let theme_names = &self - .themes - .list(**cx.default_global::()) - .map(|meta| meta.name) - .collect::>(); + let staff_mode = cx.global::().0; let language_names = &self.languages.language_names(); - let settings_schema = cx - .global::() - .json_schema(&SettingsJsonSchemaParams { - theme_names, + let settings_schema = cx.global::().json_schema( + &SettingsJsonSchemaParams { language_names, - }); + staff_mode, + }, + cx, + ); Some( future::ready(serde_json::json!({ "json": { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index def504963348fd36f4bb8a7cedf406e6f6fdc79a..18abb2e2c517fa62a5be68b4ceb2a1ce5b70e561 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -23,9 +23,7 @@ use node_runtime::NodeRuntime; use parking_lot::Mutex; use project::Fs; use serde::{Deserialize, Serialize}; -use settings::{ - default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore, -}; +use settings::{default_settings, handle_settings_file_changes, watch_config_file, SettingsStore}; use simplelog::ConfigBuilder; use smol::process::Command; use std::{ @@ -56,7 +54,6 @@ use welcome::{show_welcome_experience, FIRST_OPEN}; use fs::RealFs; #[cfg(debug_assertions)] use staff_mode::StaffMode; -use theme::ThemeRegistry; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use workspace::{ dock::FocusDock, item::ItemHandle, notifications::NotifyResultExt, AppState, OpenSettings, @@ -84,7 +81,6 @@ fn main() { load_embedded_fonts(&app); let fs = Arc::new(RealFs); - let themes = ThemeRegistry::new(Assets, app.font_cache()); let user_settings_file_rx = watch_config_file(app.background(), fs.clone(), paths::SETTINGS.clone()); let user_keymap_file_rx = @@ -124,7 +120,6 @@ fn main() { app.run(move |cx| { cx.set_global(*RELEASE_CHANNEL); - cx.set_global(themes.clone()); #[cfg(debug_assertions)] cx.set_global(StaffMode(true)); @@ -148,11 +143,12 @@ fn main() { let languages = Arc::new(languages); let node_runtime = NodeRuntime::new(http.clone(), cx.background().to_owned()); - languages::init(languages.clone(), themes.clone(), node_runtime.clone()); + languages::init(languages.clone(), node_runtime.clone()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); cx.set_global(client.clone()); + theme::init(Assets, cx); context_menu::init(cx); project::Project::init(&client, cx); client::init(&client, cx); @@ -171,13 +167,12 @@ fn main() { theme_testbench::init(cx); copilot::init(http.clone(), node_runtime, cx); - cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx)) - .detach(); + cx.spawn(|cx| watch_themes(fs.clone(), cx)).detach(); - languages.set_theme(cx.global::().theme.clone()); - cx.observe_global::({ + languages.set_theme(theme::current(cx).clone()); + cx.observe_global::({ let languages = languages.clone(); - move |cx| languages.set_theme(cx.global::().theme.clone()) + move |cx| languages.set_theme(theme::current(cx).clone()) }) .detach(); @@ -190,7 +185,6 @@ fn main() { let app_state = Arc::new(AppState { languages, - themes, client: client.clone(), user_store, fs, @@ -208,10 +202,13 @@ fn main() { journal::init(app_state.clone(), cx); language_selector::init(cx); theme_selector::init(cx); - zed::init(&app_state, cx); + activity_indicator::init(cx); + lsp_log::init(cx); + call::init(app_state.client.clone(), app_state.user_store.clone(), cx); collab_ui::init(&app_state, cx); feedback::init(cx); welcome::init(cx); + zed::init(&app_state, cx); cx.set_menus(menus::menus()); @@ -584,11 +581,7 @@ fn load_embedded_fonts(app: &App) { } #[cfg(debug_assertions)] -async fn watch_themes( - fs: Arc, - themes: Arc, - mut cx: AsyncAppContext, -) -> Option<()> { +async fn watch_themes(fs: Arc, mut cx: AsyncAppContext) -> Option<()> { let mut events = fs .watch("styles/src".as_ref(), Duration::from_millis(100)) .await; @@ -600,7 +593,7 @@ async fn watch_themes( .await .log_err()?; if output.status.success() { - cx.update(|cx| theme_selector::reload(themes.clone(), cx)) + cx.update(|cx| theme_selector::reload(cx)) } else { eprintln!( "build script failed {}", diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index d6e4f26b675666f2f9e44c8939558f402e9b6b5a..5b271762ed2d24e3969851af3316224b41aaa15b 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -30,10 +30,11 @@ use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; use settings::{ - adjust_font_size_delta, KeymapFileContent, Settings, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH, + adjust_font_size_delta, KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH, }; use std::{borrow::Cow, str, sync::Arc}; use terminal_view::terminal_button::TerminalButton; +use theme::ThemeSettings; use util::{channel::ReleaseChannel, paths, ResultExt}; use uuid::Uuid; use welcome::BaseKeymap; @@ -124,7 +125,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { }); cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| { adjust_font_size_delta(cx, |size, cx| { - if cx.global::().buffer_font_size + *size > MIN_FONT_SIZE { + if cx.global::().buffer_font_size + *size > MIN_FONT_SIZE { *size -= 1.0; } }) @@ -258,9 +259,6 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { } } }); - activity_indicator::init(cx); - lsp_log::init(cx); - call::init(app_state.client.clone(), app_state.user_store.clone(), cx); load_default_keymap(cx); } @@ -1910,7 +1908,7 @@ mod tests { cx.update(|cx| { cx.set_global(SettingsStore::test(cx)); - cx.set_global(ThemeRegistry::new(Assets, cx.font_cache().clone())); + theme::init(Assets, cx); welcome::init(cx); cx.add_global_action(|_: &A, _cx| {}); @@ -2038,15 +2036,25 @@ mod tests { ]) .unwrap(); let themes = ThemeRegistry::new(Assets, cx.font_cache().clone()); - let settings = Settings::defaults(Assets, cx.font_cache(), &themes); + let mut settings = SettingsStore::default(); + settings + .set_default_settings(&settings::default_settings(), cx) + .unwrap(); + cx.set_global(settings); + theme::init(Assets, cx); let mut has_default_theme = false; for theme_name in themes.list(false).map(|meta| meta.name) { let theme = themes.get(&theme_name).unwrap(); - if theme.meta.name == settings.theme.meta.name { + assert_eq!(theme.meta.name, theme_name); + if theme.meta.name + == settings::get_setting::(None, cx) + .theme + .meta + .name + { has_default_theme = true; } - assert_eq!(theme.meta.name, theme_name); } assert!(has_default_theme); } @@ -2056,10 +2064,9 @@ mod tests { let mut languages = LanguageRegistry::test(); languages.set_executor(cx.background().clone()); let languages = Arc::new(languages); - let themes = ThemeRegistry::new((), cx.font_cache().clone()); let http = FakeHttpClient::with_404_response(); let node_runtime = NodeRuntime::new(http, cx.background().to_owned()); - languages::init(languages.clone(), themes, node_runtime); + languages::init(languages.clone(), node_runtime); for name in languages.language_names() { languages.language_for_name(&name); } @@ -2073,6 +2080,7 @@ mod tests { let state = Arc::get_mut(&mut app_state).unwrap(); state.initialize_workspace = initialize_workspace; state.build_window_options = build_window_options; + theme::init((), cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); workspace::init(app_state.clone(), cx); language::init(cx); From 2d5f03e14852891d9090c8728aba4753a48c0401 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 15:05:20 -0700 Subject: [PATCH 20/25] Remove optional path argument when getting settings --- crates/auto_update/src/auto_update.rs | 6 +++--- crates/client/src/client.rs | 5 ++--- crates/client/src/telemetry.rs | 2 +- .../collab_ui/src/sharing_status_indicator.rs | 2 +- crates/copilot/src/copilot.rs | 3 +-- crates/copilot_button/src/copilot_button.rs | 13 ++++++------- crates/diagnostics/src/diagnostics.rs | 2 +- crates/editor/src/blink_manager.rs | 2 +- crates/editor/src/display_map.rs | 2 +- crates/editor/src/editor.rs | 16 ++++++++-------- crates/editor/src/element.rs | 19 +++++++++---------- crates/editor/src/hover_popover.rs | 2 +- crates/editor/src/multi_buffer.rs | 4 ++-- crates/journal/src/journal.rs | 2 +- crates/language/src/buffer.rs | 4 ++-- crates/language/src/language_settings.rs | 15 ++++----------- crates/project/src/project.rs | 9 ++++----- crates/project/src/terminals.rs | 2 +- crates/settings/src/settings_file.rs | 13 +++---------- crates/terminal/src/terminal.rs | 2 +- crates/terminal_view/src/terminal_element.rs | 4 ++-- crates/terminal_view/src/terminal_view.rs | 12 ++++++------ crates/theme/src/theme.rs | 4 +--- crates/theme_testbench/src/theme_testbench.rs | 2 +- crates/vim/src/vim.rs | 4 ++-- crates/welcome/src/base_keymap_picker.rs | 2 +- crates/welcome/src/welcome.rs | 2 +- crates/workspace/src/dock.rs | 5 ++--- crates/workspace/src/item.rs | 4 ++-- crates/workspace/src/pane.rs | 2 +- crates/workspace/src/pane_group.rs | 3 +-- crates/workspace/src/workspace.rs | 4 ++-- crates/zed/src/languages/yaml.rs | 2 +- crates/zed/src/main.rs | 6 +++--- crates/zed/src/zed.rs | 16 +++++----------- 35 files changed, 84 insertions(+), 113 deletions(-) diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 300340a225e1cee0d4f3b3e1e2aac1938d820cb8..7353c2597f49a1ac54d89901ec9874b36afc8a90 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -83,12 +83,12 @@ pub fn init(http_client: Arc, server_url: String, cx: &mut AppCo let auto_updater = cx.add_model(|cx| { let updater = AutoUpdater::new(version, http_client, server_url); - let mut update_subscription = settings::get_setting::(None, cx) + let mut update_subscription = settings::get::(cx) .0 .then(|| updater.start_polling(cx)); cx.observe_global::(move |updater, cx| { - if settings::get_setting::(None, cx).0 { + if settings::get::(cx).0 { if update_subscription.is_none() { update_subscription = Some(updater.start_polling(cx)) } @@ -281,7 +281,7 @@ impl AutoUpdater { let release_channel = cx .has_global::() .then(|| cx.global::().display_name()); - let telemetry = settings::get_setting::(None, cx).metrics; + let telemetry = settings::get::(cx).metrics; (installation_id, release_channel, telemetry) }); diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 041126271d513eb0d5e81c84eea96e3856d621a6..8950b51a4588be68d91859773f6a48e3ce27d859 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -781,7 +781,7 @@ impl Client { self.telemetry().report_mixpanel_event( "read credentials from keychain", Default::default(), - *settings::get_setting::(None, cx), + *settings::get::(cx), ); }); } @@ -1075,8 +1075,7 @@ impl Client { let telemetry = self.telemetry.clone(); let http = self.http.clone(); - let telemetry_settings = - cx.read(|cx| *settings::get_setting::(None, cx)); + let telemetry_settings = cx.read(|cx| *settings::get::(cx)); executor.clone().spawn(async move { // Generate a pair of asymmetric encryption keys. The public key will be used by the diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 075c2956ec43ef922e57fa131104433c3b4ac8e7..b3bdc72c91cc8e9f5bb09ded6034f2d55928e46c 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -247,7 +247,7 @@ impl Telemetry { is_staff: bool, cx: &AppContext, ) { - if !settings::get_setting::(None, cx).metrics { + if !settings::get::(cx).metrics { return; } diff --git a/crates/collab_ui/src/sharing_status_indicator.rs b/crates/collab_ui/src/sharing_status_indicator.rs index 8394036f398ef8fd1bbfc0ce5e237d71af3631f9..3a1dde072f867b0791ae8fab90d6c6328857bb2a 100644 --- a/crates/collab_ui/src/sharing_status_indicator.rs +++ b/crates/collab_ui/src/sharing_status_indicator.rs @@ -16,7 +16,7 @@ pub fn init(cx: &mut AppContext) { if let Some(room) = call.read(cx).room() { if room.read(cx).is_screen_sharing() { if status_indicator.is_none() - && settings::get_setting::(None, cx).show_call_status_icon + && settings::get::(cx).show_call_status_icon { status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator)); } diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index bb3727585b22018c1fa121a8ca175f3108ba668e..de9104a6848d504eb78d28ab45da896d771dca29 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -318,7 +318,7 @@ impl Copilot { fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext) { let http = self.http.clone(); let node_runtime = self.node_runtime.clone(); - if all_language_settings(None, cx).copilot_enabled(None, None) { + if all_language_settings(cx).copilot_enabled(None, None) { if matches!(self.server, CopilotServer::Disabled) { let start_task = cx .spawn({ @@ -786,7 +786,6 @@ impl Copilot { let uri = registered_buffer.uri.clone(); let position = position.to_point_utf16(buffer); let settings = language_settings( - None, buffer.language_at(position).map(|l| l.name()).as_deref(), cx, ); diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index 560506a52938b05ab1375ba527de7af5b3f1cd85..73cd8f6a1d8ae4cbec8fe89b53d912a25026e759 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -41,7 +41,7 @@ impl View for CopilotButton { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let all_language_settings = &all_language_settings(None, cx); + let all_language_settings = &all_language_settings(cx); if !all_language_settings.copilot.feature_enabled { return Empty::new().into_any(); } @@ -198,7 +198,7 @@ impl CopilotButton { if let Some(language) = self.language.clone() { let fs = fs.clone(); let language_enabled = - language_settings::language_settings(None, Some(language.as_ref()), cx) + language_settings::language_settings(Some(language.as_ref()), cx) .show_copilot_suggestions; menu_options.push(ContextMenuItem::handler( format!( @@ -210,7 +210,7 @@ impl CopilotButton { )); } - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); if let Some(path) = self.path.as_ref() { let path_enabled = settings.copilot_enabled_for_path(path); @@ -282,7 +282,7 @@ impl CopilotButton { let path = snapshot.file_at(suggestion_anchor).map(|file| file.path()); self.editor_enabled = Some( - all_language_settings(None, cx) + all_language_settings(cx) .copilot_enabled(language_name.as_deref(), path.map(|p| p.as_ref())), ); self.language = language_name; @@ -364,15 +364,14 @@ async fn configure_disabled_globs( } fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None); + let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(None, None); update_settings_file::(fs, cx, move |file| { file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) }); } fn toggle_copilot_for_language(language: Arc, fs: Arc, cx: &mut AppContext) { - let show_copilot_suggestions = - all_language_settings(None, cx).copilot_enabled(Some(&language), None); + let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(Some(&language), None); update_settings_file::(fs, cx, move |file| { file.languages .entry(language) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 458ca51ce35eecdda59eb706e60f1e846d2164c8..3385596f17fbc9e578ac3e9a10b246c7e07e62b0 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -679,7 +679,7 @@ impl Item for ProjectDiagnosticsEditor { fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let (message, highlights) = highlight_diagnostic_message(Vec::new(), &diagnostic.message); Arc::new(move |cx| { - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); let theme = &settings.theme.editor; let style = theme.diagnostic_header.clone(); let font_size = (style.text_scale_factor diff --git a/crates/editor/src/blink_manager.rs b/crates/editor/src/blink_manager.rs index 43d1ec6130365415e982003e8e23a62f61667487..24ea4774aa5deda8c45154d7b8bf7c4f30a4709c 100644 --- a/crates/editor/src/blink_manager.rs +++ b/crates/editor/src/blink_manager.rs @@ -64,7 +64,7 @@ impl BlinkManager { } fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext) { - if settings::get_setting::(None, cx).cursor_blink { + if settings::get::(cx).cursor_blink { if epoch == self.blink_epoch && self.enabled && !self.blinking_paused { self.visible = !self.visible; cx.notify(); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 4c36be682b0d5aa64ce7b5555497a1d9f5e66a5b..366e47ddc640fa6c9ee205debdc7b837b205ebe8 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -277,7 +277,7 @@ impl DisplayMap { .as_singleton() .and_then(|buffer| buffer.read(cx).language()) .map(|language| language.name()); - language_settings(None, language_name.as_deref(), cx).tab_size + language_settings(language_name.as_deref(), cx).tab_size } #[cfg(test)] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 87c3b7afdca8c3e78e3d8869856d2402f9674dfa..9eed6c655ea3c493c090ba5f072bfca595bf348a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1246,7 +1246,7 @@ impl Editor { ) -> Self { let editor_view_id = cx.view_id(); let display_map = cx.add_model(|cx| { - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx); DisplayMap::new( buffer.clone(), @@ -1427,7 +1427,7 @@ impl Editor { fn style(&self, cx: &AppContext) -> EditorStyle { build_style( - settings::get_setting::(None, cx), + settings::get::(cx), self.get_field_editor_theme.as_deref(), self.override_text_style.as_deref(), cx, @@ -2385,7 +2385,7 @@ impl Editor { } fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext) { - if !settings::get_setting::(None, cx).show_completions_on_input { + if !settings::get::(cx).show_completions_on_input { return; } @@ -3156,7 +3156,7 @@ impl Editor { let language_name = snapshot .language_at(location) .map(|language| language.name()); - let settings = all_language_settings(None, cx); + let settings = all_language_settings(cx); settings.copilot_enabled(language_name.as_deref(), path) } @@ -6902,7 +6902,7 @@ impl Editor { .map(|a| a.to_string()); let telemetry = project.read(cx).client().telemetry().clone(); - let telemetry_settings = *settings::get_setting::(None, cx); + let telemetry_settings = *settings::get::(cx); let event = ClickhouseEvent::Copilot { suggestion_id, @@ -6937,8 +6937,8 @@ impl Editor { .untyped_user_settings() .get("vim_mode") == Some(&serde_json::Value::Bool(true)); - let telemetry_settings = *settings::get_setting::(None, cx); - let copilot_enabled = all_language_settings(None, cx).copilot_enabled(None, None); + let telemetry_settings = *settings::get::(cx); + let copilot_enabled = all_language_settings(cx).copilot_enabled(None, None); let copilot_enabled_for_language = self .buffer .read(cx) @@ -7616,7 +7616,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend } Arc::new(move |cx: &mut BlockContext| { - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); let theme = &settings.theme.editor; let style = diagnostic_style(diagnostic.severity, is_valid, theme); let font_size = (style.text_scale_factor diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index dcce3c80f0788b8d809de0c4d06ad27a104eb43b..0a17fc8baf915fdfbf4663490b064649eeadabd8 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -550,7 +550,7 @@ impl EditorElement { let scroll_top = scroll_position.y() * line_height; let show_gutter = matches!( - settings::get_setting::(None, cx) + settings::get::(cx) .git .git_gutter .unwrap_or_default(), @@ -2060,15 +2060,14 @@ impl Element for EditorElement { )); } - let show_scrollbars = - match settings::get_setting::(None, cx).show_scrollbars { - ShowScrollbars::Auto => { - snapshot.has_scrollbar_info() || editor.scroll_manager.scrollbars_visible() - } - ShowScrollbars::System => editor.scroll_manager.scrollbars_visible(), - ShowScrollbars::Always => true, - ShowScrollbars::Never => false, - }; + let show_scrollbars = match settings::get::(cx).show_scrollbars { + ShowScrollbars::Auto => { + snapshot.has_scrollbar_info() || editor.scroll_manager.scrollbars_visible() + } + ShowScrollbars::System => editor.scroll_manager.scrollbars_visible(), + ShowScrollbars::Always => true, + ShowScrollbars::Never => false, + }; let include_root = editor .project diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 24f09f108204769dcd0c382c6b4c06c89b5e407c..9192dc75e13997dcd77f10da172ce7084b479a77 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -37,7 +37,7 @@ pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext) { /// The internal hover action dispatches between `show_hover` or `hide_hover` /// depending on whether a point to hover over is provided. pub fn hover_at(editor: &mut Editor, point: Option, cx: &mut ViewContext) { - if settings::get_setting::(None, cx).hover_popover_enabled { + if settings::get::(cx).hover_popover_enabled { if let Some(point) = point { show_hover(editor, point, false, cx); } else { diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 99df8ce796778c2e97e68887b361d83f048bc0ef..eb69e8e7c18b87bf87b7ce4608f7f865968be9eb 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1380,7 +1380,7 @@ impl MultiBuffer { cx: &'a AppContext, ) -> &'a LanguageSettings { let language = self.language_at(point, cx); - language_settings(None, language.map(|l| l.name()).as_deref(), cx) + language_settings(language.map(|l| l.name()).as_deref(), cx) } pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle)) { @@ -2782,7 +2782,7 @@ impl MultiBufferSnapshot { ) -> &'a LanguageSettings { self.point_to_buffer_offset(point) .map(|(buffer, offset)| buffer.settings_at(offset, cx)) - .unwrap_or_else(|| language_settings(None, None, cx)) + .unwrap_or_else(|| language_settings(None, cx)) } pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 34af4ed0e460255ca4c6040458c0bedeb4db31f1..74868e2f91ffc8ed0434336db7765b36504726fc 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -53,7 +53,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { } pub fn new_journal_entry(app_state: Arc, cx: &mut AppContext) { - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); let journal_dir = match journal_dir(settings.path.as_ref().unwrap()) { Some(journal_dir) => journal_dir, None => { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index fd1430b1e42454c1783f13755833a36cbd8aaf59..aee646091a407d327579e7a8497d588e17583ede 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1827,7 +1827,7 @@ impl BufferSnapshot { pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { let language_name = self.language_at(position).map(|language| language.name()); - let settings = language_settings(None, language_name.as_deref(), cx); + let settings = language_settings(language_name.as_deref(), cx); if settings.hard_tabs { IndentSize::tab() } else { @@ -2152,7 +2152,7 @@ impl BufferSnapshot { cx: &'a AppContext, ) -> &'a LanguageSettings { let language = self.language_at(position); - language_settings(None, language.map(|l| l.name()).as_deref(), cx) + language_settings(language.map(|l| l.name()).as_deref(), cx) } pub fn language_scope_at(&self, position: D) -> Option { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 4d33366a7184de3871144a24f7c4f313675fc4c4..cd932ce791fbd769ff249868c5f9e1988e6580f6 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -12,19 +12,12 @@ pub fn init(cx: &mut AppContext) { settings::register_setting::(cx); } -pub fn language_settings<'a>( - path: Option<&Path>, - language: Option<&str>, - cx: &'a AppContext, -) -> &'a LanguageSettings { - settings::get_setting::(path, cx).language(language) +pub fn language_settings<'a>(language: Option<&str>, cx: &'a AppContext) -> &'a LanguageSettings { + settings::get::(cx).language(language) } -pub fn all_language_settings<'a>( - path: Option<&Path>, - cx: &'a AppContext, -) -> &'a AllLanguageSettings { - settings::get_setting::(path, cx) +pub fn all_language_settings<'a>(cx: &'a AppContext) -> &'a AllLanguageSettings { + settings::get::(cx) } #[derive(Debug, Clone)] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a487b23716015e2751a75b66a04d0f675275222e..cdfe35f268561edfd5325baf2512703bcfe4b902 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -631,7 +631,7 @@ impl Project { } fn on_settings_changed(&mut self, cx: &mut ModelContext) { - let settings = all_language_settings(None, cx); + let settings = all_language_settings(cx); let mut language_servers_to_start = Vec::new(); for buffer in self.opened_buffers.values() { @@ -2208,7 +2208,7 @@ impl Project { language: Arc, cx: &mut ModelContext, ) { - if !language_settings(None, Some(&language.name()), cx).enable_language_server { + if !language_settings(Some(&language.name()), cx).enable_language_server { return; } @@ -2229,7 +2229,7 @@ impl Project { None => continue, }; - let lsp = settings::get_setting::(None, cx) + let lsp = settings::get::(cx) .lsp .get(&adapter.name.0); let override_options = lsp.map(|s| s.initialization_options.clone()).flatten(); @@ -3259,8 +3259,7 @@ impl Project { for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers { let settings = buffer.read_with(&cx, |buffer, cx| { let language_name = buffer.language().map(|language| language.name()); - language_settings(buffer_abs_path.as_deref(), language_name.as_deref(), cx) - .clone() + language_settings(language_name.as_deref(), cx).clone() }); let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save; diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 26f2215bea40b9ed2717d2bbe0de719dc693e7b8..7bd9ce8aecb011e287005803e5e186573b35899c 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -19,7 +19,7 @@ impl Project { "creating terminals as a guest is not supported yet" )); } else { - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); let terminal = TerminalBuilder::new( working_directory.clone(), diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index ed2741651249d88b9a18567f04848c33b45c0465..2f71d5b9afcd1bc4c97f474b7431c8c35dbc17ac 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -4,14 +4,7 @@ use assets::Assets; use fs::Fs; use futures::{channel::mpsc, StreamExt}; use gpui::{executor::Background, AppContext, AssetSource}; -use std::{ - borrow::Cow, - io::ErrorKind, - path::{Path, PathBuf}, - str, - sync::Arc, - time::Duration, -}; +use std::{borrow::Cow, io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration}; use util::{paths, ResultExt}; pub fn register_setting(cx: &mut AppContext) { @@ -20,8 +13,8 @@ pub fn register_setting(cx: &mut AppContext) { }); } -pub fn get_setting<'a, T: Setting>(path: Option<&Path>, cx: &'a AppContext) -> &'a T { - cx.global::().get(path) +pub fn get<'a, T: Setting>(cx: &'a AppContext) -> &'a T { + cx.global::().get(None) } pub fn default_settings() -> Cow<'static, str> { diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index f918d91b6d77c22ba98ecc6ac1151c7410533184..412e9fb1daf2174e071eca008a54c0b462a9af8a 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1149,7 +1149,7 @@ impl Terminal { } pub fn mouse_up(&mut self, e: &MouseUp, origin: Vector2F, cx: &mut ModelContext) { - let setting = settings::get_setting::(None, cx); + let setting = settings::get::(cx); let position = e.position.sub(origin); if self.mouse_mode(e.shift) { diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 8976a34210493d3892dc101205c4e120d249cd1c..6137f8eff68fe8c427183d848deff77d598e28e6 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -522,8 +522,8 @@ impl Element for TerminalElement { view: &mut TerminalView, cx: &mut LayoutContext, ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { - let settings = settings::get_setting::(None, cx); - let terminal_settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); + let terminal_settings = settings::get::(cx); //Setup layout information let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone. diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 944366e358a3ce13055121e25a2f1a0efee95368..0a7a69bf7392aa5dfc28e6175d8a51ae1c0590ee 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -103,7 +103,7 @@ impl TerminalView { _: &workspace::NewTerminal, cx: &mut ViewContext, ) { - let strategy = settings::get_setting::(None, cx); + let strategy = settings::get::(cx); let working_directory = get_working_directory(workspace, cx, strategy.working_directory.clone()); @@ -217,7 +217,7 @@ impl TerminalView { self.terminal.update(cx, |term, cx| { term.try_keystroke( &Keystroke::parse("ctrl-cmd-space").unwrap(), - settings::get_setting::(None, cx).option_as_meta, + settings::get::(cx).option_as_meta, ) }); } @@ -243,7 +243,7 @@ impl TerminalView { return true; } - match settings::get_setting::(None, cx).blinking { + match settings::get::(cx).blinking { //If the user requested to never blink, don't blink it. TerminalBlink::Off => true, //If the terminal is controlling it, check terminal mode @@ -336,7 +336,7 @@ impl TerminalView { self.terminal.update(cx, |term, cx| { term.try_keystroke( &keystroke, - settings::get_setting::(None, cx).option_as_meta, + settings::get::(cx).option_as_meta, ); }); } @@ -399,7 +399,7 @@ impl View for TerminalView { self.terminal.update(cx, |term, cx| { term.try_keystroke( &event.keystroke, - settings::get_setting::(None, cx).option_as_meta, + settings::get::(cx).option_as_meta, ) }) } @@ -601,7 +601,7 @@ impl Item for TerminalView { .flatten() .or_else(|| { cx.read(|cx| { - let strategy = settings::get_setting::(None, cx) + let strategy = settings::get::(cx) .working_directory .clone(); workspace diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index ae3bcb765d6bdb21da1d98d18175564fe54c7f80..aa8e37814f5204cf18d96bbd05bf0df32fa4e2cd 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -17,9 +17,7 @@ pub use theme_registry::ThemeRegistry; pub use theme_settings::ThemeSettings; pub fn current(cx: &AppContext) -> Arc { - settings::get_setting::(None, cx) - .theme - .clone() + settings::get::(cx).theme.clone() } pub fn init(source: impl AssetSource, cx: &mut AppContext) { diff --git a/crates/theme_testbench/src/theme_testbench.rs b/crates/theme_testbench/src/theme_testbench.rs index 52eca5f8ad3d26a1298778809d1cdd804c51e5e8..d0f50da07ccb8344e839e4acd5d7146687772737 100644 --- a/crates/theme_testbench/src/theme_testbench.rs +++ b/crates/theme_testbench/src/theme_testbench.rs @@ -219,7 +219,7 @@ impl ThemeTestbench { } fn render_label(text: String, style: &Style, cx: &mut ViewContext) -> Label { - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); let font_cache = cx.font_cache(); let family_id = settings.buffer_font_family; let font_size = settings::font_size_for_setting(settings.buffer_font_size, cx); diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 23a28b354437ee1316970ecbecd1cd44cf38f408..efb339eed7596c5f625a676fe1e4441a5e8e5313 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -95,11 +95,11 @@ pub fn init(cx: &mut AppContext) { filter.filtered_namespaces.insert("vim"); }); cx.update_default_global(|vim: &mut Vim, cx: &mut AppContext| { - vim.set_enabled(settings::get_setting::(None, cx).0, cx) + vim.set_enabled(settings::get::(cx).0, cx) }); cx.observe_global::(|cx| { cx.update_default_global(|vim: &mut Vim, cx: &mut AppContext| { - vim.set_enabled(settings::get_setting::(None, cx).0, cx) + vim.set_enabled(settings::get::(cx).0, cx) }); }) .detach(); diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index c9dfec9eb060aa1cf941e437896a3647e886c25d..e44b391d84b3f46d96e84c8e97589b6ecbde699e 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -40,7 +40,7 @@ pub struct BaseKeymapSelectorDelegate { impl BaseKeymapSelectorDelegate { fn new(fs: Arc, cx: &mut ViewContext) -> Self { - let base = settings::get_setting::(None, cx); + let base = settings::get::(cx); let selected_index = BaseKeymap::OPTIONS .iter() .position(|(_, value)| value == base) diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 29b013156dc4906964192f452b8bad5056b8f418..89b4e42cbbe551b43e0f9ead4f3f78460f98a733 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -64,7 +64,7 @@ impl View for WelcomePage { let theme = theme::current(cx); let width = theme.welcome.page_width; - let telemetry_settings = *settings::get_setting::(None, cx); + let telemetry_settings = *settings::get::(cx); enum Metrics {} enum Diagnostics {} diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index beac87f99c49cd7ed4a4c5adde68b393089f6022..d1ec80de9598c8caa08c866c7b751ff987749419 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -172,9 +172,8 @@ impl Dock { background_actions: BackgroundActions, cx: &mut ViewContext, ) -> Self { - let position = DockPosition::Hidden( - settings::get_setting::(None, cx).default_dock_anchor, - ); + let position = + DockPosition::Hidden(settings::get::(cx).default_dock_anchor); let workspace = cx.weak_handle(); let pane = cx.add_view(|cx| Pane::new(workspace, Some(position.anchor()), background_actions, cx)); diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index e8c10c6cd2ec228d11d8d7a0f3ec4953e275a7e3..16905849a9d5e923f3574b476cb949c4bc0312e1 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -450,7 +450,7 @@ impl ItemHandle for ViewHandle { } ItemEvent::Edit => { - let settings = settings::get_setting::(None, cx); + let settings = settings::get::(cx); let debounce_delay = settings.git.gutter_debounce; if let AutosaveSetting::AfterDelay { milliseconds } = @@ -501,7 +501,7 @@ impl ItemHandle for ViewHandle { cx.observe_focus(self, move |workspace, item, focused, cx| { if !focused - && settings::get_setting::(None, cx).autosave + && settings::get::(cx).autosave == AutosaveSetting::OnFocusChange { Pane::autosave_item(&item, workspace.project.clone(), cx) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 3717cfc200e224f35e023d0adc0f4f6b6ad974cf..200f83700b6c899d793d551f01dcf830cb9f6235 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1024,7 +1024,7 @@ impl Pane { } else if is_dirty && (can_save || is_singleton) { let will_autosave = cx.read(|cx| { matches!( - settings::get_setting::(None, cx).autosave, + settings::get::(cx).autosave, AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange ) && Self::can_autosave_item(&*item, cx) }); diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index edc7c617d0098ef16d85014f2b38a14f7afd83ef..6e7580a103f3bf7a27217cdb0b246b789caffc44 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -379,8 +379,7 @@ impl PaneAxis { .with_children(self.members.iter().enumerate().map(|(ix, member)| { let mut flex = 1.0; if member.contains(active_pane) { - flex = settings::get_setting::(None, cx) - .active_pane_magnification; + flex = settings::get::(cx).active_pane_magnification; } let mut member = member.render( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a94bf3ce1c9e4bb02ffa9c79781078e8f27a8be8..282daf8d8f8a83f36e1f5e86e952c181d02e30ac 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2359,7 +2359,7 @@ impl Workspace { item.workspace_deactivated(cx); } if matches!( - settings::get_setting::(None, cx).autosave, + settings::get::(cx).autosave, AutosaveSetting::OnWindowChange | AutosaveSetting::OnFocusChange ) { for item in pane.items() { @@ -3140,7 +3140,7 @@ pub fn join_remote_project( } pub fn restart(_: &Restart, cx: &mut AppContext) { - let should_confirm = settings::get_setting::(None, cx).confirm_quit; + let should_confirm = settings::get::(cx).confirm_quit; cx.spawn(|mut cx| async move { let mut workspaces = cx .window_ids() diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index b62168775c3145afaa317b464dfdcf5a777b5916..bd5f2b4c021e7d8239d76ea29d8ef88ddcf8015b 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -107,7 +107,7 @@ impl LspAdapter for YamlLspAdapter { "keyOrdering": false }, "[yaml]": { - "editor.tabSize": language_settings(None, Some("YAML"), cx).tab_size, + "editor.tabSize": language_settings(Some("YAML"), cx).tab_size, } })) .boxed(), diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 18abb2e2c517fa62a5be68b4ceb2a1ce5b70e561..299e77efce6758d8851374632cb737bbdc010f6c 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -180,7 +180,7 @@ fn main() { client.telemetry().report_mixpanel_event( "start app", Default::default(), - *settings::get_setting::(None, cx), + *settings::get::(cx), ); let app_state = Arc::new(AppState { @@ -441,7 +441,7 @@ fn init_panic_hook(app_version: String) { } fn upload_previous_panics(http: Arc, cx: &mut AppContext) { - let telemetry_settings = *settings::get_setting::(None, cx); + let telemetry_settings = *settings::get::(cx); cx.background() .spawn({ @@ -800,7 +800,7 @@ pub fn dock_default_item_factory( workspace: &mut Workspace, cx: &mut ViewContext, ) -> Option> { - let strategy = settings::get_setting::(None, cx) + let strategy = settings::get::(cx) .working_directory .clone(); let working_directory = get_working_directory(workspace, cx, strategy); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 5b271762ed2d24e3969851af3316224b41aaa15b..8f241639ea8edbeb614955de8c8223a23de4ec93 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -368,7 +368,7 @@ pub fn build_window_options( } fn quit(_: &Quit, cx: &mut gpui::AppContext) { - let should_confirm = settings::get_setting::(None, cx).confirm_quit; + let should_confirm = settings::get::(cx).confirm_quit; cx.spawn(|mut cx| async move { let mut workspaces = cx .window_ids() @@ -484,7 +484,7 @@ pub fn load_default_keymap(cx: &mut AppContext) { KeymapFileContent::load_asset(path, cx).unwrap(); } - if let Some(asset_path) = settings::get_setting::(None, cx).asset_path() { + if let Some(asset_path) = settings::get::(cx).asset_path() { KeymapFileContent::load_asset(asset_path, cx).unwrap(); } } @@ -503,12 +503,11 @@ pub fn handle_keymap_file_changes( keymap_content.clone().add_to_cx(cx).log_err(); }); - let mut old_base_keymap = - cx.read(|cx| *settings::get_setting::(None, cx)); + let mut old_base_keymap = cx.read(|cx| *settings::get::(cx)); drop(settings_subscription); settings_subscription = Some(cx.update(|cx| { cx.observe_global::(move |cx| { - let new_base_keymap = *settings::get_setting::(None, cx); + let new_base_keymap = *settings::get::(cx); if new_base_keymap != old_base_keymap { old_base_keymap = new_base_keymap.clone(); @@ -2047,12 +2046,7 @@ mod tests { for theme_name in themes.list(false).map(|meta| meta.name) { let theme = themes.get(&theme_name).unwrap(); assert_eq!(theme.meta.name, theme_name); - if theme.meta.name - == settings::get_setting::(None, cx) - .theme - .meta - .name - { + if theme.meta.name == settings::get::(cx).theme.meta.name { has_default_theme = true; } } From 258723566f721cfad53e7e260f74cd4d94a0c1c7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 15:06:11 -0700 Subject: [PATCH 21/25] Rename settings::register_setting -> settings::register --- crates/auto_update/src/auto_update.rs | 2 +- crates/client/src/client.rs | 2 +- crates/editor/src/editor.rs | 2 +- crates/journal/src/journal.rs | 2 +- crates/language/src/language_settings.rs | 2 +- crates/project/src/project.rs | 2 +- crates/settings/src/settings_file.rs | 2 +- crates/terminal/src/terminal.rs | 2 +- crates/theme/src/theme.rs | 2 +- crates/vim/src/vim.rs | 2 +- crates/welcome/src/welcome.rs | 2 +- crates/workspace/src/workspace.rs | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 7353c2597f49a1ac54d89901ec9874b36afc8a90..822886b58018e1cb8cef694a2cd2b8274a20c949 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -77,7 +77,7 @@ impl Setting for AutoUpdateSetting { } pub fn init(http_client: Arc, server_url: String, cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) { let auto_updater = cx.add_model(|cx| { diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 8950b51a4588be68d91859773f6a48e3ce27d859..311d9a2b8872cd3d63ad861cc986849d93d1e240 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -71,7 +71,7 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5); actions!(client, [SignIn, SignOut]); pub fn init_settings(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); } pub fn init(client: &Arc, cx: &mut AppContext) { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9eed6c655ea3c493c090ba5f072bfca595bf348a..ee4f19b0a10de949391eb9bbdfa9982b0adf3ace 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -290,7 +290,7 @@ pub enum Direction { } pub fn init_settings(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); } pub fn init(cx: &mut AppContext) { diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 74868e2f91ffc8ed0434336db7765b36504726fc..99fe997dc5eeb4c428c8331958a1b8ef048eb357 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -47,7 +47,7 @@ impl settings::Setting for JournalSettings { } pub fn init(app_state: Arc, cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); cx.add_global_action(move |_: &NewJournalEntry, cx| new_journal_entry(app_state.clone(), cx)); } diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index cd932ce791fbd769ff249868c5f9e1988e6580f6..b47982819a5a59dbc79791c04ec4918c7f99a00a 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use std::{num::NonZeroU32, path::Path, sync::Arc}; pub fn init(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); } pub fn language_settings<'a>(language: Option<&str>, cx: &'a AppContext) -> &'a LanguageSettings { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index cdfe35f268561edfd5325baf2512703bcfe4b902..13809622f9e3191cf4b12eaaa0347c3f98e0f050 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -390,7 +390,7 @@ impl FormatTrigger { impl Project { pub fn init_settings(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); } pub fn init(client: &Arc, cx: &mut AppContext) { diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 2f71d5b9afcd1bc4c97f474b7431c8c35dbc17ac..79e0d1eb317ccdb81fbb7726e115b4b19b31935e 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -7,7 +7,7 @@ use gpui::{executor::Background, AppContext, AssetSource}; use std::{borrow::Cow, io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration}; use util::{paths, ResultExt}; -pub fn register_setting(cx: &mut AppContext) { +pub fn register(cx: &mut AppContext) { cx.update_global::(|store, cx| { store.register_setting::(cx); }); diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 412e9fb1daf2174e071eca008a54c0b462a9af8a..728f2e348f2e97f581ddc6b58cf8179b8c2fe86a 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -116,7 +116,7 @@ impl EventListener for ZedListener { } pub fn init(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); } #[derive(Deserialize)] diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index aa8e37814f5204cf18d96bbd05bf0df32fa4e2cd..d014179a0790bae4200bda90b2d34d5285d14e77 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -22,7 +22,7 @@ pub fn current(cx: &AppContext) -> Arc { pub fn init(source: impl AssetSource, cx: &mut AppContext) { cx.set_global(ThemeRegistry::new(source, cx.font_cache().clone())); - settings::register_setting::(cx); + settings::register::(cx); } #[derive(Deserialize, Default)] diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index efb339eed7596c5f625a676fe1e4441a5e8e5313..d10ec5e824b7a720b8136a4b3d694d3c42d0b62e 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -42,7 +42,7 @@ actions!(vim, [Tab, Enter]); impl_actions!(vim, [Number, SwitchMode, PushOperator]); pub fn init(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); editor_events::init(cx); normal::init(cx); diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 89b4e42cbbe551b43e0f9ead4f3f78460f98a733..000c2fd8d97fbddadeda4ca7cb884f697a083e0b 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -20,7 +20,7 @@ pub use base_keymap_setting::BaseKeymap; pub const FIRST_OPEN: &str = "first_open"; pub fn init(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 282daf8d8f8a83f36e1f5e86e952c181d02e30ac..8ca6358f9aeafc8397bcc3c24aa8a157503db638 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -185,7 +185,7 @@ pub type WorkspaceId = i64; impl_actions!(workspace, [ActivatePane]); pub fn init_settings(cx: &mut AppContext) { - settings::register_setting::(cx); + settings::register::(cx); } pub fn init(app_state: Arc, cx: &mut AppContext) { From 42eca3048f58222ad00ddc9fc9ff354b622f6908 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 15:56:32 -0700 Subject: [PATCH 22/25] Move font size adjustment code to the theme crate --- crates/diagnostics/src/diagnostics.rs | 4 +- crates/editor/src/editor.rs | 6 +-- crates/settings/src/font_size.rs | 19 -------- crates/settings/src/settings.rs | 2 - crates/terminal/src/terminal.rs | 9 +++- crates/terminal_view/src/terminal_element.rs | 10 ++-- crates/theme/src/theme.rs | 4 +- crates/theme/src/theme_settings.rs | 48 ++++++++++++++++++- crates/theme_testbench/src/theme_testbench.rs | 2 +- crates/zed/src/zed.rs | 21 ++------ 10 files changed, 69 insertions(+), 56 deletions(-) delete mode 100644 crates/settings/src/font_size.rs diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 3385596f17fbc9e578ac3e9a10b246c7e07e62b0..a202a6082c0522901a349c9537e49a2fe7e2c6b8 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -682,9 +682,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let settings = settings::get::(cx); let theme = &settings.theme.editor; let style = theme.diagnostic_header.clone(); - let font_size = (style.text_scale_factor - * settings::font_size_for_setting(settings.buffer_font_size, cx)) - .round(); + let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round(); let icon_width = cx.em_width * style.icon_width_factor; let icon = if diagnostic.severity == DiagnosticSeverity::ERROR { Svg::new("icons/circle_x_mark_12.svg") diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ee4f19b0a10de949391eb9bbdfa9982b0adf3ace..0fb7a10a166b7dd31b7b5014b65d1454d33db479 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7446,7 +7446,7 @@ fn build_style( let font_id = font_cache .select_font(font_family_id, &font_properties) .unwrap(); - let font_size = settings::font_size_for_setting(settings.buffer_font_size, cx); + let font_size = settings.buffer_font_size(cx); EditorStyle { text: TextStyle { color: settings.theme.editor.text_color, @@ -7619,9 +7619,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend let settings = settings::get::(cx); let theme = &settings.theme.editor; let style = diagnostic_style(diagnostic.severity, is_valid, theme); - let font_size = (style.text_scale_factor - * settings::font_size_for_setting(settings.buffer_font_size, cx)) - .round(); + let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round(); Flex::column() .with_children(highlighted_lines.iter().map(|(line, highlights)| { Label::new( diff --git a/crates/settings/src/font_size.rs b/crates/settings/src/font_size.rs deleted file mode 100644 index 79abfeb5504ebf8e7262cbe76b4c28f869ead9a2..0000000000000000000000000000000000000000 --- a/crates/settings/src/font_size.rs +++ /dev/null @@ -1,19 +0,0 @@ -use gpui::AppContext; - -#[derive(Default)] -pub struct FontSizeDelta(pub f32); - -pub fn adjust_font_size_delta(cx: &mut AppContext, f: fn(&mut f32, cx: &mut AppContext)) { - cx.update_default_global::(|size, cx| { - f(&mut size.0, cx); - }); - cx.refresh_windows(); -} - -pub fn font_size_for_setting(size: f32, cx: &AppContext) -> f32 { - if cx.has_global::() { - size + cx.global::().0 - } else { - size - } -} diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index e4784d61ac3b9791e91ab68762c0c5b2fadbff27..4ab504a96f28b7f72f3d40eda48049b38e0db306 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,11 +1,9 @@ -mod font_size; mod keymap_file; mod settings_file; mod settings_store; use std::{borrow::Cow, str}; -pub use font_size::{adjust_font_size_delta, font_size_for_setting}; use gpui::AssetSource; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; pub use settings_file::*; diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 728f2e348f2e97f581ddc6b58cf8179b8c2fe86a..98d85d00e1c51922347f690a6700a64819b51c87 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -123,7 +123,7 @@ pub fn init(cx: &mut AppContext) { pub struct TerminalSettings { pub shell: Shell, pub working_directory: WorkingDirectory, - pub font_size: Option, + font_size: Option, pub font_family: Option, pub line_height: TerminalLineHeight, pub font_features: Option, @@ -149,6 +149,13 @@ pub struct TerminalSettingsContent { pub copy_on_select: Option, } +impl TerminalSettings { + pub fn font_size(&self, cx: &AppContext) -> Option { + self.font_size + .map(|size| theme::adjusted_font_size(size, cx)) + } +} + impl settings::Setting for TerminalSettings { const KEY: Option<&'static str> = Some("terminal"); diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 6137f8eff68fe8c427183d848deff77d598e28e6..18c85db980ba14aab27ad91254ee022db57b1a0f 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -16,7 +16,6 @@ use gpui::{ use itertools::Itertools; use language::CursorShape; use ordered_float::OrderedFloat; -use settings::font_size_for_setting; use terminal::{ alacritty_terminal::{ ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor}, @@ -531,12 +530,9 @@ impl Element for TerminalElement { let tooltip_style = settings.theme.tooltip.clone(); let font_cache = cx.font_cache(); - let font_size = font_size_for_setting( - terminal_settings - .font_size - .unwrap_or(settings.buffer_font_size), - cx, - ); + let font_size = terminal_settings + .font_size(cx) + .unwrap_or(settings.buffer_font_size(cx)); let font_family_name = terminal_settings .font_family .as_ref() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d014179a0790bae4200bda90b2d34d5285d14e77..7a94f45656a891544dcabce3bf5e2ab2d7336f7a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -13,8 +13,8 @@ use serde_json::Value; use std::{collections::HashMap, sync::Arc}; use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle}; -pub use theme_registry::ThemeRegistry; -pub use theme_settings::ThemeSettings; +pub use theme_registry::*; +pub use theme_settings::*; pub fn current(cx: &AppContext) -> Arc { settings::get::(cx).theme.clone() diff --git a/crates/theme/src/theme_settings.rs b/crates/theme/src/theme_settings.rs index 85958e576d856a89bde8354642272cab5e18ad00..9c8f1e3553b936326e0860995539edb7dbd23fdc 100644 --- a/crates/theme/src/theme_settings.rs +++ b/crates/theme/src/theme_settings.rs @@ -12,15 +12,19 @@ use settings::SettingsJsonSchemaParams; use std::sync::Arc; use util::ResultExt as _; +const MIN_FONT_SIZE: f32 = 6.0; + #[derive(Clone)] pub struct ThemeSettings { pub buffer_font_family_name: String, pub buffer_font_features: fonts::Features, pub buffer_font_family: FamilyId, - pub buffer_font_size: f32, + buffer_font_size: f32, pub theme: Arc, } +pub struct AdjustedBufferFontSize(pub f32); + #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct ThemeSettingsContent { #[serde(default)] @@ -33,6 +37,48 @@ pub struct ThemeSettingsContent { pub theme: Option, } +impl ThemeSettings { + pub fn buffer_font_size(&self, cx: &AppContext) -> f32 { + if cx.has_global::() { + cx.global::().0 + } else { + self.buffer_font_size + } + .max(MIN_FONT_SIZE) + } +} + +pub fn adjusted_font_size(size: f32, cx: &AppContext) -> f32 { + if cx.has_global::() { + let buffer_font_size = settings::get::(cx).buffer_font_size; + let delta = cx.global::().0 - buffer_font_size; + size + delta + } else { + size + } + .max(MIN_FONT_SIZE) +} + +pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut f32)) { + if !cx.has_global::() { + let buffer_font_size = settings::get::(cx).buffer_font_size; + cx.set_global(AdjustedBufferFontSize(buffer_font_size)); + } + + cx.update_global::(|delta, cx| { + f(&mut delta.0); + delta.0 = delta + .0 + .max(MIN_FONT_SIZE - settings::get::(cx).buffer_font_size); + }); + cx.refresh_windows(); +} + +pub fn reset_font_size(cx: &mut AppContext) { + cx.remove_global::(); + cx.refresh_windows(); +} + impl settings::Setting for ThemeSettings { const KEY: Option<&'static str> = None; diff --git a/crates/theme_testbench/src/theme_testbench.rs b/crates/theme_testbench/src/theme_testbench.rs index d0f50da07ccb8344e839e4acd5d7146687772737..258249b59932c8c087d5e27a85cb75dea7dbf2f2 100644 --- a/crates/theme_testbench/src/theme_testbench.rs +++ b/crates/theme_testbench/src/theme_testbench.rs @@ -222,7 +222,7 @@ impl ThemeTestbench { let settings = settings::get::(cx); let font_cache = cx.font_cache(); let family_id = settings.buffer_font_family; - let font_size = settings::font_size_for_setting(settings.buffer_font_size, cx); + let font_size = settings.buffer_font_size(cx); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 8f241639ea8edbeb614955de8c8223a23de4ec93..376ecfc06bd43aeb2753345aa2301b8bd7d60cf7 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -29,12 +29,9 @@ use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; -use settings::{ - adjust_font_size_delta, KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH, -}; +use settings::{KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH}; use std::{borrow::Cow, str, sync::Arc}; use terminal_view::terminal_button::TerminalButton; -use theme::ThemeSettings; use util::{channel::ReleaseChannel, paths, ResultExt}; use uuid::Uuid; use welcome::BaseKeymap; @@ -76,8 +73,6 @@ actions!( ] ); -const MIN_FONT_SIZE: f32 = 6.0; - pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { cx.add_action(about); cx.add_global_action(|_: &Hide, cx: &mut gpui::AppContext| { @@ -121,18 +116,12 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { cx.add_global_action(quit); cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| { - adjust_font_size_delta(cx, |size, _| *size += 1.0) + theme::adjust_font_size(cx, |size| *size += 1.0) }); cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| { - adjust_font_size_delta(cx, |size, cx| { - if cx.global::().buffer_font_size + *size > MIN_FONT_SIZE { - *size -= 1.0; - } - }) - }); - cx.add_global_action(move |_: &ResetBufferFontSize, cx| { - adjust_font_size_delta(cx, |size, _| *size = 0.0) + theme::adjust_font_size(cx, |size| *size -= 1.0) }); + cx.add_global_action(move |_: &ResetBufferFontSize, cx| theme::reset_font_size(cx)); cx.add_global_action(move |_: &install_cli::Install, cx| { cx.spawn(|cx| async move { install_cli::install_cli(&cx) @@ -639,7 +628,7 @@ mod tests { collections::HashSet, path::{Path, PathBuf}, }; - use theme::ThemeRegistry; + use theme::{ThemeRegistry, ThemeSettings}; use util::http::FakeHttpClient; use workspace::{ item::{Item, ItemHandle}, From 667f476f7f2254fa94cbaca391c610897b839228 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 16:02:04 -0700 Subject: [PATCH 23/25] Remove unused watched_json file --- crates/settings/src/watched_json.rs | 126 ---------------------------- 1 file changed, 126 deletions(-) delete mode 100644 crates/settings/src/watched_json.rs diff --git a/crates/settings/src/watched_json.rs b/crates/settings/src/watched_json.rs deleted file mode 100644 index 16be82fa354e45cb9b6039b07b4fe37408b731a6..0000000000000000000000000000000000000000 --- a/crates/settings/src/watched_json.rs +++ /dev/null @@ -1,126 +0,0 @@ -use fs::Fs; -use futures::StreamExt; -use gpui::{executor, AppContext}; -use postage::sink::Sink as _; -use postage::{prelude::Stream, watch}; -use serde::Deserialize; - -use std::{path::Path, sync::Arc, time::Duration}; -use theme::ThemeRegistry; -use util::ResultExt; - -use crate::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; - -#[derive(Clone)] -pub struct WatchedJsonFile(pub watch::Receiver); - -impl WatchedJsonFile -where - T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, -{ - pub async fn new( - fs: Arc, - executor: &executor::Background, - path: impl Into>, - ) -> Self { - let path = path.into(); - let settings = Self::load(fs.clone(), &path).await.unwrap_or_default(); - let mut events = fs.watch(&path, Duration::from_millis(500)).await; - let (mut tx, rx) = watch::channel_with(settings); - executor - .spawn(async move { - while events.next().await.is_some() { - if let Some(settings) = Self::load(fs.clone(), &path).await { - if tx.send(settings).await.is_err() { - break; - } - } - } - }) - .detach(); - Self(rx) - } - - ///Loads the given watched JSON file. In the special case that the file is - ///empty (ignoring whitespace) or is not a file, this will return T::default() - async fn load(fs: Arc, path: &Path) -> Option { - if !fs.is_file(path).await { - return Some(T::default()); - } - - fs.load(path).await.log_err().and_then(|data| { - if data.trim().is_empty() { - Some(T::default()) - } else { - parse_json_with_comments(&data).log_err() - } - }) - } - - pub fn current(&self) -> T { - self.0.borrow().clone() - } -} - -pub fn watch_files( - defaults: Settings, - settings_file: WatchedJsonFile, - theme_registry: Arc, - keymap_file: WatchedJsonFile, - cx: &mut AppContext, -) { - watch_settings_file(defaults, settings_file, theme_registry, cx); - watch_keymap_file(keymap_file, cx); -} - -pub(crate) fn watch_settings_file( - defaults: Settings, - mut file: WatchedJsonFile, - theme_registry: Arc, - cx: &mut AppContext, -) { - settings_updated(&defaults, file.0.borrow().clone(), &theme_registry, cx); - cx.spawn(|mut cx| async move { - while let Some(content) = file.0.recv().await { - cx.update(|cx| settings_updated(&defaults, content, &theme_registry, cx)); - } - }) - .detach(); -} - -fn keymap_updated(content: KeymapFileContent, cx: &mut AppContext) { - cx.clear_bindings(); - KeymapFileContent::load_defaults(cx); - content.add_to_cx(cx).log_err(); -} - -fn settings_updated( - defaults: &Settings, - content: SettingsFileContent, - theme_registry: &Arc, - cx: &mut AppContext, -) { - let mut settings = defaults.clone(); - settings.set_user_settings(content, theme_registry, cx.font_cache()); - cx.set_global(settings); - cx.refresh_windows(); -} - -fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut AppContext) { - cx.spawn(|mut cx| async move { - let mut settings_subscription = None; - while let Some(content) = file.0.recv().await { - cx.update(|cx| { - let old_base_keymap = cx.global::().base_keymap; - keymap_updated(content.clone(), cx); - settings_subscription = Some(cx.observe_global::(move |cx| { - let settings = cx.global::(); - if settings.base_keymap != old_base_keymap { - keymap_updated(content.clone(), cx); - } - })); - }); - } - }) - .detach(); -} From 823e15d85a5dbc1605c83d5bf5958364c0477f21 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 16:02:21 -0700 Subject: [PATCH 24/25] Refresh windows when settings file changes --- crates/settings/src/settings.rs | 3 +-- crates/settings/src/settings_file.rs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 4ab504a96f28b7f72f3d40eda48049b38e0db306..840797c6ad644cc851c298712e7db080a0650d78 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -2,12 +2,11 @@ mod keymap_file; mod settings_file; mod settings_store; -use std::{borrow::Cow, str}; - use gpui::AssetSource; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; pub use settings_file::*; pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; +use std::{borrow::Cow, str}; pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 79e0d1eb317ccdb81fbb7726e115b4b19b31935e..30848713d9ce4bff41ca06ecc965e26594efe887 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -89,6 +89,7 @@ pub fn handle_settings_file_changes( .set_user_settings(&user_settings_content, cx) .log_err(); }); + cx.refresh_windows(); }); } }) From a56793c2140ed864569d141f54a5c7e5d17d245c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 May 2023 16:14:05 -0700 Subject: [PATCH 25/25] Clear buffer font size adjustments when buffer font size setting changes --- crates/theme/src/theme.rs | 11 +++++++++++ crates/theme/src/theme_settings.rs | 8 +++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 7a94f45656a891544dcabce3bf5e2ab2d7336f7a..eb404cdaad6ad5a8d5e7fa5c572e0ad709e6c4e4 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -10,6 +10,7 @@ use gpui::{ }; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; +use settings::SettingsStore; use std::{collections::HashMap, sync::Arc}; use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle}; @@ -23,6 +24,16 @@ pub fn current(cx: &AppContext) -> Arc { pub fn init(source: impl AssetSource, cx: &mut AppContext) { cx.set_global(ThemeRegistry::new(source, cx.font_cache().clone())); settings::register::(cx); + + let mut prev_buffer_font_size = settings::get::(cx).buffer_font_size; + cx.observe_global::(move |cx| { + let buffer_font_size = settings::get::(cx).buffer_font_size; + if buffer_font_size != prev_buffer_font_size { + prev_buffer_font_size = buffer_font_size; + reset_font_size(cx); + } + }) + .detach(); } #[derive(Deserialize, Default)] diff --git a/crates/theme/src/theme_settings.rs b/crates/theme/src/theme_settings.rs index 9c8f1e3553b936326e0860995539edb7dbd23fdc..f86d3fd8dd38477993f490d1824aab6ee232d646 100644 --- a/crates/theme/src/theme_settings.rs +++ b/crates/theme/src/theme_settings.rs @@ -19,7 +19,7 @@ pub struct ThemeSettings { pub buffer_font_family_name: String, pub buffer_font_features: fonts::Features, pub buffer_font_family: FamilyId, - buffer_font_size: f32, + pub(crate) buffer_font_size: f32, pub theme: Arc, } @@ -75,8 +75,10 @@ pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut f32)) { } pub fn reset_font_size(cx: &mut AppContext) { - cx.remove_global::(); - cx.refresh_windows(); + if cx.has_global::() { + cx.remove_global::(); + cx.refresh_windows(); + } } impl settings::Setting for ThemeSettings {