brought up to speed with main

This commit is contained in:
KCaverly 2023-07-11 14:50:48 -04:00
commit dd0dbdc5bd
48 changed files with 1178 additions and 459 deletions

View file

@ -16,8 +16,4 @@ jobs:
Restart your Zed or head to https://zed.dev/releases/stable/latest to grab it.
```md
# Changelog
${{ github.event.release.body }}
```

212
Cargo.lock generated
View file

@ -318,9 +318,9 @@ dependencies = [
[[package]]
name = "async-channel"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833"
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
dependencies = [
"concurrent-queue",
"event-listener",
@ -374,7 +374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
dependencies = [
"async-lock",
"autocfg 1.1.0",
"autocfg",
"blocking",
"futures-lite",
]
@ -401,7 +401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
dependencies = [
"async-lock",
"autocfg 1.1.0",
"autocfg",
"cfg-if 1.0.0",
"concurrent-queue",
"futures-lite",
@ -430,7 +430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f"
dependencies = [
"async-io",
"autocfg 1.1.0",
"autocfg",
"blocking",
"futures-lite",
]
@ -452,7 +452,7 @@ checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9"
dependencies = [
"async-io",
"async-lock",
"autocfg 1.1.0",
"autocfg",
"blocking",
"cfg-if 1.0.0",
"event-listener",
@ -481,7 +481,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -529,7 +529,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -572,7 +572,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -658,15 +658,6 @@ dependencies = [
"workspace",
]
[[package]]
name = "autocfg"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -839,7 +830,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn 2.0.23",
"syn 2.0.25",
"which",
]
@ -1001,7 +992,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
dependencies = [
"memchr",
"regex-automata 0.3.1",
"regex-automata 0.3.2",
"serde",
]
@ -1295,7 +1286,7 @@ dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -1362,7 +1353,7 @@ dependencies = [
"sum_tree",
"tempfile",
"thiserror",
"time 0.3.22",
"time 0.3.23",
"tiny_http",
"url",
"util",
@ -1464,7 +1455,7 @@ dependencies = [
"sha-1 0.9.8",
"sqlx",
"theme",
"time 0.3.22",
"time 0.3.23",
"tokio",
"tokio-tungstenite",
"toml",
@ -1506,6 +1497,7 @@ dependencies = [
"theme",
"theme_selector",
"util",
"vcs_menu",
"workspace",
"zed-actions",
]
@ -1905,7 +1897,7 @@ version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"cfg-if 1.0.0",
"crossbeam-utils",
"memoffset 0.9.0",
@ -1994,12 +1986,12 @@ dependencies = [
[[package]]
name = "dashmap"
version = "5.4.0"
version = "5.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d"
dependencies = [
"cfg-if 1.0.0",
"hashbrown 0.12.3",
"hashbrown 0.14.0",
"lock_api",
"once_cell",
"parking_lot_core 0.9.8",
@ -2322,9 +2314,9 @@ dependencies = [
[[package]]
name = "equivalent"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "erased-serde"
@ -2648,7 +2640,7 @@ dependencies = [
"smol",
"sum_tree",
"tempfile",
"time 0.3.22",
"time 0.3.23",
"util",
]
@ -2798,7 +2790,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -3038,7 +3030,7 @@ dependencies = [
"smol",
"sqlez",
"sum_tree",
"time 0.3.22",
"time 0.3.23",
"tiny-skia",
"usvg",
"util",
@ -3412,7 +3404,7 @@ version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"hashbrown 0.12.3",
"serde",
]
@ -3537,7 +3529,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi 0.3.2",
"rustix 0.38.3",
"rustix 0.38.4",
"windows-sys",
]
@ -4023,7 +4015,7 @@ version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"scopeguard",
]
@ -4127,7 +4119,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"rawpointer",
]
@ -4190,7 +4182,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@ -4199,7 +4191,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@ -4251,7 +4243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@ -4513,18 +4505,17 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480"
checksum = "f9bc3e36fd683e004fd59c64a425e0e991616f5a8b617c3b9a933a93c168facc"
dependencies = [
"autocfg 0.1.8",
"byteorder",
"lazy_static",
"libm",
@ -4553,7 +4544,7 @@ version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-traits",
]
@ -4563,7 +4554,7 @@ version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-integer",
"num-traits",
]
@ -4574,7 +4565,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-integer",
"num-traits",
]
@ -4585,7 +4576,7 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"libm",
]
@ -4742,7 +4733,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -5030,7 +5021,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -5059,16 +5050,16 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "plist"
version = "1.4.3"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590"
checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06"
dependencies = [
"base64 0.21.2",
"indexmap 1.9.3",
"line-wrap",
"quick-xml",
"serde",
"time 0.3.22",
"time 0.3.23",
]
[[package]]
@ -5127,7 +5118,7 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"bitflags 1.3.2",
"cfg-if 1.0.0",
"concurrent-queue",
@ -5183,7 +5174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387"
dependencies = [
"proc-macro2",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -5231,9 +5222,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.63"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
dependencies = [
"unicode-ident",
]
@ -5502,9 +5493,9 @@ dependencies = [
[[package]]
name = "quick-xml"
version = "0.28.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1"
checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51"
dependencies = [
"memchr",
]
@ -5742,8 +5733,8 @@ checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
dependencies = [
"aho-corasick 1.0.2",
"memchr",
"regex-automata 0.3.1",
"regex-syntax 0.7.3",
"regex-automata 0.3.2",
"regex-syntax 0.7.4",
]
[[package]]
@ -5757,13 +5748,13 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9aaecc05d5c4b5f7da074b9a0d1a0867e71fd36e7fc0482d8bcfe8e8fc56290"
checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf"
dependencies = [
"aho-corasick 1.0.2",
"memchr",
"regex-syntax 0.7.3",
"regex-syntax 0.7.4",
]
[[package]]
@ -5774,9 +5765,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846"
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
[[package]]
name = "region"
@ -6054,7 +6045,7 @@ dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.23",
"syn 2.0.25",
"walkdir",
]
@ -6140,9 +6131,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.3"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
dependencies = [
"bitflags 2.3.3",
"errno 0.3.1",
@ -6346,7 +6337,7 @@ dependencies = [
"serde_json",
"sqlx",
"thiserror",
"time 0.3.22",
"time 0.3.23",
"tracing",
"url",
"uuid 1.4.0",
@ -6374,7 +6365,7 @@ dependencies = [
"rust_decimal",
"sea-query-derive",
"serde_json",
"time 0.3.22",
"time 0.3.23",
"uuid 1.4.0",
]
@ -6389,7 +6380,7 @@ dependencies = [
"sea-query",
"serde_json",
"sqlx",
"time 0.3.22",
"time 0.3.23",
"uuid 1.4.0",
]
@ -6511,22 +6502,22 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99"
[[package]]
name = "serde"
version = "1.0.167"
version = "1.0.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7daf513456463b42aa1d94cff7e0c24d682b429f020b9afa4f5ba5c40a22b237"
checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.167"
version = "1.0.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b69b106b68bc8054f0e974e70d19984040f8a5cf9215ca82626ea4853f82c4b9"
checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -6581,7 +6572,7 @@ checksum = "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -6807,7 +6798,7 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@ -7000,7 +6991,7 @@ dependencies = [
"sqlx-rt",
"stringprep",
"thiserror",
"time 0.3.22",
"time 0.3.23",
"tokio-stream",
"url",
"uuid 1.4.0",
@ -7249,9 +7240,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.23"
version = "2.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
dependencies = [
"proc-macro2",
"quote",
@ -7319,9 +7310,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target-lexicon"
version = "0.12.8"
version = "0.12.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac"
checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0"
[[package]]
name = "tempdir"
@ -7339,7 +7330,7 @@ version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"cfg-if 1.0.0",
"fastrand",
"redox_syscall 0.3.5",
@ -7505,7 +7496,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -7578,9 +7569,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.22"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd"
checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"
dependencies = [
"itoa 1.0.8",
"serde",
@ -7596,9 +7587,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
[[package]]
name = "time-macros"
version = "0.2.9"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"
dependencies = [
"time-core",
]
@ -7651,7 +7642,7 @@ version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"backtrace",
"bytes 1.4.0",
"libc",
@ -7694,7 +7685,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -7899,7 +7890,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]
@ -8483,6 +8474,19 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vcs_menu"
version = "0.1.0"
dependencies = [
"anyhow",
"fuzzy",
"gpui",
"picker",
"theme",
"util",
"workspace",
]
[[package]]
name = "vector_store"
version = "0.1.0"
@ -8678,7 +8682,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
"wasm-bindgen-shared",
]
@ -8712,7 +8716,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -8725,9 +8729,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "wasm-encoder"
version = "0.29.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"
checksum = "b2f8e9778e04cbf44f58acc301372577375a666b966c50b03ef46144f80436a8"
dependencies = [
"leb128",
]
@ -8949,9 +8953,9 @@ dependencies = [
[[package]]
name = "wast"
version = "60.0.0"
version = "61.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd06cc744b536e30387e72a48fdd492105b9c938bb4f415c39c616a7a0a697ad"
checksum = "dc6b347851b52fd500657d301155c79e8c67595501d179cef87b6f04ebd25ac4"
dependencies = [
"leb128",
"memchr",
@ -8961,11 +8965,11 @@ dependencies = [
[[package]]
name = "wat"
version = "1.0.66"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5abe520f0ab205366e9ac7d3e6b2fc71de44e32a2b58f2ec871b6b575bdcea3b"
checksum = "459e764d27c3ab7beba1ebd617cc025c7e76dea6e7c5ce3189989a970aea3491"
dependencies = [
"wast 60.0.0",
"wast 61.0.0",
]
[[package]]
@ -9295,9 +9299,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.8"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9482fe6ceabdf32f3966bfdd350ba69256a97c30253dc616fe0005af24f164e"
checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529"
dependencies = [
"memchr",
]
@ -9601,7 +9605,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.25",
]
[[package]]

View file

@ -65,6 +65,7 @@ members = [
"crates/util",
"crates/vector_store",
"crates/vim",
"crates/vcs_menu",
"crates/workspace",
"crates/welcome",
"crates/xtask",

View file

@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.5 1.5H13.5M13.5 1.5V5.5M13.5 1.5C12.1332 2.86683 10.3668 4.63317 9 6" stroke="white" stroke-linecap="round"/>
<path d="M1.5 9.5V13.5M1.5 13.5L6 9M1.5 13.5H5.5" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 315 B

View file

@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 6L9 6M9 6L9 2M9 6C10.3668 4.63316 12.1332 2.86683 13.5 1.5" stroke="white" stroke-linecap="round"/>
<path d="M6 13L6 9M6 9L1.5 13.5M6 9L2 9" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 297 B

View file

@ -39,6 +39,7 @@
"cmd-shift-n": "workspace::NewWindow",
"cmd-o": "workspace::Open",
"alt-cmd-o": "projects::OpenRecent",
"alt-cmd-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",
"ctrl-`": "terminal_panel::ToggleFocus",
"shift-escape": "workspace::ToggleZoom"

View file

@ -2,6 +2,7 @@
{
"bindings": {
"cmd-shift-o": "projects::OpenRecent",
"cmd-shift-b": "branches::OpenRecent",
"cmd-alt-tab": "project_panel::ToggleFocus"
}
},

View file

@ -35,8 +35,11 @@
"l": "vim::Right",
"right": "vim::Right",
"$": "vim::EndOfLine",
"^": "vim::FirstNonWhitespace",
"shift-g": "vim::EndOfDocument",
"w": "vim::NextWordStart",
"{": "vim::StartOfParagraph",
"}": "vim::EndOfParagraph",
"shift-w": [
"vim::NextWordStart",
{
@ -92,7 +95,10 @@
],
"ctrl-o": "pane::GoBack",
"ctrl-]": "editor::GoToDefinition",
"escape": "editor::Cancel",
"escape": [
"vim::SwitchMode",
"Normal"
],
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
"1": [
"vim::Number",
@ -165,7 +171,6 @@
"shift-a": "vim::InsertEndOfLine",
"x": "vim::DeleteRight",
"shift-x": "vim::DeleteLeft",
"^": "vim::FirstNonWhitespace",
"o": "vim::InsertLineBelow",
"shift-o": "vim::InsertLineAbove",
"~": "vim::ChangeCase",
@ -305,6 +310,10 @@
"vim::PushOperator",
"Replace"
],
"ctrl-c": [
"vim::SwitchMode",
"Normal"
],
"> >": "editor::Indent",
"< <": "editor::Outdent"
}
@ -321,7 +330,10 @@
"bindings": {
"tab": "vim::Tab",
"enter": "vim::Enter",
"escape": "editor::Cancel"
"escape": [
"vim::SwitchMode",
"Normal"
]
}
}
]

View file

@ -2061,6 +2061,8 @@ impl ConversationEditor {
let remaining_tokens = self.conversation.read(cx).remaining_tokens()?;
let remaining_tokens_style = if remaining_tokens <= 0 {
&style.no_remaining_tokens
} else if remaining_tokens <= 500 {
&style.low_remaining_tokens
} else {
&style.remaining_tokens
};

View file

@ -4,7 +4,7 @@ pub mod room;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use client::{proto, Client, TypedEnvelope, User, UserStore};
use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore};
use collections::HashSet;
use futures::{future::Shared, FutureExt};
use postage::watch;
@ -198,6 +198,7 @@ impl ActiveCall {
let result = invite.await;
this.update(&mut cx, |this, cx| {
this.pending_invites.remove(&called_user_id);
this.report_call_event("invite", cx);
cx.notify();
});
result
@ -243,21 +244,26 @@ impl ActiveCall {
};
let join = Room::join(&call, self.client.clone(), self.user_store.clone(), cx);
cx.spawn(|this, mut cx| async move {
let room = join.await?;
this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx))
.await?;
this.update(&mut cx, |this, cx| {
this.report_call_event("accept incoming", cx)
});
Ok(())
})
}
pub fn decline_incoming(&mut self) -> Result<()> {
pub fn decline_incoming(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
let call = self
.incoming_call
.0
.borrow_mut()
.take()
.ok_or_else(|| anyhow!("no incoming call"))?;
self.report_call_event_for_room("decline incoming", call.room_id, cx);
self.client.send(proto::DeclineCall {
room_id: call.room_id,
})?;
@ -266,6 +272,7 @@ impl ActiveCall {
pub fn hang_up(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
cx.notify();
self.report_call_event("hang up", cx);
if let Some((room, _)) = self.room.take() {
room.update(cx, |room, cx| room.leave(cx))
} else {
@ -273,12 +280,28 @@ impl ActiveCall {
}
}
pub fn toggle_screen_sharing(&self, cx: &mut AppContext) {
if let Some(room) = self.room().cloned() {
let toggle_screen_sharing = room.update(cx, |room, cx| {
if room.is_screen_sharing() {
self.report_call_event("disable screen share", cx);
Task::ready(room.unshare_screen(cx))
} else {
self.report_call_event("enable screen share", cx);
room.share_screen(cx)
}
});
toggle_screen_sharing.detach_and_log_err(cx);
}
}
pub fn share_project(
&mut self,
project: ModelHandle<Project>,
cx: &mut ModelContext<Self>,
) -> Task<Result<u64>> {
if let Some((room, _)) = self.room.as_ref() {
self.report_call_event("share project", cx);
room.update(cx, |room, cx| room.share_project(project, cx))
} else {
Task::ready(Err(anyhow!("no active call")))
@ -291,6 +314,7 @@ impl ActiveCall {
cx: &mut ModelContext<Self>,
) -> Result<()> {
if let Some((room, _)) = self.room.as_ref() {
self.report_call_event("unshare project", cx);
room.update(cx, |room, cx| room.unshare_project(project, cx))
} else {
Err(anyhow!("no active call"))
@ -352,4 +376,19 @@ impl ActiveCall {
pub fn pending_invites(&self) -> &HashSet<u64> {
&self.pending_invites
}
fn report_call_event(&self, operation: &'static str, cx: &AppContext) {
if let Some(room) = self.room() {
self.report_call_event_for_room(operation, room.read(cx).id(), cx)
}
}
fn report_call_event_for_room(&self, operation: &'static str, room_id: u64, cx: &AppContext) {
let telemetry = self.client.telemetry();
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
let event = ClickhouseEvent::Call { operation, room_id };
telemetry.report_clickhouse_event(event, telemetry_settings);
}
}

View file

@ -201,6 +201,7 @@ impl Bundle {
self.zed_version_string()
);
}
Self::LocalPath { executable, .. } => {
let executable_parent = executable
.parent()

View file

@ -70,6 +70,10 @@ pub enum ClickhouseEvent {
suggestion_accepted: bool,
file_extension: Option<String>,
},
Call {
operation: &'static str,
room_id: u64,
},
}
#[cfg(debug_assertions)]

View file

@ -157,7 +157,7 @@ async fn test_basic_calls(
// User C receives the call, but declines it.
let call_c = incoming_call_c.next().await.unwrap().unwrap();
assert_eq!(call_c.calling_user.github_login, "user_b");
active_call_c.update(cx_c, |call, _| call.decline_incoming().unwrap());
active_call_c.update(cx_c, |call, cx| call.decline_incoming(cx).unwrap());
assert!(incoming_call_c.next().await.unwrap().is_none());
deterministic.run_until_parked();
@ -1080,7 +1080,7 @@ async fn test_calls_on_multiple_connections(
// User B declines the call on one of the two connections, causing both connections
// to stop ringing.
active_call_b2.update(cx_b2, |call, _| call.decline_incoming().unwrap());
active_call_b2.update(cx_b2, |call, cx| call.decline_incoming(cx).unwrap());
deterministic.run_until_parked();
assert!(incoming_call_b1.next().await.unwrap().is_none());
assert!(incoming_call_b2.next().await.unwrap().is_none());
@ -5945,7 +5945,7 @@ async fn test_contacts(
[("user_b".to_string(), "online", "busy")]
);
active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap());
active_call_b.update(cx_b, |call, cx| call.decline_incoming(cx).unwrap());
deterministic.run_until_parked();
assert_eq!(
contacts(&client_a, cx_a),

View file

@ -365,7 +365,7 @@ async fn apply_client_operation(
}
log::info!("{}: declining incoming call", client.username);
active_call.update(cx, |call, _| call.decline_incoming())?;
active_call.update(cx, |call, cx| call.decline_incoming(cx))?;
}
ClientOperation::LeaveCall => {

View file

@ -39,6 +39,7 @@ recent_projects = {path = "../recent_projects"}
settings = { path = "../settings" }
theme = { path = "../theme" }
theme_selector = { path = "../theme_selector" }
vcs_menu = { path = "../vcs_menu" }
util = { path = "../util" }
workspace = { path = "../workspace" }
zed-actions = {path = "../zed-actions"}

View file

@ -1,8 +1,5 @@
use crate::{
branch_list::{build_branch_list, BranchList},
contact_notification::ContactNotification,
contacts_popover,
face_pile::FacePile,
contact_notification::ContactNotification, contacts_popover, face_pile::FacePile,
toggle_deafen, toggle_mute, toggle_screen_sharing, LeaveCall, ToggleDeafen, ToggleMute,
ToggleScreenSharing,
};
@ -27,6 +24,7 @@ use recent_projects::{build_recent_projects, RecentProjects};
use std::{ops::Range, sync::Arc};
use theme::{AvatarStyle, Theme};
use util::ResultExt;
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
use workspace::{FollowNextCollaborator, Workspace, WORKSPACE_DB};
const MAX_PROJECT_NAME_LENGTH: usize = 40;
@ -37,7 +35,6 @@ actions!(
[
ToggleContactsMenu,
ToggleUserMenu,
ToggleVcsMenu,
ToggleProjectMenu,
SwitchBranch,
ShareProject,
@ -229,15 +226,23 @@ impl CollabTitlebarItem {
let mut ret = Flex::row().with_child(
Stack::new()
.with_child(
MouseEventHandler::<ToggleProjectMenu, Self>::new(0, cx, |mouse_state, _| {
MouseEventHandler::<ToggleProjectMenu, Self>::new(0, cx, |mouse_state, cx| {
let style = project_style
.in_state(self.project_popover.is_some())
.style_for(mouse_state);
enum RecentProjectsTooltip {}
Label::new(name, style.text.clone())
.contained()
.with_style(style.container)
.aligned()
.left()
.with_tooltip::<RecentProjectsTooltip>(
0,
"Recent projects".into(),
Some(Box::new(recent_projects::OpenRecent)),
theme.tooltip.clone(),
cx,
)
.into_any_named("title-project-name")
})
.with_cursor_style(CursorStyle::PointingHand)
@ -264,7 +269,8 @@ impl CollabTitlebarItem {
MouseEventHandler::<ToggleVcsMenu, Self>::new(
0,
cx,
|mouse_state, _| {
|mouse_state, cx| {
enum BranchPopoverTooltip {}
let style = git_style
.in_state(self.branch_popover.is_some())
.style_for(mouse_state);
@ -274,6 +280,13 @@ impl CollabTitlebarItem {
.with_margin_right(item_spacing)
.aligned()
.left()
.with_tooltip::<BranchPopoverTooltip>(
0,
"Recent branches".into(),
Some(Box::new(ToggleVcsMenu)),
theme.tooltip.clone(),
cx,
)
.into_any_named("title-project-branch")
},
)

View file

@ -1,4 +1,3 @@
mod branch_list;
mod collab_titlebar_item;
mod contact_finder;
mod contact_list;
@ -12,7 +11,7 @@ mod sharing_status_indicator;
use call::{ActiveCall, Room};
pub use collab_titlebar_item::{CollabTitlebarItem, ToggleContactsMenu};
use gpui::{actions, AppContext, Task};
use gpui::{actions, AppContext};
use std::sync::Arc;
use util::ResultExt;
use workspace::AppState;
@ -29,7 +28,7 @@ actions!(
);
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
branch_list::init(cx);
vcs_menu::init(cx);
collab_titlebar_item::init(cx);
contact_list::init(cx);
contact_finder::init(cx);
@ -45,16 +44,9 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
}
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
let toggle_screen_sharing = room.update(cx, |room, cx| {
if room.is_screen_sharing() {
Task::ready(room.unshare_screen(cx))
} else {
room.share_screen(cx)
}
});
toggle_screen_sharing.detach_and_log_err(cx);
}
ActiveCall::global(cx).update(cx, |call, cx| {
call.toggle_screen_sharing(cx);
});
}
pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {

View file

@ -99,8 +99,8 @@ impl IncomingCallNotification {
})
.detach_and_log_err(cx);
} else {
active_call.update(cx, |active_call, _| {
active_call.decline_incoming().log_err();
active_call.update(cx, |active_call, cx| {
active_call.decline_incoming(cx).log_err();
});
}
}

View file

@ -41,8 +41,7 @@ const FALLBACK_DB_NAME: &'static str = "FALLBACK_MEMORY_DB";
const DB_FILE_NAME: &'static str = "db.sqlite";
lazy_static::lazy_static! {
// !!!!!!! CHANGE BACK TO DEFAULT FALSE BEFORE SHIPPING
static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty());
pub static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty());
pub static ref BACKUP_DB_PATH: RwLock<Option<PathBuf>> = RwLock::new(None);
pub static ref ALL_FILE_DB_FAILED: AtomicBool = AtomicBool::new(false);
}

View file

@ -5123,7 +5123,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
selection.collapse_to(
movement::start_of_paragraph(map, selection.head()),
movement::start_of_paragraph(map, selection.head(), 1),
SelectionGoal::None,
)
});
@ -5143,7 +5143,7 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
selection.collapse_to(
movement::end_of_paragraph(map, selection.head()),
movement::end_of_paragraph(map, selection.head(), 1),
SelectionGoal::None,
)
});
@ -5162,7 +5162,10 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, _| {
(movement::start_of_paragraph(map, head), SelectionGoal::None)
(
movement::start_of_paragraph(map, head, 1),
SelectionGoal::None,
)
});
})
}
@ -5179,7 +5182,10 @@ impl Editor {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_heads_with(|map, head, _| {
(movement::end_of_paragraph(map, head), SelectionGoal::None)
(
movement::end_of_paragraph(map, head, 1),
SelectionGoal::None,
)
});
})
}
@ -7216,6 +7222,47 @@ impl Editor {
}
results
}
pub fn background_highlights_in_range_for<T: 'static>(
&self,
search_range: Range<Anchor>,
display_snapshot: &DisplaySnapshot,
theme: &Theme,
) -> Vec<(Range<DisplayPoint>, Color)> {
let mut results = Vec::new();
let buffer = &display_snapshot.buffer_snapshot;
let Some((color_fetcher, ranges)) = self.background_highlights
.get(&TypeId::of::<T>()) else {
return vec![];
};
let color = color_fetcher(theme);
let start_ix = match ranges.binary_search_by(|probe| {
let cmp = probe.end.cmp(&search_range.start, buffer);
if cmp.is_gt() {
Ordering::Greater
} else {
Ordering::Less
}
}) {
Ok(i) | Err(i) => i,
};
for range in &ranges[start_ix..] {
if range.start.cmp(&search_range.end, buffer).is_ge() {
break;
}
let start = range
.start
.to_point(buffer)
.to_display_point(display_snapshot);
let end = range
.end
.to_point(buffer)
.to_display_point(display_snapshot);
results.push((start..end, color))
}
results
}
pub fn highlight_text<T: 'static>(
&mut self,
@ -7518,7 +7565,7 @@ impl Editor {
fn report_editor_event(
&self,
name: &'static str,
operation: &'static str,
file_extension: Option<String>,
cx: &AppContext,
) {
@ -7555,7 +7602,7 @@ impl Editor {
let event = ClickhouseEvent::Editor {
file_extension,
vim_mode,
operation: name,
operation,
copilot_enabled,
copilot_enabled_for_language,
};

View file

@ -22,7 +22,10 @@ use language::{
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point,
};
use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings};
use project::FakeFs;
use std::sync::atomic;
use std::sync::atomic::AtomicUsize;
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
use unindent::Unindent;
use util::{
@ -1796,7 +1799,7 @@ async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
"});
}
// Ensure that comment continuations can be disabled.
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.extend_comment_on_newline = Some(false);
});
let mut cx = EditorTestContext::new(cx).await;
@ -4546,7 +4549,7 @@ 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 overridden tabsize is sent to language server
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.languages.insert(
"Rust".into(),
LanguageSettingsContent {
@ -4660,7 +4663,7 @@ 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 overridden tabsize is sent to language server
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.languages.insert(
"Rust".into(),
LanguageSettingsContent {
@ -7084,6 +7087,142 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
});
}
#[gpui::test]
async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let language_name: Arc<str> = "Rust".into();
let mut language = Language::new(
LanguageConfig {
name: Arc::clone(&language_name),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let server_restarts = Arc::new(AtomicUsize::new(0));
let closure_restarts = Arc::clone(&server_restarts);
let language_server_name = "test language server";
let mut fake_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
name: language_server_name,
initialization_options: Some(json!({
"testOptionValue": true
})),
initializer: Some(Box::new(move |fake_server| {
let task_restarts = Arc::clone(&closure_restarts);
fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
task_restarts.fetch_add(1, atomic::Ordering::Release);
futures::future::ready(Ok(()))
});
})),
..Default::default()
}))
.await;
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/a",
json!({
"main.rs": "fn main() { let a = 5; }",
"other.rs": "// Test file",
}),
)
.await;
let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let (_, _workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let _buffer = project
.update(cx, |project, cx| {
project.open_local_buffer("/a/main.rs", cx)
})
.await
.unwrap();
let _fake_server = fake_servers.next().await.unwrap();
update_test_language_settings(cx, |language_settings| {
language_settings.languages.insert(
Arc::clone(&language_name),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
..Default::default()
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
0,
"Should not restart LSP server on an unrelated change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
"Some other server name".into(),
LspSettings {
initialization_options: Some(json!({
"some other init value": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
0,
"Should not restart LSP server on an unrelated LSP settings change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: Some(json!({
"anotherInitValue": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
1,
"Should restart LSP server on a related LSP settings change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: Some(json!({
"anotherInitValue": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
1,
"Should not restart LSP server on a related LSP settings change that is the same"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: None,
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
2,
"Should restart LSP server on another related LSP settings change"
);
}
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
let point = DisplayPoint::new(row as u32, column as u32);
point..point
@ -7203,7 +7342,7 @@ fn handle_copilot_completion_request(
});
}
pub(crate) fn update_test_settings(
pub(crate) fn update_test_language_settings(
cx: &mut TestAppContext,
f: impl Fn(&mut AllLanguageSettingsContent),
) {
@ -7214,6 +7353,17 @@ pub(crate) fn update_test_settings(
});
}
pub(crate) fn update_test_project_settings(
cx: &mut TestAppContext,
f: impl Fn(&mut ProjectSettings),
) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _, _>(|store, cx| {
store.update_user_settings::<ProjectSettings>(cx, f);
});
});
}
pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
cx.foreground().forbid_parking();
@ -7227,5 +7377,5 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC
crate::init(cx);
});
update_test_settings(cx, f);
update_test_language_settings(cx, f);
}

View file

@ -1086,11 +1086,13 @@ impl EditorElement {
})
}
};
for (row, _) in &editor.background_highlights_in_range(
start_anchor..end_anchor,
&layout.position_map.snapshot,
&theme,
) {
for (row, _) in &editor
.background_highlights_in_range_for::<crate::items::BufferSearchHighlights>(
start_anchor..end_anchor,
&layout.position_map.snapshot,
&theme,
)
{
let start_display = row.start;
let end_display = row.end;
@ -2149,6 +2151,9 @@ impl Element<Editor> for EditorElement {
ShowScrollbar::Auto => {
// Git
(is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
||
// Selections
(is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
// Scrollmanager
|| editor.scroll_manager.scrollbars_visible()
}
@ -2911,7 +2916,7 @@ mod tests {
use super::*;
use crate::{
display_map::{BlockDisposition, BlockProperties},
editor_tests::{init_test, update_test_settings},
editor_tests::{init_test, update_test_language_settings},
Editor, MultiBuffer,
};
use gpui::TestAppContext;
@ -3108,7 +3113,7 @@ mod tests {
let resize_step = 10.0;
let mut editor_width = 200.0;
while editor_width <= 1000.0 {
update_test_settings(cx, |s| {
update_test_language_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);

View file

@ -847,7 +847,7 @@ mod tests {
use text::Point;
use workspace::Workspace;
use crate::editor_tests::update_test_settings;
use crate::editor_tests::update_test_language_settings;
use super::*;
@ -1476,7 +1476,7 @@ mod tests {
),
] {
edits_made += 1;
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@ -1520,7 +1520,7 @@ mod tests {
edits_made += 1;
let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: false,
show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@ -1577,7 +1577,7 @@ mod tests {
let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
edits_made += 1;
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@ -2269,7 +2269,7 @@ unedited (2nd) buffer should have the same hint");
crate::init(cx);
});
update_test_settings(cx, f);
update_test_language_settings(cx, f);
}
async fn prepare_test_objects(

View file

@ -883,7 +883,7 @@ impl ProjectItem for Editor {
}
}
enum BufferSearchHighlights {}
pub(crate) enum BufferSearchHighlights {}
impl SearchableItem for Editor {
type Match = Range<Anchor>;

View file

@ -193,7 +193,11 @@ pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPo
})
}
pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
pub fn start_of_paragraph(
map: &DisplaySnapshot,
display_point: DisplayPoint,
mut count: usize,
) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == 0 {
return map.max_point();
@ -203,7 +207,11 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
for row in (0..point.row + 1).rev() {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
}
count -= 1;
found_non_blank_line = false;
}
found_non_blank_line |= !blank;
@ -212,7 +220,11 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
DisplayPoint::zero()
}
pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
pub fn end_of_paragraph(
map: &DisplaySnapshot,
display_point: DisplayPoint,
mut count: usize,
) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == map.max_buffer_row() {
return DisplayPoint::zero();
@ -222,7 +234,11 @@ pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> D
for row in point.row..map.max_buffer_row() + 1 {
let blank = map.buffer_snapshot.is_line_blank(row);
if found_non_blank_line && blank {
return Point::new(row, 0).to_display_point(map);
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
}
count -= 1;
found_non_blank_line = false;
}
found_non_blank_line |= !blank;

View file

@ -210,6 +210,10 @@ impl<'a> EditorTestContext<'a> {
self.assert_selections(expected_selections, marked_text.to_string())
}
pub fn editor_state(&mut self) -> String {
generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
}
#[track_caller]
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
let expected_ranges = self.ranges(marked_text);
@ -248,14 +252,8 @@ impl<'a> EditorTestContext<'a> {
self.assert_selections(expected_selections, expected_marked_text)
}
#[track_caller]
fn assert_selections(
&mut self,
expected_selections: Vec<Range<usize>>,
expected_marked_text: String,
) {
let actual_selections = self
.editor
fn editor_selections(&self) -> Vec<Range<usize>> {
self.editor
.read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
.into_iter()
.map(|s| {
@ -265,12 +263,22 @@ impl<'a> EditorTestContext<'a> {
s.start..s.end
}
})
.collect::<Vec<_>>();
.collect::<Vec<_>>()
}
#[track_caller]
fn assert_selections(
&mut self,
expected_selections: Vec<Range<usize>>,
expected_marked_text: String,
) {
let actual_selections = self.editor_selections();
let actual_marked_text =
generate_marked_text(&self.buffer_text(), &actual_selections, true);
if expected_selections != actual_selections {
panic!(
indoc! {"
{}Editor has unexpected selections.
Expected selections:

View file

@ -90,7 +90,8 @@ pub struct LanguageServerName(pub Arc<str>);
/// once at startup, and caches the results.
pub struct CachedLspAdapter {
pub name: LanguageServerName,
pub initialization_options: Option<Value>,
initialization_options: Option<Value>,
initialization_overrides: Mutex<Option<Value>>,
pub disk_based_diagnostic_sources: Vec<String>,
pub disk_based_diagnostics_progress_token: Option<String>,
pub language_ids: HashMap<String, String>,
@ -109,6 +110,7 @@ impl CachedLspAdapter {
Arc::new(CachedLspAdapter {
name,
initialization_options,
initialization_overrides: Mutex::new(None),
disk_based_diagnostic_sources,
disk_based_diagnostics_progress_token,
language_ids,
@ -208,6 +210,30 @@ impl CachedLspAdapter {
) -> Option<CodeLabel> {
self.adapter.label_for_symbol(name, kind, language).await
}
pub fn update_initialization_overrides(&self, new: Option<&Value>) -> bool {
let mut current = self.initialization_overrides.lock();
if current.as_ref() != new {
*current = new.cloned();
true
} else {
false
}
}
pub fn initialization_options(&self) -> Option<Value> {
let initialization_options = self.initialization_options.as_ref();
let override_options = self.initialization_overrides.lock().clone();
match (initialization_options, override_options) {
(None, override_options) => override_options,
(initialization_options, None) => initialization_options.cloned(),
(Some(initialization_options), Some(override_options)) => {
let mut initialization_options = initialization_options.clone();
merge_json_value_into(override_options, &mut initialization_options);
Some(initialization_options)
}
}
}
}
pub trait LspAdapterDelegate: Send + Sync {
@ -428,6 +454,7 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D
#[cfg(any(test, feature = "test-support"))]
pub struct FakeLspAdapter {
pub name: &'static str,
pub initialization_options: Option<Value>,
pub capabilities: lsp::ServerCapabilities,
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
pub disk_based_diagnostics_progress_token: Option<String>,
@ -1681,6 +1708,7 @@ impl Default for FakeLspAdapter {
capabilities: lsp::LanguageServer::full_capabilities(),
initializer: None,
disk_based_diagnostics_progress_token: None,
initialization_options: None,
disk_based_diagnostics_sources: Vec::new(),
}
}
@ -1730,6 +1758,10 @@ impl LspAdapter for Arc<FakeLspAdapter> {
async fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
self.disk_based_diagnostics_progress_token.clone()
}
async fn initialization_options(&self) -> Option<Value> {
self.initialization_options.clone()
}
}
fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option<u32>)]) {

View file

@ -78,8 +78,8 @@ use std::{
use terminals::Terminals;
use text::Anchor;
use util::{
debug_panic, defer, http::HttpClient, merge_json_value_into,
paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _,
debug_panic, defer, http::HttpClient, paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt,
TryFutureExt as _,
};
pub use fs::*;
@ -801,7 +801,7 @@ impl Project {
.lsp
.get(&adapter.name.0)
.and_then(|s| s.initialization_options.as_ref());
if adapter.initialization_options.as_ref() != new_lsp_settings {
if adapter.update_initialization_overrides(new_lsp_settings) {
language_servers_to_restart.push((worktree, Arc::clone(language)));
}
}
@ -2546,20 +2546,13 @@ impl Project {
let project_settings = settings::get::<ProjectSettings>(cx);
let lsp = project_settings.lsp.get(&adapter.name.0);
let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
let mut initialization_options = adapter.initialization_options.clone();
match (&mut initialization_options, override_options) {
(Some(initialization_options), Some(override_options)) => {
merge_json_value_into(override_options, initialization_options);
}
(None, override_options) => initialization_options = override_options,
_ => {}
}
adapter.update_initialization_overrides(override_options.as_ref());
let server_id = pending_server.server_id;
let container_dir = pending_server.container_dir.clone();
let state = LanguageServerState::Starting({
let adapter = adapter.clone();
let initialization_options = adapter.initialization_options();
let server_name = adapter.name.0.clone();
let languages = self.languages.clone();
let language = language.clone();

View file

@ -675,6 +675,9 @@ impl ProjectSearchView {
if match_ranges.is_empty() {
self.active_match_index = None;
} else {
self.active_match_index = Some(0);
self.select_match(Direction::Next, cx);
self.update_match_index(cx);
let prev_search_id = mem::replace(&mut self.search_id, self.model.read(cx).search_id);
let is_new_search = self.search_id != prev_search_id;
self.results_editor.update(cx, |editor, cx| {

View file

@ -1030,6 +1030,7 @@ pub struct AssistantStyle {
pub system_sender: Interactive<ContainedText>,
pub model: Interactive<ContainedText>,
pub remaining_tokens: ContainedText,
pub low_remaining_tokens: ContainedText,
pub no_remaining_tokens: ContainedText,
pub error_icon: Icon,
pub api_key_editor: FieldEditor,

View file

@ -0,0 +1,16 @@
[package]
name = "vcs_menu"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fuzzy = {path = "../fuzzy"}
gpui = {path = "../gpui"}
picker = {path = "../picker"}
util = {path = "../util"}
theme = {path = "../theme"}
workspace = {path = "../workspace"}
anyhow.workspace = true

View file

@ -1,15 +1,17 @@
use anyhow::{anyhow, bail};
use anyhow::{anyhow, bail, Result};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle};
use gpui::{actions, elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle};
use picker::{Picker, PickerDelegate, PickerEvent};
use std::{ops::Not, sync::Arc};
use util::ResultExt;
use workspace::{Toast, Workspace};
actions!(branches, [OpenRecent]);
pub fn init(cx: &mut AppContext) {
Picker::<BranchListDelegate>::init(cx);
cx.add_async_action(toggle);
}
pub type BranchList = Picker<BranchListDelegate>;
pub fn build_branch_list(
@ -28,6 +30,34 @@ pub fn build_branch_list(
.with_theme(|theme| theme.picker.clone())
}
fn toggle(
_: &mut Workspace,
_: &OpenRecent,
cx: &mut ViewContext<Workspace>,
) -> Option<Task<Result<()>>> {
Some(cx.spawn(|workspace, mut cx| async move {
workspace.update(&mut cx, |workspace, cx| {
workspace.toggle_modal(cx, |_, cx| {
let workspace = cx.handle();
cx.add_view(|cx| {
Picker::new(
BranchListDelegate {
matches: vec![],
workspace,
selected_index: 0,
last_query: String::default(),
},
cx,
)
.with_theme(|theme| theme.picker.clone())
.with_max_size(800., 1200.)
})
});
})?;
Ok(())
}))
}
pub struct BranchListDelegate {
matches: Vec<StringMatch>,
workspace: ViewHandle<Workspace>,

View file

@ -31,6 +31,8 @@ pub enum Motion {
CurrentLine,
StartOfLine,
EndOfLine,
StartOfParagraph,
EndOfParagraph,
StartOfDocument,
EndOfDocument,
Matching,
@ -72,6 +74,8 @@ actions!(
StartOfLine,
EndOfLine,
CurrentLine,
StartOfParagraph,
EndOfParagraph,
StartOfDocument,
EndOfDocument,
Matching,
@ -92,6 +96,12 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(|_: &mut Workspace, _: &StartOfLine, cx: _| motion(Motion::StartOfLine, cx));
cx.add_action(|_: &mut Workspace, _: &EndOfLine, cx: _| motion(Motion::EndOfLine, cx));
cx.add_action(|_: &mut Workspace, _: &CurrentLine, cx: _| motion(Motion::CurrentLine, cx));
cx.add_action(|_: &mut Workspace, _: &StartOfParagraph, cx: _| {
motion(Motion::StartOfParagraph, cx)
});
cx.add_action(|_: &mut Workspace, _: &EndOfParagraph, cx: _| {
motion(Motion::EndOfParagraph, cx)
});
cx.add_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| {
motion(Motion::StartOfDocument, cx)
});
@ -142,7 +152,8 @@ impl Motion {
pub fn linewise(&self) -> bool {
use Motion::*;
match self {
Down | Up | StartOfDocument | EndOfDocument | CurrentLine | NextLineStart => true,
Down | Up | StartOfDocument | EndOfDocument | CurrentLine | NextLineStart
| StartOfParagraph | EndOfParagraph => true,
EndOfLine
| NextWordEnd { .. }
| Matching
@ -172,6 +183,8 @@ impl Motion {
| Backspace
| Right
| StartOfLine
| StartOfParagraph
| EndOfParagraph
| NextWordStart { .. }
| PreviousWordStart { .. }
| FirstNonWhitespace
@ -197,6 +210,8 @@ impl Motion {
| Backspace
| Right
| StartOfLine
| StartOfParagraph
| EndOfParagraph
| NextWordStart { .. }
| PreviousWordStart { .. }
| FirstNonWhitespace
@ -235,6 +250,14 @@ impl Motion {
FirstNonWhitespace => (first_non_whitespace(map, point), SelectionGoal::None),
StartOfLine => (start_of_line(map, point), SelectionGoal::None),
EndOfLine => (end_of_line(map, point), SelectionGoal::None),
StartOfParagraph => (
movement::start_of_paragraph(map, point, times),
SelectionGoal::None,
),
EndOfParagraph => (
map.clip_at_line_end(movement::end_of_paragraph(map, point, times)),
SelectionGoal::None,
),
CurrentLine => (end_of_line(map, point), SelectionGoal::None),
StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
EndOfDocument => (
@ -502,10 +525,13 @@ fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint
if line_end == point {
line_end = map.max_point().to_point(map);
}
line_end.column = line_end.column.saturating_sub(1);
let line_range = map.prev_line_boundary(point).0..line_end;
let ranges = map.buffer_snapshot.bracket_ranges(line_range.clone());
let visible_line_range =
line_range.start..Point::new(line_range.end.row, line_range.end.column.saturating_sub(1));
let ranges = map
.buffer_snapshot
.bracket_ranges(visible_line_range.clone());
if let Some(ranges) = ranges {
let line_range = line_range.start.to_offset(&map.buffer_snapshot)
..line_range.end.to_offset(&map.buffer_snapshot);
@ -590,3 +616,131 @@ fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) ->
let new_row = (point.row() + times as u32).min(map.max_buffer_row());
map.clip_point(DisplayPoint::new(new_row, 0), Bias::Left)
}
#[cfg(test)]
mod test {
use crate::test::NeovimBackedTestContext;
use indoc::indoc;
#[gpui::test]
async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
let initial_state = indoc! {r"ˇabc
def
paragraph
the second
third and
final"};
// goes down once
cx.set_shared_state(initial_state).await;
cx.simulate_shared_keystrokes(["}"]).await;
cx.assert_shared_state(indoc! {r"abc
def
ˇ
paragraph
the second
third and
final"})
.await;
// goes up once
cx.simulate_shared_keystrokes(["{"]).await;
cx.assert_shared_state(initial_state).await;
// goes down twice
cx.simulate_shared_keystrokes(["2", "}"]).await;
cx.assert_shared_state(indoc! {r"abc
def
paragraph
the second
ˇ
third and
final"})
.await;
// goes down over multiple blanks
cx.simulate_shared_keystrokes(["}"]).await;
cx.assert_shared_state(indoc! {r"abc
def
paragraph
the second
third and
finaˇl"})
.await;
// goes up twice
cx.simulate_shared_keystrokes(["2", "{"]).await;
cx.assert_shared_state(indoc! {r"abc
def
ˇ
paragraph
the second
third and
final"})
.await
}
#[gpui::test]
async fn test_matching(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state(indoc! {r"func ˇ(a string) {
do(something(with<Types>.and_arrays[0, 2]))
}"})
.await;
cx.simulate_shared_keystrokes(["%"]).await;
cx.assert_shared_state(indoc! {r"func (a stringˇ) {
do(something(with<Types>.and_arrays[0, 2]))
}"})
.await;
// test it works on the last character of the line
cx.set_shared_state(indoc! {r"func (a string) ˇ{
do(something(with<Types>.and_arrays[0, 2]))
}"})
.await;
cx.simulate_shared_keystrokes(["%"]).await;
cx.assert_shared_state(indoc! {r"func (a string) {
do(something(with<Types>.and_arrays[0, 2]))
ˇ}"})
.await;
// test it works on immediate nesting
cx.set_shared_state("ˇ{()}").await;
cx.simulate_shared_keystrokes(["%"]).await;
cx.assert_shared_state("{()ˇ}").await;
cx.simulate_shared_keystrokes(["%"]).await;
cx.assert_shared_state("ˇ{()}").await;
// test it works on immediate nesting inside braces
cx.set_shared_state("{\n ˇ{()}\n}").await;
cx.simulate_shared_keystrokes(["%"]).await;
cx.assert_shared_state("{\n {()ˇ}\n}").await;
// test it jumps to the next paren on a line
cx.set_shared_state("func ˇboop() {\n}").await;
cx.simulate_shared_keystrokes(["%"]).await;
cx.assert_shared_state("func boop(ˇ) {\n}").await;
}
}

View file

@ -1,29 +1,51 @@
use editor::scroll::autoscroll::Autoscroll;
use gpui::ViewContext;
use language::Point;
use language::{Bias, Point};
use workspace::Workspace;
use crate::{motion::Motion, normal::ChangeCase, Vim};
use crate::{normal::ChangeCase, state::Mode, Vim};
pub fn change_case(_: &mut Workspace, _: &ChangeCase, cx: &mut ViewContext<Workspace>) {
Vim::update(cx, |vim, cx| {
let count = vim.pop_number_operator(cx);
let count = vim.pop_number_operator(cx).unwrap_or(1) as u32;
vim.update_active_editor(cx, |editor, cx| {
editor.set_clip_at_line_ends(false, cx);
editor.transact(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.move_with(|map, selection| {
if selection.start == selection.end {
Motion::Right.expand_selection(map, selection, count, true);
let mut ranges = Vec::new();
let mut cursor_positions = Vec::new();
let snapshot = editor.buffer().read(cx).snapshot(cx);
for selection in editor.selections.all::<Point>(cx) {
match vim.state.mode {
Mode::Visual { line: true } => {
let start = Point::new(selection.start.row, 0);
let end =
Point::new(selection.end.row, snapshot.line_len(selection.end.row));
ranges.push(start..end);
cursor_positions.push(start..start);
}
Mode::Visual { line: false } => {
ranges.push(selection.start..selection.end);
cursor_positions.push(selection.start..selection.start);
}
Mode::Insert | Mode::Normal => {
let start = selection.start;
let mut end = start;
for _ in 0..count {
end = snapshot.clip_point(end + Point::new(0, 1), Bias::Right);
}
})
});
let selections = editor.selections.all::<Point>(cx);
for selection in selections.into_iter().rev() {
ranges.push(start..end);
if end.column == snapshot.line_len(end.row) {
end = snapshot.clip_point(end - Point::new(0, 1), Bias::Left);
}
cursor_positions.push(end..end)
}
}
}
editor.transact(cx, |editor, cx| {
for range in ranges.into_iter().rev() {
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor.buffer().update(cx, |buffer, cx| {
let range = selection.start..selection.end;
let text = snapshot
.text_for_range(selection.start..selection.end)
.text_for_range(range.start..range.end)
.flat_map(|s| s.chars())
.flat_map(|c| {
if c.is_lowercase() {
@ -37,28 +59,46 @@ pub fn change_case(_: &mut Workspace, _: &ChangeCase, cx: &mut ViewContext<Works
buffer.edit([(range, text)], None, cx)
})
}
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges(cursor_positions)
})
});
editor.set_clip_at_line_ends(true, cx);
});
vim.switch_mode(Mode::Normal, true, cx)
})
}
#[cfg(test)]
mod test {
use crate::{state::Mode, test::VimTestContext};
use indoc::indoc;
use crate::{state::Mode, test::NeovimBackedTestContext};
#[gpui::test]
async fn test_change_case(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(indoc! {"ˇabC\n"}, Mode::Normal);
cx.simulate_keystrokes(["~"]);
cx.assert_editor_state("AˇbC\n");
cx.simulate_keystrokes(["2", "~"]);
cx.assert_editor_state("ABcˇ\n");
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state("ˇabC\n").await;
cx.simulate_shared_keystrokes(["~"]).await;
cx.assert_shared_state("AˇbC\n").await;
cx.simulate_shared_keystrokes(["2", "~"]).await;
cx.assert_shared_state("ABˇc\n").await;
cx.set_state(indoc! {"a😀C«dÉ1*fˇ»\n"}, Mode::Normal);
cx.simulate_keystrokes(["~"]);
cx.assert_editor_state("a😀CDé1*Fˇ\n");
// works in visual mode
cx.set_shared_state("a😀C«dÉ1*fˇ»\n").await;
cx.simulate_shared_keystrokes(["~"]).await;
cx.assert_shared_state("a😀CˇDé1*F\n").await;
// works with multibyte characters
cx.simulate_shared_keystrokes(["~"]).await;
cx.set_shared_state("aˇC😀é1*F\n").await;
cx.simulate_shared_keystrokes(["4", "~"]).await;
cx.assert_shared_state("ac😀É1ˇ*F\n").await;
// works with line selections
cx.set_shared_state("abˇC\n").await;
cx.simulate_shared_keystrokes(["shift-v", "~"]).await;
cx.assert_shared_state("ˇABc\n").await;
// works with multiple cursors (zed only)
cx.set_state("aˇßcdˇe\n", Mode::Normal);
cx.simulate_keystroke("~");
cx.assert_state("aSSˇcdˇE\n", Mode::Normal);
}
}

View file

@ -4,6 +4,7 @@ mod neovim_connection;
mod vim_binding_test_context;
mod vim_test_context;
use command_palette::CommandPalette;
pub use neovim_backed_binding_test_context::*;
pub use neovim_backed_test_context::*;
pub use vim_binding_test_context::*;
@ -139,3 +140,16 @@ async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
cx.simulate_keystrokes(["shift-v", "down", ">", ">"]);
cx.assert_editor_state("aa\n b«b\n cˇ»c");
}
#[gpui::test]
async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state("aˇbc\n", Mode::Normal);
cx.simulate_keystrokes(["i", "cmd-shift-p"]);
assert!(cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
cx.simulate_keystroke("escape");
assert!(!cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
cx.assert_state("aˇbc\n", Mode::Insert);
}

View file

@ -1,9 +1,10 @@
use std::ops::{Deref, DerefMut};
use indoc::indoc;
use std::ops::{Deref, DerefMut, Range};
use collections::{HashMap, HashSet};
use gpui::ContextHandle;
use language::OffsetRangeExt;
use util::test::marked_text_offsets;
use util::test::{generate_marked_text, marked_text_offsets};
use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext};
use crate::state::Mode;
@ -112,6 +113,43 @@ impl<'a> NeovimBackedTestContext<'a> {
context_handle
}
pub async fn assert_shared_state(&mut self, marked_text: &str) {
let neovim = self.neovim_state().await;
if neovim != marked_text {
panic!(
indoc! {"Test is incorrect (currently expected != neovim state)
# currently expected:
{}
# neovim state:
{}
# zed state:
{}"},
marked_text,
neovim,
self.editor_state(),
)
}
self.assert_editor_state(marked_text)
}
pub async fn neovim_state(&mut self) -> String {
generate_marked_text(
self.neovim.text().await.as_str(),
&vec![self.neovim_selection().await],
true,
)
}
async fn neovim_selection(&mut self) -> Range<usize> {
let mut neovim_selection = self.neovim.selection().await;
// Zed selections adjust themselves to make the end point visually make sense
if neovim_selection.start > neovim_selection.end {
neovim_selection.start.column += 1;
}
neovim_selection.to_offset(&self.buffer_snapshot())
}
pub async fn assert_state_matches(&mut self) {
assert_eq!(
self.neovim.text().await,
@ -120,13 +158,8 @@ impl<'a> NeovimBackedTestContext<'a> {
self.assertion_context()
);
let mut neovim_selection = self.neovim.selection().await;
// Zed selections adjust themselves to make the end point visually make sense
if neovim_selection.start > neovim_selection.end {
neovim_selection.start.column += 1;
}
let neovim_selection = neovim_selection.to_offset(&self.buffer_snapshot());
self.assert_editor_selections(vec![neovim_selection]);
let selections = vec![self.neovim_selection().await];
self.assert_editor_selections(selections);
if let Some(neovim_mode) = self.neovim.mode().await {
assert_eq!(neovim_mode, self.mode(), "{}", self.assertion_context(),);

View file

@ -167,15 +167,25 @@ impl NeovimConnection {
.await
.expect("Could not get neovim window");
if !selection.is_empty() {
panic!("Setting neovim state with non empty selection not yet supported");
}
let cursor = selection.start;
nvim_window
.set_cursor((cursor.row as i64 + 1, cursor.column as i64))
.await
.expect("Could not set nvim cursor position");
if !selection.is_empty() {
self.nvim
.input("v")
.await
.expect("could not enter visual mode");
let cursor = selection.end;
nvim_window
.set_cursor((cursor.row as i64 + 1, cursor.column as i64))
.await
.expect("Could not set nvim cursor position");
}
if let Some(NeovimData::Get { mode, state }) = self.data.back() {
if *mode == Some(Mode::Normal) && *state == marked_text {
return;

View file

@ -21,12 +21,14 @@ impl<'a> VimTestContext<'a> {
cx.update(|cx| {
search::init(cx);
crate::init(cx);
command_palette::init(cx);
});
cx.update(|cx| {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
});
settings::KeymapFile::load_asset("keymaps/default.json", cx).unwrap();
settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap();
});

View file

@ -12,7 +12,7 @@ mod visual;
use anyhow::Result;
use collections::CommandPaletteFilter;
use editor::{Bias, Cancel, Editor, EditorMode, Event};
use editor::{Bias, Editor, EditorMode, Event};
use gpui::{
actions, impl_actions, AppContext, Subscription, ViewContext, ViewHandle, WeakViewHandle,
WindowContext,
@ -64,22 +64,6 @@ pub fn init(cx: &mut AppContext) {
Vim::update(cx, |vim, cx| vim.push_number(n, cx));
});
// Editor Actions
cx.add_action(|_: &mut Editor, _: &Cancel, cx| {
// If we are in aren't in normal mode or have an active operator, swap to normal mode
// Otherwise forward cancel on to the editor
let vim = Vim::read(cx);
if vim.state.mode != Mode::Normal || vim.active_operator().is_some() {
WindowContext::defer(cx, |cx| {
Vim::update(cx, |state, cx| {
state.switch_mode(Mode::Normal, false, cx);
});
});
} else {
cx.propagate_action();
}
});
cx.add_action(|_: &mut Workspace, _: &Tab, cx| {
Vim::active_editor_input_ignored(" ".into(), cx)
});
@ -109,10 +93,7 @@ pub fn observe_keystrokes(cx: &mut WindowContext) {
cx.observe_keystrokes(|_keystroke, _result, handled_by, cx| {
if let Some(handled_by) = handled_by {
// Keystroke is handled by the vim system, so continue forward
// Also short circuit if it is the special cancel action
if handled_by.namespace() == "vim"
|| (handled_by.namespace() == "editor" && handled_by.name() == "Cancel")
{
if handled_by.namespace() == "vim" {
return true;
}
}

View file

@ -0,0 +1,18 @@
{"Put":{"state":"ˇabC\n"}}
{"Key":"~"}
{"Get":{"state":"AˇbC\n","mode":"Normal"}}
{"Key":"2"}
{"Key":"~"}
{"Get":{"state":"ABˇc\n","mode":"Normal"}}
{"Put":{"state":"a😀C«dÉ1*fˇ»\n"}}
{"Key":"~"}
{"Get":{"state":"a😀CˇDé1*F\n","mode":"Normal"}}
{"Key":"~"}
{"Put":{"state":"aˇC😀é1*F\n"}}
{"Key":"4"}
{"Key":"~"}
{"Get":{"state":"ac😀É1ˇ*F\n","mode":"Normal"}}
{"Put":{"state":"abˇC\n"}}
{"Key":"shift-v"}
{"Key":"~"}
{"Get":{"state":"ˇABc\n","mode":"Normal"}}

View file

@ -0,0 +1,17 @@
{"Put":{"state":"func ˇ(a string) {\n do(something(with<Types>.and_arrays[0, 2]))\n}"}}
{"Key":"%"}
{"Get":{"state":"func (a stringˇ) {\n do(something(with<Types>.and_arrays[0, 2]))\n}","mode":"Normal"}}
{"Put":{"state":"func (a string) ˇ{\ndo(something(with<Types>.and_arrays[0, 2]))\n}"}}
{"Key":"%"}
{"Get":{"state":"func (a string) {\ndo(something(with<Types>.and_arrays[0, 2]))\nˇ}","mode":"Normal"}}
{"Put":{"state":"ˇ{()}"}}
{"Key":"%"}
{"Get":{"state":"{()ˇ}","mode":"Normal"}}
{"Key":"%"}
{"Get":{"state":"ˇ{()}","mode":"Normal"}}
{"Put":{"state":"{\n ˇ{()}\n}"}}
{"Key":"%"}
{"Get":{"state":"{\n {()ˇ}\n}","mode":"Normal"}}
{"Put":{"state":"func ˇboop() {\n}"}}
{"Key":"%"}
{"Get":{"state":"func boop(ˇ) {\n}","mode":"Normal"}}

View file

@ -0,0 +1,13 @@
{"Put":{"state":"ˇabc\ndef\n\nparagraph\nthe second\n\n\n\nthird and\nfinal"}}
{"Key":"}"}
{"Get":{"state":"abc\ndef\nˇ\nparagraph\nthe second\n\n\n\nthird and\nfinal","mode":"Normal"}}
{"Key":"{"}
{"Get":{"state":"ˇabc\ndef\n\nparagraph\nthe second\n\n\n\nthird and\nfinal","mode":"Normal"}}
{"Key":"2"}
{"Key":"}"}
{"Get":{"state":"abc\ndef\n\nparagraph\nthe second\nˇ\n\n\nthird and\nfinal","mode":"Normal"}}
{"Key":"}"}
{"Get":{"state":"abc\ndef\n\nparagraph\nthe second\n\n\n\nthird and\nfinaˇl","mode":"Normal"}}
{"Key":"2"}
{"Key":"{"}
{"Get":{"state":"abc\ndef\nˇ\nparagraph\nthe second\n\n\n\nthird and\nfinal","mode":"Normal"}}

View file

@ -57,8 +57,9 @@ use staff_mode::StaffMode;
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace};
use zed::{
assets::Assets, build_window_options, handle_keymap_file_changes, initialize_workspace,
languages, menus,
assets::Assets,
build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
only_instance::{ensure_only_instance, IsOnlyInstance},
};
fn main() {
@ -66,6 +67,10 @@ fn main() {
init_paths();
init_logger();
if ensure_only_instance() != IsOnlyInstance::Yes {
return;
}
log::info!("========== starting zed ==========");
let mut app = gpui::App::new(Assets).unwrap();

View file

@ -0,0 +1,103 @@
use std::{
io::{Read, Write},
net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener, TcpStream},
thread,
time::Duration,
};
use util::channel::ReleaseChannel;
const LOCALHOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
const CONNECT_TIMEOUT: Duration = Duration::from_millis(10);
const RECEIVE_TIMEOUT: Duration = Duration::from_millis(35);
const SEND_TIMEOUT: Duration = Duration::from_millis(20);
fn address() -> SocketAddr {
let port = match *util::channel::RELEASE_CHANNEL {
ReleaseChannel::Dev => 43737,
ReleaseChannel::Preview => 43738,
ReleaseChannel::Stable => 43739,
};
SocketAddr::V4(SocketAddrV4::new(LOCALHOST, port))
}
fn instance_handshake() -> &'static str {
match *util::channel::RELEASE_CHANNEL {
ReleaseChannel::Dev => "Zed Editor Dev Instance Running",
ReleaseChannel::Preview => "Zed Editor Preview Instance Running",
ReleaseChannel::Stable => "Zed Editor Stable Instance Running",
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsOnlyInstance {
Yes,
No,
}
pub fn ensure_only_instance() -> IsOnlyInstance {
if *db::ZED_STATELESS {
return IsOnlyInstance::Yes;
}
if check_got_handshake() {
return IsOnlyInstance::No;
}
let listener = match TcpListener::bind(address()) {
Ok(listener) => listener,
Err(err) => {
log::warn!("Error binding to single instance port: {err}");
if check_got_handshake() {
return IsOnlyInstance::No;
}
// Avoid failing to start when some other application by chance already has
// a claim on the port. This is sub-par as any other instance that gets launched
// will be unable to communicate with this instance and will duplicate
log::warn!("Backup handshake request failed, continuing without handshake");
return IsOnlyInstance::Yes;
}
};
thread::spawn(move || {
for stream in listener.incoming() {
let mut stream = match stream {
Ok(stream) => stream,
Err(_) => return,
};
_ = stream.set_nodelay(true);
_ = stream.set_read_timeout(Some(SEND_TIMEOUT));
_ = stream.write_all(instance_handshake().as_bytes());
}
});
IsOnlyInstance::Yes
}
fn check_got_handshake() -> bool {
match TcpStream::connect_timeout(&address(), CONNECT_TIMEOUT) {
Ok(mut stream) => {
let mut buf = vec![0u8; instance_handshake().len()];
stream.set_read_timeout(Some(RECEIVE_TIMEOUT)).unwrap();
if let Err(err) = stream.read_exact(&mut buf) {
log::warn!("Connected to single instance port but failed to read: {err}");
return false;
}
if buf == instance_handshake().as_bytes() {
log::info!("Got instance handshake");
return true;
}
log::warn!("Got wrong instance handshake value");
false
}
Err(_) => false,
}
}

View file

@ -1,6 +1,7 @@
pub mod assets;
pub mod languages;
pub mod menus;
pub mod only_instance;
#[cfg(any(test, feature = "test-support"))]
pub mod test;

View file

@ -0,0 +1,55 @@
import { Theme, StyleSets } from "../common"
import { interactive } from "../element"
import { InteractiveState } from "../element/interactive"
import { background, foreground } from "../style_tree/components"
interface TabBarButtonOptions {
icon: string
color?: StyleSets
}
type TabBarButtonProps = TabBarButtonOptions & {
state?: Partial<Record<InteractiveState, Partial<TabBarButtonOptions>>>
}
export function tab_bar_button(theme: Theme, { icon, color = "base" }: TabBarButtonProps) {
const button_spacing = 8
return (
interactive({
base: {
icon: {
color: foreground(theme.middle, color),
asset: icon,
dimensions: {
width: 15,
height: 15,
},
},
container: {
corner_radius: 4,
padding: {
top: 4, bottom: 4, left: 4, right: 4
},
margin: {
left: button_spacing / 2,
right: button_spacing / 2,
},
},
},
state: {
hovered: {
container: {
background: background(theme.middle, color, "hovered"),
}
},
clicked: {
container: {
background: background(theme.middle, color, "pressed"),
}
},
},
})
)
}

View file

@ -1,233 +1,133 @@
import { text, border, background, foreground } from "./components"
import { interactive } from "../element"
import { useTheme } from "../theme"
import { text, border, background, foreground, TextStyle } from "./components"
import { Interactive, interactive } from "../element"
import { tab_bar_button } from "../component/tab_bar_button"
import { StyleSets, useTheme } from "../theme"
type RoleCycleButton = TextStyle & {
background?: string
}
// TODO: Replace these with zed types
type RemainingTokens = TextStyle & {
background: string,
margin: { top: number, right: number },
padding: {
right: number,
left: number,
top: number,
bottom: number,
},
corner_radius: number,
}
export default function assistant(): any {
const theme = useTheme()
const interactive_role = (color: StyleSets): Interactive<RoleCycleButton> => {
return (
interactive({
base: {
...text(theme.highest, "sans", color, { size: "sm" }),
},
state: {
hovered: {
...text(theme.highest, "sans", color, { size: "sm" }),
background: background(theme.highest, color, "hovered"),
},
clicked: {
...text(theme.highest, "sans", color, { size: "sm" }),
background: background(theme.highest, color, "pressed"),
}
},
})
)
}
const tokens_remaining = (color: StyleSets): RemainingTokens => {
return (
{
...text(theme.highest, "mono", color, { size: "xs" }),
background: background(theme.highest, "on", "default"),
margin: { top: 12, right: 20 },
padding: { right: 4, left: 4, top: 1, bottom: 1 },
corner_radius: 6,
}
)
}
return {
container: {
background: background(theme.highest),
padding: { left: 12 },
},
message_header: {
margin: { bottom: 6, top: 6 },
margin: { bottom: 4, top: 4 },
background: background(theme.highest),
},
hamburger_button: interactive({
base: {
icon: {
color: foreground(theme.highest, "variant"),
asset: "icons/hamburger_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
padding: { left: 12, right: 8.5 },
},
},
state: {
hovered: {
icon: {
color: foreground(theme.highest, "hovered"),
},
},
},
hamburger_button: tab_bar_button(theme, {
icon: "icons/hamburger_15.svg",
}),
split_button: interactive({
base: {
icon: {
color: foreground(theme.highest, "variant"),
asset: "icons/split_message_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
padding: { left: 8.5, right: 8.5 },
},
},
state: {
hovered: {
icon: {
color: foreground(theme.highest, "hovered"),
},
},
},
split_button: tab_bar_button(theme, {
icon: "icons/split_message_15.svg",
}),
quote_button: interactive({
base: {
icon: {
color: foreground(theme.highest, "variant"),
asset: "icons/quote_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
padding: { left: 8.5, right: 8.5 },
},
},
state: {
hovered: {
icon: {
color: foreground(theme.highest, "hovered"),
},
},
},
quote_button: tab_bar_button(theme, {
icon: "icons/radix/quote.svg",
}),
assist_button: interactive({
base: {
icon: {
color: foreground(theme.highest, "variant"),
asset: "icons/assist_15.svg",
dimensions: {
width: 15,
height: 15,
},
},
container: {
padding: { left: 8.5, right: 8.5 },
},
},
state: {
hovered: {
icon: {
color: foreground(theme.highest, "hovered"),
},
},
},
assist_button: tab_bar_button(theme, {
icon: "icons/radix/magic-wand.svg",
}),
zoom_in_button: interactive({
base: {
icon: {
color: foreground(theme.highest, "variant"),
asset: "icons/maximize_8.svg",
dimensions: {
width: 12,
height: 12,
},
},
container: {
padding: { left: 10, right: 10 },
},
},
state: {
hovered: {
icon: {
color: foreground(theme.highest, "hovered"),
},
},
},
zoom_in_button: tab_bar_button(theme, {
icon: "icons/radix/maximize.svg",
}),
zoom_out_button: interactive({
base: {
icon: {
color: foreground(theme.highest, "variant"),
asset: "icons/minimize_8.svg",
dimensions: {
width: 12,
height: 12,
},
},
container: {
padding: { left: 10, right: 10 },
},
},
state: {
hovered: {
icon: {
color: foreground(theme.highest, "hovered"),
},
},
},
zoom_out_button: tab_bar_button(theme, {
icon: "icons/radix/minimize.svg",
}),
plus_button: interactive({
base: {
icon: {
color: foreground(theme.highest, "variant"),
asset: "icons/plus_12.svg",
dimensions: {
width: 12,
height: 12,
},
},
container: {
padding: { left: 10, right: 10 },
},
},
state: {
hovered: {
icon: {
color: foreground(theme.highest, "hovered"),
},
},
},
plus_button: tab_bar_button(theme, {
icon: "icons/radix/plus.svg",
}),
title: {
...text(theme.highest, "sans", "default", { size: "sm" }),
...text(theme.highest, "sans", "default", { size: "xs" }),
},
saved_conversation: {
container: interactive({
base: {
background: background(theme.highest, "on"),
background: background(theme.middle),
padding: { top: 4, bottom: 4 },
border: border(theme.middle, "default", { top: true, overlay: true }),
},
state: {
hovered: {
background: background(theme.highest, "on", "hovered"),
background: background(theme.middle, "hovered"),
},
clicked: {
background: background(theme.middle, "pressed"),
}
},
}),
saved_at: {
margin: { left: 8 },
...text(theme.highest, "sans", "default", { size: "xs" }),
...text(theme.highest, "sans", "variant", { size: "xs" }),
},
title: {
margin: { left: 16 },
margin: { left: 12 },
...text(theme.highest, "sans", "default", {
size: "sm",
weight: "bold",
}),
},
},
user_sender: {
default: {
...text(theme.highest, "sans", "default", {
size: "sm",
weight: "bold",
}),
},
},
assistant_sender: {
default: {
...text(theme.highest, "sans", "accent", {
size: "sm",
weight: "bold",
}),
},
},
system_sender: {
default: {
...text(theme.highest, "sans", "variant", {
size: "sm",
weight: "bold",
}),
},
},
user_sender: interactive_role("base"),
assistant_sender: interactive_role("accent"),
system_sender: interactive_role("warning"),
sent_at: {
margin: { top: 2, left: 8 },
...text(theme.highest, "sans", "default", { size: "2xs" }),
...text(theme.highest, "sans", "variant", { size: "2xs" }),
},
model: interactive({
base: {
background: background(theme.highest, "on"),
margin: { left: 12, right: 12, top: 12 },
padding: 4,
background: background(theme.highest),
margin: { left: 12, right: 4, top: 12 },
padding: { right: 4, left: 4, top: 1, bottom: 1 },
corner_radius: 4,
...text(theme.highest, "sans", "default", { size: "xs" }),
},
@ -238,20 +138,9 @@ export default function assistant(): any {
},
},
}),
remaining_tokens: {
background: background(theme.highest, "on"),
margin: { top: 12, right: 24 },
padding: 4,
corner_radius: 4,
...text(theme.highest, "sans", "positive", { size: "xs" }),
},
no_remaining_tokens: {
background: background(theme.highest, "on"),
margin: { top: 12, right: 24 },
padding: 4,
corner_radius: 4,
...text(theme.highest, "sans", "negative", { size: "xs" }),
},
remaining_tokens: tokens_remaining("positive"),
low_remaining_tokens: tokens_remaining("warning"),
no_remaining_tokens: tokens_remaining("negative"),
error_icon: {
margin: { left: 8 },
color: foreground(theme.highest, "negative"),
@ -259,7 +148,7 @@ export default function assistant(): any {
},
api_key_editor: {
background: background(theme.highest, "on"),
corner_radius: 6,
corner_radius: 4,
text: text(theme.highest, "mono", "on"),
placeholder_text: text(theme.highest, "mono", "on", "disabled", {
size: "xs",

View file

@ -12,8 +12,17 @@ export interface Theme {
name: string
is_light: boolean
/**
* App background, other elements that should sit directly on top of the background.
*/
lowest: Layer
/**
* Panels, tabs, other UI surfaces that sit on top of the background.
*/
middle: Layer
/**
* Editors like code buffers, conversation editors, etc.
*/
highest: Layer
ramps: RampSet