Compare commits
560 Commits
diagnostic
...
linux/trac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ac6a2f5c2 | ||
|
|
ba7d5a3d4c | ||
|
|
6f99399224 | ||
|
|
2f2047ab22 | ||
|
|
d01d76482d | ||
|
|
a46a562dc2 | ||
|
|
4bb8a0845f | ||
|
|
c4bca874b6 | ||
|
|
46c0aa5fc2 | ||
|
|
2db06c1567 | ||
|
|
c59d5fbae7 | ||
|
|
8df098ff35 | ||
|
|
639b21a7c5 | ||
|
|
c65673feae | ||
|
|
6c9da838b7 | ||
|
|
a173beeb81 | ||
|
|
002ce6c343 | ||
|
|
2c30b8836e | ||
|
|
0d527dfb9e | ||
|
|
110ce8a267 | ||
|
|
c6b9f1920f | ||
|
|
df935df5a3 | ||
|
|
68b5ea4e60 | ||
|
|
5e1521eded | ||
|
|
ba28827de5 | ||
|
|
c22dbbebe2 | ||
|
|
8f29ff8a63 | ||
|
|
ae414e21f0 | ||
|
|
2dd486733b | ||
|
|
f44e81b3b5 | ||
|
|
c093bc8aa8 | ||
|
|
09e7b481b8 | ||
|
|
8cfa690271 | ||
|
|
3cdd465226 | ||
|
|
8203b6875b | ||
|
|
ce7074c883 | ||
|
|
6cc8412a05 | ||
|
|
2a97aad273 | ||
|
|
275dd3fa81 | ||
|
|
3cb2a1404c | ||
|
|
dd9b2e2cde | ||
|
|
b691d1baf2 | ||
|
|
bc0359a474 | ||
|
|
23c84f8dc0 | ||
|
|
29226170f1 | ||
|
|
9a523ef730 | ||
|
|
9b688655a8 | ||
|
|
9a6f30fd95 | ||
|
|
b3dad0bfcb | ||
|
|
18d6be250f | ||
|
|
5e1c690888 | ||
|
|
034d905435 | ||
|
|
0d7bd0c535 | ||
|
|
ed50dea042 | ||
|
|
5c95d2806b | ||
|
|
05e2e4d929 | ||
|
|
30479bf062 | ||
|
|
a40a16ab98 | ||
|
|
2e7db8f855 | ||
|
|
b0ecda6370 | ||
|
|
efc2336be5 | ||
|
|
9e36a66fec | ||
|
|
97f315356d | ||
|
|
0b6ef995d4 | ||
|
|
414cff5c14 | ||
|
|
2925f3d33c | ||
|
|
1a0242eff7 | ||
|
|
ea9ba6863d | ||
|
|
af697d9cc2 | ||
|
|
75377bbe0f | ||
|
|
032b203519 | ||
|
|
f555f66a8c | ||
|
|
d3f869acd8 | ||
|
|
c617d48e16 | ||
|
|
7f50055d70 | ||
|
|
c5e5add094 | ||
|
|
65e463b599 | ||
|
|
11c7374f76 | ||
|
|
cd7268f21f | ||
|
|
a1eaf1bb3c | ||
|
|
ab83820b6e | ||
|
|
800bdf34d5 | ||
|
|
79f3646325 | ||
|
|
813cc3f5e5 | ||
|
|
c190ed49da | ||
|
|
5c7e6b7eff | ||
|
|
1c1fd6aaa1 | ||
|
|
750df6c93d | ||
|
|
a53b3b6b10 | ||
|
|
078ce330c6 | ||
|
|
19490d8806 | ||
|
|
61e4b6413a | ||
|
|
950e7e5414 | ||
|
|
68accaeb00 | ||
|
|
ca27f42a9d | ||
|
|
d70c577293 | ||
|
|
c77ea47f43 | ||
|
|
821aa0811d | ||
|
|
fa602001e3 | ||
|
|
9b7bc04a87 | ||
|
|
a61188d137 | ||
|
|
1bd585186a | ||
|
|
e69f9d6cf9 | ||
|
|
c4dbe32f20 | ||
|
|
f2711b2fca | ||
|
|
1260b52c82 | ||
|
|
fc8749ffd7 | ||
|
|
2a923e338f | ||
|
|
56e3fc794a | ||
|
|
c8b106245c | ||
|
|
398c2f91dd | ||
|
|
3a5d116ffe | ||
|
|
e3cd1dd2d0 | ||
|
|
b1f8fc88a1 | ||
|
|
d450a1d9e6 | ||
|
|
818e6e53d6 | ||
|
|
ed09bb949c | ||
|
|
52583fe1ed | ||
|
|
8ec478cbcd | ||
|
|
6d10b16f79 | ||
|
|
05af87e416 | ||
|
|
75d2e04a1d | ||
|
|
492040dec4 | ||
|
|
47aa761ca9 | ||
|
|
98699a65c1 | ||
|
|
f024fcff3d | ||
|
|
2f05f5bc5c | ||
|
|
22a9293cba | ||
|
|
cceebee397 | ||
|
|
38fb841d1f | ||
|
|
48763d0663 | ||
|
|
089cc85d4a | ||
|
|
995b082c64 | ||
|
|
64755a7aea | ||
|
|
3348c3ab4c | ||
|
|
dceb0827e8 | ||
|
|
c1e18059f8 | ||
|
|
351a3c0815 | ||
|
|
28c5e33e0c | ||
|
|
5c7a8f779a | ||
|
|
b7cb2381f2 | ||
|
|
7db68547fa | ||
|
|
eb845ee201 | ||
|
|
8ea2bd4c7e | ||
|
|
7460381285 | ||
|
|
eab98eb9c9 | ||
|
|
6eda9c9745 | ||
|
|
8dd7c2cddf | ||
|
|
3bbe574341 | ||
|
|
51ee60b421 | ||
|
|
193be271a8 | ||
|
|
ce48555f8d | ||
|
|
ecd9422d11 | ||
|
|
0eb26d29ee | ||
|
|
3a43adba00 | ||
|
|
3419f5fc42 | ||
|
|
e7214a429d | ||
|
|
c9ac7b8e35 | ||
|
|
e243856559 | ||
|
|
c516b8f038 | ||
|
|
03447b9e18 | ||
|
|
464a4439f7 | ||
|
|
0e60730742 | ||
|
|
25ad3185e0 | ||
|
|
bac6e2fee7 | ||
|
|
065ab93ca7 | ||
|
|
83592306c5 | ||
|
|
e650c0166d | ||
|
|
f1859e3645 | ||
|
|
b1a0188467 | ||
|
|
218629cdd4 | ||
|
|
0761383752 | ||
|
|
b616f9c27f | ||
|
|
5e465f2029 | ||
|
|
7d767ff0a3 | ||
|
|
3cabd4bf64 | ||
|
|
2972bdc0e2 | ||
|
|
a295b90597 | ||
|
|
891f195f7b | ||
|
|
6e1b99b039 | ||
|
|
00d1561156 | ||
|
|
d5fbf75ccf | ||
|
|
61bbb3539a | ||
|
|
c560a24e7d | ||
|
|
da03610555 | ||
|
|
363ac6bc96 | ||
|
|
97159bd88d | ||
|
|
0b57df5deb | ||
|
|
7652a8ae23 | ||
|
|
1d193585b0 | ||
|
|
af5efcea1f | ||
|
|
228202a469 | ||
|
|
e1fbef0dfd | ||
|
|
7d7fd7d25d | ||
|
|
6a1b257d39 | ||
|
|
a695322f83 | ||
|
|
cb2d05b78f | ||
|
|
45d4de75b3 | ||
|
|
20c1f8245a | ||
|
|
b16075d00c | ||
|
|
da22e0dd0b | ||
|
|
fb3ef0d140 | ||
|
|
e71b642f44 | ||
|
|
6cedfa0ce7 | ||
|
|
209b1d1931 | ||
|
|
6986ac4c27 | ||
|
|
d50d1611b9 | ||
|
|
1260c616ba | ||
|
|
89951f7e66 | ||
|
|
cd81dad2fa | ||
|
|
3a08d7ab43 | ||
|
|
49dc63812a | ||
|
|
c0a3642f77 | ||
|
|
4d5441c09d | ||
|
|
2dc840132b | ||
|
|
5d766f61fa | ||
|
|
18b4573064 | ||
|
|
d044dc8485 | ||
|
|
f00bea5d0f | ||
|
|
b43df6048b | ||
|
|
eb914682b3 | ||
|
|
5b7e31c075 | ||
|
|
922fcaf5a6 | ||
|
|
9f88460870 | ||
|
|
e5d1cf84cf | ||
|
|
41d2c52638 | ||
|
|
d1a55d64a8 | ||
|
|
db06244972 | ||
|
|
597469bbbd | ||
|
|
e0c192d831 | ||
|
|
b2a0a7fa3c | ||
|
|
0b1a589183 | ||
|
|
7e694d1bcf | ||
|
|
890443241d | ||
|
|
b014f9f017 | ||
|
|
f40d2313fb | ||
|
|
2dee4f87fd | ||
|
|
54afa6f69f | ||
|
|
55511d1591 | ||
|
|
6c0cb9eaa3 | ||
|
|
24e7b69f8f | ||
|
|
a4cdca5141 | ||
|
|
86cd87e993 | ||
|
|
88000eb7e2 | ||
|
|
ab5a462e0c | ||
|
|
79430fc7d2 | ||
|
|
f96e4ba84f | ||
|
|
7be1ffb9ec | ||
|
|
93a5d0ca29 | ||
|
|
328d98dddc | ||
|
|
76ab9e4d66 | ||
|
|
c477c12956 | ||
|
|
1ffd87b87e | ||
|
|
df11b646da | ||
|
|
ed94bd41eb | ||
|
|
8949460bd7 | ||
|
|
c6c5907693 | ||
|
|
dea928b00c | ||
|
|
77b2da2b42 | ||
|
|
5a8c2a4a88 | ||
|
|
d46e494bd9 | ||
|
|
82435075a5 | ||
|
|
40748b0a15 | ||
|
|
3ee3c6a3bd | ||
|
|
6cc3a4d95c | ||
|
|
b58dfe502e | ||
|
|
03e2f240ee | ||
|
|
145cd798c0 | ||
|
|
9ef9baef6f | ||
|
|
d2a2faf7a2 | ||
|
|
10f7ca65cf | ||
|
|
354427413a | ||
|
|
9813297892 | ||
|
|
78bc3a9a36 | ||
|
|
73de99bee0 | ||
|
|
0ed1b29b01 | ||
|
|
5b754915e4 | ||
|
|
9298d3b525 | ||
|
|
89739d5874 | ||
|
|
d272e402ea | ||
|
|
5c93506e9f | ||
|
|
7df8b6fe10 | ||
|
|
6fba1e46a8 | ||
|
|
988ee93a81 | ||
|
|
00a505e41a | ||
|
|
ed9f6e2141 | ||
|
|
fe7d53cb96 | ||
|
|
edca195e3c | ||
|
|
d3b3e072a7 | ||
|
|
6b04b668ad | ||
|
|
4072ad2858 | ||
|
|
cb0b8b4c4b | ||
|
|
c58a8f1a04 | ||
|
|
abb46473c9 | ||
|
|
9bdb154a9b | ||
|
|
f69c8ca74e | ||
|
|
04a79780d8 | ||
|
|
4dd05a80e0 | ||
|
|
44c479c50c | ||
|
|
c8709978a1 | ||
|
|
f78f6a6e1e | ||
|
|
fefc91c6ad | ||
|
|
3076567f6b | ||
|
|
6eb537643a | ||
|
|
40eb84109d | ||
|
|
51601cf6bd | ||
|
|
2c545ce0bc | ||
|
|
58e9952d7b | ||
|
|
25c8cf0c5c | ||
|
|
d501a877a0 | ||
|
|
97abf35529 | ||
|
|
0150192e26 | ||
|
|
710c387395 | ||
|
|
5a6c55149a | ||
|
|
d5b0df6efa | ||
|
|
4e2a08edb7 | ||
|
|
c20a1ee032 | ||
|
|
f5f73efa8a | ||
|
|
d8c93e1bfd | ||
|
|
95b06097ee | ||
|
|
963b0c010a | ||
|
|
558808b97d | ||
|
|
4b19eac5c8 | ||
|
|
47174cea50 | ||
|
|
0129d4e250 | ||
|
|
3d4f275c52 | ||
|
|
cd2533de5a | ||
|
|
acc9648753 | ||
|
|
bc35235800 | ||
|
|
2ca83b2f17 | ||
|
|
ddf07253c4 | ||
|
|
aff7a83815 | ||
|
|
8524e87319 | ||
|
|
59c005b086 | ||
|
|
522692ef50 | ||
|
|
d665f28671 | ||
|
|
8c4fb34f6e | ||
|
|
d4891a62bb | ||
|
|
17bc0d1b17 | ||
|
|
db0d843fb1 | ||
|
|
ad4e52842c | ||
|
|
ca18549e02 | ||
|
|
5cbb360952 | ||
|
|
5ff7c893be | ||
|
|
99e4b3a4cf | ||
|
|
9af4b6bfc7 | ||
|
|
c84d432b5f | ||
|
|
6cea9813ad | ||
|
|
490a75aee6 | ||
|
|
4f364d6d09 | ||
|
|
89d2ace713 | ||
|
|
84a44bef8a | ||
|
|
6b9ddbfef2 | ||
|
|
8af8493da6 | ||
|
|
39edbe1c50 | ||
|
|
f6fa6600bc | ||
|
|
b55961b57a | ||
|
|
01b836a191 | ||
|
|
41180b8d81 | ||
|
|
81475ac4cd | ||
|
|
ba59e66314 | ||
|
|
5ede48337c | ||
|
|
3701e190ce | ||
|
|
5dc26c261d | ||
|
|
64d815a176 | ||
|
|
5dc54863a4 | ||
|
|
e4ba336971 | ||
|
|
195a270e18 | ||
|
|
3a26a4809d | ||
|
|
479c5df491 | ||
|
|
51404d4ea0 | ||
|
|
05c4c7872c | ||
|
|
0af6e442a7 | ||
|
|
7003b0f211 | ||
|
|
f489c8b79f | ||
|
|
be02b2faf4 | ||
|
|
258a8a37d8 | ||
|
|
78e0f71a28 | ||
|
|
59104a08fd | ||
|
|
7aa28c9b24 | ||
|
|
bb1d52b485 | ||
|
|
ca035dbdd8 | ||
|
|
71cc95d315 | ||
|
|
3707734f0a | ||
|
|
e19627d92f | ||
|
|
bb75d87285 | ||
|
|
eecbf203dc | ||
|
|
7fe5c27597 | ||
|
|
221edfc267 | ||
|
|
d95c424d18 | ||
|
|
d6d56191da | ||
|
|
2e87e1d26e | ||
|
|
e8862c45cc | ||
|
|
0c28b6a11a | ||
|
|
16fce64d3a | ||
|
|
b075ce8f04 | ||
|
|
54828ab836 | ||
|
|
6322351f00 | ||
|
|
78091fa91e | ||
|
|
d5735dab9a | ||
|
|
c793bbde84 | ||
|
|
03c54623d4 | ||
|
|
0afb3abfd2 | ||
|
|
2b46a4a0e9 | ||
|
|
bedf57db89 | ||
|
|
4855da53df | ||
|
|
15d3e54ae3 | ||
|
|
064bdab459 | ||
|
|
38cb95f427 | ||
|
|
7cc2538fe1 | ||
|
|
fc19cc0ddf | ||
|
|
e6def62c23 | ||
|
|
ff2347dff5 | ||
|
|
6319ae0b4a | ||
|
|
a8bd602334 | ||
|
|
af45db6d1e | ||
|
|
fab4b01655 | ||
|
|
2f6cb49d84 | ||
|
|
411ee7a47c | ||
|
|
831f7dbbc0 | ||
|
|
78fd378702 | ||
|
|
d5a6ca4914 | ||
|
|
ea69846281 | ||
|
|
ff8486e67f | ||
|
|
9bc3c6810b | ||
|
|
45ae0dcc2d | ||
|
|
e40c49a143 | ||
|
|
0d43d484f6 | ||
|
|
6ca09bd4ba | ||
|
|
53f702c92f | ||
|
|
b03653321f | ||
|
|
993109aee1 | ||
|
|
4cb45e63f4 | ||
|
|
1413b5af93 | ||
|
|
d9c21b4eb1 | ||
|
|
44f66aa426 | ||
|
|
3b84b106e2 | ||
|
|
3539a7c04a | ||
|
|
a8481099ca | ||
|
|
6c28b7e8b8 | ||
|
|
0d8e6e6b12 | ||
|
|
bf03f66d02 | ||
|
|
0f59607100 | ||
|
|
902d7150fe | ||
|
|
55ba80ddd1 | ||
|
|
dcb8dc16ca | ||
|
|
eb7a09b459 | ||
|
|
1248788780 | ||
|
|
64bb79b71d | ||
|
|
a5af5b2883 | ||
|
|
068b1c235c | ||
|
|
8edfd0a963 | ||
|
|
0ed5327b1c | ||
|
|
10d3ad4e33 | ||
|
|
066cdc2297 | ||
|
|
01ba1ddef7 | ||
|
|
86167138a9 | ||
|
|
e0c1ab650e | ||
|
|
407188f816 | ||
|
|
6181ac6bad | ||
|
|
0705fb9b97 | ||
|
|
042be3529d | ||
|
|
1a40e98413 | ||
|
|
af8e7af265 | ||
|
|
ce51c264a6 | ||
|
|
702fd8f168 | ||
|
|
2e758dcb64 | ||
|
|
38d9ee3731 | ||
|
|
95c69d0696 | ||
|
|
599102573a | ||
|
|
da281d6d8f | ||
|
|
22dc88ed3d | ||
|
|
3c0310273b | ||
|
|
14bf07c916 | ||
|
|
284559742d | ||
|
|
85acc2be44 | ||
|
|
6c70a809ec | ||
|
|
a35947c883 | ||
|
|
f8ad5fe3e9 | ||
|
|
e2cfbc54ad | ||
|
|
0a13b9ee01 | ||
|
|
eb7b5a7131 | ||
|
|
7798f64d1b | ||
|
|
21764c38dd | ||
|
|
cfbf5dca7a | ||
|
|
2f43d52e7e | ||
|
|
e1f4dfc068 | ||
|
|
9e3c5f3e12 | ||
|
|
f780504b68 | ||
|
|
76b0120665 | ||
|
|
0ac9af94e0 | ||
|
|
ec086945fc | ||
|
|
8451dba6a7 | ||
|
|
7f56f4e78e | ||
|
|
6fa347dff7 | ||
|
|
c3df9b79c6 | ||
|
|
72dac24acf | ||
|
|
001f17c011 | ||
|
|
3c3dad6830 | ||
|
|
1b28f93c64 | ||
|
|
2fd00a8f35 | ||
|
|
bee3441c78 | ||
|
|
113546f766 | ||
|
|
5e9f9b4edd | ||
|
|
ec95a33d8c | ||
|
|
b82350979f | ||
|
|
e16bbe048f | ||
|
|
ab41eddd8b | ||
|
|
4cb8d6f40e | ||
|
|
127b9ed857 | ||
|
|
c30f6a1582 | ||
|
|
8ccd2a0c99 | ||
|
|
57b87be3a0 | ||
|
|
80c14c9198 | ||
|
|
ec9e700e70 | ||
|
|
a06189bbed | ||
|
|
53b0720d54 | ||
|
|
b6ea393d14 | ||
|
|
98659eabf1 | ||
|
|
3722275cfa | ||
|
|
ef84ce76e3 | ||
|
|
44a58647e4 | ||
|
|
4e98c23463 | ||
|
|
1914a42b1c | ||
|
|
6afed19a00 | ||
|
|
2509af723f | ||
|
|
8078e58494 | ||
|
|
b69c3129d0 | ||
|
|
e2c5ce588b | ||
|
|
7005aaa54d | ||
|
|
9db269735d | ||
|
|
77e88c1ded | ||
|
|
57c40299a5 | ||
|
|
0d5485bd6c | ||
|
|
a600799840 | ||
|
|
05b6581147 | ||
|
|
43d1a8040d | ||
|
|
e829a8c3b0 | ||
|
|
87845a349d | ||
|
|
953393f6ce | ||
|
|
75f8be6a0f | ||
|
|
e174f16d50 | ||
|
|
aa60fc2f19 | ||
|
|
5548773b2e | ||
|
|
3eac83eece | ||
|
|
243a0e764d | ||
|
|
6fa6e0718c | ||
|
|
834089feb1 | ||
|
|
9174858225 | ||
|
|
a910f192db | ||
|
|
5f5e6b8616 | ||
|
|
07dbd2bce8 | ||
|
|
48581167b7 | ||
|
|
00dfd217d8 | ||
|
|
22490f7968 | ||
|
|
880940856d | ||
|
|
c354793871 | ||
|
|
2f057785f7 | ||
|
|
fd39f20842 | ||
|
|
0c7e745be8 |
@@ -1,15 +0,0 @@
|
|||||||
# This config is different from config.toml in this directory, as the latter is recognized by Cargo.
|
|
||||||
# This file is placed in $HOME/.cargo/config.toml on CI runs. Cargo then merges Zeds .cargo/config.toml with $HOME/.cargo/config.toml
|
|
||||||
# with preference for settings from Zeds config.toml.
|
|
||||||
# TL;DR: If a value is set in both ci-config.toml and config.toml, config.toml value takes precedence.
|
|
||||||
# Arrays are merged together though. See: https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure
|
|
||||||
# The intent for this file is to configure CI build process with a divergance from Zed developers experience; for example, in this config file
|
|
||||||
# we use `-D warnings` for rustflags (which makes compilation fail in presence of warnings during build process). Placing that in developers `config.toml`
|
|
||||||
# would be incovenient.
|
|
||||||
# We *could* override things like RUSTFLAGS manually by setting them as environment variables, but that is less DRY; worse yet, if you forget to set proper environment variables
|
|
||||||
# in one spot, that's going to trigger a rebuild of all of the artifacts. Using ci-config.toml we can define these overrides for CI in one spot and not worry about it.
|
|
||||||
[build]
|
|
||||||
rustflags = ["-D", "warnings"]
|
|
||||||
|
|
||||||
[alias]
|
|
||||||
xtask = "run --package xtask --"
|
|
||||||
5
.cargo/collab-config.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# This file is used to build collab in a Docker image.
|
||||||
|
# In particular, we don't use clang.
|
||||||
|
[build]
|
||||||
|
# v0 mangling scheme provides more detailed backtraces around closures
|
||||||
|
rustflags = ["-C", "symbol-mangling-version=v0", "--cfg", "tokio_unstable"]
|
||||||
@@ -4,3 +4,11 @@ rustflags = ["-C", "symbol-mangling-version=v0", "--cfg", "tokio_unstable"]
|
|||||||
|
|
||||||
[alias]
|
[alias]
|
||||||
xtask = "run --package xtask --"
|
xtask = "run --package xtask --"
|
||||||
|
|
||||||
|
[target.x86_64-unknown-linux-gnu]
|
||||||
|
linker = "clang"
|
||||||
|
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
|
||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "clang"
|
||||||
|
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
|
.git
|
||||||
|
.github
|
||||||
|
**/.gitignore
|
||||||
|
**/.gitkeep
|
||||||
|
.gitattributes
|
||||||
|
.mailmap
|
||||||
**/target
|
**/target
|
||||||
zed.xcworkspace
|
zed.xcworkspace
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
compose.yml
|
||||||
plugins/bin
|
plugins/bin
|
||||||
script/node_modules
|
script/node_modules
|
||||||
styles/node_modules
|
styles/node_modules
|
||||||
|
|||||||
38
.github/ISSUE_TEMPLATE/0_feature_request.yml
vendored
@@ -2,23 +2,23 @@ name: Feature Request
|
|||||||
description: "Tip: open this issue template from within Zed with the `request feature` command palette action"
|
description: "Tip: open this issue template from within Zed with the `request feature` command palette action"
|
||||||
labels: ["admin read", "triage", "enhancement"]
|
labels: ["admin read", "triage", "enhancement"]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Check for existing issues
|
label: Check for existing issues
|
||||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||||
options:
|
options:
|
||||||
- label: Completed
|
- label: Completed
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the feature
|
|
||||||
description: A clear and concise description of what you want to happen.
|
|
||||||
validations:
|
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: |
|
label: Describe the feature
|
||||||
If applicable, add mockups / screenshots to help present your vision of the feature
|
description: A clear and concise description of what you want to happen.
|
||||||
description: Drag images into the text input below
|
validations:
|
||||||
validations:
|
required: true
|
||||||
required: false
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: |
|
||||||
|
If applicable, add mockups / screenshots to help present your vision of the feature
|
||||||
|
description: Drag images into the text input below
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|||||||
76
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
@@ -1,40 +1,46 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: |
|
description: |
|
||||||
Use this template for **non-crash-related** bug reports.
|
Use this template for **non-crash-related** bug reports.
|
||||||
Tip: open this issue template from within Zed with the `file bug report` command palette action.
|
Tip: open this issue template from within Zed with the `file bug report` command palette action.
|
||||||
labels: ["admin read", "triage", "defect"]
|
labels: ["admin read", "triage", "defect"]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Check for existing issues
|
label: Check for existing issues
|
||||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||||
options:
|
options:
|
||||||
- label: Completed
|
- label: Completed
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the bug / provide steps to reproduce it
|
|
||||||
description: A clear and concise description of what the bug is.
|
|
||||||
validations:
|
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: environment
|
attributes:
|
||||||
attributes:
|
label: Describe the bug / provide steps to reproduce it
|
||||||
label: Environment
|
description: A clear and concise description of what the bug is.
|
||||||
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
|
validations:
|
||||||
validations:
|
required: true
|
||||||
required: true
|
- type: textarea
|
||||||
- type: textarea
|
id: environment
|
||||||
attributes:
|
attributes:
|
||||||
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
|
label: Environment
|
||||||
description: Drag issues into the text input below
|
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
label: If applicable, add mockups / screenshots to help explain present your vision of the feature
|
||||||
description: |
|
description: Drag issues into the text input below
|
||||||
Drag Zed.log into the text input below.
|
validations:
|
||||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
required: false
|
||||||
validations:
|
- type: textarea
|
||||||
required: false
|
attributes:
|
||||||
|
label: If applicable, attach your Zed.log file to this issue.
|
||||||
|
description: |
|
||||||
|
macOS: `~/Library/Logs/Zed/Zed.log`
|
||||||
|
Linux: `~/.local/share/zed/logs/Zed.log` or $XDG_DATA_HOME
|
||||||
|
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||||
|
value: |
|
||||||
|
<details><summary>Zed.log</summary><pre>
|
||||||
|
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||||
|
|
||||||
|
<!-- Click above this line and paste or drag-and-drop your log--></pre></details>
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|||||||
61
.github/ISSUE_TEMPLATE/2_crash_report.yml
vendored
@@ -1,33 +1,38 @@
|
|||||||
name: Crash Report
|
name: Crash Report
|
||||||
description: |
|
description: |
|
||||||
Use this template for crash reports.
|
Use this template for crash reports.
|
||||||
labels: ["admin read", "triage", "defect", "panic / crash"]
|
labels: ["admin read", "triage", "defect", "panic / crash"]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Check for existing issues
|
label: Check for existing issues
|
||||||
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
description: Check the backlog of issues to reduce the chances of creating duplicates; if an issue already exists, place a `+1` (👍) on it.
|
||||||
options:
|
options:
|
||||||
- label: Completed
|
- label: Completed
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the bug / provide steps to reproduce it
|
|
||||||
description: A clear and concise description of what the bug is.
|
|
||||||
validations:
|
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: environment
|
attributes:
|
||||||
attributes:
|
label: Describe the bug / provide steps to reproduce it
|
||||||
label: Environment
|
description: A clear and concise description of what the bug is.
|
||||||
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
|
validations:
|
||||||
validations:
|
required: true
|
||||||
required: true
|
- type: textarea
|
||||||
- type: textarea
|
id: environment
|
||||||
attributes:
|
attributes:
|
||||||
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
label: Environment
|
||||||
description: |
|
description: Run the `copy system specs into clipboard` command palette action and paste the output in the field below.
|
||||||
Drag Zed.log into the text input below.
|
validations:
|
||||||
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
required: true
|
||||||
validations:
|
- type: textarea
|
||||||
required: false
|
attributes:
|
||||||
|
label: If applicable, attach your `~/Library/Logs/Zed/Zed.log` file to this issue.
|
||||||
|
description: |
|
||||||
|
Drag Zed.log into the text input below.
|
||||||
|
If you only need the most recent lines, you can run the `zed: open log` command palette action to see the last 1000.
|
||||||
|
value: |
|
||||||
|
<details><summary>Zed.log</summary><pre>
|
||||||
|
<!-- Click below this line and paste or drag-and-drop your log-->
|
||||||
|
|
||||||
|
<!-- Click above this line and paste or drag-and-drop your log--></pre></details>
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|||||||
2
.github/pull_request_template.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Release Notes:
|
Release Notes:
|
||||||
|
|
||||||
- Added/Fixed/Improved ... ([#<public_issue_number_if_exists>](https://github.com/zed-industries/zed/issues/<public_issue_number_if_exists>)).
|
- Added/Fixed/Improved ... ([#NNNNN](https://github.com/zed-industries/zed/issues/NNNNN)).
|
||||||
|
|
||||||
Optionally, include screenshots / media showcasing your addition that can be included in the release notes.
|
Optionally, include screenshots / media showcasing your addition that can be included in the release notes.
|
||||||
|
|
||||||
|
|||||||
112
.github/workflows/ci.yml
vendored
@@ -38,9 +38,6 @@ jobs:
|
|||||||
- name: Remove untracked files
|
- name: Remove untracked files
|
||||||
run: git clean -df
|
run: git clean -df
|
||||||
|
|
||||||
- name: Set up default .cargo/config.toml
|
|
||||||
run: cp ./.cargo/ci-config.toml ~/.cargo/config.toml
|
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
run: |
|
run: |
|
||||||
if ! which typos > /dev/null; then
|
if ! which typos > /dev/null; then
|
||||||
@@ -54,6 +51,9 @@ jobs:
|
|||||||
- name: Check unused dependencies
|
- name: Check unused dependencies
|
||||||
uses: bnjbvr/cargo-machete@main
|
uses: bnjbvr/cargo-machete@main
|
||||||
|
|
||||||
|
- name: Check licenses are present
|
||||||
|
run: script/check-licenses
|
||||||
|
|
||||||
- name: Check license generation
|
- name: Check license generation
|
||||||
run: script/generate-licenses /tmp/zed_licenses_output
|
run: script/generate-licenses /tmp/zed_licenses_output
|
||||||
|
|
||||||
@@ -74,8 +74,8 @@ jobs:
|
|||||||
version: v1.29.0
|
version: v1.29.0
|
||||||
- uses: bufbuild/buf-breaking-action@v1
|
- uses: bufbuild/buf-breaking-action@v1
|
||||||
with:
|
with:
|
||||||
input: "crates/rpc/proto/"
|
input: "crates/proto/proto/"
|
||||||
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/rpc/proto/"
|
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/"
|
||||||
|
|
||||||
macos_tests:
|
macos_tests:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
clean: false
|
clean: false
|
||||||
|
|
||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
run: cargo xtask clippy
|
run: ./script/clippy
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: ./.github/actions/run_tests
|
uses: ./.github/actions/run_tests
|
||||||
@@ -101,7 +101,6 @@ jobs:
|
|||||||
- name: Build other binaries and features
|
- name: Build other binaries and features
|
||||||
run: cargo build --workspace --bins --all-features; cargo check -p gpui --features "macos-blade"
|
run: cargo build --workspace --bins --all-features; cargo check -p gpui --features "macos-blade"
|
||||||
|
|
||||||
# todo(linux): Actually run the tests
|
|
||||||
linux_tests:
|
linux_tests:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
name: (Linux) Run Clippy and tests
|
name: (Linux) Run Clippy and tests
|
||||||
@@ -118,7 +117,10 @@ jobs:
|
|||||||
clean: false
|
clean: false
|
||||||
|
|
||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
run: cargo xtask clippy
|
run: ./script/clippy
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
uses: ./.github/actions/run_tests
|
||||||
|
|
||||||
- name: Build Zed
|
- name: Build Zed
|
||||||
run: cargo build -p zed
|
run: cargo build -p zed
|
||||||
@@ -140,7 +142,7 @@ jobs:
|
|||||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
run: cargo xtask clippy
|
run: ./script/clippy
|
||||||
|
|
||||||
- name: Build Zed
|
- name: Build Zed
|
||||||
run: cargo build -p zed
|
run: cargo build -p zed
|
||||||
@@ -252,7 +254,7 @@ jobs:
|
|||||||
target/aarch64-apple-darwin/release/Zed-aarch64.dmg
|
target/aarch64-apple-darwin/release/Zed-aarch64.dmg
|
||||||
target/x86_64-apple-darwin/release/Zed-x86_64.dmg
|
target/x86_64-apple-darwin/release/Zed-x86_64.dmg
|
||||||
target/release/Zed.dmg
|
target/release/Zed.dmg
|
||||||
body_file: target/release-notes.md
|
body_path: target/release-notes.md
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@@ -305,10 +307,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Generate license file
|
- name: Create Linux .tar.gz bundle
|
||||||
run: script/generate-licenses
|
|
||||||
|
|
||||||
- name: Create and upload Linux .tar.gz bundle
|
|
||||||
run: script/bundle-linux
|
run: script/bundle-linux
|
||||||
|
|
||||||
- name: Upload Linux bundle to workflow run if main branch or specific label
|
- name: Upload Linux bundle to workflow run if main branch or specific label
|
||||||
@@ -316,7 +315,7 @@ jobs:
|
|||||||
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||||
with:
|
with:
|
||||||
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
|
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
|
||||||
path: zed-*.tar.gz
|
path: target/release/zed-*.tar.gz
|
||||||
|
|
||||||
- name: Upload app bundle to release
|
- name: Upload app bundle to release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
@@ -328,3 +327,86 @@ jobs:
|
|||||||
body: ""
|
body: ""
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
bundle-linux-aarch64:
|
||||||
|
timeout-minutes: 60
|
||||||
|
name: Create arm64 Linux bundle
|
||||||
|
runs-on:
|
||||||
|
- hosted-linux-arm-1
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||||
|
needs: [linux_tests]
|
||||||
|
env:
|
||||||
|
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
clean: false
|
||||||
|
- name: "Setup jq"
|
||||||
|
uses: dcarbone/install-jq-action@v2
|
||||||
|
|
||||||
|
- name: Set up Clang
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y llvm-10 clang-10 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
|
||||||
|
echo "/usr/lib/llvm-10/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- uses: rui314/setup-mold@v1
|
||||||
|
with:
|
||||||
|
mold-version: 2.32.0
|
||||||
|
|
||||||
|
- name: rustup
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Limit target directory size
|
||||||
|
run: script/clear-target-dir-if-larger-than 100
|
||||||
|
|
||||||
|
- name: Determine version and release channel
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
version=$(script/get-crate-version zed)
|
||||||
|
channel=$(cat crates/zed/RELEASE_CHANNEL)
|
||||||
|
echo "Publishing version: ${version} on release channel ${channel}"
|
||||||
|
echo "RELEASE_CHANNEL=${channel}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
expected_tag_name=""
|
||||||
|
case ${channel} in
|
||||||
|
stable)
|
||||||
|
expected_tag_name="v${version}";;
|
||||||
|
preview)
|
||||||
|
expected_tag_name="v${version}-pre";;
|
||||||
|
nightly)
|
||||||
|
expected_tag_name="v${version}-nightly";;
|
||||||
|
*)
|
||||||
|
echo "can't publish a release on channel ${channel}"
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
if [[ $GITHUB_REF_NAME != $expected_tag_name ]]; then
|
||||||
|
echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create and upload Linux .tar.gz bundle
|
||||||
|
run: script/bundle-linux
|
||||||
|
|
||||||
|
- name: Upload Linux bundle to workflow run if main branch or specific label
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
|
||||||
|
with:
|
||||||
|
name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
|
||||||
|
path: target/release/zed-*.tar.gz
|
||||||
|
|
||||||
|
- name: Upload app bundle to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
||||||
|
files: target/release/zed-linux-aarch64.tar.gz
|
||||||
|
body: ""
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
2
.github/workflows/danger.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
- uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 9
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
|||||||
5
.github/workflows/deploy_collab.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
|||||||
uses: ./.github/actions/check_style
|
uses: ./.github/actions/check_style
|
||||||
|
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: cargo xtask clippy
|
run: ./script/clippy
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
@@ -75,6 +75,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
clean: false
|
clean: false
|
||||||
|
|
||||||
|
- name: Set up default .cargo/config.toml
|
||||||
|
run: cp ./.cargo/collab-config.toml ./.cargo/config.toml
|
||||||
|
|
||||||
- name: Build docker image
|
- name: Build docker image
|
||||||
run: docker build . --build-arg GITHUB_SHA=$GITHUB_SHA --tag registry.digitalocean.com/zed/collab:$GITHUB_SHA
|
run: docker build . --build-arg GITHUB_SHA=$GITHUB_SHA --tag registry.digitalocean.com/zed/collab:$GITHUB_SHA
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/release_nightly.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
uses: ./.github/actions/check_style
|
uses: ./.github/actions/check_style
|
||||||
|
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: cargo xtask clippy
|
run: ./script/clippy
|
||||||
tests:
|
tests:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
name: Run tests
|
name: Run tests
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
/.direnv
|
||||||
.idea
|
.idea
|
||||||
**/target
|
**/target
|
||||||
**/cargo-target
|
**/cargo-target
|
||||||
|
|||||||
27
.mailmap
@@ -7,14 +7,24 @@
|
|||||||
# Reference: https://git-scm.com/docs/gitmailmap
|
# Reference: https://git-scm.com/docs/gitmailmap
|
||||||
|
|
||||||
# Keep these entries sorted alphabetically.
|
# Keep these entries sorted alphabetically.
|
||||||
# In Zed: `editor: sort lines case sensitive`
|
# In Zed: `editor: sort lines case insensitive`
|
||||||
|
|
||||||
|
Alex Viscreanu <alexviscreanu@gmail.com>
|
||||||
|
Alex Viscreanu <alexviscreanu@gmail.com> <alexandru.viscreanu@kiwi.com>
|
||||||
|
amtoaer <amtoaer@gmail.com>
|
||||||
|
amtoaer <amtoaer@gmail.com> <amtoaer@outlook.com>
|
||||||
Antonio Scandurra <me@as-cii.com>
|
Antonio Scandurra <me@as-cii.com>
|
||||||
Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
|
Antonio Scandurra <me@as-cii.com> <antonio@zed.dev>
|
||||||
|
Bennet Bo Fenner <bennet@zed.dev>
|
||||||
|
Bennet Bo Fenner <bennet@zed.dev> <53836821+bennetbo@users.noreply.github.com>
|
||||||
|
Bennet Bo Fenner <bennet@zed.dev> <bennetbo@gmx.de>
|
||||||
Christian Bergschneider <christian.bergschneider@gmx.de>
|
Christian Bergschneider <christian.bergschneider@gmx.de>
|
||||||
Christian Bergschneider <christian.bergschneider@gmx.de> <magiclake@gmx.de>
|
Christian Bergschneider <christian.bergschneider@gmx.de> <magiclake@gmx.de>
|
||||||
Conrad Irwin <conrad@zed.dev>
|
Conrad Irwin <conrad@zed.dev>
|
||||||
Conrad Irwin <conrad@zed.dev> <conrad.irwin@gmail.com>
|
Conrad Irwin <conrad@zed.dev> <conrad.irwin@gmail.com>
|
||||||
|
Danilo Leal <danilo@zed.dev>
|
||||||
|
Danilo Leal <danilo@zed.dev> <67129314+danilo-leal@users.noreply.github.com>
|
||||||
|
Evren Sen <146845123+evrsen@users.noreply.github.com>
|
||||||
Fernando Tagawa <tagawafernando@gmail.com>
|
Fernando Tagawa <tagawafernando@gmail.com>
|
||||||
Fernando Tagawa <tagawafernando@gmail.com> <fernando.tagawa.gamail.com@gmail.com>
|
Fernando Tagawa <tagawafernando@gmail.com> <fernando.tagawa.gamail.com@gmail.com>
|
||||||
Greg Morenz <greg-morenz@droid.cafe>
|
Greg Morenz <greg-morenz@droid.cafe>
|
||||||
@@ -48,12 +58,27 @@ Nate Butler <iamnbutler@gmail.com> <nate@zed.dev>
|
|||||||
Nathan Sobo <nathan@zed.dev>
|
Nathan Sobo <nathan@zed.dev>
|
||||||
Nathan Sobo <nathan@zed.dev> <nathan@warp.dev>
|
Nathan Sobo <nathan@zed.dev> <nathan@warp.dev>
|
||||||
Nathan Sobo <nathan@zed.dev> <nathansobo@gmail.com>
|
Nathan Sobo <nathan@zed.dev> <nathansobo@gmail.com>
|
||||||
|
Nigel Jose <nigelmjose@gmail.com>
|
||||||
|
Nigel Jose <nigelmjose@gmail.com> <nigel.jose@student.manchester.ac.uk>
|
||||||
|
Peter Tripp <peter@zed.dev>
|
||||||
|
Peter Tripp <peter@zed.dev> <petertripp@gmail.com>
|
||||||
Petros Amoiridis <petros@hey.com>
|
Petros Amoiridis <petros@hey.com>
|
||||||
Petros Amoiridis <petros@hey.com> <petros@zed.dev>
|
Petros Amoiridis <petros@hey.com> <petros@zed.dev>
|
||||||
Piotr Osiewicz <piotr@zed.dev>
|
Piotr Osiewicz <piotr@zed.dev>
|
||||||
Piotr Osiewicz <piotr@zed.dev> <24362066+osiewicz@users.noreply.github.com>
|
Piotr Osiewicz <piotr@zed.dev> <24362066+osiewicz@users.noreply.github.com>
|
||||||
|
Pocæus <github@pocaeus.com>
|
||||||
|
Pocæus <github@pocaeus.com> <pseudomata@proton.me>
|
||||||
|
Rashid Almheiri <r.muhairi@pm.me>
|
||||||
|
Rashid Almheiri <r.muhairi@pm.me> <69181766+huwaireb@users.noreply.github.com>
|
||||||
|
Richard Feldman <oss@rtfeldman.com>
|
||||||
|
Richard Feldman <oss@rtfeldman.com> <richard@zed.dev>
|
||||||
Robert Clover <git@clo4.net>
|
Robert Clover <git@clo4.net>
|
||||||
Robert Clover <git@clo4.net> <robert@clover.gdn>
|
Robert Clover <git@clo4.net> <robert@clover.gdn>
|
||||||
|
Sergey Onufrienko <sergey@onufrienko.com>
|
||||||
Thorsten Ball <thorsten@zed.dev>
|
Thorsten Ball <thorsten@zed.dev>
|
||||||
Thorsten Ball <thorsten@zed.dev> <me@thorstenball.com>
|
Thorsten Ball <thorsten@zed.dev> <me@thorstenball.com>
|
||||||
Thorsten Ball <thorsten@zed.dev> <mrnugget@gmail.com>
|
Thorsten Ball <thorsten@zed.dev> <mrnugget@gmail.com>
|
||||||
|
Vitaly Slobodin <vitaliy.slobodin@gmail.com>
|
||||||
|
Vitaly Slobodin <vitaliy.slobodin@gmail.com> <vitaly_slobodin@fastmail.com>
|
||||||
|
WindSoilder <WindSoilder@outlook.com>
|
||||||
|
张小白 <364772080@qq.com>
|
||||||
|
|||||||
@@ -14,11 +14,24 @@
|
|||||||
},
|
},
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"tab_size": 2,
|
"tab_size": 2,
|
||||||
|
"preferred_line_length": 100,
|
||||||
|
"formatter": "prettier"
|
||||||
|
},
|
||||||
|
"JSONC": {
|
||||||
|
"tab_size": 2,
|
||||||
|
"preferred_line_length": 100,
|
||||||
"formatter": "prettier"
|
"formatter": "prettier"
|
||||||
},
|
},
|
||||||
"JavaScript": {
|
"JavaScript": {
|
||||||
"tab_size": 2,
|
"tab_size": 2,
|
||||||
"formatter": "prettier"
|
"formatter": "prettier"
|
||||||
|
},
|
||||||
|
"Rust": {
|
||||||
|
"tasks": {
|
||||||
|
"variables": {
|
||||||
|
"RUST_DEFAULT_PACKAGE_RUN": "zed"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"formatter": "auto",
|
"formatter": "auto",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"label": "clippy",
|
"label": "clippy",
|
||||||
"command": "cargo",
|
"command": "./script/clippy",
|
||||||
"args": ["xtask", "clippy"]
|
"args": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ We plan to set aside time each week to pair program with contributors on promisi
|
|||||||
|
|
||||||
Zed is made up of several smaller crates - let's go over those you're most likely to interact with:
|
Zed is made up of several smaller crates - let's go over those you're most likely to interact with:
|
||||||
|
|
||||||
- [`gpui`](/crates/gpui) is a GPU-accelerated UI framework which provides all of the building blocks for Zed. **We recommend familiarizing yourself with the root level GPUI documentation**
|
- [`gpui`](/crates/gpui) is a GPU-accelerated UI framework which provides all of the building blocks for Zed. **We recommend familiarizing yourself with the root level GPUI documentation.**
|
||||||
- [`editor`](/crates/editor) contains the core `Editor` type that drives both the code editor and all various input fields within Zed. It also handles a display layer for LSP features such as Inlay Hints or code completions.
|
- [`editor`](/crates/editor) contains the core `Editor` type that drives both the code editor and all various input fields within Zed. It also handles a display layer for LSP features such as Inlay Hints or code completions.
|
||||||
- [`project`](/crates/project) manages files and navigation within the filetree. It is also Zed's side of communication with LSP.
|
- [`project`](/crates/project) manages files and navigation within the filetree. It is also Zed's side of communication with LSP.
|
||||||
- [`workspace`](/crates/workspace) handles local state serialization and groups projects together.
|
- [`workspace`](/crates/workspace) handles local state serialization and groups projects together.
|
||||||
|
|||||||
1180
Cargo.lock
generated
94
Cargo.toml
@@ -21,6 +21,7 @@ members = [
|
|||||||
"crates/command_palette_hooks",
|
"crates/command_palette_hooks",
|
||||||
"crates/copilot",
|
"crates/copilot",
|
||||||
"crates/db",
|
"crates/db",
|
||||||
|
"crates/dev_server_projects",
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
"crates/extension",
|
"crates/extension",
|
||||||
@@ -44,6 +45,7 @@ members = [
|
|||||||
"crates/html_to_markdown",
|
"crates/html_to_markdown",
|
||||||
"crates/http",
|
"crates/http",
|
||||||
"crates/image_viewer",
|
"crates/image_viewer",
|
||||||
|
"crates/indexed_docs",
|
||||||
"crates/inline_completion_button",
|
"crates/inline_completion_button",
|
||||||
"crates/install_cli",
|
"crates/install_cli",
|
||||||
"crates/journal",
|
"crates/journal",
|
||||||
@@ -61,47 +63,53 @@ members = [
|
|||||||
"crates/multi_buffer",
|
"crates/multi_buffer",
|
||||||
"crates/node_runtime",
|
"crates/node_runtime",
|
||||||
"crates/notifications",
|
"crates/notifications",
|
||||||
|
"crates/ollama",
|
||||||
"crates/open_ai",
|
"crates/open_ai",
|
||||||
"crates/outline",
|
"crates/outline",
|
||||||
|
"crates/outline_panel",
|
||||||
|
"crates/paths",
|
||||||
"crates/picker",
|
"crates/picker",
|
||||||
"crates/prettier",
|
"crates/prettier",
|
||||||
"crates/project",
|
"crates/project",
|
||||||
"crates/project_panel",
|
"crates/project_panel",
|
||||||
"crates/project_symbols",
|
"crates/project_symbols",
|
||||||
|
"crates/proto",
|
||||||
"crates/quick_action_bar",
|
"crates/quick_action_bar",
|
||||||
"crates/recent_projects",
|
"crates/recent_projects",
|
||||||
"crates/refineable",
|
"crates/refineable",
|
||||||
"crates/refineable/derive_refineable",
|
"crates/refineable/derive_refineable",
|
||||||
"crates/release_channel",
|
"crates/release_channel",
|
||||||
"crates/dev_server_projects",
|
"crates/repl",
|
||||||
"crates/rich_text",
|
"crates/rich_text",
|
||||||
"crates/rope",
|
"crates/rope",
|
||||||
"crates/rpc",
|
"crates/rpc",
|
||||||
"crates/task",
|
|
||||||
"crates/tasks_ui",
|
|
||||||
"crates/search",
|
"crates/search",
|
||||||
"crates/semantic_index",
|
"crates/semantic_index",
|
||||||
"crates/semantic_version",
|
"crates/semantic_version",
|
||||||
"crates/settings",
|
"crates/settings",
|
||||||
"crates/snippet",
|
"crates/snippet",
|
||||||
|
"crates/snippet_provider",
|
||||||
"crates/sqlez",
|
"crates/sqlez",
|
||||||
"crates/sqlez_macros",
|
"crates/sqlez_macros",
|
||||||
"crates/story",
|
"crates/story",
|
||||||
"crates/storybook",
|
"crates/storybook",
|
||||||
"crates/sum_tree",
|
"crates/sum_tree",
|
||||||
"crates/tab_switcher",
|
|
||||||
"crates/supermaven",
|
"crates/supermaven",
|
||||||
"crates/supermaven_api",
|
"crates/supermaven_api",
|
||||||
|
"crates/tab_switcher",
|
||||||
|
"crates/task",
|
||||||
|
"crates/tasks_ui",
|
||||||
|
"crates/telemetry_events",
|
||||||
"crates/terminal",
|
"crates/terminal",
|
||||||
"crates/terminal_view",
|
"crates/terminal_view",
|
||||||
"crates/text",
|
"crates/text",
|
||||||
"crates/theme",
|
"crates/theme",
|
||||||
"crates/theme_importer",
|
"crates/theme_importer",
|
||||||
"crates/theme_selector",
|
"crates/theme_selector",
|
||||||
"crates/telemetry_events",
|
|
||||||
"crates/time_format",
|
"crates/time_format",
|
||||||
|
"crates/title_bar",
|
||||||
"crates/ui",
|
"crates/ui",
|
||||||
"crates/ui_text_field",
|
"crates/ui_input",
|
||||||
"crates/util",
|
"crates/util",
|
||||||
"crates/vcs_menu",
|
"crates/vcs_menu",
|
||||||
"crates/vim",
|
"crates/vim",
|
||||||
@@ -130,8 +138,10 @@ members = [
|
|||||||
"extensions/prisma",
|
"extensions/prisma",
|
||||||
"extensions/purescript",
|
"extensions/purescript",
|
||||||
"extensions/ruby",
|
"extensions/ruby",
|
||||||
|
"extensions/snippets",
|
||||||
"extensions/svelte",
|
"extensions/svelte",
|
||||||
"extensions/terraform",
|
"extensions/terraform",
|
||||||
|
"extensions/test-extension",
|
||||||
"extensions/toml",
|
"extensions/toml",
|
||||||
"extensions/uiua",
|
"extensions/uiua",
|
||||||
"extensions/vue",
|
"extensions/vue",
|
||||||
@@ -150,10 +160,8 @@ assets = { path = "crates/assets" }
|
|||||||
assistant = { path = "crates/assistant" }
|
assistant = { path = "crates/assistant" }
|
||||||
assistant_slash_command = { path = "crates/assistant_slash_command" }
|
assistant_slash_command = { path = "crates/assistant_slash_command" }
|
||||||
assistant_tooling = { path = "crates/assistant_tooling" }
|
assistant_tooling = { path = "crates/assistant_tooling" }
|
||||||
async-watch = "0.3.1"
|
|
||||||
audio = { path = "crates/audio" }
|
audio = { path = "crates/audio" }
|
||||||
auto_update = { path = "crates/auto_update" }
|
auto_update = { path = "crates/auto_update" }
|
||||||
base64 = "0.13"
|
|
||||||
breadcrumbs = { path = "crates/breadcrumbs" }
|
breadcrumbs = { path = "crates/breadcrumbs" }
|
||||||
call = { path = "crates/call" }
|
call = { path = "crates/call" }
|
||||||
channel = { path = "crates/channel" }
|
channel = { path = "crates/channel" }
|
||||||
@@ -163,12 +171,11 @@ clock = { path = "crates/clock" }
|
|||||||
collab = { path = "crates/collab" }
|
collab = { path = "crates/collab" }
|
||||||
collab_ui = { path = "crates/collab_ui" }
|
collab_ui = { path = "crates/collab_ui" }
|
||||||
collections = { path = "crates/collections" }
|
collections = { path = "crates/collections" }
|
||||||
color = { path = "crates/color" }
|
|
||||||
command_palette = { path = "crates/command_palette" }
|
command_palette = { path = "crates/command_palette" }
|
||||||
command_palette_hooks = { path = "crates/command_palette_hooks" }
|
command_palette_hooks = { path = "crates/command_palette_hooks" }
|
||||||
copilot = { path = "crates/copilot" }
|
copilot = { path = "crates/copilot" }
|
||||||
dashmap = "5.5.3"
|
|
||||||
db = { path = "crates/db" }
|
db = { path = "crates/db" }
|
||||||
|
dev_server_projects = { path = "crates/dev_server_projects" }
|
||||||
diagnostics = { path = "crates/diagnostics" }
|
diagnostics = { path = "crates/diagnostics" }
|
||||||
editor = { path = "crates/editor" }
|
editor = { path = "crates/editor" }
|
||||||
extension = { path = "crates/extension" }
|
extension = { path = "crates/extension" }
|
||||||
@@ -189,9 +196,10 @@ gpui_macros = { path = "crates/gpui_macros" }
|
|||||||
headless = { path = "crates/headless" }
|
headless = { path = "crates/headless" }
|
||||||
html_to_markdown = { path = "crates/html_to_markdown" }
|
html_to_markdown = { path = "crates/html_to_markdown" }
|
||||||
http = { path = "crates/http" }
|
http = { path = "crates/http" }
|
||||||
install_cli = { path = "crates/install_cli" }
|
|
||||||
image_viewer = { path = "crates/image_viewer" }
|
image_viewer = { path = "crates/image_viewer" }
|
||||||
|
indexed_docs = { path = "crates/indexed_docs" }
|
||||||
inline_completion_button = { path = "crates/inline_completion_button" }
|
inline_completion_button = { path = "crates/inline_completion_button" }
|
||||||
|
install_cli = { path = "crates/install_cli" }
|
||||||
journal = { path = "crates/journal" }
|
journal = { path = "crates/journal" }
|
||||||
language = { path = "crates/language" }
|
language = { path = "crates/language" }
|
||||||
language_selector = { path = "crates/language_selector" }
|
language_selector = { path = "crates/language_selector" }
|
||||||
@@ -207,79 +215,91 @@ menu = { path = "crates/menu" }
|
|||||||
multi_buffer = { path = "crates/multi_buffer" }
|
multi_buffer = { path = "crates/multi_buffer" }
|
||||||
node_runtime = { path = "crates/node_runtime" }
|
node_runtime = { path = "crates/node_runtime" }
|
||||||
notifications = { path = "crates/notifications" }
|
notifications = { path = "crates/notifications" }
|
||||||
|
ollama = { path = "crates/ollama" }
|
||||||
open_ai = { path = "crates/open_ai" }
|
open_ai = { path = "crates/open_ai" }
|
||||||
outline = { path = "crates/outline" }
|
outline = { path = "crates/outline" }
|
||||||
|
outline_panel = { path = "crates/outline_panel" }
|
||||||
|
paths = { path = "crates/paths" }
|
||||||
picker = { path = "crates/picker" }
|
picker = { path = "crates/picker" }
|
||||||
plugin = { path = "crates/plugin" }
|
plugin = { path = "crates/plugin" }
|
||||||
plugin_macros = { path = "crates/plugin_macros" }
|
plugin_macros = { path = "crates/plugin_macros" }
|
||||||
prettier = { path = "crates/prettier" }
|
prettier = { path = "crates/prettier" }
|
||||||
project = { path = "crates/project" }
|
project = { path = "crates/project" }
|
||||||
worktree = { path = "crates/worktree" }
|
|
||||||
project_panel = { path = "crates/project_panel" }
|
project_panel = { path = "crates/project_panel" }
|
||||||
project_symbols = { path = "crates/project_symbols" }
|
project_symbols = { path = "crates/project_symbols" }
|
||||||
|
proto = { path = "crates/proto" }
|
||||||
quick_action_bar = { path = "crates/quick_action_bar" }
|
quick_action_bar = { path = "crates/quick_action_bar" }
|
||||||
recent_projects = { path = "crates/recent_projects" }
|
recent_projects = { path = "crates/recent_projects" }
|
||||||
release_channel = { path = "crates/release_channel" }
|
release_channel = { path = "crates/release_channel" }
|
||||||
dev_server_projects = { path = "crates/dev_server_projects" }
|
repl = { path = "crates/repl" }
|
||||||
rich_text = { path = "crates/rich_text" }
|
rich_text = { path = "crates/rich_text" }
|
||||||
rope = { path = "crates/rope" }
|
rope = { path = "crates/rope" }
|
||||||
rpc = { path = "crates/rpc" }
|
rpc = { path = "crates/rpc" }
|
||||||
task = { path = "crates/task" }
|
|
||||||
tasks_ui = { path = "crates/tasks_ui" }
|
|
||||||
search = { path = "crates/search" }
|
search = { path = "crates/search" }
|
||||||
semantic_index = { path = "crates/semantic_index" }
|
semantic_index = { path = "crates/semantic_index" }
|
||||||
semantic_version = { path = "crates/semantic_version" }
|
semantic_version = { path = "crates/semantic_version" }
|
||||||
settings = { path = "crates/settings" }
|
settings = { path = "crates/settings" }
|
||||||
snippet = { path = "crates/snippet" }
|
snippet = { path = "crates/snippet" }
|
||||||
|
snippet_provider = { path = "crates/snippet_provider" }
|
||||||
sqlez = { path = "crates/sqlez" }
|
sqlez = { path = "crates/sqlez" }
|
||||||
sqlez_macros = { path = "crates/sqlez_macros" }
|
sqlez_macros = { path = "crates/sqlez_macros" }
|
||||||
supermaven = { path = "crates/supermaven" }
|
|
||||||
supermaven_api = { path = "crates/supermaven_api" }
|
|
||||||
story = { path = "crates/story" }
|
story = { path = "crates/story" }
|
||||||
storybook = { path = "crates/storybook" }
|
storybook = { path = "crates/storybook" }
|
||||||
sum_tree = { path = "crates/sum_tree" }
|
sum_tree = { path = "crates/sum_tree" }
|
||||||
|
supermaven = { path = "crates/supermaven" }
|
||||||
|
supermaven_api = { path = "crates/supermaven_api" }
|
||||||
tab_switcher = { path = "crates/tab_switcher" }
|
tab_switcher = { path = "crates/tab_switcher" }
|
||||||
|
task = { path = "crates/task" }
|
||||||
|
tasks_ui = { path = "crates/tasks_ui" }
|
||||||
|
telemetry_events = { path = "crates/telemetry_events" }
|
||||||
terminal = { path = "crates/terminal" }
|
terminal = { path = "crates/terminal" }
|
||||||
terminal_view = { path = "crates/terminal_view" }
|
terminal_view = { path = "crates/terminal_view" }
|
||||||
text = { path = "crates/text" }
|
text = { path = "crates/text" }
|
||||||
theme = { path = "crates/theme" }
|
theme = { path = "crates/theme" }
|
||||||
theme_importer = { path = "crates/theme_importer" }
|
theme_importer = { path = "crates/theme_importer" }
|
||||||
theme_selector = { path = "crates/theme_selector" }
|
theme_selector = { path = "crates/theme_selector" }
|
||||||
telemetry_events = { path = "crates/telemetry_events" }
|
|
||||||
time_format = { path = "crates/time_format" }
|
time_format = { path = "crates/time_format" }
|
||||||
|
title_bar = { path = "crates/title_bar" }
|
||||||
ui = { path = "crates/ui" }
|
ui = { path = "crates/ui" }
|
||||||
ui_text_field = { path = "crates/ui_text_field" }
|
ui_input = { path = "crates/ui_input" }
|
||||||
util = { path = "crates/util" }
|
util = { path = "crates/util" }
|
||||||
vcs_menu = { path = "crates/vcs_menu" }
|
vcs_menu = { path = "crates/vcs_menu" }
|
||||||
vim = { path = "crates/vim" }
|
vim = { path = "crates/vim" }
|
||||||
welcome = { path = "crates/welcome" }
|
welcome = { path = "crates/welcome" }
|
||||||
workspace = { path = "crates/workspace" }
|
workspace = { path = "crates/workspace" }
|
||||||
|
worktree = { path = "crates/worktree" }
|
||||||
zed = { path = "crates/zed" }
|
zed = { path = "crates/zed" }
|
||||||
zed_actions = { path = "crates/zed_actions" }
|
zed_actions = { path = "crates/zed_actions" }
|
||||||
|
|
||||||
anyhow = "1.0.57"
|
alacritty_terminal = "0.23"
|
||||||
any_vec = "0.13"
|
any_vec = "0.13"
|
||||||
ashpd = "0.8.0"
|
anyhow = "1.0.57"
|
||||||
|
ashpd = { git = "https://github.com/bilelmoussaoui/ashpd", rev = "29f2e1a" }
|
||||||
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
|
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
|
||||||
|
async-dispatcher = { version = "0.1" }
|
||||||
async-fs = "1.6"
|
async-fs = "1.6"
|
||||||
async-recursion = "1.0.0"
|
async-recursion = "1.0.0"
|
||||||
async-tar = "0.4.2"
|
async-tar = "0.4.2"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
async-watch = "0.3.1"
|
||||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||||
|
base64 = "0.13"
|
||||||
bitflags = "2.4.2"
|
bitflags = "2.4.2"
|
||||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "bdaf8c534fbbc9fbca71d1cf272f45640b3a068d" }
|
blade-graphics = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
|
||||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "bdaf8c534fbbc9fbca71d1cf272f45640b3a068d" }
|
blade-macros = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
|
||||||
blade-util = { git = "https://github.com/kvark/blade", rev = "bdaf8c534fbbc9fbca71d1cf272f45640b3a068d" }
|
blade-util = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
|
||||||
cap-std = "3.0"
|
cap-std = "3.0"
|
||||||
cargo_toml = "0.20"
|
cargo_toml = "0.20"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
clap = { version = "4.4", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
clickhouse = { version = "0.11.6" }
|
clickhouse = { version = "0.11.6" }
|
||||||
ctor = "0.2.6"
|
cocoa = "0.25"
|
||||||
signal-hook = "0.3.17"
|
|
||||||
core-foundation = { version = "0.9.3" }
|
core-foundation = { version = "0.9.3" }
|
||||||
core-foundation-sys = "0.8.6"
|
core-foundation-sys = "0.8.6"
|
||||||
|
ctor = "0.2.6"
|
||||||
|
dashmap = "5.5.3"
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
|
dirs = "4.0"
|
||||||
emojis = "0.6.1"
|
emojis = "0.6.1"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
exec = "0.3.1"
|
exec = "0.3.1"
|
||||||
@@ -287,16 +307,17 @@ fork = "0.1.23"
|
|||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
futures-batch = "0.6.1"
|
futures-batch = "0.6.1"
|
||||||
futures-lite = "1.13"
|
futures-lite = "1.13"
|
||||||
git2 = { version = "0.18", default-features = false }
|
git2 = { version = "0.19", default-features = false }
|
||||||
globset = "0.4"
|
globset = "0.4"
|
||||||
heed = { version = "0.20.1", features = ["read-txn-no-tls"] }
|
heed = { version = "0.20.1", features = ["read-txn-no-tls"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
html5ever = "0.27.0"
|
html5ever = "0.27.0"
|
||||||
ignore = "0.4.22"
|
ignore = "0.4.22"
|
||||||
|
image = "0.25.1"
|
||||||
|
indexmap = { version = "1.6.2", features = ["serde"] }
|
||||||
indoc = "1"
|
indoc = "1"
|
||||||
# We explicitly disable http2 support in isahc.
|
# We explicitly disable http2 support in isahc.
|
||||||
isahc = { version = "1.7.2", default-features = false, features = [
|
isahc = { version = "1.7.2", default-features = false, features = [
|
||||||
"static-curl",
|
|
||||||
"text-decoding",
|
"text-decoding",
|
||||||
] }
|
] }
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
@@ -307,6 +328,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
|||||||
markup5ever_rcdom = "0.3.0"
|
markup5ever_rcdom = "0.3.0"
|
||||||
nanoid = "0.4"
|
nanoid = "0.4"
|
||||||
nix = "0.28"
|
nix = "0.28"
|
||||||
|
num-format = "0.4.4"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
ordered-float = "2.1.1"
|
ordered-float = "2.1.1"
|
||||||
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
||||||
@@ -323,6 +345,9 @@ rand = "0.8.5"
|
|||||||
refineable = { path = "./crates/refineable" }
|
refineable = { path = "./crates/refineable" }
|
||||||
regex = "1.5"
|
regex = "1.5"
|
||||||
repair_json = "0.1.0"
|
repair_json = "0.1.0"
|
||||||
|
runtimelib = { version = "0.12", default-features = false, features = [
|
||||||
|
"async-dispatcher-runtime",
|
||||||
|
] }
|
||||||
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
|
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
|
||||||
rust-embed = { version = "8.4", features = ["include-exclude"] }
|
rust-embed = { version = "8.4", features = ["include-exclude"] }
|
||||||
schemars = "0.8"
|
schemars = "0.8"
|
||||||
@@ -338,6 +363,8 @@ serde_repr = "0.1"
|
|||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
shellexpand = "2.1.0"
|
shellexpand = "2.1.0"
|
||||||
shlex = "1.3.0"
|
shlex = "1.3.0"
|
||||||
|
signal-hook = "0.3.17"
|
||||||
|
similar = "1.3"
|
||||||
smallvec = { version = "1.6", features = ["union"] }
|
smallvec = { version = "1.6", features = ["union"] }
|
||||||
smol = "1.2"
|
smol = "1.2"
|
||||||
strum = { version = "0.25.0", features = ["derive"] }
|
strum = { version = "0.25.0", features = ["derive"] }
|
||||||
@@ -399,12 +426,13 @@ wit-component = "0.201"
|
|||||||
sys-locale = "0.3.1"
|
sys-locale = "0.3.1"
|
||||||
|
|
||||||
[workspace.dependencies.windows]
|
[workspace.dependencies.windows]
|
||||||
version = "0.56.0"
|
version = "0.57"
|
||||||
features = [
|
features = [
|
||||||
"implement",
|
"implement",
|
||||||
"Foundation_Numerics",
|
"Foundation_Numerics",
|
||||||
"System",
|
"System",
|
||||||
"System_Threading",
|
"System_Threading",
|
||||||
|
"UI_ViewManagement",
|
||||||
"Wdk_System_SystemServices",
|
"Wdk_System_SystemServices",
|
||||||
"Win32_Globalization",
|
"Win32_Globalization",
|
||||||
"Win32_Graphics_Direct2D",
|
"Win32_Graphics_Direct2D",
|
||||||
@@ -440,11 +468,12 @@ features = [
|
|||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "7b4894ba2ae81b988846676f54c0988d4027ef4f" }
|
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "7b4894ba2ae81b988846676f54c0988d4027ef4f" }
|
||||||
# Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released.
|
# Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released.
|
||||||
pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "30419d07660dc11a21e42ef4a7fa329600cff152" }
|
pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "4968e819c0d9b015437ffc694511e175801a17c7" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
split-debuginfo = "unpacked"
|
split-debuginfo = "unpacked"
|
||||||
debug = "limited"
|
debug = "limited"
|
||||||
|
codegen-units = 16
|
||||||
|
|
||||||
[profile.dev.package]
|
[profile.dev.package]
|
||||||
taffy = { opt-level = 3 }
|
taffy = { opt-level = 3 }
|
||||||
@@ -463,6 +492,11 @@ codegen-units = 1
|
|||||||
[profile.release.package]
|
[profile.release.package]
|
||||||
zed = { codegen-units = 16 }
|
zed = { codegen-units = 16 }
|
||||||
|
|
||||||
|
[profile.release-fast]
|
||||||
|
inherits = "release"
|
||||||
|
lto = false
|
||||||
|
codegen-units = 16
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
dbg_macro = "deny"
|
dbg_macro = "deny"
|
||||||
todo = "deny"
|
todo = "deny"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# syntax = docker/dockerfile:1.2
|
# syntax = docker/dockerfile:1.2
|
||||||
|
|
||||||
FROM rust:1.78-bookworm as builder
|
FROM rust:1.79-bookworm as builder
|
||||||
WORKDIR app
|
WORKDIR app
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|||||||
32
README.md
@@ -4,42 +4,36 @@
|
|||||||
|
|
||||||
Welcome to Zed, a high-performance, multiplayer code editor from the creators of [Atom](https://github.com/atom/atom) and [Tree-sitter](https://github.com/tree-sitter/tree-sitter).
|
Welcome to Zed, a high-performance, multiplayer code editor from the creators of [Atom](https://github.com/atom/atom) and [Tree-sitter](https://github.com/tree-sitter/tree-sitter).
|
||||||
|
|
||||||
## Installation
|
--------
|
||||||
|
|
||||||
You can [download](https://zed.dev/download) Zed today for macOS (v10.15+).
|
### Installation
|
||||||
|
|
||||||
Support for additional platforms is on our [roadmap](https://zed.dev/roadmap):
|
|
||||||
|
|
||||||
- Linux ([tracking issue](https://github.com/zed-industries/zed/issues/7015))
|
<a href="https://repology.org/project/zed-editor/versions">
|
||||||
|
<img src="https://repology.org/badge/vertical-allrepos/zed-editor.svg?minversion=0.143.5" alt="Packaging status" align="right">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
On macOS and Linux you can [download Zed directly](https://zed.dev/download) or [install Zed via your local package manager](https://zed.dev/docs/linux#installing-via-a-package-manager).
|
||||||
|
|
||||||
|
Other platforms are not yet available:
|
||||||
|
|
||||||
- Windows ([tracking issue](https://github.com/zed-industries/zed/issues/5394))
|
- Windows ([tracking issue](https://github.com/zed-industries/zed/issues/5394))
|
||||||
- Web ([tracking issue](https://github.com/zed-industries/zed/issues/5396))
|
- Web ([tracking issue](https://github.com/zed-industries/zed/issues/5396))
|
||||||
|
|
||||||
For macOS users, you can also install Zed using [Homebrew](https://brew.sh/):
|
### Developing Zed
|
||||||
|
|
||||||
```sh
|
|
||||||
brew install --cask zed
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, to install the Preview release:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
brew install --cask zed@preview
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing Zed
|
|
||||||
|
|
||||||
- [Building Zed for macOS](./docs/src/development/macos.md)
|
- [Building Zed for macOS](./docs/src/development/macos.md)
|
||||||
- [Building Zed for Linux](./docs/src/development/linux.md)
|
- [Building Zed for Linux](./docs/src/development/linux.md)
|
||||||
- [Building Zed for Windows](./docs/src/development/windows.md)
|
- [Building Zed for Windows](./docs/src/development/windows.md)
|
||||||
- [Running Collaboration Locally](./docs/src/development/local-collaboration.md)
|
- [Running Collaboration Locally](./docs/src/development/local-collaboration.md)
|
||||||
|
|
||||||
## Contributing
|
### Contributing
|
||||||
|
|
||||||
See [CONTRIBUTING.md](./CONTRIBUTING.md) for ways you can contribute to Zed.
|
See [CONTRIBUTING.md](./CONTRIBUTING.md) for ways you can contribute to Zed.
|
||||||
|
|
||||||
Also... we're hiring! Check out our [jobs](https://zed.dev/jobs) page for open roles.
|
Also... we're hiring! Check out our [jobs](https://zed.dev/jobs) page for open roles.
|
||||||
|
|
||||||
## Licensing
|
### Licensing
|
||||||
|
|
||||||
License information for third party dependencies must be correctly provided for CI to pass.
|
License information for third party dependencies must be correctly provided for CI to pass.
|
||||||
|
|
||||||
|
|||||||
BIN
assets/fonts/plex-mono/ZedPlexMono-Bold.ttf
Normal file
BIN
assets/fonts/plex-mono/ZedPlexMono-BoldItalic.ttf
Normal file
BIN
assets/fonts/plex-mono/ZedPlexMono-Italic.ttf
Normal file
BIN
assets/fonts/plex-mono/ZedPlexMono-Regular.ttf
Normal file
92
assets/fonts/plex-mono/license.txt
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
assets/fonts/plex-sans/ZedPlexSans-Bold.ttf
Normal file
BIN
assets/fonts/plex-sans/ZedPlexSans-BoldItalic.ttf
Normal file
BIN
assets/fonts/plex-sans/ZedPlexSans-Italic.ttf
Normal file
BIN
assets/fonts/plex-sans/ZedPlexSans-Regular.ttf
Normal file
92
assets/fonts/plex-sans/license.txt
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
1
assets/icons/book.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-book"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/></svg>
|
||||||
|
After Width: | Height: | Size: 289 B |
1
assets/icons/book_copy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-book-copy"><path d="M2 16V4a2 2 0 0 1 2-2h11"/><path d="M5 14H4a2 2 0 1 0 0 4h1"/><path d="M22 18H11a2 2 0 1 0 0 4h11V6H11a2 2 0 0 0-2 2v12"/></svg>
|
||||||
|
After Width: | Height: | Size: 351 B |
1
assets/icons/book_plus.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-book-plus"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/><path d="M9 10h6"/><path d="M12 7v6"/></svg>
|
||||||
|
After Width: | Height: | Size: 332 B |
1
assets/icons/chevron_up_down.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevrons-up-down"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
|
||||||
|
After Width: | Height: | Size: 276 B |
6
assets/icons/context.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.6667 2H3.33333C2.59695 2 2 2.59695 2 3.33333V12.6667C2 13.403 2.59695 14 3.33333 14H12.6667C13.403 14 14 13.403 14 12.6667V3.33333C14 2.59695 13.403 2 12.6667 2Z" stroke="#888888" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9 5H5" stroke="#888888" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10.5 8H5" stroke="#888888" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9 10.9502H5" stroke="#888888" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 683 B |
@@ -94,6 +94,7 @@
|
|||||||
"lua": "lua",
|
"lua": "lua",
|
||||||
"m4a": "audio",
|
"m4a": "audio",
|
||||||
"m4v": "video",
|
"m4v": "video",
|
||||||
|
"markdown": "document",
|
||||||
"md": "document",
|
"md": "document",
|
||||||
"mdb": "storage",
|
"mdb": "storage",
|
||||||
"mdf": "storage",
|
"mdf": "storage",
|
||||||
|
|||||||
1
assets/icons/font.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-type"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" x2="15" y1="20" y2="20"/><line x1="12" x2="12" y1="4" y2="20"/></svg>
|
||||||
|
After Width: | Height: | Size: 329 B |
1
assets/icons/font_size.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-a-large-small"><path d="M21 14h-5"/><path d="M16 16v-3.5a2.5 2.5 0 0 1 5 0V16"/><path d="M4.5 13h6"/><path d="m3 16 4.5-9 4.5 9"/></svg>
|
||||||
|
After Width: | Height: | Size: 339 B |
1
assets/icons/font_weight.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bold"><path d="M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8"/></svg>
|
||||||
|
After Width: | Height: | Size: 296 B |
4
assets/icons/generic_close.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.5 4.5L4.5 11.5" stroke="black" stroke-linecap="square" stroke-linejoin="round"/>
|
||||||
|
<path d="M4.5 4.5L11.5 11.5" stroke="black" stroke-linecap="square" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 291 B |
3
assets/icons/generic_maximize.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.5 4.5H4.5V11.5H11.5V4.5Z" stroke="#FBF1C7"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 161 B |
3
assets/icons/generic_minimize.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4 8H12" stroke="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 138 B |
4
assets/icons/generic_restore.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9.5 6.5H3.5V12.5H9.5V6.5Z" stroke="#FBF1C7"/>
|
||||||
|
<path d="M10 8.5L12.5 8.5L12.5 3.5L7.5 3.5L7.5 6" stroke="#FBF1C7"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 228 B |
6
assets/icons/line_height.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4 13.6667H12" stroke="#B3B3B3" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M4 2.33333H12" stroke="#B3B3B3" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M5 11L8 5L11 11" stroke="#B3B3B3" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M6 9H10" stroke="#B3B3B3" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 539 B |
1
assets/icons/list_tree.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list-tree"><path d="M21 12h-8"/><path d="M21 6H8"/><path d="M21 18h-8"/><path d="M3 6v4c0 1.1.9 2 2 2h3"/><path d="M3 10v6c0 1.1.9 2 2 2h3"/></svg>
|
||||||
|
After Width: | Height: | Size: 349 B |
1
assets/icons/rotate_ccw.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
|
||||||
|
After Width: | Height: | Size: 302 B |
1
assets/icons/rotate_cw.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-cw"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg>
|
||||||
|
After Width: | Height: | Size: 303 B |
3
assets/icons/stop.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9.88889 1H2.11111C1.49746 1 1 1.49746 1 2.11111V9.88889C1 10.5025 1.49746 11 2.11111 11H9.88889C10.5025 11 11 10.5025 11 9.88889V2.11111C11 1.49746 10.5025 1 9.88889 1Z" stroke="#C56757" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 369 B |
1
assets/icons/text-cursor.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-text-cursor"><path d="M17 22h-1a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h1"/><path d="M7 22h1a4 4 0 0 0 4-4v-1"/><path d="M7 2h1a4 4 0 0 1 4 4v1"/></svg>
|
||||||
|
After Width: | Height: | Size: 345 B |
1
assets/icons/visible.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eye"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||||
|
After Width: | Height: | Size: 301 B |
@@ -1,13 +1,16 @@
|
|||||||
[
|
[
|
||||||
// todo(linux): Review the editor bindings
|
|
||||||
// Standard Linux bindings
|
// Standard Linux bindings
|
||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"up": "menu::SelectPrev",
|
"up": "menu::SelectPrev",
|
||||||
|
"shift-tab": "menu::SelectPrev",
|
||||||
|
"home": "menu::SelectFirst",
|
||||||
"pageup": "menu::SelectFirst",
|
"pageup": "menu::SelectFirst",
|
||||||
"shift-pageup": "menu::SelectFirst",
|
"shift-pageup": "menu::SelectFirst",
|
||||||
"ctrl-p": "menu::SelectPrev",
|
"ctrl-p": "menu::SelectPrev",
|
||||||
"down": "menu::SelectNext",
|
"down": "menu::SelectNext",
|
||||||
|
"tab": "menu::SelectNext",
|
||||||
|
"end": "menu::SelectLast",
|
||||||
"pagedown": "menu::SelectLast",
|
"pagedown": "menu::SelectLast",
|
||||||
"shift-pagedown": "menu::SelectFirst",
|
"shift-pagedown": "menu::SelectFirst",
|
||||||
"ctrl-n": "menu::SelectNext",
|
"ctrl-n": "menu::SelectNext",
|
||||||
@@ -42,41 +45,34 @@
|
|||||||
"tab": "editor::Tab",
|
"tab": "editor::Tab",
|
||||||
"shift-tab": "editor::TabPrev",
|
"shift-tab": "editor::TabPrev",
|
||||||
"ctrl-k": "editor::CutToEndOfLine",
|
"ctrl-k": "editor::CutToEndOfLine",
|
||||||
"ctrl-t": "editor::Transpose",
|
// "ctrl-t": "editor::Transpose",
|
||||||
// "ctrl-backspace": "editor::DeleteToBeginningOfLine",
|
|
||||||
// "ctrl-delete": "editor::DeleteToEndOfLine",
|
|
||||||
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
|
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
|
||||||
// "ctrl-w": "editor::DeleteToPreviousWordStart",
|
|
||||||
"ctrl-delete": "editor::DeleteToNextWordEnd",
|
"ctrl-delete": "editor::DeleteToNextWordEnd",
|
||||||
// "alt-h": "editor::DeleteToPreviousWordStart",
|
"shift-delete": "editor::Cut",
|
||||||
// "alt-d": "editor::DeleteToNextWordEnd",
|
|
||||||
"ctrl-x": "editor::Cut",
|
"ctrl-x": "editor::Cut",
|
||||||
"ctrl-c": "editor::Copy",
|
|
||||||
"ctrl-insert": "editor::Copy",
|
"ctrl-insert": "editor::Copy",
|
||||||
"ctrl-v": "editor::Paste",
|
"ctrl-c": "editor::Copy",
|
||||||
"shift-insert": "editor::Paste",
|
"shift-insert": "editor::Paste",
|
||||||
|
"ctrl-v": "editor::Paste",
|
||||||
|
"ctrl-y": "editor::Redo",
|
||||||
"ctrl-z": "editor::Undo",
|
"ctrl-z": "editor::Undo",
|
||||||
"ctrl-shift-z": "editor::Redo",
|
"ctrl-shift-z": "editor::Redo",
|
||||||
"up": "editor::MoveUp",
|
"up": "editor::MoveUp",
|
||||||
// "ctrl-up": "editor::MoveToStartOfParagraph", todo(linux) Should be "scroll down by 1 line"
|
"ctrl-up": "editor::LineUp",
|
||||||
"pageup": "editor::PageUp",
|
"ctrl-down": "editor::LineDown",
|
||||||
// "shift-pageup": "editor::MovePageUp", todo(linux) should be 'select page up'
|
"pageup": "editor::MovePageUp",
|
||||||
|
"alt-pageup": "editor::PageUp",
|
||||||
|
"shift-pageup": "editor::SelectPageUp",
|
||||||
"home": "editor::MoveToBeginningOfLine",
|
"home": "editor::MoveToBeginningOfLine",
|
||||||
"down": "editor::MoveDown",
|
"down": "editor::MoveDown",
|
||||||
// "ctrl-down": "editor::MoveToEndOfParagraph", todo(linux) should be "scroll up by 1 line"
|
"pagedown": "editor::MovePageDown",
|
||||||
"pagedown": "editor::PageDown",
|
"alt-pagedown": "editor::PageDown",
|
||||||
// "shift-pagedown": "editor::MovePageDown", todo(linux) should be 'select page down'
|
"shift-pagedown": "editor::SelectPageDown",
|
||||||
"end": "editor::MoveToEndOfLine",
|
"end": "editor::MoveToEndOfLine",
|
||||||
"left": "editor::MoveLeft",
|
"left": "editor::MoveLeft",
|
||||||
"right": "editor::MoveRight",
|
"right": "editor::MoveRight",
|
||||||
"ctrl-left": "editor::MoveToPreviousWordStart",
|
"ctrl-left": "editor::MoveToPreviousWordStart",
|
||||||
// "alt-b": "editor::MoveToPreviousWordStart",
|
|
||||||
"ctrl-right": "editor::MoveToNextWordEnd",
|
"ctrl-right": "editor::MoveToNextWordEnd",
|
||||||
// "alt-f": "editor::MoveToNextWordEnd",
|
|
||||||
// "cmd-left": "editor::MoveToBeginningOfLine",
|
|
||||||
// "ctrl-a": "editor::MoveToBeginningOfLine",
|
|
||||||
// "cmd-right": "editor::MoveToEndOfLine",
|
|
||||||
// "ctrl-e": "editor::MoveToEndOfLine",
|
|
||||||
"ctrl-home": "editor::MoveToBeginning",
|
"ctrl-home": "editor::MoveToBeginning",
|
||||||
"ctrl-end": "editor::MoveToEnd",
|
"ctrl-end": "editor::MoveToEnd",
|
||||||
"shift-up": "editor::SelectUp",
|
"shift-up": "editor::SelectUp",
|
||||||
@@ -87,61 +83,24 @@
|
|||||||
"ctrl-shift-right": "editor::SelectToNextWordEnd",
|
"ctrl-shift-right": "editor::SelectToNextWordEnd",
|
||||||
"ctrl-shift-up": "editor::AddSelectionAbove",
|
"ctrl-shift-up": "editor::AddSelectionAbove",
|
||||||
"ctrl-shift-down": "editor::AddSelectionBelow",
|
"ctrl-shift-down": "editor::AddSelectionBelow",
|
||||||
// "ctrl-shift-up": "editor::SelectToStartOfParagraph",
|
|
||||||
// "ctrl-shift-down": "editor::SelectToEndOfParagraph",
|
|
||||||
"ctrl-shift-home": "editor::SelectToBeginning",
|
"ctrl-shift-home": "editor::SelectToBeginning",
|
||||||
"ctrl-shift-end": "editor::SelectToEnd",
|
"ctrl-shift-end": "editor::SelectToEnd",
|
||||||
"ctrl-a": "editor::SelectAll",
|
"ctrl-a": "editor::SelectAll",
|
||||||
"ctrl-l": "editor::SelectLine",
|
"ctrl-l": "editor::SelectLine",
|
||||||
"ctrl-shift-i": "editor::Format",
|
"ctrl-shift-i": "editor::Format",
|
||||||
// "cmd-shift-left": [
|
// "cmd-shift-left": ["editor::SelectToBeginningOfLine", {"stop_at_soft_wraps": true }],
|
||||||
// "editor::SelectToBeginningOfLine",
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
// {
|
// "ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
// "stop_at_soft_wraps": true
|
// "cmd-shift-right": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
// }
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
// ],
|
// "ctrl-shift-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"shift-home": [
|
// "alt-v": ["editor::MovePageUp", { "center_cursor": true }],
|
||||||
"editor::SelectToBeginningOfLine",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// "ctrl-shift-a": [
|
|
||||||
// "editor::SelectToBeginningOfLine",
|
|
||||||
// {
|
|
||||||
// "stop_at_soft_wraps": true
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// "cmd-shift-right": [
|
|
||||||
// "editor::SelectToEndOfLine",
|
|
||||||
// {
|
|
||||||
// "stop_at_soft_wraps": true
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
"shift-end": [
|
|
||||||
"editor::SelectToEndOfLine",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// "ctrl-shift-e": [
|
|
||||||
// "editor::SelectToEndOfLine",
|
|
||||||
// {
|
|
||||||
// "stop_at_soft_wraps": true
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// "alt-v": [
|
|
||||||
// "editor::MovePageUp",
|
|
||||||
// {
|
|
||||||
// "center_cursor": true
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
"ctrl-alt-space": "editor::ShowCharacterPalette",
|
"ctrl-alt-space": "editor::ShowCharacterPalette",
|
||||||
"ctrl-;": "editor::ToggleLineNumbers",
|
"ctrl-;": "editor::ToggleLineNumbers",
|
||||||
"ctrl-k ctrl-r": "editor::RevertSelectedHunks",
|
"ctrl-k ctrl-r": "editor::RevertSelectedHunks",
|
||||||
"ctrl-'": "editor::ToggleHunkDiff",
|
"ctrl-'": "editor::ToggleHunkDiff",
|
||||||
"ctrl-\"": "editor::ExpandAllHunkDiffs",
|
"ctrl-\"": "editor::ExpandAllHunkDiffs",
|
||||||
"ctrl-alt-g b": "editor::ToggleGitBlame"
|
"alt-g b": "editor::ToggleGitBlame"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -153,19 +112,11 @@
|
|||||||
"ctrl-enter": "editor::NewlineAbove",
|
"ctrl-enter": "editor::NewlineAbove",
|
||||||
"alt-z": "editor::ToggleSoftWrap",
|
"alt-z": "editor::ToggleSoftWrap",
|
||||||
"ctrl-f": "buffer_search::Deploy",
|
"ctrl-f": "buffer_search::Deploy",
|
||||||
"ctrl-h": [
|
"ctrl-h": ["buffer_search::Deploy", { "replace_enabled": true }],
|
||||||
"buffer_search::Deploy",
|
// "cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
||||||
{
|
"ctrl->": "assistant::QuoteSelection",
|
||||||
"replace_enabled": true
|
"ctrl-<": "assistant::InsertIntoEditor",
|
||||||
}
|
"ctrl-alt-e": "editor::SelectEnclosingSymbol"
|
||||||
],
|
|
||||||
// "cmd-e": [
|
|
||||||
// "buffer_search::Deploy",
|
|
||||||
// {
|
|
||||||
// "focus": false
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
"ctrl->": "assistant::QuoteSelection"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -277,6 +228,7 @@
|
|||||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||||
"ctrl-w": "pane::CloseActiveItem",
|
"ctrl-w": "pane::CloseActiveItem",
|
||||||
|
"ctrl-f4": "pane::CloseActiveItem",
|
||||||
"alt-ctrl-t": "pane::CloseInactiveItems",
|
"alt-ctrl-t": "pane::CloseInactiveItems",
|
||||||
"alt-ctrl-shift-w": "workspace::CloseInactiveTabsAndPanes",
|
"alt-ctrl-shift-w": "workspace::CloseInactiveTabsAndPanes",
|
||||||
"ctrl-k u": "pane::CloseCleanItems",
|
"ctrl-k u": "pane::CloseCleanItems",
|
||||||
@@ -295,6 +247,13 @@
|
|||||||
"ctrl-alt-shift-x": "search::ToggleRegex"
|
"ctrl-alt-shift-x": "search::ToggleRegex"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "Terminal",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-w": ["terminal::SendKeystroke", "ctrl-w"],
|
||||||
|
"ctrl-e": ["terminal::SendKeystroke", "ctrl-e"]
|
||||||
|
}
|
||||||
|
},
|
||||||
// Bindings from VS Code
|
// Bindings from VS Code
|
||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
@@ -312,38 +271,13 @@
|
|||||||
"ctrl-shift-right": "editor::SelectToNextWordEnd",
|
"ctrl-shift-right": "editor::SelectToNextWordEnd",
|
||||||
"ctrl-shift-up": "editor::SelectLargerSyntaxNode", //todo(linux) tmp keybinding
|
"ctrl-shift-up": "editor::SelectLargerSyntaxNode", //todo(linux) tmp keybinding
|
||||||
"ctrl-shift-down": "editor::SelectSmallerSyntaxNode", //todo(linux) tmp keybinding
|
"ctrl-shift-down": "editor::SelectSmallerSyntaxNode", //todo(linux) tmp keybinding
|
||||||
"ctrl-d": [
|
"ctrl-d": ["editor::SelectNext", { "replace_newest": false }],
|
||||||
"editor::SelectNext",
|
|
||||||
{
|
|
||||||
"replace_newest": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-shift-l": "editor::SelectAllMatches",
|
"ctrl-shift-l": "editor::SelectAllMatches",
|
||||||
"ctrl-shift-d": [
|
"ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||||
"editor::SelectPrevious",
|
"ctrl-k ctrl-d": ["editor::SelectNext", { "replace_newest": true }],
|
||||||
{
|
"ctrl-k ctrl-shift-d": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||||
"replace_newest": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-k ctrl-d": [
|
|
||||||
"editor::SelectNext",
|
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-k ctrl-shift-d": [
|
|
||||||
"editor::SelectPrevious",
|
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-k ctrl-i": "editor::Hover",
|
"ctrl-k ctrl-i": "editor::Hover",
|
||||||
"ctrl-/": [
|
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": false }],
|
||||||
"editor::ToggleComments",
|
|
||||||
{
|
|
||||||
"advance_downwards": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-u": "editor::UndoSelection",
|
"ctrl-u": "editor::UndoSelection",
|
||||||
"ctrl-shift-u": "editor::RedoSelection",
|
"ctrl-shift-u": "editor::RedoSelection",
|
||||||
"f8": "editor::GoToDiagnostic",
|
"f8": "editor::GoToDiagnostic",
|
||||||
@@ -351,16 +285,23 @@
|
|||||||
"f2": "editor::Rename",
|
"f2": "editor::Rename",
|
||||||
"f12": "editor::GoToDefinition",
|
"f12": "editor::GoToDefinition",
|
||||||
"alt-f12": "editor::GoToDefinitionSplit",
|
"alt-f12": "editor::GoToDefinitionSplit",
|
||||||
|
"ctrl-shift-f10": "editor::GoToDefinitionSplit",
|
||||||
"ctrl-f12": "editor::GoToTypeDefinition",
|
"ctrl-f12": "editor::GoToTypeDefinition",
|
||||||
"shift-f12": "editor::GoToImplementation",
|
"shift-f12": "editor::GoToImplementation",
|
||||||
"alt-ctrl-f12": "editor::GoToTypeDefinitionSplit",
|
"alt-ctrl-f12": "editor::GoToTypeDefinitionSplit",
|
||||||
"alt-shift-f12": "editor::FindAllReferences",
|
"alt-shift-f12": "editor::FindAllReferences",
|
||||||
"ctrl-m": "editor::MoveToEnclosingBracket",
|
"ctrl-m": "editor::MoveToEnclosingBracket",
|
||||||
|
"ctrl-shift-\\": "editor::MoveToEnclosingBracket",
|
||||||
"ctrl-shift-[": "editor::Fold",
|
"ctrl-shift-[": "editor::Fold",
|
||||||
"ctrl-shift-]": "editor::UnfoldLines",
|
"ctrl-shift-]": "editor::UnfoldLines",
|
||||||
"ctrl-space": "editor::ShowCompletions",
|
"ctrl-space": "editor::ShowCompletions",
|
||||||
"ctrl-.": "editor::ToggleCodeActions",
|
"ctrl-.": "editor::ToggleCodeActions",
|
||||||
"alt-ctrl-r": "editor::RevealInFinder",
|
"alt-ctrl-r": "editor::RevealInFileManager",
|
||||||
|
"ctrl-k r": "editor::RevealInFileManager",
|
||||||
|
"ctrl-k p": "editor::CopyPath",
|
||||||
|
"ctrl-\\": "pane::SplitRight",
|
||||||
|
"ctrl-k v": "markdown::OpenPreviewToTheSide",
|
||||||
|
"ctrl-shift-v": "markdown::OpenPreview",
|
||||||
"ctrl-alt-shift-c": "editor::DisplayCursorNames"
|
"ctrl-alt-shift-c": "editor::DisplayCursorNames"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -385,8 +326,10 @@
|
|||||||
"alt-9": ["pane::ActivateItem", 8],
|
"alt-9": ["pane::ActivateItem", 8],
|
||||||
"alt-0": "pane::ActivateLastItem",
|
"alt-0": "pane::ActivateLastItem",
|
||||||
"ctrl-alt--": "pane::GoBack",
|
"ctrl-alt--": "pane::GoBack",
|
||||||
"ctrl-alt-_": "pane::GoForward",
|
"ctrl-alt-shift--": "pane::GoForward",
|
||||||
"ctrl-shift-t": "pane::ReopenClosedItem",
|
"ctrl-shift-t": "pane::ReopenClosedItem",
|
||||||
|
"f3": "search::SelectNextMatch",
|
||||||
|
"shift-f3": "search::SelectPrevMatch",
|
||||||
"ctrl-shift-f": "project_search::ToggleFocus"
|
"ctrl-shift-f": "project_search::ToggleFocus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -394,12 +337,7 @@
|
|||||||
"context": "Workspace",
|
"context": "Workspace",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
// Change the default action on `menu::Confirm` by setting the parameter
|
// Change the default action on `menu::Confirm` by setting the parameter
|
||||||
// "alt-cmd-o": [
|
// "alt-ctrl-o": ["projects::OpenRecent", { "create_new_window": true }],
|
||||||
// "projects::OpenRecent",
|
|
||||||
// {
|
|
||||||
// "create_new_window": true
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
"alt-ctrl-o": "projects::OpenRecent",
|
"alt-ctrl-o": "projects::OpenRecent",
|
||||||
"alt-ctrl-shift-b": "branches::OpenRecent",
|
"alt-ctrl-shift-b": "branches::OpenRecent",
|
||||||
"ctrl-~": "workspace::NewTerminal",
|
"ctrl-~": "workspace::NewTerminal",
|
||||||
@@ -418,27 +356,24 @@
|
|||||||
"alt-7": ["workspace::ActivatePane", 6],
|
"alt-7": ["workspace::ActivatePane", 6],
|
||||||
"alt-8": ["workspace::ActivatePane", 7],
|
"alt-8": ["workspace::ActivatePane", 7],
|
||||||
"alt-9": ["workspace::ActivatePane", 8],
|
"alt-9": ["workspace::ActivatePane", 8],
|
||||||
"ctrl-alt-b": "workspace::ToggleLeftDock",
|
"ctrl-alt-b": "workspace::ToggleRightDock",
|
||||||
"ctrl-b": "workspace::ToggleRightDock",
|
"ctrl-b": "workspace::ToggleLeftDock",
|
||||||
"ctrl-j": "workspace::ToggleBottomDock",
|
"ctrl-j": "workspace::ToggleBottomDock",
|
||||||
"ctrl-alt-y": "workspace::CloseAllDocks",
|
"ctrl-alt-y": "workspace::CloseAllDocks",
|
||||||
"ctrl-shift-f": "pane::DeploySearch",
|
"ctrl-shift-f": "pane::DeploySearch",
|
||||||
"ctrl-shift-h": [
|
"ctrl-shift-h": ["pane::DeploySearch", { "replace_enabled": true }],
|
||||||
"pane::DeploySearch",
|
|
||||||
{
|
|
||||||
"replace_enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-k ctrl-s": "zed::OpenKeymap",
|
"ctrl-k ctrl-s": "zed::OpenKeymap",
|
||||||
"ctrl-k ctrl-t": "theme_selector::Toggle",
|
"ctrl-k ctrl-t": "theme_selector::Toggle",
|
||||||
"ctrl-shift-t": "project_symbols::Toggle",
|
"ctrl-t": "project_symbols::Toggle",
|
||||||
"ctrl-p": "file_finder::Toggle",
|
"ctrl-p": "file_finder::Toggle",
|
||||||
"ctrl-tab": "tab_switcher::Toggle",
|
"ctrl-tab": "tab_switcher::Toggle",
|
||||||
"ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
|
"ctrl-shift-tab": ["tab_switcher::Toggle", { "select_last": true }],
|
||||||
"ctrl-e": "file_finder::Toggle",
|
"ctrl-e": "file_finder::Toggle",
|
||||||
"ctrl-shift-p": "command_palette::Toggle",
|
"ctrl-shift-p": "command_palette::Toggle",
|
||||||
|
"f1": "command_palette::Toggle",
|
||||||
"ctrl-shift-m": "diagnostics::Deploy",
|
"ctrl-shift-m": "diagnostics::Deploy",
|
||||||
"ctrl-shift-e": "project_panel::ToggleFocus",
|
"ctrl-shift-e": "project_panel::ToggleFocus",
|
||||||
|
"ctrl-shift-b": "outline_panel::ToggleFocus",
|
||||||
"ctrl-?": "assistant::ToggleFocus",
|
"ctrl-?": "assistant::ToggleFocus",
|
||||||
"ctrl-alt-s": "workspace::SaveAll",
|
"ctrl-alt-s": "workspace::SaveAll",
|
||||||
"ctrl-k m": "language_selector::Toggle",
|
"ctrl-k m": "language_selector::Toggle",
|
||||||
@@ -451,6 +386,7 @@
|
|||||||
"ctrl-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
|
"ctrl-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
|
||||||
"ctrl-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
|
"ctrl-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
|
||||||
"ctrl-k shift-down": ["workspace::SwapPaneInDirection", "Down"],
|
"ctrl-k shift-down": ["workspace::SwapPaneInDirection", "Down"],
|
||||||
|
"ctrl-shift-x": "zed::Extensions",
|
||||||
"alt-t": "task::Rerun",
|
"alt-t": "task::Rerun",
|
||||||
"alt-shift-t": "task::Spawn"
|
"alt-shift-t": "task::Spawn"
|
||||||
}
|
}
|
||||||
@@ -467,7 +403,7 @@
|
|||||||
"ctrl-alt-delete": "editor::DeleteToNextSubwordEnd",
|
"ctrl-alt-delete": "editor::DeleteToNextSubwordEnd",
|
||||||
"ctrl-alt-d": "editor::DeleteToNextSubwordEnd",
|
"ctrl-alt-d": "editor::DeleteToNextSubwordEnd",
|
||||||
"ctrl-alt-left": "editor::MoveToPreviousSubwordStart",
|
"ctrl-alt-left": "editor::MoveToPreviousSubwordStart",
|
||||||
"ctrl-alt-b": "editor::MoveToPreviousSubwordStart",
|
// "ctrl-alt-b": "editor::MoveToPreviousSubwordStart",
|
||||||
"ctrl-alt-right": "editor::MoveToNextSubwordEnd",
|
"ctrl-alt-right": "editor::MoveToNextSubwordEnd",
|
||||||
"ctrl-alt-f": "editor::MoveToNextSubwordEnd",
|
"ctrl-alt-f": "editor::MoveToNextSubwordEnd",
|
||||||
"ctrl-alt-shift-left": "editor::SelectToPreviousSubwordStart",
|
"ctrl-alt-shift-left": "editor::SelectToPreviousSubwordStart",
|
||||||
@@ -550,6 +486,7 @@
|
|||||||
"ctrl-enter": "assistant::Assist",
|
"ctrl-enter": "assistant::Assist",
|
||||||
"ctrl-s": "workspace::Save",
|
"ctrl-s": "workspace::Save",
|
||||||
"ctrl->": "assistant::QuoteSelection",
|
"ctrl->": "assistant::QuoteSelection",
|
||||||
|
"ctrl-<": "assistant::InsertIntoEditor",
|
||||||
"shift-enter": "assistant::Split",
|
"shift-enter": "assistant::Split",
|
||||||
"ctrl-r": "assistant::CycleMessageRole",
|
"ctrl-r": "assistant::CycleMessageRole",
|
||||||
"enter": "assistant::ConfirmCommand",
|
"enter": "assistant::ConfirmCommand",
|
||||||
@@ -562,6 +499,20 @@
|
|||||||
"ctrl-enter": "project_search::SearchInNew"
|
"ctrl-enter": "project_search::SearchInNew"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "OutlinePanel",
|
||||||
|
"bindings": {
|
||||||
|
"escape": "menu::Cancel",
|
||||||
|
"left": "outline_panel::CollapseSelectedEntry",
|
||||||
|
"right": "outline_panel::ExpandSelectedEntry",
|
||||||
|
"ctrl-alt-c": "outline_panel::CopyPath",
|
||||||
|
"alt-ctrl-shift-c": "outline_panel::CopyRelativePath",
|
||||||
|
"alt-ctrl-r": "outline_panel::RevealInFileManager",
|
||||||
|
"space": "outline_panel::Open",
|
||||||
|
"shift-down": "menu::SelectNext",
|
||||||
|
"shift-up": "menu::SelectPrev"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "ProjectPanel",
|
"context": "ProjectPanel",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
@@ -578,12 +529,16 @@
|
|||||||
"alt-ctrl-shift-c": "project_panel::CopyRelativePath",
|
"alt-ctrl-shift-c": "project_panel::CopyRelativePath",
|
||||||
"f2": "project_panel::Rename",
|
"f2": "project_panel::Rename",
|
||||||
"enter": "project_panel::Rename",
|
"enter": "project_panel::Rename",
|
||||||
"backspace": "project_panel::Trash",
|
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
"delete": "project_panel::Trash",
|
"shift-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
|
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
"alt-ctrl-r": "project_panel::RevealInFinder",
|
"alt-ctrl-r": "project_panel::RevealInFileManager",
|
||||||
"alt-shift-f": "project_panel::NewSearchInDirectory"
|
"alt-shift-f": "project_panel::NewSearchInDirectory",
|
||||||
|
"shift-down": "menu::SelectNext",
|
||||||
|
"shift-up": "menu::SelectPrev",
|
||||||
|
"escape": "menu::Cancel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -638,13 +593,20 @@
|
|||||||
"ctrl-insert": "terminal::Copy",
|
"ctrl-insert": "terminal::Copy",
|
||||||
"shift-ctrl-v": "terminal::Paste",
|
"shift-ctrl-v": "terminal::Paste",
|
||||||
"shift-insert": "terminal::Paste",
|
"shift-insert": "terminal::Paste",
|
||||||
|
"ctrl-enter": "assistant::InlineAssist",
|
||||||
"up": ["terminal::SendKeystroke", "up"],
|
"up": ["terminal::SendKeystroke", "up"],
|
||||||
"pageup": ["terminal::SendKeystroke", "pageup"],
|
"pageup": ["terminal::SendKeystroke", "pageup"],
|
||||||
"down": ["terminal::SendKeystroke", "down"],
|
"down": ["terminal::SendKeystroke", "down"],
|
||||||
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
||||||
"escape": ["terminal::SendKeystroke", "escape"],
|
"escape": ["terminal::SendKeystroke", "escape"],
|
||||||
"enter": ["terminal::SendKeystroke", "enter"],
|
"enter": ["terminal::SendKeystroke", "enter"],
|
||||||
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"]
|
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
|
||||||
|
"shift-pageup": "terminal::ScrollPageUp",
|
||||||
|
"shift-pagedown": "terminal::ScrollPageDown",
|
||||||
|
"shift-up": "terminal::ScrollLineUp",
|
||||||
|
"shift-down": "terminal::ScrollLineDown",
|
||||||
|
"shift-home": "terminal::ScrollToTop",
|
||||||
|
"shift-end": "terminal::ScrollToBottom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,10 +3,14 @@
|
|||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"up": "menu::SelectPrev",
|
"up": "menu::SelectPrev",
|
||||||
|
"shift-tab": "menu::SelectPrev",
|
||||||
|
"home": "menu::SelectFirst",
|
||||||
"pageup": "menu::SelectFirst",
|
"pageup": "menu::SelectFirst",
|
||||||
"shift-pageup": "menu::SelectFirst",
|
"shift-pageup": "menu::SelectFirst",
|
||||||
"ctrl-p": "menu::SelectPrev",
|
"ctrl-p": "menu::SelectPrev",
|
||||||
"down": "menu::SelectNext",
|
"down": "menu::SelectNext",
|
||||||
|
"tab": "menu::SelectNext",
|
||||||
|
"end": "menu::SelectLast",
|
||||||
"pagedown": "menu::SelectLast",
|
"pagedown": "menu::SelectLast",
|
||||||
"shift-pagedown": "menu::SelectFirst",
|
"shift-pagedown": "menu::SelectFirst",
|
||||||
"ctrl-n": "menu::SelectNext",
|
"ctrl-n": "menu::SelectNext",
|
||||||
@@ -61,13 +65,17 @@
|
|||||||
"cmd-shift-z": "editor::Redo",
|
"cmd-shift-z": "editor::Redo",
|
||||||
"up": "editor::MoveUp",
|
"up": "editor::MoveUp",
|
||||||
"ctrl-up": "editor::MoveToStartOfParagraph",
|
"ctrl-up": "editor::MoveToStartOfParagraph",
|
||||||
"pageup": "editor::PageUp",
|
"pageup": "editor::MovePageUp",
|
||||||
"shift-pageup": "editor::MovePageUp",
|
"shift-pageup": "editor::SelectPageUp",
|
||||||
|
"cmd-pageup": "editor::PageUp",
|
||||||
|
"ctrl-pageup": "editor::LineUp",
|
||||||
"home": "editor::MoveToBeginningOfLine",
|
"home": "editor::MoveToBeginningOfLine",
|
||||||
"down": "editor::MoveDown",
|
"down": "editor::MoveDown",
|
||||||
"ctrl-down": "editor::MoveToEndOfParagraph",
|
"ctrl-down": "editor::MoveToEndOfParagraph",
|
||||||
"pagedown": "editor::PageDown",
|
"pagedown": "editor::MovePageDown",
|
||||||
"shift-pagedown": "editor::MovePageDown",
|
"shift-pagedown": "editor::SelectPageDown",
|
||||||
|
"cmd-pagedown": "editor::PageDown",
|
||||||
|
"ctrl-pagedown": "editor::LineDown",
|
||||||
"end": "editor::MoveToEndOfLine",
|
"end": "editor::MoveToEndOfLine",
|
||||||
"left": "editor::MoveLeft",
|
"left": "editor::MoveLeft",
|
||||||
"right": "editor::MoveRight",
|
"right": "editor::MoveRight",
|
||||||
@@ -105,54 +113,14 @@
|
|||||||
"cmd-a": "editor::SelectAll",
|
"cmd-a": "editor::SelectAll",
|
||||||
"cmd-l": "editor::SelectLine",
|
"cmd-l": "editor::SelectLine",
|
||||||
"cmd-shift-i": "editor::Format",
|
"cmd-shift-i": "editor::Format",
|
||||||
"cmd-shift-left": [
|
"cmd-shift-left": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"editor::SelectToBeginningOfLine",
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
{
|
"ctrl-shift-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"stop_at_soft_wraps": true
|
"cmd-shift-right": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
}
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
],
|
"ctrl-shift-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": true }],
|
||||||
"shift-home": [
|
"ctrl-v": ["editor::MovePageDown", { "center_cursor": true }],
|
||||||
"editor::SelectToBeginningOfLine",
|
"alt-v": ["editor::MovePageUp", { "center_cursor": true }],
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-shift-a": [
|
|
||||||
"editor::SelectToBeginningOfLine",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-shift-right": [
|
|
||||||
"editor::SelectToEndOfLine",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shift-end": [
|
|
||||||
"editor::SelectToEndOfLine",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-shift-e": [
|
|
||||||
"editor::SelectToEndOfLine",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-v": [
|
|
||||||
"editor::MovePageDown",
|
|
||||||
{
|
|
||||||
"center_cursor": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"alt-v": [
|
|
||||||
"editor::MovePageUp",
|
|
||||||
{
|
|
||||||
"center_cursor": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-cmd-space": "editor::ShowCharacterPalette",
|
"ctrl-cmd-space": "editor::ShowCharacterPalette",
|
||||||
"cmd-;": "editor::ToggleLineNumbers",
|
"cmd-;": "editor::ToggleLineNumbers",
|
||||||
"cmd-alt-z": "editor::RevertSelectedHunks",
|
"cmd-alt-z": "editor::RevertSelectedHunks",
|
||||||
@@ -167,28 +135,20 @@
|
|||||||
"enter": "editor::Newline",
|
"enter": "editor::Newline",
|
||||||
"shift-enter": "editor::Newline",
|
"shift-enter": "editor::Newline",
|
||||||
"cmd-shift-enter": "editor::NewlineAbove",
|
"cmd-shift-enter": "editor::NewlineAbove",
|
||||||
"cmd-enter": "editor::NewlineBelow",
|
|
||||||
"alt-z": "editor::ToggleSoftWrap",
|
"alt-z": "editor::ToggleSoftWrap",
|
||||||
"cmd-f": "buffer_search::Deploy",
|
"cmd-f": "buffer_search::Deploy",
|
||||||
"cmd-alt-f": [
|
"cmd-alt-f": ["buffer_search::Deploy", { "replace_enabled": true }],
|
||||||
"buffer_search::Deploy",
|
"cmd-alt-l": ["buffer_search::Deploy", { "selection_search_enabled": true }],
|
||||||
{
|
"cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
||||||
"replace_enabled": true
|
"cmd->": "assistant::QuoteSelection",
|
||||||
}
|
"cmd-<": "assistant::InsertIntoEditor",
|
||||||
],
|
"cmd-alt-e": "editor::SelectEnclosingSymbol"
|
||||||
"cmd-alt-l": [
|
}
|
||||||
"buffer_search::Deploy",
|
},
|
||||||
{
|
{
|
||||||
"selection_search_enabled": true
|
"context": "Editor && mode == full && !jupyter",
|
||||||
}
|
"bindings": {
|
||||||
],
|
"cmd-enter": "editor::NewlineBelow"
|
||||||
"cmd-e": [
|
|
||||||
"buffer_search::Deploy",
|
|
||||||
{
|
|
||||||
"focus": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd->": "assistant::QuoteSelection"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -233,6 +193,7 @@
|
|||||||
"cmd-enter": "assistant::Assist",
|
"cmd-enter": "assistant::Assist",
|
||||||
"cmd-s": "workspace::Save",
|
"cmd-s": "workspace::Save",
|
||||||
"cmd->": "assistant::QuoteSelection",
|
"cmd->": "assistant::QuoteSelection",
|
||||||
|
"cmd-<": "assistant::InsertIntoEditor",
|
||||||
"shift-enter": "assistant::Split",
|
"shift-enter": "assistant::Split",
|
||||||
"ctrl-r": "assistant::CycleMessageRole",
|
"ctrl-r": "assistant::CycleMessageRole",
|
||||||
"enter": "assistant::ConfirmCommand",
|
"enter": "assistant::ConfirmCommand",
|
||||||
@@ -278,6 +239,7 @@
|
|||||||
"context": "ProjectSearchBar",
|
"context": "ProjectSearchBar",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"escape": "project_search::ToggleFocus",
|
"escape": "project_search::ToggleFocus",
|
||||||
|
"cmd-shift-j": "project_search::ToggleFilters",
|
||||||
"cmd-shift-f": "search::FocusSearch",
|
"cmd-shift-f": "search::FocusSearch",
|
||||||
"cmd-shift-h": "search::ToggleReplace",
|
"cmd-shift-h": "search::ToggleReplace",
|
||||||
"alt-cmd-g": "search::ToggleRegex",
|
"alt-cmd-g": "search::ToggleRegex",
|
||||||
@@ -302,6 +264,7 @@
|
|||||||
"context": "ProjectSearchView",
|
"context": "ProjectSearchView",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"escape": "project_search::ToggleFocus",
|
"escape": "project_search::ToggleFocus",
|
||||||
|
"cmd-shift-j": "project_search::ToggleFilters",
|
||||||
"cmd-shift-h": "search::ToggleReplace",
|
"cmd-shift-h": "search::ToggleReplace",
|
||||||
"alt-cmd-g": "search::ToggleRegex",
|
"alt-cmd-g": "search::ToggleRegex",
|
||||||
"alt-cmd-x": "search::ToggleRegex"
|
"alt-cmd-x": "search::ToggleRegex"
|
||||||
@@ -349,38 +312,13 @@
|
|||||||
"alt-shift-down": "editor::DuplicateLineDown",
|
"alt-shift-down": "editor::DuplicateLineDown",
|
||||||
"ctrl-shift-right": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-right": "editor::SelectLargerSyntaxNode",
|
||||||
"ctrl-shift-left": "editor::SelectSmallerSyntaxNode",
|
"ctrl-shift-left": "editor::SelectSmallerSyntaxNode",
|
||||||
"cmd-d": [
|
"cmd-d": ["editor::SelectNext", { "replace_newest": false }],
|
||||||
"editor::SelectNext",
|
|
||||||
{
|
|
||||||
"replace_newest": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-shift-l": "editor::SelectAllMatches",
|
"cmd-shift-l": "editor::SelectAllMatches",
|
||||||
"ctrl-cmd-d": [
|
"ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||||
"editor::SelectPrevious",
|
"cmd-k cmd-d": ["editor::SelectNext", { "replace_newest": true }],
|
||||||
{
|
"cmd-k ctrl-cmd-d": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||||
"replace_newest": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-k cmd-d": [
|
|
||||||
"editor::SelectNext",
|
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-k ctrl-cmd-d": [
|
|
||||||
"editor::SelectPrevious",
|
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-k cmd-i": "editor::Hover",
|
"cmd-k cmd-i": "editor::Hover",
|
||||||
"cmd-/": [
|
"cmd-/": ["editor::ToggleComments", { "advance_downwards": false }],
|
||||||
"editor::ToggleComments",
|
|
||||||
{
|
|
||||||
"advance_downwards": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-u": "editor::UndoSelection",
|
"cmd-u": "editor::UndoSelection",
|
||||||
"cmd-shift-u": "editor::RedoSelection",
|
"cmd-shift-u": "editor::RedoSelection",
|
||||||
"f8": "editor::GoToDiagnostic",
|
"f8": "editor::GoToDiagnostic",
|
||||||
@@ -393,11 +331,17 @@
|
|||||||
"alt-cmd-f12": "editor::GoToTypeDefinitionSplit",
|
"alt-cmd-f12": "editor::GoToTypeDefinitionSplit",
|
||||||
"alt-shift-f12": "editor::FindAllReferences",
|
"alt-shift-f12": "editor::FindAllReferences",
|
||||||
"ctrl-m": "editor::MoveToEnclosingBracket",
|
"ctrl-m": "editor::MoveToEnclosingBracket",
|
||||||
|
"cmd-shift-\\": "editor::MoveToEnclosingBracket",
|
||||||
"alt-cmd-[": "editor::Fold",
|
"alt-cmd-[": "editor::Fold",
|
||||||
"alt-cmd-]": "editor::UnfoldLines",
|
"alt-cmd-]": "editor::UnfoldLines",
|
||||||
"ctrl-space": "editor::ShowCompletions",
|
"ctrl-space": "editor::ShowCompletions",
|
||||||
"cmd-.": "editor::ToggleCodeActions",
|
"cmd-.": "editor::ToggleCodeActions",
|
||||||
"alt-cmd-r": "editor::RevealInFinder",
|
"alt-cmd-r": "editor::RevealInFileManager",
|
||||||
|
"cmd-k r": "editor::RevealInFileManager",
|
||||||
|
"cmd-k p": "editor::CopyPath",
|
||||||
|
"cmd-\\": "pane::SplitRight",
|
||||||
|
"cmd-k v": "markdown::OpenPreviewToTheSide",
|
||||||
|
"cmd-shift-v": "markdown::OpenPreview",
|
||||||
"ctrl-cmd-c": "editor::DisplayCursorNames"
|
"ctrl-cmd-c": "editor::DisplayCursorNames"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -422,7 +366,7 @@
|
|||||||
"ctrl-9": ["pane::ActivateItem", 8],
|
"ctrl-9": ["pane::ActivateItem", 8],
|
||||||
"ctrl-0": "pane::ActivateLastItem",
|
"ctrl-0": "pane::ActivateLastItem",
|
||||||
"ctrl--": "pane::GoBack",
|
"ctrl--": "pane::GoBack",
|
||||||
"ctrl-_": "pane::GoForward",
|
"ctrl-shift--": "pane::GoForward",
|
||||||
"cmd-shift-t": "pane::ReopenClosedItem",
|
"cmd-shift-t": "pane::ReopenClosedItem",
|
||||||
"cmd-shift-f": "project_search::ToggleFocus"
|
"cmd-shift-f": "project_search::ToggleFocus"
|
||||||
}
|
}
|
||||||
@@ -431,12 +375,7 @@
|
|||||||
"context": "Workspace",
|
"context": "Workspace",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
// Change the default action on `menu::Confirm` by setting the parameter
|
// Change the default action on `menu::Confirm` by setting the parameter
|
||||||
// "alt-cmd-o": [
|
// "alt-cmd-o": ["projects::OpenRecent", {"create_new_window": true }],
|
||||||
// "projects::OpenRecent",
|
|
||||||
// {
|
|
||||||
// "create_new_window": true
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
"alt-cmd-o": "projects::OpenRecent",
|
"alt-cmd-o": "projects::OpenRecent",
|
||||||
"alt-cmd-b": "branches::OpenRecent",
|
"alt-cmd-b": "branches::OpenRecent",
|
||||||
"ctrl-~": "workspace::NewTerminal",
|
"ctrl-~": "workspace::NewTerminal",
|
||||||
@@ -460,12 +399,7 @@
|
|||||||
"cmd-j": "workspace::ToggleBottomDock",
|
"cmd-j": "workspace::ToggleBottomDock",
|
||||||
"alt-cmd-y": "workspace::CloseAllDocks",
|
"alt-cmd-y": "workspace::CloseAllDocks",
|
||||||
"cmd-shift-f": "pane::DeploySearch",
|
"cmd-shift-f": "pane::DeploySearch",
|
||||||
"cmd-shift-h": [
|
"cmd-shift-h": ["pane::DeploySearch", { "replace_enabled": true }],
|
||||||
"pane::DeploySearch",
|
|
||||||
{
|
|
||||||
"replace_enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-k cmd-s": "zed::OpenKeymap",
|
"cmd-k cmd-s": "zed::OpenKeymap",
|
||||||
"cmd-k cmd-t": "theme_selector::Toggle",
|
"cmd-k cmd-t": "theme_selector::Toggle",
|
||||||
"cmd-t": "project_symbols::Toggle",
|
"cmd-t": "project_symbols::Toggle",
|
||||||
@@ -475,6 +409,7 @@
|
|||||||
"cmd-shift-p": "command_palette::Toggle",
|
"cmd-shift-p": "command_palette::Toggle",
|
||||||
"cmd-shift-m": "diagnostics::Deploy",
|
"cmd-shift-m": "diagnostics::Deploy",
|
||||||
"cmd-shift-e": "project_panel::ToggleFocus",
|
"cmd-shift-e": "project_panel::ToggleFocus",
|
||||||
|
"cmd-shift-b": "outline_panel::ToggleFocus",
|
||||||
"cmd-?": "assistant::ToggleFocus",
|
"cmd-?": "assistant::ToggleFocus",
|
||||||
"cmd-alt-s": "workspace::SaveAll",
|
"cmd-alt-s": "workspace::SaveAll",
|
||||||
"cmd-k m": "language_selector::Toggle",
|
"cmd-k m": "language_selector::Toggle",
|
||||||
@@ -487,6 +422,7 @@
|
|||||||
"cmd-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
|
"cmd-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
|
||||||
"cmd-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
|
"cmd-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
|
||||||
"cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"],
|
"cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"],
|
||||||
|
"cmd-shift-x": "zed::Extensions",
|
||||||
"alt-t": "task::Rerun",
|
"alt-t": "task::Rerun",
|
||||||
"alt-shift-t": "task::Spawn"
|
"alt-shift-t": "task::Spawn"
|
||||||
}
|
}
|
||||||
@@ -584,12 +520,27 @@
|
|||||||
"cmd-enter": "project_search::SearchInNew"
|
"cmd-enter": "project_search::SearchInNew"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "OutlinePanel",
|
||||||
|
"bindings": {
|
||||||
|
"escape": "menu::Cancel",
|
||||||
|
"left": "outline_panel::CollapseSelectedEntry",
|
||||||
|
"right": "outline_panel::ExpandSelectedEntry",
|
||||||
|
"cmd-alt-c": "outline_panel::CopyPath",
|
||||||
|
"alt-cmd-shift-c": "outline_panel::CopyRelativePath",
|
||||||
|
"alt-cmd-r": "outline_panel::RevealInFileManager",
|
||||||
|
"space": "outline_panel::Open",
|
||||||
|
"shift-down": "menu::SelectNext",
|
||||||
|
"shift-up": "menu::SelectPrev"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "ProjectPanel",
|
"context": "ProjectPanel",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"left": "project_panel::CollapseSelectedEntry",
|
"left": "project_panel::CollapseSelectedEntry",
|
||||||
"right": "project_panel::ExpandSelectedEntry",
|
"right": "project_panel::ExpandSelectedEntry",
|
||||||
"cmd-n": "project_panel::NewFile",
|
"cmd-n": "project_panel::NewFile",
|
||||||
|
"cmd-d": "project_panel::Duplicate",
|
||||||
"alt-cmd-n": "project_panel::NewDirectory",
|
"alt-cmd-n": "project_panel::NewDirectory",
|
||||||
"cmd-x": "project_panel::Cut",
|
"cmd-x": "project_panel::Cut",
|
||||||
"cmd-c": "project_panel::Copy",
|
"cmd-c": "project_panel::Copy",
|
||||||
@@ -597,11 +548,14 @@
|
|||||||
"cmd-alt-c": "project_panel::CopyPath",
|
"cmd-alt-c": "project_panel::CopyPath",
|
||||||
"alt-cmd-shift-c": "project_panel::CopyRelativePath",
|
"alt-cmd-shift-c": "project_panel::CopyRelativePath",
|
||||||
"enter": "project_panel::Rename",
|
"enter": "project_panel::Rename",
|
||||||
|
"f2": "project_panel::Rename",
|
||||||
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
"cmd-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": true }],
|
||||||
"cmd-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
"cmd-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
"alt-cmd-r": "project_panel::RevealInFinder",
|
"alt-cmd-r": "project_panel::RevealInFileManager",
|
||||||
|
"cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
|
|
||||||
"alt-shift-f": "project_panel::NewSearchInDirectory",
|
"alt-shift-f": "project_panel::NewSearchInDirectory",
|
||||||
"shift-down": "menu::SelectNext",
|
"shift-down": "menu::SelectNext",
|
||||||
"shift-up": "menu::SelectPrev",
|
"shift-up": "menu::SelectPrev",
|
||||||
@@ -614,6 +568,12 @@
|
|||||||
"space": "project_panel::Open"
|
"space": "project_panel::Open"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && jupyter",
|
||||||
|
"bindings": {
|
||||||
|
"cmd-enter": "repl::Run"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "CollabPanel && not_editing",
|
"context": "CollabPanel && not_editing",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
@@ -667,6 +627,7 @@
|
|||||||
"cmd-c": "terminal::Copy",
|
"cmd-c": "terminal::Copy",
|
||||||
"cmd-v": "terminal::Paste",
|
"cmd-v": "terminal::Paste",
|
||||||
"cmd-k": "terminal::Clear",
|
"cmd-k": "terminal::Clear",
|
||||||
|
"ctrl-enter": "assistant::InlineAssist",
|
||||||
// Some nice conveniences
|
// Some nice conveniences
|
||||||
"cmd-backspace": ["terminal::SendText", "\u0015"],
|
"cmd-backspace": ["terminal::SendText", "\u0015"],
|
||||||
"cmd-right": ["terminal::SendText", "\u0005"],
|
"cmd-right": ["terminal::SendText", "\u0005"],
|
||||||
@@ -682,7 +643,17 @@
|
|||||||
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
||||||
"escape": ["terminal::SendKeystroke", "escape"],
|
"escape": ["terminal::SendKeystroke", "escape"],
|
||||||
"enter": ["terminal::SendKeystroke", "enter"],
|
"enter": ["terminal::SendKeystroke", "enter"],
|
||||||
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"]
|
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
|
||||||
|
"cmd-up": "terminal::ScrollPageUp",
|
||||||
|
"cmd-down": "terminal::ScrollPageDown",
|
||||||
|
"shift-pageup": "terminal::ScrollPageUp",
|
||||||
|
"shift-pagedown": "terminal::ScrollPageDown",
|
||||||
|
"shift-up": "terminal::ScrollLineUp",
|
||||||
|
"shift-down": "terminal::ScrollLineDown",
|
||||||
|
"cmd-home": "terminal::ScrollToTop",
|
||||||
|
"cmd-end": "terminal::ScrollToBottom",
|
||||||
|
"shift-home": "terminal::ScrollToTop",
|
||||||
|
"shift-end": "terminal::ScrollToBottom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
93
assets/keymaps/linux/atom.json
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Default Keymap (Atom) for Zed on Linux
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-f5": "workspace::Reload", // window:reload
|
||||||
|
"ctrl-k ctrl-n": "workspace::ActivatePreviousPane", // window:focus-next-pane
|
||||||
|
"ctrl-k ctrl-p": "workspace::ActivateNextPane" // window:focus-previous-pane
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-l": "language_selector::Toggle", // grammar-selector:show
|
||||||
|
"ctrl-|": "pane::RevealInProjectPanel", // tree-view:reveal-active-file
|
||||||
|
"ctrl-b": "editor::GoToDefinition", // fuzzy-finder:toggle-buffer-finder
|
||||||
|
"ctrl-alt-b": "editor::GoToDefinitionSplit", // N/A: From JetBrains
|
||||||
|
"ctrl-<": "editor::ScrollCursorCenter", // editor:scroll-to-cursor
|
||||||
|
"f3": ["editor::SelectNext", { "replace_newest": true }], // find-and-replace:find-next
|
||||||
|
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
|
||||||
|
"alt-shift-down": "editor::AddSelectionBelow", // editor:add-selection-below
|
||||||
|
"alt-shift-up": "editor::AddSelectionAbove", // editor:add-selection-above
|
||||||
|
"ctrl-k ctrl-u": "editor::ConvertToUpperCase", // editor:upper-case
|
||||||
|
"ctrl-k ctrl-l": "editor::ConvertToLowerCase", // editor:lower-case
|
||||||
|
"ctrl-j": "editor::JoinLines", // editor:join-lines
|
||||||
|
"ctrl-shift-d": "editor::DuplicateLineDown", // editor:duplicate-lines
|
||||||
|
"ctrl-up": "editor::MoveLineUp", // editor:move-line-up
|
||||||
|
"ctrl-down": "editor::MoveLineDown", // editor:move-line-down
|
||||||
|
"ctrl-shift-m": "markdown::OpenPreviewToTheSide" // markdown-preview:toggle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && mode == full",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-r": "outline::Toggle" // symbols-view:toggle-project-symbols
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "BufferSearchBar",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-f3": "search::SelectNextMatch", // find-and-replace:find-next-selected
|
||||||
|
"ctrl-shift-f3": "search::SelectPrevMatch" // find-and-replace:find-previous-selected
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Workspace",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-\\": "workspace::ToggleLeftDock", // tree-view:toggle
|
||||||
|
"ctrl-k ctrl-b": "workspace::ToggleLeftDock", // tree-view:toggle
|
||||||
|
"ctrl-t": "file_finder::Toggle", // fuzzy-finder:toggle-file-finder
|
||||||
|
"ctrl-r": "project_symbols::Toggle" // symbols-view:toggle-project-symbols
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Pane",
|
||||||
|
"bindings": {
|
||||||
|
// "ctrl-0": "project_panel::ToggleFocus", // tree-view:toggle-focus
|
||||||
|
"ctrl-1": ["pane::ActivateItem", 0], // tree-view:open-selected-entry-in-pane-1
|
||||||
|
"ctrl-2": ["pane::ActivateItem", 1], // tree-view:open-selected-entry-in-pane-2
|
||||||
|
"ctrl-3": ["pane::ActivateItem", 2], // tree-view:open-selected-entry-in-pane-3
|
||||||
|
"ctrl-4": ["pane::ActivateItem", 3], // tree-view:open-selected-entry-in-pane-4
|
||||||
|
"ctrl-5": ["pane::ActivateItem", 4], // tree-view:open-selected-entry-in-pane-5
|
||||||
|
"ctrl-6": ["pane::ActivateItem", 5], // tree-view:open-selected-entry-in-pane-6
|
||||||
|
"ctrl-7": ["pane::ActivateItem", 6], // tree-view:open-selected-entry-in-pane-7
|
||||||
|
"ctrl-8": ["pane::ActivateItem", 7], // tree-view:open-selected-entry-in-pane-8
|
||||||
|
"ctrl-9": ["pane::ActivateItem", 8] // tree-view:open-selected-entry-in-pane-9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "ProjectPanel",
|
||||||
|
"bindings": {
|
||||||
|
"f2": "project_panel::Rename", // tree-view:rename
|
||||||
|
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
|
"ctrl-x": "project_panel::Cut", // tree-view:cut
|
||||||
|
"ctrl-c": "project_panel::Copy", // tree-view:copy
|
||||||
|
"ctrl-v": "project_panel::Paste" // tree-view:paste
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "ProjectPanel && not_editing",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-c": "project_panel::CopyPath", // tree-view:copy-full-path
|
||||||
|
"ctrl-[": "project_panel::CollapseSelectedEntry", // tree-view:collapse-directory
|
||||||
|
"ctrl-b": "project_panel::CollapseSelectedEntry", // tree-view:collapse-directory
|
||||||
|
"ctrl-]": "project_panel::ExpandSelectedEntry", // tree-view:expand-item
|
||||||
|
"ctrl-f": "project_panel::ExpandSelectedEntry", // tree-view:expand-item
|
||||||
|
"a": "project_panel::NewFile", // tree-view:add-file
|
||||||
|
"d": "project_panel::Duplicate", // tree-view:duplicate
|
||||||
|
"home": "menu::SelectFirst", // core:move-to-top
|
||||||
|
"end": "menu::SelectLast", // core:move-to-bottom
|
||||||
|
"shift-a": "project_panel::NewDirectory" // tree-view:add-folder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
90
assets/keymaps/linux/jetbrains.json
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-[": "pane::ActivatePrevItem",
|
||||||
|
"ctrl-shift-]": "pane::ActivateNextItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl->": "zed::IncreaseBufferFontSize",
|
||||||
|
"ctrl-<": "zed::DecreaseBufferFontSize",
|
||||||
|
"ctrl-shift-j": "editor::JoinLines",
|
||||||
|
"ctrl-d": "editor::DuplicateLineDown",
|
||||||
|
"ctrl-y": "editor::DeleteLine",
|
||||||
|
"ctrl-pagedown": "editor::MovePageDown",
|
||||||
|
"ctrl-pageup": "editor::MovePageUp",
|
||||||
|
// "ctrl-alt-shift-b": "editor::SelectToPreviousWordStart",
|
||||||
|
"ctrl-alt-enter": "editor::NewlineAbove",
|
||||||
|
"shift-enter": "editor::NewlineBelow",
|
||||||
|
// "ctrl--": "editor::Fold", // TODO: `ctrl-numpad--` (numpad not implemented)
|
||||||
|
// "ctrl-+": "editor::UnfoldLines", // TODO: `ctrl-numpad+` (numpad not implemented)
|
||||||
|
"alt-shift-g": "editor::SplitSelectionIntoLines",
|
||||||
|
"alt-j": ["editor::SelectNext", { "replace_newest": false }],
|
||||||
|
"alt-shift-j": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||||
|
"ctrl-/": ["editor::ToggleComments", { "advance_downwards": true }],
|
||||||
|
"alt-up": "editor::SelectLargerSyntaxNode",
|
||||||
|
"alt-down": "editor::SelectSmallerSyntaxNode",
|
||||||
|
"shift-alt-up": "editor::MoveLineUp",
|
||||||
|
"shift-alt-down": "editor::MoveLineDown",
|
||||||
|
"ctrl-alt-l": "editor::Format",
|
||||||
|
"shift-f6": "editor::Rename",
|
||||||
|
"ctrl-alt-left": "pane::GoBack",
|
||||||
|
"ctrl-alt-right": "pane::GoForward",
|
||||||
|
"alt-f7": "editor::FindAllReferences",
|
||||||
|
"ctrl-alt-f7": "editor::FindAllReferences",
|
||||||
|
// "ctrl-b": "editor::GoToDefinition", // Conflicts with workspace::ToggleLeftDock
|
||||||
|
// "ctrl-alt-b": "editor::GoToDefinitionSplit", // Conflicts with workspace::ToggleLeftDock
|
||||||
|
"ctrl-shift-b": "editor::GoToTypeDefinition",
|
||||||
|
"ctrl-alt-shift-b": "editor::GoToTypeDefinitionSplit",
|
||||||
|
"f2": "editor::GoToDiagnostic",
|
||||||
|
"shift-f2": "editor::GoToPrevDiagnostic",
|
||||||
|
"ctrl-alt-shift-down": "editor::GoToHunk",
|
||||||
|
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
|
||||||
|
"ctrl-home": "editor::MoveToBeginning",
|
||||||
|
"ctrl-end": "editor::MoveToEnd",
|
||||||
|
"ctrl-shift-home": "editor::SelectToBeginning",
|
||||||
|
"ctrl-shift-end": "editor::SelectToEnd"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && mode == full",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-f12": "outline::Toggle",
|
||||||
|
"alt-7": "outline::Toggle",
|
||||||
|
"ctrl-shift-n": "file_finder::Toggle",
|
||||||
|
"ctrl-g": "go_to_line::Toggle",
|
||||||
|
"alt-enter": "editor::ToggleCodeActions"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Workspace",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-n": "file_finder::Toggle",
|
||||||
|
"ctrl-shift-a": "command_palette::Toggle",
|
||||||
|
"shift shift": "command_palette::Toggle",
|
||||||
|
"ctrl-alt-shift-n": "project_symbols::Toggle",
|
||||||
|
"alt-1": "workspace::ToggleLeftDock",
|
||||||
|
"ctrl-e": "tab_switcher::Toggle",
|
||||||
|
"alt-6": "diagnostics::Deploy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Pane",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-alt-left": "pane::GoBack",
|
||||||
|
"ctrl-alt-right": "pane::GoForward"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "ProjectPanel",
|
||||||
|
"bindings": {
|
||||||
|
"enter": "project_panel::Open",
|
||||||
|
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
|
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
|
"shift-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
|
"shift-f6": "project_panel::Rename"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
53
assets/keymaps/linux/sublime_text.json
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-[": "pane::ActivatePrevItem",
|
||||||
|
"ctrl-shift-]": "pane::ActivateNextItem",
|
||||||
|
"ctrl-pagedown": "pane::ActivatePrevItem",
|
||||||
|
"ctrl-pageup": "pane::ActivateNextItem",
|
||||||
|
"ctrl-shift-tab": "pane::ActivateNextItem",
|
||||||
|
"ctrl-tab": "pane::ActivatePrevItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-up": "editor::AddSelectionAbove",
|
||||||
|
"ctrl-shift-down": "editor::AddSelectionBelow",
|
||||||
|
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
||||||
|
"ctrl-shift-l": "editor::SplitSelectionIntoLines",
|
||||||
|
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",
|
||||||
|
"ctrl-shift-d": "editor::DuplicateLineDown",
|
||||||
|
"f12": "editor::GoToDefinition",
|
||||||
|
"ctrl-f12": "editor::GoToDefinitionSplit",
|
||||||
|
"shift-f12": "editor::FindAllReferences",
|
||||||
|
"ctrl-shift-f12": "editor::FindAllReferences",
|
||||||
|
"ctrl-.": "editor::GoToHunk",
|
||||||
|
"ctrl-,": "editor::GoToPrevHunk",
|
||||||
|
"shift-alt-m": "markdown::OpenPreviewToTheSide",
|
||||||
|
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
|
||||||
|
"ctrl-delete": "editor::DeleteToNextWordEnd"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && mode == full",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-r": "outline::Toggle"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Pane",
|
||||||
|
"bindings": {
|
||||||
|
"f4": "search::SelectNextMatch",
|
||||||
|
"shift-f4": "search::SelectPrevMatch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Workspace",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-k ctrl-b": "workspace::ToggleLeftDock",
|
||||||
|
// "ctrl-0": "project_panel::ToggleFocus", // normally resets zoom
|
||||||
|
"shift-ctrl-r": "project_symbols::Toggle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
// Default Keymap (Atom) for Zed on MacOS
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"ctrl-alt-cmd-l": "workspace::Reload",
|
||||||
"cmd-k cmd-p": "workspace::ActivatePreviousPane",
|
"cmd-k cmd-p": "workspace::ActivatePreviousPane",
|
||||||
"cmd-k cmd-n": "workspace::ActivateNextPane"
|
"cmd-k cmd-n": "workspace::ActivateNextPane"
|
||||||
}
|
}
|
||||||
@@ -8,24 +10,23 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"ctrl-shift-l": "language_selector::Toggle",
|
||||||
|
"cmd-|": "pane::RevealInProjectPanel",
|
||||||
"cmd-b": "editor::GoToDefinition",
|
"cmd-b": "editor::GoToDefinition",
|
||||||
"alt-cmd-b": "editor::GoToDefinitionSplit",
|
"alt-cmd-b": "editor::GoToDefinitionSplit",
|
||||||
"cmd-<": "editor::ScrollCursorCenter",
|
"cmd-<": "editor::ScrollCursorCenter",
|
||||||
"cmd-g": [
|
"cmd-g": ["editor::SelectNext", { "replace_newest": true }],
|
||||||
"editor::SelectNext",
|
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-cmd-g": [
|
|
||||||
"editor::SelectPrevious",
|
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-shift-down": "editor::AddSelectionBelow",
|
"ctrl-shift-down": "editor::AddSelectionBelow",
|
||||||
"ctrl-shift-up": "editor::AddSelectionAbove",
|
"ctrl-shift-up": "editor::AddSelectionAbove",
|
||||||
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine"
|
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
|
||||||
|
"cmd-k cmd-u": "editor::ConvertToUpperCase",
|
||||||
|
"cmd-k cmd-l": "editor::ConvertToLowerCase",
|
||||||
|
"alt-enter": "editor::Newline",
|
||||||
|
"cmd-shift-d": "editor::DuplicateLineDown",
|
||||||
|
"ctrl-cmd-up": "editor::MoveLineUp",
|
||||||
|
"ctrl-cmd-down": "editor::MoveLineDown",
|
||||||
|
"ctrl-shift-m": "markdown::OpenPreviewToTheSide"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -69,12 +70,26 @@
|
|||||||
{
|
{
|
||||||
"context": "ProjectPanel",
|
"context": "ProjectPanel",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"f2": "project_panel::Rename",
|
||||||
|
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
|
"cmd-x": "project_panel::Cut",
|
||||||
|
"cmd-c": "project_panel::Copy",
|
||||||
|
"cmd-v": "project_panel::Paste"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "ProjectPanel && not_editing",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-shift-c": "project_panel::CopyPath",
|
||||||
"ctrl-[": "project_panel::CollapseSelectedEntry",
|
"ctrl-[": "project_panel::CollapseSelectedEntry",
|
||||||
"ctrl-b": "project_panel::CollapseSelectedEntry",
|
"ctrl-b": "project_panel::CollapseSelectedEntry",
|
||||||
"alt-b": "project_panel::CollapseSelectedEntry",
|
|
||||||
"ctrl-]": "project_panel::ExpandSelectedEntry",
|
"ctrl-]": "project_panel::ExpandSelectedEntry",
|
||||||
"ctrl-f": "project_panel::ExpandSelectedEntry",
|
"ctrl-f": "project_panel::ExpandSelectedEntry",
|
||||||
"ctrl-shift-c": "project_panel::CopyPath"
|
"a": "project_panel::NewFile",
|
||||||
|
"d": "project_panel::Duplicate",
|
||||||
|
"home": "menu::SelectFirst",
|
||||||
|
"end": "menu::SelectLast",
|
||||||
|
"shift-a": "project_panel::NewDirectory"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -21,24 +21,9 @@
|
|||||||
"cmd--": "editor::Fold",
|
"cmd--": "editor::Fold",
|
||||||
"cmd-+": "editor::UnfoldLines",
|
"cmd-+": "editor::UnfoldLines",
|
||||||
"alt-shift-g": "editor::SplitSelectionIntoLines",
|
"alt-shift-g": "editor::SplitSelectionIntoLines",
|
||||||
"ctrl-g": [
|
"ctrl-g": ["editor::SelectNext", { "replace_newest": false }],
|
||||||
"editor::SelectNext",
|
"ctrl-cmd-g": ["editor::SelectPrevious", { "replace_newest": false }],
|
||||||
{
|
"cmd-/": ["editor::ToggleComments", { "advance_downwards": true }],
|
||||||
"replace_newest": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-cmd-g": [
|
|
||||||
"editor::SelectPrevious",
|
|
||||||
{
|
|
||||||
"replace_newest": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cmd-/": [
|
|
||||||
"editor::ToggleComments",
|
|
||||||
{
|
|
||||||
"advance_downwards": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"alt-up": "editor::SelectLargerSyntaxNode",
|
"alt-up": "editor::SelectLargerSyntaxNode",
|
||||||
"alt-down": "editor::SelectSmallerSyntaxNode",
|
"alt-down": "editor::SelectSmallerSyntaxNode",
|
||||||
"shift-alt-up": "editor::MoveLineUp",
|
"shift-alt-up": "editor::MoveLineUp",
|
||||||
@@ -54,7 +39,7 @@
|
|||||||
"cmd-shift-b": "editor::GoToTypeDefinition",
|
"cmd-shift-b": "editor::GoToTypeDefinition",
|
||||||
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
|
"cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
|
||||||
"f2": "editor::GoToDiagnostic",
|
"f2": "editor::GoToDiagnostic",
|
||||||
"cmd-f2": "editor::GoToPrevDiagnostic",
|
"shift-f2": "editor::GoToPrevDiagnostic",
|
||||||
"ctrl-alt-shift-down": "editor::GoToHunk",
|
"ctrl-alt-shift-down": "editor::GoToHunk",
|
||||||
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
|
"ctrl-alt-shift-up": "editor::GoToPrevHunk",
|
||||||
"cmd-home": "editor::MoveToBeginning",
|
"cmd-home": "editor::MoveToBeginning",
|
||||||
@@ -78,6 +63,7 @@
|
|||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-shift-o": "file_finder::Toggle",
|
"cmd-shift-o": "file_finder::Toggle",
|
||||||
"cmd-shift-a": "command_palette::Toggle",
|
"cmd-shift-a": "command_palette::Toggle",
|
||||||
|
"shift shift": "command_palette::Toggle",
|
||||||
"cmd-alt-o": "project_symbols::Toggle",
|
"cmd-alt-o": "project_symbols::Toggle",
|
||||||
"cmd-1": "workspace::ToggleLeftDock",
|
"cmd-1": "workspace::ToggleLeftDock",
|
||||||
"cmd-6": "diagnostics::Deploy"
|
"cmd-6": "diagnostics::Deploy"
|
||||||
@@ -94,6 +80,10 @@
|
|||||||
"context": "ProjectPanel",
|
"context": "ProjectPanel",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"enter": "project_panel::Open",
|
"enter": "project_panel::Open",
|
||||||
|
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
|
"backspace": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
|
"delete": ["project_panel::Trash", { "skip_prompt": false }],
|
||||||
|
"shift-delete": ["project_panel::Delete", { "skip_prompt": false }],
|
||||||
"shift-f6": "project_panel::Rename"
|
"shift-f6": "project_panel::Rename"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,7 @@
|
|||||||
"ctrl-pagedown": "pane::ActivatePrevItem",
|
"ctrl-pagedown": "pane::ActivatePrevItem",
|
||||||
"ctrl-pageup": "pane::ActivateNextItem",
|
"ctrl-pageup": "pane::ActivateNextItem",
|
||||||
"ctrl-shift-tab": "pane::ActivateNextItem",
|
"ctrl-shift-tab": "pane::ActivateNextItem",
|
||||||
"ctrl-tab": "pane::ActivatePrevItem",
|
"ctrl-tab": "pane::ActivatePrevItem"
|
||||||
"cmd-+": "zed::IncreaseBufferFontSize"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -19,12 +18,16 @@
|
|||||||
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
||||||
"cmd-shift-l": "editor::SplitSelectionIntoLines",
|
"cmd-shift-l": "editor::SplitSelectionIntoLines",
|
||||||
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
|
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
|
||||||
|
"cmd-shift-d": "editor::DuplicateLineDown",
|
||||||
"shift-f12": "editor::FindAllReferences",
|
"shift-f12": "editor::FindAllReferences",
|
||||||
"alt-cmd-down": "editor::GoToDefinition",
|
"alt-cmd-down": "editor::GoToDefinition",
|
||||||
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
|
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
|
||||||
"alt-shift-cmd-down": "editor::FindAllReferences",
|
"alt-shift-cmd-down": "editor::FindAllReferences",
|
||||||
"ctrl-.": "editor::GoToHunk",
|
"ctrl-.": "editor::GoToHunk",
|
||||||
"ctrl-,": "editor::GoToPrevHunk",
|
"ctrl-,": "editor::GoToPrevHunk",
|
||||||
|
"cmd-k cmd-u": "editor::ConvertToUpperCase",
|
||||||
|
"cmd-k cmd-l": "editor::ConvertToLowerCase",
|
||||||
|
"shift-alt-m": "markdown::OpenPreviewToTheSide",
|
||||||
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
|
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
|
||||||
"ctrl-delete": "editor::DeleteToNextWordEnd"
|
"ctrl-delete": "editor::DeleteToNextWordEnd"
|
||||||
}
|
}
|
||||||
@@ -22,34 +22,14 @@
|
|||||||
"alt-shift-delete": "editor::DeleteToNextWordEnd",
|
"alt-shift-delete": "editor::DeleteToNextWordEnd",
|
||||||
"ctrl-backspace": "editor::DeleteToPreviousSubwordStart",
|
"ctrl-backspace": "editor::DeleteToPreviousSubwordStart",
|
||||||
"ctrl-delete": "editor::DeleteToNextSubwordEnd",
|
"ctrl-delete": "editor::DeleteToNextSubwordEnd",
|
||||||
"alt-left": [
|
"alt-left": ["editor::MoveToPreviousWordStart", { "stop_at_soft_wraps": true }],
|
||||||
"editor::MoveToPreviousWordStart",
|
"alt-right": ["editor::MoveToNextWordEnd", { "stop_at_soft_wraps": true }],
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"alt-right": [
|
|
||||||
"editor::MoveToNextWordEnd",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-left": "editor::MoveToPreviousSubwordStart",
|
"ctrl-left": "editor::MoveToPreviousSubwordStart",
|
||||||
"ctrl-right": "editor::MoveToNextSubwordEnd",
|
"ctrl-right": "editor::MoveToNextSubwordEnd",
|
||||||
"cmd-shift-left": "editor::SelectToBeginningOfLine",
|
"cmd-shift-left": "editor::SelectToBeginningOfLine",
|
||||||
"cmd-shift-right": "editor::SelectToEndOfLine",
|
"cmd-shift-right": "editor::SelectToEndOfLine",
|
||||||
"alt-shift-left": [
|
"alt-shift-left": ["editor::SelectToPreviousWordStart", { "stop_at_soft_wraps": true }],
|
||||||
"editor::SelectToPreviousWordStart",
|
"alt-shift-right": ["editor::SelectToNextWordEnd", { "stop_at_soft_wraps": true }],
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"alt-shift-right": [
|
|
||||||
"editor::SelectToNextWordEnd",
|
|
||||||
{
|
|
||||||
"stop_at_soft_wraps": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-shift-left": "editor::SelectToPreviousSubwordStart",
|
"ctrl-shift-left": "editor::SelectToPreviousSubwordStart",
|
||||||
"ctrl-shift-right": "editor::SelectToNextSubwordEnd",
|
"ctrl-shift-right": "editor::SelectToNextSubwordEnd",
|
||||||
"ctrl-w": "editor::SelectNext",
|
"ctrl-w": "editor::SelectNext",
|
||||||
@@ -87,7 +67,15 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "ProjectPanel",
|
"context": "ProjectPanel",
|
||||||
"bindings": {}
|
"bindings": {
|
||||||
|
"cmd-backspace": ["project_panel::Trash", { "skip_prompt": true }],
|
||||||
|
"cmd-d": "project_panel::Duplicate",
|
||||||
|
"cmd-n": "project_panel::NewFolder",
|
||||||
|
"return": "project_panel::Rename",
|
||||||
|
"cmd-c": "project_panel::Copy",
|
||||||
|
"cmd-v": "project_panel::Paste",
|
||||||
|
"cmd-alt-c": "project_panel::CopyPath"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Dock",
|
"context": "Dock",
|
||||||
@@ -8,22 +8,8 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor && VimControl && !VimWaiting && !menu",
|
"context": "Editor && VimControl && !VimWaiting && !menu",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"i": [
|
"i": ["vim::PushOperator", { "Object": { "around": false } }],
|
||||||
"vim::PushOperator",
|
"a": ["vim::PushOperator", { "Object": { "around": true } }],
|
||||||
{
|
|
||||||
"Object": {
|
|
||||||
"around": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"a": [
|
|
||||||
"vim::PushOperator",
|
|
||||||
{
|
|
||||||
"Object": {
|
|
||||||
"around": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
":": "command_palette::Toggle",
|
":": "command_palette::Toggle",
|
||||||
"h": "vim::Left",
|
"h": "vim::Left",
|
||||||
"left": "vim::Left",
|
"left": "vim::Left",
|
||||||
@@ -39,6 +25,7 @@
|
|||||||
"right": "vim::Right",
|
"right": "vim::Right",
|
||||||
"space": "vim::Space",
|
"space": "vim::Space",
|
||||||
"$": "vim::EndOfLine",
|
"$": "vim::EndOfLine",
|
||||||
|
"end": "vim::EndOfLine",
|
||||||
"^": "vim::FirstNonWhitespace",
|
"^": "vim::FirstNonWhitespace",
|
||||||
"_": "vim::StartOfLineDownward",
|
"_": "vim::StartOfLineDownward",
|
||||||
"g _": "vim::EndOfLineDownward",
|
"g _": "vim::EndOfLineDownward",
|
||||||
@@ -46,83 +33,32 @@
|
|||||||
"{": "vim::StartOfParagraph",
|
"{": "vim::StartOfParagraph",
|
||||||
"}": "vim::EndOfParagraph",
|
"}": "vim::EndOfParagraph",
|
||||||
"|": "vim::GoToColumn",
|
"|": "vim::GoToColumn",
|
||||||
|
|
||||||
// Word motions
|
// Word motions
|
||||||
"w": "vim::NextWordStart",
|
"w": "vim::NextWordStart",
|
||||||
"e": "vim::NextWordEnd",
|
"e": "vim::NextWordEnd",
|
||||||
"b": "vim::PreviousWordStart",
|
"b": "vim::PreviousWordStart",
|
||||||
"g e": "vim::PreviousWordEnd",
|
"g e": "vim::PreviousWordEnd",
|
||||||
|
|
||||||
// Subword motions
|
// Subword motions
|
||||||
// "w": "vim::NextSubwordStart",
|
// "w": "vim::NextSubwordStart",
|
||||||
// "b": "vim::PreviousSubwordStart",
|
// "b": "vim::PreviousSubwordStart",
|
||||||
// "e": "vim::NextSubwordEnd",
|
// "e": "vim::NextSubwordEnd",
|
||||||
// "g e": "vim::PreviousSubwordEnd",
|
// "g e": "vim::PreviousSubwordEnd",
|
||||||
|
"shift-w": ["vim::NextWordStart", { "ignorePunctuation": true }],
|
||||||
"shift-w": [
|
"shift-e": ["vim::NextWordEnd", { "ignorePunctuation": true }],
|
||||||
"vim::NextWordStart",
|
"shift-b": ["vim::PreviousWordStart", { "ignorePunctuation": true }],
|
||||||
{
|
|
||||||
"ignorePunctuation": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shift-e": [
|
|
||||||
"vim::NextWordEnd",
|
|
||||||
{
|
|
||||||
"ignorePunctuation": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shift-b": [
|
|
||||||
"vim::PreviousWordStart",
|
|
||||||
{
|
|
||||||
"ignorePunctuation": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g shift-e": ["vim::PreviousWordEnd", { "ignorePunctuation": true }],
|
"g shift-e": ["vim::PreviousWordEnd", { "ignorePunctuation": true }],
|
||||||
|
|
||||||
"/": "vim::Search",
|
"/": "vim::Search",
|
||||||
"?": [
|
"g /": "pane::DeploySearch",
|
||||||
"vim::Search",
|
"?": ["vim::Search", { "backwards": true }],
|
||||||
{
|
|
||||||
"backwards": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"*": "vim::MoveToNext",
|
"*": "vim::MoveToNext",
|
||||||
"#": "vim::MoveToPrev",
|
"#": "vim::MoveToPrev",
|
||||||
"n": "vim::MoveToNextMatch",
|
"n": "vim::MoveToNextMatch",
|
||||||
"shift-n": "vim::MoveToPrevMatch",
|
"shift-n": "vim::MoveToPrevMatch",
|
||||||
"%": "vim::Matching",
|
"%": "vim::Matching",
|
||||||
"f": [
|
"f": ["vim::PushOperator", { "FindForward": { "before": false } }],
|
||||||
"vim::PushOperator",
|
"t": ["vim::PushOperator", { "FindForward": { "before": true } }],
|
||||||
{
|
"shift-f": ["vim::PushOperator", { "FindBackward": { "after": false } }],
|
||||||
"FindForward": {
|
"shift-t": ["vim::PushOperator", { "FindBackward": { "after": true } }],
|
||||||
"before": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"t": [
|
|
||||||
"vim::PushOperator",
|
|
||||||
{
|
|
||||||
"FindForward": {
|
|
||||||
"before": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shift-f": [
|
|
||||||
"vim::PushOperator",
|
|
||||||
{
|
|
||||||
"FindBackward": {
|
|
||||||
"after": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shift-t": [
|
|
||||||
"vim::PushOperator",
|
|
||||||
{
|
|
||||||
"FindBackward": {
|
|
||||||
"after": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"m": ["vim::PushOperator", "Mark"],
|
"m": ["vim::PushOperator", "Mark"],
|
||||||
"'": ["vim::PushOperator", { "Jump": { "line": true } }],
|
"'": ["vim::PushOperator", { "Jump": { "line": true } }],
|
||||||
"`": ["vim::PushOperator", { "Jump": { "line": false } }],
|
"`": ["vim::PushOperator", { "Jump": { "line": false } }],
|
||||||
@@ -139,7 +75,8 @@
|
|||||||
"ctrl-q": "vim::ToggleVisualBlock",
|
"ctrl-q": "vim::ToggleVisualBlock",
|
||||||
"shift-k": "editor::Hover",
|
"shift-k": "editor::Hover",
|
||||||
"shift-r": "vim::ToggleReplace",
|
"shift-r": "vim::ToggleReplace",
|
||||||
"0": "vim::StartOfLine", // When no number operator present, use start of line motion
|
"0": "vim::StartOfLine",
|
||||||
|
"home": "vim::StartOfLine",
|
||||||
"ctrl-f": "vim::PageDown",
|
"ctrl-f": "vim::PageDown",
|
||||||
"pagedown": "vim::PageDown",
|
"pagedown": "vim::PageDown",
|
||||||
"ctrl-b": "vim::PageUp",
|
"ctrl-b": "vim::PageUp",
|
||||||
@@ -161,93 +98,29 @@
|
|||||||
"g shift-n": "vim::SelectPreviousMatch",
|
"g shift-n": "vim::SelectPreviousMatch",
|
||||||
"g l": "vim::SelectNext",
|
"g l": "vim::SelectNext",
|
||||||
"g shift-l": "vim::SelectPrevious",
|
"g shift-l": "vim::SelectPrevious",
|
||||||
"g >": [
|
"g >": ["editor::SelectNext", { "replace_newest": true }],
|
||||||
"editor::SelectNext",
|
"g <": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g <": [
|
|
||||||
"editor::SelectPrevious",
|
|
||||||
{
|
|
||||||
"replace_newest": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g a": "editor::SelectAllMatches",
|
"g a": "editor::SelectAllMatches",
|
||||||
"g s": "outline::Toggle",
|
"g s": "outline::Toggle",
|
||||||
"g shift-s": "project_symbols::Toggle",
|
"g shift-s": "project_symbols::Toggle",
|
||||||
"g .": "editor::ToggleCodeActions", // zed specific
|
"g .": "editor::ToggleCodeActions", // zed specific
|
||||||
"g shift-a": "editor::FindAllReferences", // zed specific
|
"g shift-a": "editor::FindAllReferences", // zed specific
|
||||||
"g space": "editor::OpenExcerpts", // zed specific
|
"g space": "editor::OpenExcerpts", // zed specific
|
||||||
"g *": [
|
"g *": ["vim::MoveToNext", { "partialWord": true }],
|
||||||
"vim::MoveToNext",
|
"g #": ["vim::MoveToPrev", { "partialWord": true }],
|
||||||
{
|
"g j": ["vim::Down", { "displayLines": true }],
|
||||||
"partialWord": true
|
"g down": ["vim::Down", { "displayLines": true }],
|
||||||
}
|
"g k": ["vim::Up", { "displayLines": true }],
|
||||||
],
|
"g up": ["vim::Up", { "displayLines": true }],
|
||||||
"g #": [
|
"g $": ["vim::EndOfLine", { "displayLines": true }],
|
||||||
"vim::MoveToPrev",
|
"g end": ["vim::EndOfLine", { "displayLines": true }],
|
||||||
{
|
"g 0": ["vim::StartOfLine", { "displayLines": true }],
|
||||||
"partialWord": true
|
"g home": ["vim::StartOfLine", { "displayLines": true }],
|
||||||
}
|
"g ^": ["vim::FirstNonWhitespace", { "displayLines": true }],
|
||||||
],
|
"g v": "vim::RestoreVisualSelection",
|
||||||
"g j": [
|
|
||||||
"vim::Down",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g down": [
|
|
||||||
"vim::Down",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g k": [
|
|
||||||
"vim::Up",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g up": [
|
|
||||||
"vim::Up",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g $": [
|
|
||||||
"vim::EndOfLine",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g end": [
|
|
||||||
"vim::EndOfLine",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g 0": [
|
|
||||||
"vim::StartOfLine",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g home": [
|
|
||||||
"vim::StartOfLine",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g ^": [
|
|
||||||
"vim::FirstNonWhitespace",
|
|
||||||
{
|
|
||||||
"displayLines": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g ]": "editor::GoToDiagnostic",
|
"g ]": "editor::GoToDiagnostic",
|
||||||
"g [": "editor::GoToPrevDiagnostic",
|
"g [": "editor::GoToPrevDiagnostic",
|
||||||
"g i": ["workspace::SendKeystrokes", "` ^ i"],
|
"g i": "vim::InsertAtPrevious",
|
||||||
"g ,": "vim::ChangeListNewer",
|
"g ,": "vim::ChangeListNewer",
|
||||||
"g ;": "vim::ChangeListOlder",
|
"g ;": "vim::ChangeListOlder",
|
||||||
"shift-h": "vim::WindowTop",
|
"shift-h": "vim::WindowTop",
|
||||||
@@ -261,18 +134,8 @@
|
|||||||
"z c": "editor::Fold",
|
"z c": "editor::Fold",
|
||||||
"z o": "editor::UnfoldLines",
|
"z o": "editor::UnfoldLines",
|
||||||
"z f": "editor::FoldSelectedRanges",
|
"z f": "editor::FoldSelectedRanges",
|
||||||
"shift-z shift-q": [
|
"shift-z shift-q": ["pane::CloseActiveItem", { "saveIntent": "skip" }],
|
||||||
"pane::CloseActiveItem",
|
"shift-z shift-z": ["pane::CloseActiveItem", { "saveIntent": "saveAll" }],
|
||||||
{
|
|
||||||
"saveIntent": "skip"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shift-z shift-z": [
|
|
||||||
"pane::CloseActiveItem",
|
|
||||||
{
|
|
||||||
"saveIntent": "saveAll"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// Count support
|
// Count support
|
||||||
"1": ["vim::Number", 1],
|
"1": ["vim::Number", 1],
|
||||||
"2": ["vim::Number", 2],
|
"2": ["vim::Number", 2],
|
||||||
@@ -284,6 +147,7 @@
|
|||||||
"8": ["vim::Number", 8],
|
"8": ["vim::Number", 8],
|
||||||
"9": ["vim::Number", 9],
|
"9": ["vim::Number", 9],
|
||||||
// window related commands (ctrl-w X)
|
// window related commands (ctrl-w X)
|
||||||
|
"ctrl-w": null,
|
||||||
"ctrl-w left": ["workspace::ActivatePaneInDirection", "Left"],
|
"ctrl-w left": ["workspace::ActivatePaneInDirection", "Left"],
|
||||||
"ctrl-w right": ["workspace::ActivatePaneInDirection", "Right"],
|
"ctrl-w right": ["workspace::ActivatePaneInDirection", "Right"],
|
||||||
"ctrl-w up": ["workspace::ActivatePaneInDirection", "Up"],
|
"ctrl-w up": ["workspace::ActivatePaneInDirection", "Up"],
|
||||||
@@ -327,7 +191,6 @@
|
|||||||
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
|
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
|
||||||
"ctrl-w n": ["workspace::NewFileInDirection", "Up"],
|
"ctrl-w n": ["workspace::NewFileInDirection", "Up"],
|
||||||
"ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"],
|
"ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"],
|
||||||
|
|
||||||
"ctrl-w d": "editor::GoToDefinitionSplit",
|
"ctrl-w d": "editor::GoToDefinitionSplit",
|
||||||
"ctrl-w g d": "editor::GoToDefinitionSplit",
|
"ctrl-w g d": "editor::GoToDefinitionSplit",
|
||||||
"ctrl-w shift-d": "editor::GoToTypeDefinitionSplit",
|
"ctrl-w shift-d": "editor::GoToTypeDefinitionSplit",
|
||||||
@@ -368,19 +231,21 @@
|
|||||||
"ctrl-a": "vim::Increment",
|
"ctrl-a": "vim::Increment",
|
||||||
"ctrl-x": "vim::Decrement",
|
"ctrl-x": "vim::Decrement",
|
||||||
"p": "vim::Paste",
|
"p": "vim::Paste",
|
||||||
"shift-p": [
|
"shift-p": ["vim::Paste", { "before": true }],
|
||||||
"vim::Paste",
|
"u": "vim::Undo",
|
||||||
{
|
"ctrl-r": "vim::Redo",
|
||||||
"before": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"u": "editor::Undo",
|
|
||||||
"ctrl-r": "editor::Redo",
|
|
||||||
"r": ["vim::PushOperator", "Replace"],
|
"r": ["vim::PushOperator", "Replace"],
|
||||||
"s": "vim::Substitute",
|
"s": "vim::Substitute",
|
||||||
"shift-s": "vim::SubstituteLine",
|
"shift-s": "vim::SubstituteLine",
|
||||||
">": ["vim::PushOperator", "Indent"],
|
">": ["vim::PushOperator", "Indent"],
|
||||||
"<": ["vim::PushOperator", "Outdent"],
|
"<": ["vim::PushOperator", "Outdent"],
|
||||||
|
"g u": ["vim::PushOperator", "Lowercase"],
|
||||||
|
"g shift-u": ["vim::PushOperator", "Uppercase"],
|
||||||
|
"g ~": ["vim::PushOperator", "OppositeCase"],
|
||||||
|
"\"": ["vim::PushOperator", "Register"],
|
||||||
|
"q": "vim::ToggleRecord",
|
||||||
|
"shift-q": "vim::ReplayLastRecording",
|
||||||
|
"@": ["vim::PushOperator", "ReplayRegister"],
|
||||||
"ctrl-pagedown": "pane::ActivateNextItem",
|
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||||
// tree-sitter related commands
|
// tree-sitter related commands
|
||||||
@@ -395,13 +260,14 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor && vim_mode == visual && vim_operator == none && !VimWaiting",
|
"context": "Editor && vim_mode == visual && vim_operator == none && !VimWaiting",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"\"": ["vim::PushOperator", "Register"],
|
||||||
// tree-sitter related commands
|
// tree-sitter related commands
|
||||||
"[ x": "editor::SelectLargerSyntaxNode",
|
"[ x": "editor::SelectLargerSyntaxNode",
|
||||||
"] x": "editor::SelectSmallerSyntaxNode"
|
"] x": "editor::SelectSmallerSyntaxNode"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && VimCount",
|
"context": "Editor && VimCount && vim_mode != insert",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"0": ["vim::Number", 0]
|
"0": ["vim::Number", 0]
|
||||||
}
|
}
|
||||||
@@ -416,12 +282,7 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor && vim_mode == normal && vim_operator == c",
|
"context": "Editor && vim_mode == normal && vim_operator == c",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"s": [
|
"s": ["vim::PushOperator", { "ChangeSurrounds": {} }]
|
||||||
"vim::PushOperator",
|
|
||||||
{
|
|
||||||
"ChangeSurrounds": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -430,6 +291,27 @@
|
|||||||
"d": "vim::CurrentLine"
|
"d": "vim::CurrentLine"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && vim_operator == gu",
|
||||||
|
"bindings": {
|
||||||
|
"g u": "vim::CurrentLine",
|
||||||
|
"u": "vim::CurrentLine"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && vim_operator == gU",
|
||||||
|
"bindings": {
|
||||||
|
"g shift-u": "vim::CurrentLine",
|
||||||
|
"shift-u": "vim::CurrentLine"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && vim_operator == g~",
|
||||||
|
"bindings": {
|
||||||
|
"g ~": "vim::CurrentLine",
|
||||||
|
"~": "vim::CurrentLine"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && vim_mode == normal && vim_operator == d",
|
"context": "Editor && vim_mode == normal && vim_operator == d",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
@@ -445,12 +327,7 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor && vim_mode == normal && vim_operator == y",
|
"context": "Editor && vim_mode == normal && vim_operator == y",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"s": [
|
"s": ["vim::PushOperator", { "AddSurrounds": {} }]
|
||||||
"vim::PushOperator",
|
|
||||||
{
|
|
||||||
"AddSurrounds": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -475,12 +352,7 @@
|
|||||||
"context": "Editor && VimObject",
|
"context": "Editor && VimObject",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"w": "vim::Word",
|
"w": "vim::Word",
|
||||||
"shift-w": [
|
"shift-w": ["vim::Word", { "ignorePunctuation": true }],
|
||||||
"vim::Word",
|
|
||||||
{
|
|
||||||
"ignorePunctuation": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"t": "vim::Tag",
|
"t": "vim::Tag",
|
||||||
"s": "vim::Sentence",
|
"s": "vim::Sentence",
|
||||||
"p": "vim::Paragraph",
|
"p": "vim::Paragraph",
|
||||||
@@ -515,43 +387,18 @@
|
|||||||
"y": "vim::VisualYank",
|
"y": "vim::VisualYank",
|
||||||
"shift-y": "vim::VisualYank",
|
"shift-y": "vim::VisualYank",
|
||||||
"p": "vim::Paste",
|
"p": "vim::Paste",
|
||||||
"shift-p": [
|
"shift-p": ["vim::Paste", { "preserveClipboard": true }],
|
||||||
"vim::Paste",
|
|
||||||
{
|
|
||||||
"preserveClipboard": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"s": "vim::Substitute",
|
"s": "vim::Substitute",
|
||||||
"shift-s": "vim::SubstituteLine",
|
"shift-s": "vim::SubstituteLine",
|
||||||
"shift-r": "vim::SubstituteLine",
|
"shift-r": "vim::SubstituteLine",
|
||||||
"c": "vim::Substitute",
|
"c": "vim::Substitute",
|
||||||
"~": "vim::ChangeCase",
|
"~": "vim::ChangeCase",
|
||||||
"*": [
|
"*": ["vim::MoveToNext", { "partialWord": true }],
|
||||||
"vim::MoveToNext",
|
"#": ["vim::MoveToPrev", { "partialWord": true }],
|
||||||
{
|
|
||||||
"partialWord": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"#": [
|
|
||||||
"vim::MoveToPrev",
|
|
||||||
{
|
|
||||||
"partialWord": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ctrl-a": "vim::Increment",
|
"ctrl-a": "vim::Increment",
|
||||||
"ctrl-x": "vim::Decrement",
|
"ctrl-x": "vim::Decrement",
|
||||||
"g ctrl-a": [
|
"g ctrl-a": ["vim::Increment", { "step": true }],
|
||||||
"vim::Increment",
|
"g ctrl-x": ["vim::Decrement", { "step": true }],
|
||||||
{
|
|
||||||
"step": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"g ctrl-x": [
|
|
||||||
"vim::Decrement",
|
|
||||||
{
|
|
||||||
"step": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shift-i": "vim::InsertBefore",
|
"shift-i": "vim::InsertBefore",
|
||||||
"shift-a": "vim::InsertAfter",
|
"shift-a": "vim::InsertAfter",
|
||||||
"shift-j": "vim::JoinLines",
|
"shift-j": "vim::JoinLines",
|
||||||
@@ -561,34 +408,20 @@
|
|||||||
"ctrl-[": ["vim::SwitchMode", "Normal"],
|
"ctrl-[": ["vim::SwitchMode", "Normal"],
|
||||||
">": "vim::Indent",
|
">": "vim::Indent",
|
||||||
"<": "vim::Outdent",
|
"<": "vim::Outdent",
|
||||||
"i": [
|
"i": ["vim::PushOperator", { "Object": { "around": false } }],
|
||||||
"vim::PushOperator",
|
"a": ["vim::PushOperator", { "Object": { "around": true } }]
|
||||||
{
|
|
||||||
"Object": {
|
|
||||||
"around": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"a": [
|
|
||||||
"vim::PushOperator",
|
|
||||||
{
|
|
||||||
"Object": {
|
|
||||||
"around": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && vim_mode == normal && !VimWaiting",
|
"context": "Editor && vim_mode == normal && !VimWaiting",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"g c c": "editor::ToggleComments"
|
"g c c": "vim::ToggleComments"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && vim_mode == visual",
|
"context": "Editor && vim_mode == visual",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"g c": "editor::ToggleComments"
|
"g c": "vim::ToggleComments"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -597,6 +430,7 @@
|
|||||||
"escape": "vim::NormalBefore",
|
"escape": "vim::NormalBefore",
|
||||||
"ctrl-c": "vim::NormalBefore",
|
"ctrl-c": "vim::NormalBefore",
|
||||||
"ctrl-[": "vim::NormalBefore",
|
"ctrl-[": "vim::NormalBefore",
|
||||||
|
"ctrl-x": null,
|
||||||
"ctrl-x ctrl-o": "editor::ShowCompletions",
|
"ctrl-x ctrl-o": "editor::ShowCompletions",
|
||||||
"ctrl-x ctrl-a": "assistant::InlineAssist", // zed specific
|
"ctrl-x ctrl-a": "assistant::InlineAssist", // zed specific
|
||||||
"ctrl-x ctrl-c": "editor::ShowInlineCompletion", // zed specific
|
"ctrl-x ctrl-c": "editor::ShowInlineCompletion", // zed specific
|
||||||
@@ -606,8 +440,7 @@
|
|||||||
"ctrl-u": "editor::DeleteToBeginningOfLine",
|
"ctrl-u": "editor::DeleteToBeginningOfLine",
|
||||||
"ctrl-t": "vim::Indent",
|
"ctrl-t": "vim::Indent",
|
||||||
"ctrl-d": "vim::Outdent",
|
"ctrl-d": "vim::Outdent",
|
||||||
"ctrl-r \"": "editor::Paste",
|
"ctrl-r": ["vim::PushOperator", "Register"]
|
||||||
"ctrl-r +": "editor::Paste"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -616,11 +449,13 @@
|
|||||||
"escape": "vim::NormalBefore",
|
"escape": "vim::NormalBefore",
|
||||||
"ctrl-c": "vim::NormalBefore",
|
"ctrl-c": "vim::NormalBefore",
|
||||||
"ctrl-[": "vim::NormalBefore",
|
"ctrl-[": "vim::NormalBefore",
|
||||||
|
"tab": "vim::Tab",
|
||||||
|
"enter": "vim::Enter",
|
||||||
"backspace": "vim::UndoReplace"
|
"backspace": "vim::UndoReplace"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && VimWaiting",
|
"context": "Editor && vim_mode != replace && VimWaiting",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"tab": "vim::Tab",
|
"tab": "vim::Tab",
|
||||||
"enter": "vim::Enter",
|
"enter": "vim::Enter",
|
||||||
@@ -628,6 +463,13 @@
|
|||||||
"ctrl-[": ["vim::SwitchMode", "Normal"]
|
"ctrl-[": ["vim::SwitchMode", "Normal"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "Editor && vim_mode == insert && VimWaiting",
|
||||||
|
"bindings": {
|
||||||
|
"escape": "vim::NormalBefore",
|
||||||
|
"ctrl-[": "vim::NormalBefore"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "BufferSearchBar && !in_replace",
|
"context": "BufferSearchBar && !in_replace",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
@@ -638,7 +480,8 @@
|
|||||||
{
|
{
|
||||||
"context": "EmptyPane || SharedScreen",
|
"context": "EmptyPane || SharedScreen",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
":": "command_palette::Toggle"
|
":": "command_palette::Toggle",
|
||||||
|
"g /": "pane::DeploySearch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -661,10 +504,19 @@
|
|||||||
"t": "project_panel::OpenPermanent",
|
"t": "project_panel::OpenPermanent",
|
||||||
"v": "project_panel::OpenPermanent",
|
"v": "project_panel::OpenPermanent",
|
||||||
"p": "project_panel::Open",
|
"p": "project_panel::Open",
|
||||||
"x": "project_panel::RevealInFinder",
|
"x": "project_panel::RevealInFileManager",
|
||||||
"shift-g": "menu::SelectLast",
|
"shift-g": "menu::SelectLast",
|
||||||
"g g": "menu::SelectFirst",
|
"g g": "menu::SelectFirst",
|
||||||
"-": "project_panel::SelectParent"
|
"-": "project_panel::SelectParent"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "OutlinePanel",
|
||||||
|
"bindings": {
|
||||||
|
"j": "menu::SelectNext",
|
||||||
|
"k": "menu::SelectPrev",
|
||||||
|
"shift-g": "menu::SelectLast",
|
||||||
|
"g g": "menu::SelectFirst"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
{
|
{
|
||||||
// The name of the Zed theme to use for the UI.
|
// The name of the Zed theme to use for the UI.
|
||||||
//
|
//
|
||||||
// The theme can also be set to follow system preferences:
|
// `mode` is one of:
|
||||||
//
|
|
||||||
// "theme": {
|
|
||||||
// "mode": "system",
|
|
||||||
// "light": "One Light",
|
|
||||||
// "dark": "One Dark"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Where `mode` is one of:
|
|
||||||
// - "system": Use the theme that corresponds to the system's appearance
|
// - "system": Use the theme that corresponds to the system's appearance
|
||||||
// - "light": Use the theme indicated by the "light" field
|
// - "light": Use the theme indicated by the "light" field
|
||||||
// - "dark": Use the theme indicated by the "dark" field
|
// - "dark": Use the theme indicated by the "dark" field
|
||||||
"theme": "One Dark",
|
"theme": {
|
||||||
|
"mode": "system",
|
||||||
|
"light": "One Light",
|
||||||
|
"dark": "One Dark"
|
||||||
|
},
|
||||||
// The name of a base set of key bindings to use.
|
// The name of a base set of key bindings to use.
|
||||||
// This setting can take four values, each named after another
|
// This setting can take four values, each named after another
|
||||||
// text editor:
|
// text editor:
|
||||||
@@ -29,7 +25,7 @@
|
|||||||
"inline_completion_provider": "copilot"
|
"inline_completion_provider": "copilot"
|
||||||
},
|
},
|
||||||
// The name of a font to use for rendering text in the editor
|
// The name of a font to use for rendering text in the editor
|
||||||
"buffer_font_family": "Zed Mono",
|
"buffer_font_family": "Zed Plex Mono",
|
||||||
// The OpenType features to enable for text in the editor.
|
// The OpenType features to enable for text in the editor.
|
||||||
"buffer_font_features": {
|
"buffer_font_features": {
|
||||||
// Disable ligatures:
|
// Disable ligatures:
|
||||||
@@ -42,16 +38,17 @@
|
|||||||
// Set the buffer's line height.
|
// Set the buffer's line height.
|
||||||
// May take 3 values:
|
// May take 3 values:
|
||||||
// 1. Use a line height that's comfortable for reading (1.618)
|
// 1. Use a line height that's comfortable for reading (1.618)
|
||||||
// "line_height": "comfortable"
|
// "buffer_line_height": "comfortable"
|
||||||
// 2. Use a standard line height, (1.3)
|
// 2. Use a standard line height, (1.3)
|
||||||
// "line_height": "standard",
|
// "buffer_line_height": "standard",
|
||||||
// 3. Use a custom line height
|
// 3. Use a custom line height
|
||||||
// "line_height": {
|
// "buffer_line_height": {
|
||||||
// "custom": 2
|
// "custom": 2
|
||||||
// },
|
// },
|
||||||
"buffer_line_height": "comfortable",
|
"buffer_line_height": "comfortable",
|
||||||
// The name of a font to use for rendering text in the UI
|
// The name of a font to use for rendering text in the UI
|
||||||
"ui_font_family": ".SystemUIFont",
|
// (On macOS) You can set this to ".SysmtemUIFont" to use the system font
|
||||||
|
"ui_font_family": "Zed Plex Sans",
|
||||||
// The OpenType features to enable for text in the UI
|
// The OpenType features to enable for text in the UI
|
||||||
"ui_font_features": {
|
"ui_font_features": {
|
||||||
// Disable ligatures:
|
// Disable ligatures:
|
||||||
@@ -119,10 +116,10 @@
|
|||||||
// The debounce delay before re-querying the language server for completion
|
// The debounce delay before re-querying the language server for completion
|
||||||
// documentation when not included in original completion list.
|
// documentation when not included in original completion list.
|
||||||
"completion_documentation_secondary_query_debounce": 300,
|
"completion_documentation_secondary_query_debounce": 300,
|
||||||
// Whether to show wrap guides in the editor. Setting this to true will
|
// Whether to show wrap guides (vertical rulers) in the editor.
|
||||||
// show a guide at the 'preferred_line_length' value if 'soft_wrap' is set to
|
// Setting this to true will show a guide at the 'preferred_line_length' value
|
||||||
// 'preferred_line_length', and will show any additional guides as specified
|
// if softwrap is set to 'preferred_line_length', and will show any
|
||||||
// by the 'wrap_guides' setting.
|
// additional guides as specified by the 'wrap_guides' setting.
|
||||||
"show_wrap_guides": true,
|
"show_wrap_guides": true,
|
||||||
// Character counts at which to show wrap guides in the editor.
|
// Character counts at which to show wrap guides in the editor.
|
||||||
"wrap_guides": [],
|
"wrap_guides": [],
|
||||||
@@ -131,7 +128,14 @@
|
|||||||
// The default number of lines to expand excerpts in the multibuffer by.
|
// The default number of lines to expand excerpts in the multibuffer by.
|
||||||
"expand_excerpt_lines": 3,
|
"expand_excerpt_lines": 3,
|
||||||
// Globs to match against file paths to determine if a file is private.
|
// Globs to match against file paths to determine if a file is private.
|
||||||
"private_files": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"],
|
"private_files": [
|
||||||
|
"**/.env*",
|
||||||
|
"**/*.pem",
|
||||||
|
"**/*.key",
|
||||||
|
"**/*.cert",
|
||||||
|
"**/*.crt",
|
||||||
|
"**/secrets.yml"
|
||||||
|
],
|
||||||
// Whether to use additional LSP queries to format (and amend) the code after
|
// Whether to use additional LSP queries to format (and amend) the code after
|
||||||
// every "trigger" symbol input, defined by LSP server capabilities.
|
// every "trigger" symbol input, defined by LSP server capabilities.
|
||||||
"use_on_type_format": true,
|
"use_on_type_format": true,
|
||||||
@@ -139,26 +143,30 @@
|
|||||||
// opening parenthesis, bracket, brace, single or double quote characters.
|
// opening parenthesis, bracket, brace, single or double quote characters.
|
||||||
// For example, when you type (, Zed will add a closing ) at the correct position.
|
// For example, when you type (, Zed will add a closing ) at the correct position.
|
||||||
"use_autoclose": true,
|
"use_autoclose": true,
|
||||||
|
// Whether to automatically surround selected text when typing opening parenthesis,
|
||||||
|
// bracket, brace, single or double quote characters.
|
||||||
|
// For example, when you select text and type (, Zed will surround the text with ().
|
||||||
|
"use_auto_surround": true,
|
||||||
// Controls how the editor handles the autoclosed characters.
|
// Controls how the editor handles the autoclosed characters.
|
||||||
// When set to `false`(default), skipping over and auto-removing of the closing characters
|
// When set to `false`(default), skipping over and auto-removing of the closing characters
|
||||||
// happen only for auto-inserted characters.
|
// happen only for auto-inserted characters.
|
||||||
// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
|
// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
|
||||||
// no matter how they were inserted.
|
// no matter how they were inserted.
|
||||||
"always_treat_brackets_as_autoclosed": false,
|
"always_treat_brackets_as_autoclosed": false,
|
||||||
// Controls whether copilot provides suggestion immediately
|
// Controls whether inline completions are shown immediately (true)
|
||||||
// or waits for a `copilot::Toggle`
|
// or manually by triggering `editor::ShowInlineCompletion` (false).
|
||||||
"show_copilot_suggestions": true,
|
"show_inline_completions": true,
|
||||||
// Whether to show tabs and spaces in the editor.
|
// Whether to show tabs and spaces in the editor.
|
||||||
// This setting can take three values:
|
// This setting can take three values:
|
||||||
//
|
//
|
||||||
// 1. Draw tabs and spaces only for the selected text (default):
|
// 1. Draw tabs and spaces only for the selected text (default):
|
||||||
// "selection"
|
// "selection"
|
||||||
// 2. Do not draw any tabs or spaces:
|
// 2. Do not draw any tabs or spaces:
|
||||||
// "none"
|
// "none"
|
||||||
// 3. Draw all invisible symbols:
|
// 3. Draw all invisible symbols:
|
||||||
// "all"
|
// "all"
|
||||||
// 4. Draw whitespaces at boundaries only:
|
// 4. Draw whitespaces at boundaries only:
|
||||||
// "boundaries"
|
// "boundary"
|
||||||
// For a whitespace to be on a boundary, any of the following conditions need to be met:
|
// For a whitespace to be on a boundary, any of the following conditions need to be met:
|
||||||
// - It is a tab
|
// - It is a tab
|
||||||
// - It is adjacent to an edge (start or end)
|
// - It is adjacent to an edge (start or end)
|
||||||
@@ -176,7 +184,9 @@
|
|||||||
// Whether to show breadcrumbs.
|
// Whether to show breadcrumbs.
|
||||||
"breadcrumbs": true,
|
"breadcrumbs": true,
|
||||||
// Whether to show quick action buttons.
|
// Whether to show quick action buttons.
|
||||||
"quick_actions": true
|
"quick_actions": true,
|
||||||
|
// Whether to show the Selections menu in the editor toolbar
|
||||||
|
"selections_menu": true
|
||||||
},
|
},
|
||||||
// Scrollbar related settings
|
// Scrollbar related settings
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
@@ -218,6 +228,8 @@
|
|||||||
"line_numbers": true,
|
"line_numbers": true,
|
||||||
// Whether to show code action buttons in the gutter.
|
// Whether to show code action buttons in the gutter.
|
||||||
"code_actions": true,
|
"code_actions": true,
|
||||||
|
// Whether to show runnables buttons in the gutter.
|
||||||
|
"runnables": true,
|
||||||
// Whether to show fold buttons in the gutter.
|
// Whether to show fold buttons in the gutter.
|
||||||
"folds": true
|
"folds": true
|
||||||
},
|
},
|
||||||
@@ -226,6 +238,8 @@
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
/// The width of the indent guides in pixels, between 1 and 10.
|
/// The width of the indent guides in pixels, between 1 and 10.
|
||||||
"line_width": 1,
|
"line_width": 1,
|
||||||
|
/// The width of the active indent guide in pixels, between 1 and 10.
|
||||||
|
"active_line_width": 1,
|
||||||
/// Determines how indent guides are colored.
|
/// Determines how indent guides are colored.
|
||||||
/// This setting can take the following three values:
|
/// This setting can take the following three values:
|
||||||
///
|
///
|
||||||
@@ -240,6 +254,8 @@
|
|||||||
/// 2. "indent_aware"
|
/// 2. "indent_aware"
|
||||||
"background_coloring": "disabled"
|
"background_coloring": "disabled"
|
||||||
},
|
},
|
||||||
|
// Whether the editor will scroll beyond the last line.
|
||||||
|
"scroll_beyond_last_line": "one_page",
|
||||||
// The number of lines to keep above/below the cursor when scrolling.
|
// The number of lines to keep above/below the cursor when scrolling.
|
||||||
"vertical_scroll_margin": 3,
|
"vertical_scroll_margin": 3,
|
||||||
// Scroll sensitivity multiplier. This multiplier is applied
|
// Scroll sensitivity multiplier. This multiplier is applied
|
||||||
@@ -291,9 +307,39 @@
|
|||||||
// when a corresponding project entry becomes active.
|
// when a corresponding project entry becomes active.
|
||||||
// Gitignored entries are never auto revealed.
|
// Gitignored entries are never auto revealed.
|
||||||
"auto_reveal_entries": true,
|
"auto_reveal_entries": true,
|
||||||
|
// Whether to fold directories automatically and show compact folders
|
||||||
|
// (e.g. "a/b/c" ) when a directory has only one subdirectory inside.
|
||||||
|
"auto_fold_dirs": false,
|
||||||
|
/// Scrollbar-related settings
|
||||||
|
"scrollbar": {
|
||||||
|
/// When to show the scrollbar in the project panel.
|
||||||
|
///
|
||||||
|
/// Default: always
|
||||||
|
"show": "always"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outline_panel": {
|
||||||
|
// Whether to show the outline panel button in the status bar
|
||||||
|
"button": true,
|
||||||
|
// Default width of the outline panel.
|
||||||
|
"default_width": 300,
|
||||||
|
// Where to dock the outline panel. Can be 'left' or 'right'.
|
||||||
|
"dock": "left",
|
||||||
|
// Whether to show file icons in the outline panel.
|
||||||
|
"file_icons": true,
|
||||||
|
// Whether to show folder icons or chevrons for directories in the outline panel.
|
||||||
|
"folder_icons": true,
|
||||||
|
// Whether to show the git status in the outline panel.
|
||||||
|
"git_status": true,
|
||||||
|
// Amount of indentation for nested items.
|
||||||
|
"indent_size": 20,
|
||||||
|
// Whether to reveal it in the outline panel automatically,
|
||||||
|
// when a corresponding outline entry becomes active.
|
||||||
|
// Gitignored entries are never auto revealed.
|
||||||
|
"auto_reveal_entries": true,
|
||||||
/// Whether to fold directories automatically
|
/// Whether to fold directories automatically
|
||||||
/// when a directory has only one directory inside.
|
/// when a directory has only one directory inside.
|
||||||
"auto_fold_dirs": false
|
"auto_fold_dirs": true
|
||||||
},
|
},
|
||||||
"collaboration_panel": {
|
"collaboration_panel": {
|
||||||
// Whether to show the collaboration panel button in the status bar.
|
// Whether to show the collaboration panel button in the status bar.
|
||||||
@@ -354,6 +400,9 @@
|
|||||||
"show_call_status_icon": true,
|
"show_call_status_icon": true,
|
||||||
// Whether to use language servers to provide code intelligence.
|
// Whether to use language servers to provide code intelligence.
|
||||||
"enable_language_server": true,
|
"enable_language_server": true,
|
||||||
|
// Whether to perform linked edits of associated ranges, if the language server supports it.
|
||||||
|
// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
|
||||||
|
"linked_edits": true,
|
||||||
// The list of language servers to use (or disable) for all languages.
|
// The list of language servers to use (or disable) for all languages.
|
||||||
//
|
//
|
||||||
// This is typically customized on a per-language basis.
|
// This is typically customized on a per-language basis.
|
||||||
@@ -425,16 +474,16 @@
|
|||||||
// or falling back to formatting via language server:
|
// or falling back to formatting via language server:
|
||||||
// "formatter": "auto"
|
// "formatter": "auto"
|
||||||
"formatter": "auto",
|
"formatter": "auto",
|
||||||
// How to soft-wrap long lines of text. This setting can take
|
// How to soft-wrap long lines of text.
|
||||||
// three values:
|
// Possible values:
|
||||||
//
|
//
|
||||||
// 1. Do not soft wrap.
|
// 1. Do not soft wrap.
|
||||||
// "soft_wrap": "none",
|
// "soft_wrap": "none",
|
||||||
// 2. Prefer a single line generally, unless an overly long line is encountered.
|
// 2. Prefer a single line generally, unless an overly long line is encountered.
|
||||||
// "soft_wrap": "prefer_line",
|
// "soft_wrap": "prefer_line",
|
||||||
// 3. Soft wrap lines that overflow the editor:
|
// 3. Soft wrap lines that overflow the editor.
|
||||||
// "soft_wrap": "editor_width",
|
// "soft_wrap": "editor_width",
|
||||||
// 4. Soft wrap lines at the preferred line length
|
// 4. Soft wrap lines at the preferred line length.
|
||||||
// "soft_wrap": "preferred_line_length",
|
// "soft_wrap": "preferred_line_length",
|
||||||
"soft_wrap": "prefer_line",
|
"soft_wrap": "prefer_line",
|
||||||
// The column at which to soft-wrap lines, for buffers where soft-wrap
|
// The column at which to soft-wrap lines, for buffers where soft-wrap
|
||||||
@@ -490,9 +539,8 @@
|
|||||||
// "delay_ms": 600
|
// "delay_ms": 600
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"copilot": {
|
"inline_completions": {
|
||||||
// The set of glob patterns for which copilot should be disabled
|
// A list of globs representing files that inline completions should be disabled for.
|
||||||
// in any matching file.
|
|
||||||
"disabled_globs": [".env"]
|
"disabled_globs": [".env"]
|
||||||
},
|
},
|
||||||
// Settings specific to journaling
|
// Settings specific to journaling
|
||||||
@@ -618,13 +666,17 @@
|
|||||||
// "font_size": 15,
|
// "font_size": 15,
|
||||||
// Set the terminal's font family. If this option is not included,
|
// Set the terminal's font family. If this option is not included,
|
||||||
// the terminal will default to matching the buffer's font family.
|
// the terminal will default to matching the buffer's font family.
|
||||||
// "font_family": "Zed Mono",
|
// "font_family": "Zed Plex Mono",
|
||||||
// Sets the maximum number of lines in the terminal's scrollback buffer.
|
// Sets the maximum number of lines in the terminal's scrollback buffer.
|
||||||
// Default: 10_000, maximum: 100_000 (all bigger values set will be treated as 100_000), 0 disables the scrolling.
|
// Default: 10_000, maximum: 100_000 (all bigger values set will be treated as 100_000), 0 disables the scrolling.
|
||||||
// Existing terminals will not pick up this change until they are recreated.
|
// Existing terminals will not pick up this change until they are recreated.
|
||||||
// "max_scroll_history_lines": 10000,
|
// "max_scroll_history_lines": 10000,
|
||||||
},
|
},
|
||||||
"code_actions_on_format": {},
|
"code_actions_on_format": {},
|
||||||
|
/// Settings related to running tasks.
|
||||||
|
"tasks": {
|
||||||
|
"variables": {}
|
||||||
|
},
|
||||||
// An object whose keys are language names, and whose values
|
// An object whose keys are language names, and whose values
|
||||||
// are arrays of filenames or extensions of files that should
|
// are arrays of filenames or extensions of files that should
|
||||||
// use those languages.
|
// use those languages.
|
||||||
@@ -637,7 +689,10 @@
|
|||||||
// "TOML": ["Embargo.lock"]
|
// "TOML": ["Embargo.lock"]
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
"file_types": {},
|
"file_types": {
|
||||||
|
"JSON": ["flake.lock"],
|
||||||
|
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json"]
|
||||||
|
},
|
||||||
// The extensions that Zed should automatically install on startup.
|
// The extensions that Zed should automatically install on startup.
|
||||||
//
|
//
|
||||||
// If you don't want any of these extensions, add this field to your settings
|
// If you don't want any of these extensions, add this field to your settings
|
||||||
@@ -697,7 +752,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"JavaScript": {
|
"JavaScript": {
|
||||||
"language_servers": ["typescript-language-server", "!vtsls", "..."],
|
"language_servers": ["!typescript-language-server", "vtsls", "..."],
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"allowed": true
|
"allowed": true
|
||||||
}
|
}
|
||||||
@@ -707,6 +762,11 @@
|
|||||||
"allowed": true
|
"allowed": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"JSONC": {
|
||||||
|
"prettier": {
|
||||||
|
"allowed": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"Markdown": {
|
"Markdown": {
|
||||||
"format_on_save": "off",
|
"format_on_save": "off",
|
||||||
"prettier": {
|
"prettier": {
|
||||||
@@ -716,7 +776,8 @@
|
|||||||
"PHP": {
|
"PHP": {
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"allowed": true,
|
"allowed": true,
|
||||||
"plugins": ["@prettier/plugin-php"]
|
"plugins": ["@prettier/plugin-php"],
|
||||||
|
"parser": "php"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Ruby": {
|
"Ruby": {
|
||||||
@@ -740,7 +801,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TSX": {
|
"TSX": {
|
||||||
"language_servers": ["typescript-language-server", "!vtsls", "..."],
|
"language_servers": ["!typescript-language-server", "vtsls", "..."],
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"allowed": true
|
"allowed": true
|
||||||
}
|
}
|
||||||
@@ -751,7 +812,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TypeScript": {
|
"TypeScript": {
|
||||||
"language_servers": ["typescript-language-server", "!vtsls", "..."],
|
"language_servers": ["!typescript-language-server", "vtsls", "..."],
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"allowed": true
|
"allowed": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,10 @@
|
|||||||
// from the command palette or from `Zed` application menu.
|
// from the command palette or from `Zed` application menu.
|
||||||
{
|
{
|
||||||
"ui_font_size": 16,
|
"ui_font_size": 16,
|
||||||
"buffer_font_size": 16
|
"buffer_font_size": 16,
|
||||||
|
"theme": {
|
||||||
|
"mode": "system",
|
||||||
|
"light": "One Light",
|
||||||
|
"dark": "One Dark"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,25 +15,25 @@
|
|||||||
"elevated_surface.background": "#2f343eff",
|
"elevated_surface.background": "#2f343eff",
|
||||||
"surface.background": "#2f343eff",
|
"surface.background": "#2f343eff",
|
||||||
"background": "#3b414dff",
|
"background": "#3b414dff",
|
||||||
"element.background": "#2f343eff",
|
"element.background": "#2e343eff",
|
||||||
"element.hover": "#363c46ff",
|
"element.hover": "#363c46ff",
|
||||||
"element.active": "#454a56ff",
|
"element.active": "#454a56ff",
|
||||||
"element.selected": "#454a56ff",
|
"element.selected": "#454a56ff",
|
||||||
"element.disabled": "#2f343eff",
|
"element.disabled": "#2e343eff",
|
||||||
"drop_target.background": "#83899480",
|
"drop_target.background": "#83899480",
|
||||||
"ghost_element.background": "#00000000",
|
"ghost_element.background": "#00000000",
|
||||||
"ghost_element.hover": "#363c46ff",
|
"ghost_element.hover": "#363c46ff",
|
||||||
"ghost_element.active": "#454a56ff",
|
"ghost_element.active": "#454a56ff",
|
||||||
"ghost_element.selected": "#454a56ff",
|
"ghost_element.selected": "#454a56ff",
|
||||||
"ghost_element.disabled": "#2f343eff",
|
"ghost_element.disabled": "#2e343eff",
|
||||||
"text": "#c8ccd4ff",
|
"text": "#c8ccd4ff",
|
||||||
"text.muted": "#838994ff",
|
"text.muted": "#838994ff",
|
||||||
"text.placeholder": "#555a63ff",
|
"text.placeholder": "#696B77ff",
|
||||||
"text.disabled": "#555a63ff",
|
"text.disabled": "#696B77ff",
|
||||||
"text.accent": "#74ade8ff",
|
"text.accent": "#74ade8ff",
|
||||||
"icon": "#c8ccd4ff",
|
"icon": "#c8ccd4ff",
|
||||||
"icon.muted": "#838994ff",
|
"icon.muted": "#838994ff",
|
||||||
"icon.disabled": "#555a63ff",
|
"icon.disabled": "#696B77ff",
|
||||||
"icon.placeholder": "#838994ff",
|
"icon.placeholder": "#838994ff",
|
||||||
"icon.accent": "#74ade8ff",
|
"icon.accent": "#74ade8ff",
|
||||||
"status_bar.background": "#3b414dff",
|
"status_bar.background": "#3b414dff",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"editor.highlighted_line.background": "#2f343eff",
|
"editor.highlighted_line.background": "#2f343eff",
|
||||||
"editor.line_number": "#c8ccd459",
|
"editor.line_number": "#c8ccd459",
|
||||||
"editor.active_line_number": "#c8ccd4ff",
|
"editor.active_line_number": "#c8ccd4ff",
|
||||||
"editor.invisible": "#555a63ff",
|
"editor.invisible": "#696B77ff",
|
||||||
"editor.wrap_guide": "#c8ccd40d",
|
"editor.wrap_guide": "#c8ccd40d",
|
||||||
"editor.active_wrap_guide": "#c8ccd41a",
|
"editor.active_wrap_guide": "#c8ccd41a",
|
||||||
"editor.document_highlight.read_background": "#74ade81a",
|
"editor.document_highlight.read_background": "#74ade81a",
|
||||||
@@ -94,46 +94,46 @@
|
|||||||
"terminal.ansi.dim_white": "#575d65ff",
|
"terminal.ansi.dim_white": "#575d65ff",
|
||||||
"link_text.hover": "#74ade8ff",
|
"link_text.hover": "#74ade8ff",
|
||||||
"conflict": "#dec184ff",
|
"conflict": "#dec184ff",
|
||||||
"conflict.background": "#41321dff",
|
"conflict.background": "#dec1841a",
|
||||||
"conflict.border": "#5d4c2fff",
|
"conflict.border": "#5d4c2fff",
|
||||||
"created": "#a1c181ff",
|
"created": "#a1c181ff",
|
||||||
"created.background": "#222e1dff",
|
"created.background": "#a1c1811a",
|
||||||
"created.border": "#38482fff",
|
"created.border": "#38482fff",
|
||||||
"deleted": "#d07277ff",
|
"deleted": "#d07277ff",
|
||||||
"deleted.background": "#301b1bff",
|
"deleted.background": "#d072771a",
|
||||||
"deleted.border": "#4c2b2cff",
|
"deleted.border": "#4c2b2cff",
|
||||||
"error": "#d07277ff",
|
"error": "#d07277ff",
|
||||||
"error.background": "#301b1bff",
|
"error.background": "#d072771a",
|
||||||
"error.border": "#4c2b2cff",
|
"error.border": "#4c2b2cff",
|
||||||
"hidden": "#555a63ff",
|
"hidden": "#696B77ff",
|
||||||
"hidden.background": "#3b414dff",
|
"hidden.background": "#696B771a",
|
||||||
"hidden.border": "#414754ff",
|
"hidden.border": "#414754ff",
|
||||||
"hint": "#5a6f89ff",
|
"hint": "#5a6f89ff",
|
||||||
"hint.background": "#18243dff",
|
"hint.background": "#5a6f891a",
|
||||||
"hint.border": "#293b5bff",
|
"hint.border": "#293b5bff",
|
||||||
"ignored": "#555a63ff",
|
"ignored": "#696B77ff",
|
||||||
"ignored.background": "#3b414dff",
|
"ignored.background": "#696B771a",
|
||||||
"ignored.border": "#464b57ff",
|
"ignored.border": "#464b57ff",
|
||||||
"info": "#74ade8ff",
|
"info": "#74ade8ff",
|
||||||
"info.background": "#18243dff",
|
"info.background": "#74ade81a",
|
||||||
"info.border": "#293b5bff",
|
"info.border": "#293b5bff",
|
||||||
"modified": "#dec184ff",
|
"modified": "#dec184ff",
|
||||||
"modified.background": "#41321dff",
|
"modified.background": "#dec1841a",
|
||||||
"modified.border": "#5d4c2fff",
|
"modified.border": "#5d4c2fff",
|
||||||
"predictive": "#5a6a87ff",
|
"predictive": "#5a6a87ff",
|
||||||
"predictive.background": "#222e1dff",
|
"predictive.background": "#5a6a871a",
|
||||||
"predictive.border": "#38482fff",
|
"predictive.border": "#38482fff",
|
||||||
"renamed": "#74ade8ff",
|
"renamed": "#74ade8ff",
|
||||||
"renamed.background": "#18243dff",
|
"renamed.background": "#74ade81a",
|
||||||
"renamed.border": "#293b5bff",
|
"renamed.border": "#293b5bff",
|
||||||
"success": "#a1c181ff",
|
"success": "#a1c181ff",
|
||||||
"success.background": "#222e1dff",
|
"success.background": "#a1c1811a",
|
||||||
"success.border": "#38482fff",
|
"success.border": "#38482fff",
|
||||||
"unreachable": "#838994ff",
|
"unreachable": "#838994ff",
|
||||||
"unreachable.background": "#3b414dff",
|
"unreachable.background": "#8389941a",
|
||||||
"unreachable.border": "#464b57ff",
|
"unreachable.border": "#464b57ff",
|
||||||
"warning": "#dec184ff",
|
"warning": "#dec184ff",
|
||||||
"warning.background": "#41321dff",
|
"warning.background": "#dec1841a",
|
||||||
"warning.border": "#5d4c2fff",
|
"warning.border": "#5d4c2fff",
|
||||||
"players": [
|
"players": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: "3.7"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15
|
image: postgres:15
|
||||||
@@ -3,22 +3,22 @@ use editor::Editor;
|
|||||||
use extension::ExtensionStore;
|
use extension::ExtensionStore;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, svg, AppContext, CursorStyle, EventEmitter, InteractiveElement as _, Model,
|
actions, anchored, deferred, percentage, Animation, AnimationExt as _, AppContext, CursorStyle,
|
||||||
ParentElement as _, Render, SharedString, StatefulInteractiveElement, Styled, View,
|
DismissEvent, EventEmitter, InteractiveElement as _, Model, ParentElement as _, Render,
|
||||||
ViewContext, VisualContext as _,
|
SharedString, StatefulInteractiveElement, Styled, Transformation, View, ViewContext,
|
||||||
|
VisualContext as _,
|
||||||
|
};
|
||||||
|
use language::{
|
||||||
|
LanguageRegistry, LanguageServerBinaryStatus, LanguageServerId, LanguageServerName,
|
||||||
};
|
};
|
||||||
use language::{LanguageRegistry, LanguageServerBinaryStatus, LanguageServerName};
|
|
||||||
use project::{LanguageServerProgress, Project};
|
use project::{LanguageServerProgress, Project};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cmp::Reverse, fmt::Write, sync::Arc};
|
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
|
||||||
use ui::prelude::*;
|
use ui::{prelude::*, ContextMenu};
|
||||||
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
||||||
|
|
||||||
actions!(activity_indicator, [ShowErrorMessage]);
|
actions!(activity_indicator, [ShowErrorMessage]);
|
||||||
|
|
||||||
const DOWNLOAD_ICON: &str = "icons/download.svg";
|
|
||||||
const WARNING_ICON: &str = "icons/warning.svg";
|
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
ShowError { lsp_name: Arc<str>, error: String },
|
ShowError { lsp_name: Arc<str>, error: String },
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,7 @@ pub struct ActivityIndicator {
|
|||||||
statuses: Vec<LspStatus>,
|
statuses: Vec<LspStatus>,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
auto_updater: Option<Model<AutoUpdater>>,
|
auto_updater: Option<Model<AutoUpdater>>,
|
||||||
|
context_menu: Option<View<ContextMenu>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LspStatus {
|
struct LspStatus {
|
||||||
@@ -35,14 +36,14 @@ struct LspStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct PendingWork<'a> {
|
struct PendingWork<'a> {
|
||||||
language_server_name: &'a str,
|
language_server_id: LanguageServerId,
|
||||||
progress_token: &'a str,
|
progress_token: &'a str,
|
||||||
progress: &'a LanguageServerProgress,
|
progress: &'a LanguageServerProgress,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Content {
|
struct Content {
|
||||||
icon: Option<&'static str>,
|
icon: Option<gpui::AnyElement>,
|
||||||
message: String,
|
message: String,
|
||||||
on_click: Option<Arc<dyn Fn(&mut ActivityIndicator, &mut ViewContext<ActivityIndicator>)>>,
|
on_click: Option<Arc<dyn Fn(&mut ActivityIndicator, &mut ViewContext<ActivityIndicator>)>>,
|
||||||
}
|
}
|
||||||
@@ -78,6 +79,7 @@ impl ActivityIndicator {
|
|||||||
statuses: Default::default(),
|
statuses: Default::default(),
|
||||||
project: project.clone(),
|
project: project.clone(),
|
||||||
auto_updater,
|
auto_updater,
|
||||||
|
context_menu: None,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -151,7 +153,7 @@ impl ActivityIndicator {
|
|||||||
.read(cx)
|
.read(cx)
|
||||||
.language_server_statuses()
|
.language_server_statuses()
|
||||||
.rev()
|
.rev()
|
||||||
.filter_map(|status| {
|
.filter_map(|(server_id, status)| {
|
||||||
if status.pending_work.is_empty() {
|
if status.pending_work.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -159,7 +161,7 @@ impl ActivityIndicator {
|
|||||||
.pending_work
|
.pending_work
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(token, progress)| PendingWork {
|
.map(|(token, progress)| PendingWork {
|
||||||
language_server_name: status.name.as_str(),
|
language_server_id: server_id,
|
||||||
progress_token: token.as_str(),
|
progress_token: token.as_str(),
|
||||||
progress,
|
progress,
|
||||||
})
|
})
|
||||||
@@ -175,33 +177,44 @@ impl ActivityIndicator {
|
|||||||
// Show any language server has pending activity.
|
// Show any language server has pending activity.
|
||||||
let mut pending_work = self.pending_language_server_work(cx);
|
let mut pending_work = self.pending_language_server_work(cx);
|
||||||
if let Some(PendingWork {
|
if let Some(PendingWork {
|
||||||
language_server_name,
|
|
||||||
progress_token,
|
progress_token,
|
||||||
progress,
|
progress,
|
||||||
|
..
|
||||||
}) = pending_work.next()
|
}) = pending_work.next()
|
||||||
{
|
{
|
||||||
let mut message = language_server_name.to_string();
|
let mut message = progress
|
||||||
|
.title
|
||||||
message.push_str(": ");
|
.as_deref()
|
||||||
if let Some(progress_message) = progress.message.as_ref() {
|
.unwrap_or(progress_token)
|
||||||
message.push_str(progress_message);
|
.to_string();
|
||||||
} else {
|
|
||||||
message.push_str(progress_token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(percentage) = progress.percentage {
|
if let Some(percentage) = progress.percentage {
|
||||||
write!(&mut message, " ({}%)", percentage).unwrap();
|
write!(&mut message, " ({}%)", percentage).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(progress_message) = progress.message.as_ref() {
|
||||||
|
message.push_str(": ");
|
||||||
|
message.push_str(progress_message);
|
||||||
|
}
|
||||||
|
|
||||||
let additional_work_count = pending_work.count();
|
let additional_work_count = pending_work.count();
|
||||||
if additional_work_count > 0 {
|
if additional_work_count > 0 {
|
||||||
write!(&mut message, " + {} more", additional_work_count).unwrap();
|
write!(&mut message, " + {} more", additional_work_count).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Content {
|
return Content {
|
||||||
icon: None,
|
icon: Some(
|
||||||
|
Icon::new(IconName::ArrowCircle)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.with_animation(
|
||||||
|
"arrow-circle",
|
||||||
|
Animation::new(Duration::from_secs(2)).repeat(),
|
||||||
|
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
|
||||||
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message,
|
message,
|
||||||
on_click: None,
|
on_click: Some(Arc::new(Self::toggle_language_server_work_context_menu)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +235,11 @@ impl ActivityIndicator {
|
|||||||
|
|
||||||
if !downloading.is_empty() {
|
if !downloading.is_empty() {
|
||||||
return Content {
|
return Content {
|
||||||
icon: Some(DOWNLOAD_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::Download)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: format!("Downloading {}...", downloading.join(", "),),
|
message: format!("Downloading {}...", downloading.join(", "),),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
};
|
};
|
||||||
@@ -230,7 +247,11 @@ impl ActivityIndicator {
|
|||||||
|
|
||||||
if !checking_for_update.is_empty() {
|
if !checking_for_update.is_empty() {
|
||||||
return Content {
|
return Content {
|
||||||
icon: Some(DOWNLOAD_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::Download)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Checking for updates to {}...",
|
"Checking for updates to {}...",
|
||||||
checking_for_update.join(", "),
|
checking_for_update.join(", "),
|
||||||
@@ -241,7 +262,11 @@ impl ActivityIndicator {
|
|||||||
|
|
||||||
if !failed.is_empty() {
|
if !failed.is_empty() {
|
||||||
return Content {
|
return Content {
|
||||||
icon: Some(WARNING_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::ExclamationTriangle)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Failed to download {}. Click to show error.",
|
"Failed to download {}. Click to show error.",
|
||||||
failed.join(", "),
|
failed.join(", "),
|
||||||
@@ -255,7 +280,11 @@ impl ActivityIndicator {
|
|||||||
// Show any formatting failure
|
// Show any formatting failure
|
||||||
if let Some(failure) = self.project.read(cx).last_formatting_failure() {
|
if let Some(failure) = self.project.read(cx).last_formatting_failure() {
|
||||||
return Content {
|
return Content {
|
||||||
icon: Some(WARNING_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::ExclamationTriangle)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: format!("Formatting failed: {}. Click to see logs.", failure),
|
message: format!("Formatting failed: {}. Click to see logs.", failure),
|
||||||
on_click: Some(Arc::new(|_, cx| {
|
on_click: Some(Arc::new(|_, cx| {
|
||||||
cx.dispatch_action(Box::new(workspace::OpenLog));
|
cx.dispatch_action(Box::new(workspace::OpenLog));
|
||||||
@@ -267,17 +296,29 @@ impl ActivityIndicator {
|
|||||||
if let Some(updater) = &self.auto_updater {
|
if let Some(updater) = &self.auto_updater {
|
||||||
return match &updater.read(cx).status() {
|
return match &updater.read(cx).status() {
|
||||||
AutoUpdateStatus::Checking => Content {
|
AutoUpdateStatus::Checking => Content {
|
||||||
icon: Some(DOWNLOAD_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::Download)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: "Checking for Zed updates…".to_string(),
|
message: "Checking for Zed updates…".to_string(),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
},
|
},
|
||||||
AutoUpdateStatus::Downloading => Content {
|
AutoUpdateStatus::Downloading => Content {
|
||||||
icon: Some(DOWNLOAD_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::Download)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: "Downloading Zed update…".to_string(),
|
message: "Downloading Zed update…".to_string(),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
},
|
},
|
||||||
AutoUpdateStatus::Installing => Content {
|
AutoUpdateStatus::Installing => Content {
|
||||||
icon: Some(DOWNLOAD_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::Download)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: "Installing Zed update…".to_string(),
|
message: "Installing Zed update…".to_string(),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
},
|
},
|
||||||
@@ -285,14 +326,18 @@ impl ActivityIndicator {
|
|||||||
icon: None,
|
icon: None,
|
||||||
message: "Click to restart and update Zed".to_string(),
|
message: "Click to restart and update Zed".to_string(),
|
||||||
on_click: Some(Arc::new({
|
on_click: Some(Arc::new({
|
||||||
let restart = workspace::Restart {
|
let reload = workspace::Reload {
|
||||||
binary_path: Some(binary_path.clone()),
|
binary_path: Some(binary_path.clone()),
|
||||||
};
|
};
|
||||||
move |_, cx| workspace::restart(&restart, cx)
|
move |_, cx| workspace::reload(&reload, cx)
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
AutoUpdateStatus::Errored => Content {
|
AutoUpdateStatus::Errored => Content {
|
||||||
icon: Some(WARNING_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::ExclamationTriangle)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: "Auto update failed".to_string(),
|
message: "Auto update failed".to_string(),
|
||||||
on_click: Some(Arc::new(|this, cx| {
|
on_click: Some(Arc::new(|this, cx| {
|
||||||
this.dismiss_error_message(&Default::default(), cx)
|
this.dismiss_error_message(&Default::default(), cx)
|
||||||
@@ -307,7 +352,11 @@ impl ActivityIndicator {
|
|||||||
{
|
{
|
||||||
if let Some(extension_id) = extension_store.outstanding_operations().keys().next() {
|
if let Some(extension_id) = extension_store.outstanding_operations().keys().next() {
|
||||||
return Content {
|
return Content {
|
||||||
icon: Some(DOWNLOAD_ICON),
|
icon: Some(
|
||||||
|
Icon::new(IconName::Download)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
message: format!("Updating {extension_id} extension…"),
|
message: format!("Updating {extension_id} extension…"),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
};
|
};
|
||||||
@@ -316,6 +365,75 @@ impl ActivityIndicator {
|
|||||||
|
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_language_server_work_context_menu(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
if self.context_menu.take().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.build_lsp_work_context_menu(cx);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_lsp_work_context_menu(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
let mut has_work = false;
|
||||||
|
let this = cx.view().downgrade();
|
||||||
|
let context_menu = ContextMenu::build(cx, |mut menu, cx| {
|
||||||
|
for work in self.pending_language_server_work(cx) {
|
||||||
|
has_work = true;
|
||||||
|
|
||||||
|
let this = this.clone();
|
||||||
|
let title = SharedString::from(
|
||||||
|
work.progress
|
||||||
|
.title
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or(work.progress_token)
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
if work.progress.is_cancellable {
|
||||||
|
let language_server_id = work.language_server_id;
|
||||||
|
let token = work.progress_token.to_string();
|
||||||
|
menu = menu.custom_entry(
|
||||||
|
move |_| {
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.justify_between()
|
||||||
|
.child(Label::new(title.clone()))
|
||||||
|
.child(Icon::new(IconName::XCircle))
|
||||||
|
.into_any_element()
|
||||||
|
},
|
||||||
|
move |cx| {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.project.update(cx, |project, cx| {
|
||||||
|
project.cancel_language_server_work(
|
||||||
|
language_server_id,
|
||||||
|
Some(token.clone()),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
this.context_menu.take();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
menu = menu.label(title.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu
|
||||||
|
});
|
||||||
|
|
||||||
|
if has_work {
|
||||||
|
cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
|
||||||
|
this.context_menu.take();
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
cx.focus_view(&context_menu);
|
||||||
|
self.context_menu = Some(context_menu);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<Event> for ActivityIndicator {}
|
impl EventEmitter<Event> for ActivityIndicator {}
|
||||||
@@ -338,8 +456,17 @@ impl Render for ActivityIndicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
.children(content.icon.map(|icon| svg().path(icon)))
|
.gap_2()
|
||||||
|
.children(content.icon)
|
||||||
.child(Label::new(SharedString::from(content.message)).size(LabelSize::Small))
|
.child(Label::new(SharedString::from(content.message)).size(LabelSize::Small))
|
||||||
|
.children(self.context_menu.as_ref().map(|menu| {
|
||||||
|
deferred(
|
||||||
|
anchored()
|
||||||
|
.anchor(gpui::AnchorCorner::BottomLeft)
|
||||||
|
.child(menu.clone()),
|
||||||
|
)
|
||||||
|
.with_priority(1)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ pub const ANTHROPIC_API_URL: &'static str = "https://api.anthropic.com";
|
|||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
|
||||||
pub enum Model {
|
pub enum Model {
|
||||||
#[default]
|
#[default]
|
||||||
|
#[serde(alias = "claude-3-5-sonnet", rename = "claude-3-5-sonnet-20240620")]
|
||||||
|
Claude3_5Sonnet,
|
||||||
#[serde(alias = "claude-3-opus", rename = "claude-3-opus-20240229")]
|
#[serde(alias = "claude-3-opus", rename = "claude-3-opus-20240229")]
|
||||||
Claude3Opus,
|
Claude3Opus,
|
||||||
#[serde(alias = "claude-3-sonnet", rename = "claude-3-sonnet-20240229")]
|
#[serde(alias = "claude-3-sonnet", rename = "claude-3-sonnet-20240229")]
|
||||||
@@ -22,7 +24,9 @@ pub enum Model {
|
|||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
pub fn from_id(id: &str) -> Result<Self> {
|
pub fn from_id(id: &str) -> Result<Self> {
|
||||||
if id.starts_with("claude-3-opus") {
|
if id.starts_with("claude-3-5-sonnet") {
|
||||||
|
Ok(Self::Claude3_5Sonnet)
|
||||||
|
} else if id.starts_with("claude-3-opus") {
|
||||||
Ok(Self::Claude3Opus)
|
Ok(Self::Claude3Opus)
|
||||||
} else if id.starts_with("claude-3-sonnet") {
|
} else if id.starts_with("claude-3-sonnet") {
|
||||||
Ok(Self::Claude3Sonnet)
|
Ok(Self::Claude3Sonnet)
|
||||||
@@ -35,6 +39,7 @@ impl Model {
|
|||||||
|
|
||||||
pub fn id(&self) -> &'static str {
|
pub fn id(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
Model::Claude3_5Sonnet => "claude-3-5-sonnet-20240620",
|
||||||
Model::Claude3Opus => "claude-3-opus-20240229",
|
Model::Claude3Opus => "claude-3-opus-20240229",
|
||||||
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
|
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
|
||||||
Model::Claude3Haiku => "claude-3-opus-20240307",
|
Model::Claude3Haiku => "claude-3-opus-20240307",
|
||||||
@@ -43,6 +48,7 @@ impl Model {
|
|||||||
|
|
||||||
pub fn display_name(&self) -> &'static str {
|
pub fn display_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
||||||
Self::Claude3Opus => "Claude 3 Opus",
|
Self::Claude3Opus => "Claude 3 Opus",
|
||||||
Self::Claude3Sonnet => "Claude 3 Sonnet",
|
Self::Claude3Sonnet => "Claude 3 Sonnet",
|
||||||
Self::Claude3Haiku => "Claude 3 Haiku",
|
Self::Claude3Haiku => "Claude 3 Haiku",
|
||||||
|
|||||||
@@ -52,4 +52,13 @@ impl Assets {
|
|||||||
|
|
||||||
cx.text_system().add_fonts(embedded_fonts)
|
cx.text_system().add_fonts(embedded_fonts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_test_fonts(&self, cx: &AppContext) {
|
||||||
|
cx.text_system()
|
||||||
|
.add_fonts(vec![self
|
||||||
|
.load("fonts/plex-mono/ZedPlexMono-Regular.ttf")
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()])
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,16 +13,18 @@ path = "src/assistant.rs"
|
|||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
|
||||||
anthropic = { workspace = true, features = ["schemars"] }
|
anthropic = { workspace = true, features = ["schemars"] }
|
||||||
|
anyhow.workspace = true
|
||||||
assistant_slash_command.workspace = true
|
assistant_slash_command.workspace = true
|
||||||
|
async-watch.workspace = true
|
||||||
|
breadcrumbs.workspace = true
|
||||||
cargo_toml.workspace = true
|
cargo_toml.workspace = true
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette_hooks.workspace = true
|
command_palette_hooks.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
file_icons.workspace = true
|
feature_flags.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
@@ -30,14 +32,17 @@ gpui.workspace = true
|
|||||||
heed.workspace = true
|
heed.workspace = true
|
||||||
html_to_markdown.workspace = true
|
html_to_markdown.workspace = true
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
|
indexed_docs.workspace = true
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
menu.workspace = true
|
menu.workspace = true
|
||||||
multi_buffer.workspace = true
|
multi_buffer.workspace = true
|
||||||
|
ollama = { workspace = true, features = ["schemars"] }
|
||||||
open_ai = { workspace = true, features = ["schemars"] }
|
open_ai = { workspace = true, features = ["schemars"] }
|
||||||
ordered-float.workspace = true
|
ordered-float.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
|
paths.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
rope.workspace = true
|
rope.workspace = true
|
||||||
@@ -47,10 +52,13 @@ semantic_index.workspace = true
|
|||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
|
similar.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
strsim = "0.11"
|
strsim = "0.11"
|
||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry_events.workspace = true
|
||||||
|
terminal.workspace = true
|
||||||
|
terminal_view.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
tiktoken-rs.workspace = true
|
tiktoken-rs.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
|
|||||||
@@ -9,31 +9,33 @@ mod prompts;
|
|||||||
mod search;
|
mod search;
|
||||||
mod slash_command;
|
mod slash_command;
|
||||||
mod streaming_diff;
|
mod streaming_diff;
|
||||||
|
mod terminal_inline_assistant;
|
||||||
|
|
||||||
pub use assistant_panel::AssistantPanel;
|
pub use assistant_panel::{AssistantPanel, AssistantPanelEvent};
|
||||||
|
use assistant_settings::{AnthropicModel, AssistantSettings, CloudModel, OllamaModel, OpenAiModel};
|
||||||
use assistant_settings::{AnthropicModel, AssistantSettings, CloudModel, OpenAiModel};
|
|
||||||
use assistant_slash_command::SlashCommandRegistry;
|
use assistant_slash_command::SlashCommandRegistry;
|
||||||
use client::{proto, Client};
|
use client::{proto, Client};
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
pub(crate) use completion_provider::*;
|
pub(crate) use completion_provider::*;
|
||||||
pub(crate) use context_store::*;
|
pub(crate) use context_store::*;
|
||||||
|
use fs::Fs;
|
||||||
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
|
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
|
||||||
|
use indexed_docs::IndexedDocsRegistry;
|
||||||
pub(crate) use inline_assistant::*;
|
pub(crate) use inline_assistant::*;
|
||||||
pub(crate) use model_selector::*;
|
pub(crate) use model_selector::*;
|
||||||
use semantic_index::{CloudEmbeddingProvider, SemanticIndex};
|
use semantic_index::{CloudEmbeddingProvider, SemanticIndex};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
use slash_command::{
|
use slash_command::{
|
||||||
active_command, default_command, fetch_command, file_command, project_command, prompt_command,
|
active_command, default_command, diagnostics_command, docs_command, fetch_command,
|
||||||
rustdoc_command, search_command, tabs_command,
|
file_command, now_command, project_command, prompt_command, search_command, tabs_command,
|
||||||
|
term_command,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
pub(crate) use streaming_diff::*;
|
pub(crate) use streaming_diff::*;
|
||||||
use util::paths::EMBEDDINGS_DIR;
|
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
assistant,
|
assistant,
|
||||||
@@ -42,11 +44,13 @@ actions!(
|
|||||||
Split,
|
Split,
|
||||||
CycleMessageRole,
|
CycleMessageRole,
|
||||||
QuoteSelection,
|
QuoteSelection,
|
||||||
|
InsertIntoEditor,
|
||||||
ToggleFocus,
|
ToggleFocus,
|
||||||
ResetKey,
|
ResetKey,
|
||||||
InlineAssist,
|
InlineAssist,
|
||||||
InsertActivePrompt,
|
InsertActivePrompt,
|
||||||
ToggleHistory,
|
DeployHistory,
|
||||||
|
DeployPromptLibrary,
|
||||||
ApplyEdit,
|
ApplyEdit,
|
||||||
ConfirmCommand,
|
ConfirmCommand,
|
||||||
ToggleModelSelector
|
ToggleModelSelector
|
||||||
@@ -91,6 +95,7 @@ pub enum LanguageModel {
|
|||||||
Cloud(CloudModel),
|
Cloud(CloudModel),
|
||||||
OpenAi(OpenAiModel),
|
OpenAi(OpenAiModel),
|
||||||
Anthropic(AnthropicModel),
|
Anthropic(AnthropicModel),
|
||||||
|
Ollama(OllamaModel),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LanguageModel {
|
impl Default for LanguageModel {
|
||||||
@@ -105,6 +110,7 @@ impl LanguageModel {
|
|||||||
LanguageModel::OpenAi(model) => format!("openai/{}", model.id()),
|
LanguageModel::OpenAi(model) => format!("openai/{}", model.id()),
|
||||||
LanguageModel::Anthropic(model) => format!("anthropic/{}", model.id()),
|
LanguageModel::Anthropic(model) => format!("anthropic/{}", model.id()),
|
||||||
LanguageModel::Cloud(model) => format!("zed.dev/{}", model.id()),
|
LanguageModel::Cloud(model) => format!("zed.dev/{}", model.id()),
|
||||||
|
LanguageModel::Ollama(model) => format!("ollama/{}", model.id()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +119,7 @@ impl LanguageModel {
|
|||||||
LanguageModel::OpenAi(model) => model.display_name().into(),
|
LanguageModel::OpenAi(model) => model.display_name().into(),
|
||||||
LanguageModel::Anthropic(model) => model.display_name().into(),
|
LanguageModel::Anthropic(model) => model.display_name().into(),
|
||||||
LanguageModel::Cloud(model) => model.display_name().into(),
|
LanguageModel::Cloud(model) => model.display_name().into(),
|
||||||
|
LanguageModel::Ollama(model) => model.display_name().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +128,7 @@ impl LanguageModel {
|
|||||||
LanguageModel::OpenAi(model) => model.max_token_count(),
|
LanguageModel::OpenAi(model) => model.max_token_count(),
|
||||||
LanguageModel::Anthropic(model) => model.max_token_count(),
|
LanguageModel::Anthropic(model) => model.max_token_count(),
|
||||||
LanguageModel::Cloud(model) => model.max_token_count(),
|
LanguageModel::Cloud(model) => model.max_token_count(),
|
||||||
|
LanguageModel::Ollama(model) => model.max_token_count(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +137,7 @@ impl LanguageModel {
|
|||||||
LanguageModel::OpenAi(model) => model.id(),
|
LanguageModel::OpenAi(model) => model.id(),
|
||||||
LanguageModel::Anthropic(model) => model.id(),
|
LanguageModel::Anthropic(model) => model.id(),
|
||||||
LanguageModel::Cloud(model) => model.id(),
|
LanguageModel::Cloud(model) => model.id(),
|
||||||
|
LanguageModel::Ollama(model) => model.id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +163,7 @@ impl LanguageModelRequestMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct LanguageModelRequest {
|
pub struct LanguageModelRequest {
|
||||||
pub model: LanguageModel,
|
pub model: LanguageModel,
|
||||||
pub messages: Vec<LanguageModelRequestMessage>,
|
pub messages: Vec<LanguageModelRequestMessage>,
|
||||||
@@ -179,8 +188,12 @@ impl LanguageModelRequest {
|
|||||||
match &self.model {
|
match &self.model {
|
||||||
LanguageModel::OpenAi(_) => {}
|
LanguageModel::OpenAi(_) => {}
|
||||||
LanguageModel::Anthropic(_) => {}
|
LanguageModel::Anthropic(_) => {}
|
||||||
|
LanguageModel::Ollama(_) => {}
|
||||||
LanguageModel::Cloud(model) => match model {
|
LanguageModel::Cloud(model) => match model {
|
||||||
CloudModel::Claude3Opus | CloudModel::Claude3Sonnet | CloudModel::Claude3Haiku => {
|
CloudModel::Claude3Opus
|
||||||
|
| CloudModel::Claude3Sonnet
|
||||||
|
| CloudModel::Claude3Haiku
|
||||||
|
| CloudModel::Claude3_5Sonnet => {
|
||||||
preprocess_anthropic_request(self);
|
preprocess_anthropic_request(self);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -255,7 +268,7 @@ impl Assistant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
|
||||||
cx.set_global(Assistant::default());
|
cx.set_global(Assistant::default());
|
||||||
AssistantSettings::register(cx);
|
AssistantSettings::register(cx);
|
||||||
|
|
||||||
@@ -264,7 +277,7 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
|||||||
async move {
|
async move {
|
||||||
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
|
let embedding_provider = CloudEmbeddingProvider::new(client.clone());
|
||||||
let semantic_index = SemanticIndex::new(
|
let semantic_index = SemanticIndex::new(
|
||||||
EMBEDDINGS_DIR.join("semantic-index-db.0.mdb"),
|
paths::embeddings_dir().join("semantic-index-db.0.mdb"),
|
||||||
Arc::new(embedding_provider),
|
Arc::new(embedding_provider),
|
||||||
&mut cx,
|
&mut cx,
|
||||||
)
|
)
|
||||||
@@ -279,7 +292,9 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
|||||||
assistant_slash_command::init(cx);
|
assistant_slash_command::init(cx);
|
||||||
register_slash_commands(cx);
|
register_slash_commands(cx);
|
||||||
assistant_panel::init(cx);
|
assistant_panel::init(cx);
|
||||||
inline_assistant::init(client.telemetry().clone(), cx);
|
inline_assistant::init(fs.clone(), client.telemetry().clone(), cx);
|
||||||
|
terminal_inline_assistant::init(fs.clone(), client.telemetry().clone(), cx);
|
||||||
|
IndexedDocsRegistry::init_global(cx);
|
||||||
|
|
||||||
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||||
filter.hide_namespace(Assistant::NAMESPACE);
|
filter.hide_namespace(Assistant::NAMESPACE);
|
||||||
@@ -307,10 +322,31 @@ fn register_slash_commands(cx: &mut AppContext) {
|
|||||||
slash_command_registry.register_command(search_command::SearchSlashCommand, true);
|
slash_command_registry.register_command(search_command::SearchSlashCommand, true);
|
||||||
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
|
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
|
||||||
slash_command_registry.register_command(default_command::DefaultSlashCommand, true);
|
slash_command_registry.register_command(default_command::DefaultSlashCommand, true);
|
||||||
slash_command_registry.register_command(rustdoc_command::RustdocSlashCommand, false);
|
slash_command_registry.register_command(term_command::TermSlashCommand, true);
|
||||||
|
slash_command_registry.register_command(now_command::NowSlashCommand, true);
|
||||||
|
slash_command_registry.register_command(diagnostics_command::DiagnosticsSlashCommand, true);
|
||||||
|
slash_command_registry.register_command(docs_command::DocsSlashCommand, true);
|
||||||
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
|
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn humanize_token_count(count: usize) -> String {
|
||||||
|
match count {
|
||||||
|
0..=999 => count.to_string(),
|
||||||
|
1000..=9999 => {
|
||||||
|
let thousands = count / 1000;
|
||||||
|
let hundreds = (count % 1000 + 50) / 100;
|
||||||
|
if hundreds == 0 {
|
||||||
|
format!("{}k", thousands)
|
||||||
|
} else if hundreds == 10 {
|
||||||
|
format!("{}k", thousands + 1)
|
||||||
|
} else {
|
||||||
|
format!("{}.{}k", thousands, hundreds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => format!("{}k", (count + 500) / 1000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
fn init_logger() {
|
fn init_logger() {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::{preprocess_anthropic_request, LanguageModel, LanguageModelRequest};
|
||||||
pub use anthropic::Model as AnthropicModel;
|
pub use anthropic::Model as AnthropicModel;
|
||||||
use gpui::Pixels;
|
use gpui::Pixels;
|
||||||
|
pub use ollama::Model as OllamaModel;
|
||||||
pub use open_ai::Model as OpenAiModel;
|
pub use open_ai::Model as OpenAiModel;
|
||||||
use schemars::{
|
use schemars::{
|
||||||
schema::{InstanceType, Metadata, Schema, SchemaObject},
|
schema::{InstanceType, Metadata, Schema, SchemaObject},
|
||||||
@@ -14,8 +16,6 @@ use serde::{
|
|||||||
use settings::{Settings, SettingsSources};
|
use settings::{Settings, SettingsSources};
|
||||||
use strum::{EnumIter, IntoEnumIterator};
|
use strum::{EnumIter, IntoEnumIterator};
|
||||||
|
|
||||||
use crate::{preprocess_anthropic_request, LanguageModel, LanguageModelRequest};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, EnumIter)]
|
#[derive(Clone, Debug, Default, PartialEq, EnumIter)]
|
||||||
pub enum CloudModel {
|
pub enum CloudModel {
|
||||||
Gpt3Point5Turbo,
|
Gpt3Point5Turbo,
|
||||||
@@ -23,6 +23,7 @@ pub enum CloudModel {
|
|||||||
Gpt4Turbo,
|
Gpt4Turbo,
|
||||||
#[default]
|
#[default]
|
||||||
Gpt4Omni,
|
Gpt4Omni,
|
||||||
|
Claude3_5Sonnet,
|
||||||
Claude3Opus,
|
Claude3Opus,
|
||||||
Claude3Sonnet,
|
Claude3Sonnet,
|
||||||
Claude3Haiku,
|
Claude3Haiku,
|
||||||
@@ -104,6 +105,7 @@ impl CloudModel {
|
|||||||
Self::Gpt4 => "gpt-4",
|
Self::Gpt4 => "gpt-4",
|
||||||
Self::Gpt4Turbo => "gpt-4-turbo-preview",
|
Self::Gpt4Turbo => "gpt-4-turbo-preview",
|
||||||
Self::Gpt4Omni => "gpt-4o",
|
Self::Gpt4Omni => "gpt-4o",
|
||||||
|
Self::Claude3_5Sonnet => "claude-3-5-sonnet",
|
||||||
Self::Claude3Opus => "claude-3-opus",
|
Self::Claude3Opus => "claude-3-opus",
|
||||||
Self::Claude3Sonnet => "claude-3-sonnet",
|
Self::Claude3Sonnet => "claude-3-sonnet",
|
||||||
Self::Claude3Haiku => "claude-3-haiku",
|
Self::Claude3Haiku => "claude-3-haiku",
|
||||||
@@ -117,6 +119,7 @@ impl CloudModel {
|
|||||||
Self::Gpt4 => "GPT 4",
|
Self::Gpt4 => "GPT 4",
|
||||||
Self::Gpt4Turbo => "GPT 4 Turbo",
|
Self::Gpt4Turbo => "GPT 4 Turbo",
|
||||||
Self::Gpt4Omni => "GPT 4 Omni",
|
Self::Gpt4Omni => "GPT 4 Omni",
|
||||||
|
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
||||||
Self::Claude3Opus => "Claude 3 Opus",
|
Self::Claude3Opus => "Claude 3 Opus",
|
||||||
Self::Claude3Sonnet => "Claude 3 Sonnet",
|
Self::Claude3Sonnet => "Claude 3 Sonnet",
|
||||||
Self::Claude3Haiku => "Claude 3 Haiku",
|
Self::Claude3Haiku => "Claude 3 Haiku",
|
||||||
@@ -129,7 +132,10 @@ impl CloudModel {
|
|||||||
Self::Gpt3Point5Turbo => 2048,
|
Self::Gpt3Point5Turbo => 2048,
|
||||||
Self::Gpt4 => 4096,
|
Self::Gpt4 => 4096,
|
||||||
Self::Gpt4Turbo | Self::Gpt4Omni => 128000,
|
Self::Gpt4Turbo | Self::Gpt4Omni => 128000,
|
||||||
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => 200000,
|
Self::Claude3_5Sonnet
|
||||||
|
| Self::Claude3Opus
|
||||||
|
| Self::Claude3Sonnet
|
||||||
|
| Self::Claude3Haiku => 200000,
|
||||||
Self::Custom(_) => 4096, // TODO: Make this configurable
|
Self::Custom(_) => 4096, // TODO: Make this configurable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,12 +168,18 @@ pub enum AssistantProvider {
|
|||||||
model: OpenAiModel,
|
model: OpenAiModel,
|
||||||
api_url: String,
|
api_url: String,
|
||||||
low_speed_timeout_in_seconds: Option<u64>,
|
low_speed_timeout_in_seconds: Option<u64>,
|
||||||
|
available_models: Vec<OpenAiModel>,
|
||||||
},
|
},
|
||||||
Anthropic {
|
Anthropic {
|
||||||
model: AnthropicModel,
|
model: AnthropicModel,
|
||||||
api_url: String,
|
api_url: String,
|
||||||
low_speed_timeout_in_seconds: Option<u64>,
|
low_speed_timeout_in_seconds: Option<u64>,
|
||||||
},
|
},
|
||||||
|
Ollama {
|
||||||
|
model: OllamaModel,
|
||||||
|
api_url: String,
|
||||||
|
low_speed_timeout_in_seconds: Option<u64>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AssistantProvider {
|
impl Default for AssistantProvider {
|
||||||
@@ -176,6 +188,7 @@ impl Default for AssistantProvider {
|
|||||||
model: OpenAiModel::default(),
|
model: OpenAiModel::default(),
|
||||||
api_url: open_ai::OPEN_AI_API_URL.into(),
|
api_url: open_ai::OPEN_AI_API_URL.into(),
|
||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
|
available_models: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +203,7 @@ pub enum AssistantProviderContent {
|
|||||||
default_model: Option<OpenAiModel>,
|
default_model: Option<OpenAiModel>,
|
||||||
api_url: Option<String>,
|
api_url: Option<String>,
|
||||||
low_speed_timeout_in_seconds: Option<u64>,
|
low_speed_timeout_in_seconds: Option<u64>,
|
||||||
|
available_models: Option<Vec<OpenAiModel>>,
|
||||||
},
|
},
|
||||||
#[serde(rename = "anthropic")]
|
#[serde(rename = "anthropic")]
|
||||||
Anthropic {
|
Anthropic {
|
||||||
@@ -197,6 +211,12 @@ pub enum AssistantProviderContent {
|
|||||||
api_url: Option<String>,
|
api_url: Option<String>,
|
||||||
low_speed_timeout_in_seconds: Option<u64>,
|
low_speed_timeout_in_seconds: Option<u64>,
|
||||||
},
|
},
|
||||||
|
#[serde(rename = "ollama")]
|
||||||
|
Ollama {
|
||||||
|
default_model: Option<OllamaModel>,
|
||||||
|
api_url: Option<String>,
|
||||||
|
low_speed_timeout_in_seconds: Option<u64>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@@ -254,6 +274,7 @@ impl AssistantSettingsContent {
|
|||||||
default_model: settings.default_open_ai_model.clone(),
|
default_model: settings.default_open_ai_model.clone(),
|
||||||
api_url: Some(open_ai_api_url.clone()),
|
api_url: Some(open_ai_api_url.clone()),
|
||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
|
available_models: Some(Default::default()),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
settings.default_open_ai_model.clone().map(|open_ai_model| {
|
settings.default_open_ai_model.clone().map(|open_ai_model| {
|
||||||
@@ -261,6 +282,7 @@ impl AssistantSettingsContent {
|
|||||||
default_model: Some(open_ai_model),
|
default_model: Some(open_ai_model),
|
||||||
api_url: None,
|
api_url: None,
|
||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
|
available_models: Some(Default::default()),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -308,6 +330,14 @@ impl AssistantSettingsContent {
|
|||||||
*model = Some(new_model);
|
*model = Some(new_model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(AssistantProviderContent::Ollama {
|
||||||
|
default_model: model,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if let LanguageModel::Ollama(new_model) = new_model {
|
||||||
|
*model = Some(new_model);
|
||||||
|
}
|
||||||
|
}
|
||||||
provider => match new_model {
|
provider => match new_model {
|
||||||
LanguageModel::Cloud(model) => {
|
LanguageModel::Cloud(model) => {
|
||||||
*provider = Some(AssistantProviderContent::ZedDotDev {
|
*provider = Some(AssistantProviderContent::ZedDotDev {
|
||||||
@@ -319,6 +349,7 @@ impl AssistantSettingsContent {
|
|||||||
default_model: Some(model),
|
default_model: Some(model),
|
||||||
api_url: None,
|
api_url: None,
|
||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
|
available_models: Some(Default::default()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
LanguageModel::Anthropic(model) => {
|
LanguageModel::Anthropic(model) => {
|
||||||
@@ -328,6 +359,13 @@ impl AssistantSettingsContent {
|
|||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
LanguageModel::Ollama(model) => {
|
||||||
|
*provider = Some(AssistantProviderContent::Ollama {
|
||||||
|
default_model: Some(model),
|
||||||
|
api_url: None,
|
||||||
|
low_speed_timeout_in_seconds: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -456,11 +494,35 @@ impl Settings for AssistantSettings {
|
|||||||
model,
|
model,
|
||||||
api_url,
|
api_url,
|
||||||
low_speed_timeout_in_seconds,
|
low_speed_timeout_in_seconds,
|
||||||
|
available_models,
|
||||||
},
|
},
|
||||||
AssistantProviderContent::OpenAi {
|
AssistantProviderContent::OpenAi {
|
||||||
default_model: model_override,
|
default_model: model_override,
|
||||||
api_url: api_url_override,
|
api_url: api_url_override,
|
||||||
low_speed_timeout_in_seconds: low_speed_timeout_in_seconds_override,
|
low_speed_timeout_in_seconds: low_speed_timeout_in_seconds_override,
|
||||||
|
available_models: available_models_override,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
merge(model, model_override);
|
||||||
|
merge(api_url, api_url_override);
|
||||||
|
merge(available_models, available_models_override);
|
||||||
|
if let Some(low_speed_timeout_in_seconds_override) =
|
||||||
|
low_speed_timeout_in_seconds_override
|
||||||
|
{
|
||||||
|
*low_speed_timeout_in_seconds =
|
||||||
|
Some(low_speed_timeout_in_seconds_override);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
AssistantProvider::Ollama {
|
||||||
|
model,
|
||||||
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
},
|
||||||
|
AssistantProviderContent::Ollama {
|
||||||
|
default_model: model_override,
|
||||||
|
api_url: api_url_override,
|
||||||
|
low_speed_timeout_in_seconds: low_speed_timeout_in_seconds_override,
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
merge(model, model_override);
|
merge(model, model_override);
|
||||||
@@ -504,10 +566,12 @@ impl Settings for AssistantSettings {
|
|||||||
default_model: model,
|
default_model: model,
|
||||||
api_url,
|
api_url,
|
||||||
low_speed_timeout_in_seconds,
|
low_speed_timeout_in_seconds,
|
||||||
|
available_models,
|
||||||
} => AssistantProvider::OpenAi {
|
} => AssistantProvider::OpenAi {
|
||||||
model: model.unwrap_or_default(),
|
model: model.unwrap_or_default(),
|
||||||
api_url: api_url.unwrap_or_else(|| open_ai::OPEN_AI_API_URL.into()),
|
api_url: api_url.unwrap_or_else(|| open_ai::OPEN_AI_API_URL.into()),
|
||||||
low_speed_timeout_in_seconds,
|
low_speed_timeout_in_seconds,
|
||||||
|
available_models: available_models.unwrap_or_default(),
|
||||||
},
|
},
|
||||||
AssistantProviderContent::Anthropic {
|
AssistantProviderContent::Anthropic {
|
||||||
default_model: model,
|
default_model: model,
|
||||||
@@ -519,6 +583,15 @@ impl Settings for AssistantSettings {
|
|||||||
.unwrap_or_else(|| anthropic::ANTHROPIC_API_URL.into()),
|
.unwrap_or_else(|| anthropic::ANTHROPIC_API_URL.into()),
|
||||||
low_speed_timeout_in_seconds,
|
low_speed_timeout_in_seconds,
|
||||||
},
|
},
|
||||||
|
AssistantProviderContent::Ollama {
|
||||||
|
default_model: model,
|
||||||
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
} => AssistantProvider::Ollama {
|
||||||
|
model: model.unwrap_or_default(),
|
||||||
|
api_url: api_url.unwrap_or_else(|| ollama::OLLAMA_API_URL.into()),
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -555,6 +628,7 @@ mod tests {
|
|||||||
model: OpenAiModel::FourOmni,
|
model: OpenAiModel::FourOmni,
|
||||||
api_url: open_ai::OPEN_AI_API_URL.into(),
|
api_url: open_ai::OPEN_AI_API_URL.into(),
|
||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
|
available_models: Default::default(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -577,6 +651,7 @@ mod tests {
|
|||||||
model: OpenAiModel::FourOmni,
|
model: OpenAiModel::FourOmni,
|
||||||
api_url: "test-url".into(),
|
api_url: "test-url".into(),
|
||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
|
available_models: Default::default(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
SettingsStore::update_global(cx, |store, cx| {
|
SettingsStore::update_global(cx, |store, cx| {
|
||||||
@@ -597,6 +672,7 @@ mod tests {
|
|||||||
model: OpenAiModel::Four,
|
model: OpenAiModel::Four,
|
||||||
api_url: open_ai::OPEN_AI_API_URL.into(),
|
api_url: open_ai::OPEN_AI_API_URL.into(),
|
||||||
low_speed_timeout_in_seconds: None,
|
low_speed_timeout_in_seconds: None,
|
||||||
|
available_models: Default::default(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,17 @@ mod anthropic;
|
|||||||
mod cloud;
|
mod cloud;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod fake;
|
mod fake;
|
||||||
|
mod ollama;
|
||||||
mod open_ai;
|
mod open_ai;
|
||||||
|
|
||||||
pub use anthropic::*;
|
pub use anthropic::*;
|
||||||
pub use cloud::*;
|
pub use cloud::*;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use fake::*;
|
pub use fake::*;
|
||||||
|
pub use ollama::*;
|
||||||
pub use open_ai::*;
|
pub use open_ai::*;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use smol::lock::{Semaphore, SemaphoreGuardArc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assistant_settings::{AssistantProvider, AssistantSettings},
|
assistant_settings::{AssistantProvider, AssistantSettings},
|
||||||
@@ -19,129 +23,135 @@ use client::Client;
|
|||||||
use futures::{future::BoxFuture, stream::BoxStream};
|
use futures::{future::BoxFuture, stream::BoxStream};
|
||||||
use gpui::{AnyView, AppContext, BorrowAppContext, Task, WindowContext};
|
use gpui::{AnyView, AppContext, BorrowAppContext, Task, WindowContext};
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
|
/// Choose which model to use for openai provider.
|
||||||
|
/// If the model is not available, try to use the first available model, or fallback to the original model.
|
||||||
|
fn choose_openai_model(
|
||||||
|
model: &::open_ai::Model,
|
||||||
|
available_models: &[::open_ai::Model],
|
||||||
|
) -> ::open_ai::Model {
|
||||||
|
available_models
|
||||||
|
.iter()
|
||||||
|
.find(|&m| m == model)
|
||||||
|
.or_else(|| available_models.first())
|
||||||
|
.unwrap_or_else(|| model)
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
|
||||||
let mut settings_version = 0;
|
let provider = create_provider_from_settings(client.clone(), 0, cx);
|
||||||
let provider = match &AssistantSettings::get_global(cx).provider {
|
cx.set_global(CompletionProvider::new(provider, Some(client)));
|
||||||
AssistantProvider::ZedDotDev { model } => CompletionProvider::Cloud(
|
|
||||||
CloudCompletionProvider::new(model.clone(), client.clone(), settings_version, cx),
|
|
||||||
),
|
|
||||||
AssistantProvider::OpenAi {
|
|
||||||
model,
|
|
||||||
api_url,
|
|
||||||
low_speed_timeout_in_seconds,
|
|
||||||
} => CompletionProvider::OpenAi(OpenAiCompletionProvider::new(
|
|
||||||
model.clone(),
|
|
||||||
api_url.clone(),
|
|
||||||
client.http_client(),
|
|
||||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
|
||||||
settings_version,
|
|
||||||
)),
|
|
||||||
AssistantProvider::Anthropic {
|
|
||||||
model,
|
|
||||||
api_url,
|
|
||||||
low_speed_timeout_in_seconds,
|
|
||||||
} => CompletionProvider::Anthropic(AnthropicCompletionProvider::new(
|
|
||||||
model.clone(),
|
|
||||||
api_url.clone(),
|
|
||||||
client.http_client(),
|
|
||||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
|
||||||
settings_version,
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
cx.set_global(provider);
|
|
||||||
|
|
||||||
|
let mut settings_version = 0;
|
||||||
cx.observe_global::<SettingsStore>(move |cx| {
|
cx.observe_global::<SettingsStore>(move |cx| {
|
||||||
settings_version += 1;
|
settings_version += 1;
|
||||||
cx.update_global::<CompletionProvider, _>(|provider, cx| {
|
cx.update_global::<CompletionProvider, _>(|provider, cx| {
|
||||||
match (&mut *provider, &AssistantSettings::get_global(cx).provider) {
|
provider.update_settings(settings_version, cx);
|
||||||
(
|
|
||||||
CompletionProvider::OpenAi(provider),
|
|
||||||
AssistantProvider::OpenAi {
|
|
||||||
model,
|
|
||||||
api_url,
|
|
||||||
low_speed_timeout_in_seconds,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
provider.update(
|
|
||||||
model.clone(),
|
|
||||||
api_url.clone(),
|
|
||||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
|
||||||
settings_version,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
(
|
|
||||||
CompletionProvider::Anthropic(provider),
|
|
||||||
AssistantProvider::Anthropic {
|
|
||||||
model,
|
|
||||||
api_url,
|
|
||||||
low_speed_timeout_in_seconds,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
provider.update(
|
|
||||||
model.clone(),
|
|
||||||
api_url.clone(),
|
|
||||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
|
||||||
settings_version,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
(CompletionProvider::Cloud(provider), AssistantProvider::ZedDotDev { model }) => {
|
|
||||||
provider.update(model.clone(), settings_version);
|
|
||||||
}
|
|
||||||
(_, AssistantProvider::ZedDotDev { model }) => {
|
|
||||||
*provider = CompletionProvider::Cloud(CloudCompletionProvider::new(
|
|
||||||
model.clone(),
|
|
||||||
client.clone(),
|
|
||||||
settings_version,
|
|
||||||
cx,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
(
|
|
||||||
_,
|
|
||||||
AssistantProvider::OpenAi {
|
|
||||||
model,
|
|
||||||
api_url,
|
|
||||||
low_speed_timeout_in_seconds,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
*provider = CompletionProvider::OpenAi(OpenAiCompletionProvider::new(
|
|
||||||
model.clone(),
|
|
||||||
api_url.clone(),
|
|
||||||
client.http_client(),
|
|
||||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
|
||||||
settings_version,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
(
|
|
||||||
_,
|
|
||||||
AssistantProvider::Anthropic {
|
|
||||||
model,
|
|
||||||
api_url,
|
|
||||||
low_speed_timeout_in_seconds,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
*provider = CompletionProvider::Anthropic(AnthropicCompletionProvider::new(
|
|
||||||
model.clone(),
|
|
||||||
api_url.clone(),
|
|
||||||
client.http_client(),
|
|
||||||
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
|
||||||
settings_version,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CompletionProvider {
|
pub struct CompletionResponse {
|
||||||
OpenAi(OpenAiCompletionProvider),
|
pub inner: BoxFuture<'static, Result<BoxStream<'static, Result<String>>>>,
|
||||||
Anthropic(AnthropicCompletionProvider),
|
_lock: SemaphoreGuardArc,
|
||||||
Cloud(CloudCompletionProvider),
|
}
|
||||||
#[cfg(test)]
|
|
||||||
Fake(FakeCompletionProvider),
|
pub trait LanguageModelCompletionProvider: Send + Sync {
|
||||||
|
fn available_models(&self, cx: &AppContext) -> Vec<LanguageModel>;
|
||||||
|
fn settings_version(&self) -> usize;
|
||||||
|
fn is_authenticated(&self) -> bool;
|
||||||
|
fn authenticate(&self, cx: &AppContext) -> Task<Result<()>>;
|
||||||
|
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView;
|
||||||
|
fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>>;
|
||||||
|
fn model(&self) -> LanguageModel;
|
||||||
|
fn count_tokens(
|
||||||
|
&self,
|
||||||
|
request: LanguageModelRequest,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> BoxFuture<'static, Result<usize>>;
|
||||||
|
fn complete(
|
||||||
|
&self,
|
||||||
|
request: LanguageModelRequest,
|
||||||
|
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>>;
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_CONCURRENT_COMPLETION_REQUESTS: usize = 4;
|
||||||
|
|
||||||
|
pub struct CompletionProvider {
|
||||||
|
provider: Arc<RwLock<dyn LanguageModelCompletionProvider>>,
|
||||||
|
client: Option<Arc<Client>>,
|
||||||
|
request_limiter: Arc<Semaphore>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompletionProvider {
|
||||||
|
pub fn new(
|
||||||
|
provider: Arc<RwLock<dyn LanguageModelCompletionProvider>>,
|
||||||
|
client: Option<Arc<Client>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
provider,
|
||||||
|
client,
|
||||||
|
request_limiter: Arc::new(Semaphore::new(MAX_CONCURRENT_COMPLETION_REQUESTS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn available_models(&self, cx: &AppContext) -> Vec<LanguageModel> {
|
||||||
|
self.provider.read().available_models(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settings_version(&self) -> usize {
|
||||||
|
self.provider.read().settings_version()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_authenticated(&self) -> bool {
|
||||||
|
self.provider.read().is_authenticated()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
self.provider.read().authenticate(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
||||||
|
self.provider.read().authentication_prompt(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
self.provider.read().reset_credentials(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn model(&self) -> LanguageModel {
|
||||||
|
self.provider.read().model()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_tokens(
|
||||||
|
&self,
|
||||||
|
request: LanguageModelRequest,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> BoxFuture<'static, Result<usize>> {
|
||||||
|
self.provider.read().count_tokens(request, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete(
|
||||||
|
&self,
|
||||||
|
request: LanguageModelRequest,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> Task<CompletionResponse> {
|
||||||
|
let rate_limiter = self.request_limiter.clone();
|
||||||
|
let provider = self.provider.clone();
|
||||||
|
cx.background_executor().spawn(async move {
|
||||||
|
let lock = rate_limiter.acquire_arc().await;
|
||||||
|
let response = provider.read().complete(request);
|
||||||
|
CompletionResponse {
|
||||||
|
inner: response,
|
||||||
|
_lock: lock,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl gpui::Global for CompletionProvider {}
|
impl gpui::Global for CompletionProvider {}
|
||||||
@@ -151,109 +161,213 @@ impl CompletionProvider {
|
|||||||
cx.global::<Self>()
|
cx.global::<Self>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_models(&self) -> Vec<LanguageModel> {
|
pub fn update_current_as<R, T: LanguageModelCompletionProvider + 'static>(
|
||||||
match self {
|
&mut self,
|
||||||
CompletionProvider::OpenAi(provider) => provider
|
update: impl FnOnce(&mut T) -> R,
|
||||||
.available_models()
|
) -> Option<R> {
|
||||||
.map(LanguageModel::OpenAi)
|
let mut provider = self.provider.write();
|
||||||
.collect(),
|
if let Some(provider) = provider.as_any_mut().downcast_mut::<T>() {
|
||||||
CompletionProvider::Anthropic(provider) => provider
|
Some(update(provider))
|
||||||
.available_models()
|
} else {
|
||||||
.map(LanguageModel::Anthropic)
|
None
|
||||||
.collect(),
|
|
||||||
CompletionProvider::Cloud(provider) => provider
|
|
||||||
.available_models()
|
|
||||||
.map(LanguageModel::Cloud)
|
|
||||||
.collect(),
|
|
||||||
#[cfg(test)]
|
|
||||||
CompletionProvider::Fake(_) => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn settings_version(&self) -> usize {
|
pub fn update_settings(&mut self, version: usize, cx: &mut AppContext) {
|
||||||
match self {
|
let updated = match &AssistantSettings::get_global(cx).provider {
|
||||||
CompletionProvider::OpenAi(provider) => provider.settings_version(),
|
AssistantProvider::ZedDotDev { model } => self
|
||||||
CompletionProvider::Anthropic(provider) => provider.settings_version(),
|
.update_current_as::<_, CloudCompletionProvider>(|provider| {
|
||||||
CompletionProvider::Cloud(provider) => provider.settings_version(),
|
provider.update(model.clone(), version);
|
||||||
#[cfg(test)]
|
}),
|
||||||
CompletionProvider::Fake(_) => unimplemented!(),
|
AssistantProvider::OpenAi {
|
||||||
}
|
model,
|
||||||
}
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
available_models,
|
||||||
|
} => self.update_current_as::<_, OpenAiCompletionProvider>(|provider| {
|
||||||
|
provider.update(
|
||||||
|
choose_openai_model(&model, &available_models),
|
||||||
|
api_url.clone(),
|
||||||
|
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||||
|
version,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
AssistantProvider::Anthropic {
|
||||||
|
model,
|
||||||
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
} => self.update_current_as::<_, AnthropicCompletionProvider>(|provider| {
|
||||||
|
provider.update(
|
||||||
|
model.clone(),
|
||||||
|
api_url.clone(),
|
||||||
|
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||||
|
version,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
AssistantProvider::Ollama {
|
||||||
|
model,
|
||||||
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
} => self.update_current_as::<_, OllamaCompletionProvider>(|provider| {
|
||||||
|
provider.update(
|
||||||
|
model.clone(),
|
||||||
|
api_url.clone(),
|
||||||
|
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||||
|
version,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
pub fn is_authenticated(&self) -> bool {
|
// Previously configured provider was changed to another one
|
||||||
match self {
|
if updated.is_none() {
|
||||||
CompletionProvider::OpenAi(provider) => provider.is_authenticated(),
|
if let Some(client) = self.client.clone() {
|
||||||
CompletionProvider::Anthropic(provider) => provider.is_authenticated(),
|
self.provider = create_provider_from_settings(client, version, cx);
|
||||||
CompletionProvider::Cloud(provider) => provider.is_authenticated(),
|
} else {
|
||||||
#[cfg(test)]
|
log::warn!("completion provider cannot be created because client is not set");
|
||||||
CompletionProvider::Fake(_) => true,
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
|
||||||
match self {
|
|
||||||
CompletionProvider::OpenAi(provider) => provider.authenticate(cx),
|
|
||||||
CompletionProvider::Anthropic(provider) => provider.authenticate(cx),
|
|
||||||
CompletionProvider::Cloud(provider) => provider.authenticate(cx),
|
|
||||||
#[cfg(test)]
|
|
||||||
CompletionProvider::Fake(_) => Task::ready(Ok(())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
|
||||||
match self {
|
|
||||||
CompletionProvider::OpenAi(provider) => provider.authentication_prompt(cx),
|
|
||||||
CompletionProvider::Anthropic(provider) => provider.authentication_prompt(cx),
|
|
||||||
CompletionProvider::Cloud(provider) => provider.authentication_prompt(cx),
|
|
||||||
#[cfg(test)]
|
|
||||||
CompletionProvider::Fake(_) => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
|
||||||
match self {
|
|
||||||
CompletionProvider::OpenAi(provider) => provider.reset_credentials(cx),
|
|
||||||
CompletionProvider::Anthropic(provider) => provider.reset_credentials(cx),
|
|
||||||
CompletionProvider::Cloud(_) => Task::ready(Ok(())),
|
|
||||||
#[cfg(test)]
|
|
||||||
CompletionProvider::Fake(_) => Task::ready(Ok(())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn model(&self) -> LanguageModel {
|
|
||||||
match self {
|
|
||||||
CompletionProvider::OpenAi(provider) => LanguageModel::OpenAi(provider.model()),
|
|
||||||
CompletionProvider::Anthropic(provider) => LanguageModel::Anthropic(provider.model()),
|
|
||||||
CompletionProvider::Cloud(provider) => LanguageModel::Cloud(provider.model()),
|
|
||||||
#[cfg(test)]
|
|
||||||
CompletionProvider::Fake(_) => LanguageModel::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn count_tokens(
|
|
||||||
&self,
|
|
||||||
request: LanguageModelRequest,
|
|
||||||
cx: &AppContext,
|
|
||||||
) -> BoxFuture<'static, Result<usize>> {
|
|
||||||
match self {
|
|
||||||
CompletionProvider::OpenAi(provider) => provider.count_tokens(request, cx),
|
|
||||||
CompletionProvider::Anthropic(provider) => provider.count_tokens(request, cx),
|
|
||||||
CompletionProvider::Cloud(provider) => provider.count_tokens(request, cx),
|
|
||||||
#[cfg(test)]
|
|
||||||
CompletionProvider::Fake(_) => futures::FutureExt::boxed(futures::future::ready(Ok(0))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn complete(
|
|
||||||
&self,
|
|
||||||
request: LanguageModelRequest,
|
|
||||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
|
||||||
match self {
|
|
||||||
CompletionProvider::OpenAi(provider) => provider.complete(request),
|
|
||||||
CompletionProvider::Anthropic(provider) => provider.complete(request),
|
|
||||||
CompletionProvider::Cloud(provider) => provider.complete(request),
|
|
||||||
#[cfg(test)]
|
|
||||||
CompletionProvider::Fake(provider) => provider.complete(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_provider_from_settings(
|
||||||
|
client: Arc<Client>,
|
||||||
|
settings_version: usize,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Arc<RwLock<dyn LanguageModelCompletionProvider>> {
|
||||||
|
match &AssistantSettings::get_global(cx).provider {
|
||||||
|
AssistantProvider::ZedDotDev { model } => Arc::new(RwLock::new(
|
||||||
|
CloudCompletionProvider::new(model.clone(), client.clone(), settings_version, cx),
|
||||||
|
)),
|
||||||
|
AssistantProvider::OpenAi {
|
||||||
|
model,
|
||||||
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
available_models,
|
||||||
|
} => Arc::new(RwLock::new(OpenAiCompletionProvider::new(
|
||||||
|
choose_openai_model(&model, &available_models),
|
||||||
|
api_url.clone(),
|
||||||
|
client.http_client(),
|
||||||
|
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||||
|
settings_version,
|
||||||
|
))),
|
||||||
|
AssistantProvider::Anthropic {
|
||||||
|
model,
|
||||||
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
} => Arc::new(RwLock::new(AnthropicCompletionProvider::new(
|
||||||
|
model.clone(),
|
||||||
|
api_url.clone(),
|
||||||
|
client.http_client(),
|
||||||
|
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||||
|
settings_version,
|
||||||
|
))),
|
||||||
|
AssistantProvider::Ollama {
|
||||||
|
model,
|
||||||
|
api_url,
|
||||||
|
low_speed_timeout_in_seconds,
|
||||||
|
} => Arc::new(RwLock::new(OllamaCompletionProvider::new(
|
||||||
|
model.clone(),
|
||||||
|
api_url.clone(),
|
||||||
|
client.http_client(),
|
||||||
|
low_speed_timeout_in_seconds.map(Duration::from_secs),
|
||||||
|
settings_version,
|
||||||
|
cx,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use gpui::AppContext;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use settings::SettingsStore;
|
||||||
|
use smol::stream::StreamExt;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
completion_provider::MAX_CONCURRENT_COMPLETION_REQUESTS, CompletionProvider,
|
||||||
|
FakeCompletionProvider, LanguageModelRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_rate_limiting(cx: &mut AppContext) {
|
||||||
|
SettingsStore::test(cx);
|
||||||
|
let fake_provider = FakeCompletionProvider::setup_test(cx);
|
||||||
|
|
||||||
|
let provider = CompletionProvider::new(Arc::new(RwLock::new(fake_provider.clone())), None);
|
||||||
|
|
||||||
|
// Enqueue some requests
|
||||||
|
for i in 0..MAX_CONCURRENT_COMPLETION_REQUESTS * 2 {
|
||||||
|
let response = provider.complete(
|
||||||
|
LanguageModelRequest {
|
||||||
|
temperature: i as f32 / 10.0,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
cx.background_executor()
|
||||||
|
.spawn(async move {
|
||||||
|
let response = response.await;
|
||||||
|
let mut stream = response.inner.await.unwrap();
|
||||||
|
while let Some(message) = stream.next().await {
|
||||||
|
message.unwrap();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
cx.background_executor().run_until_parked();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
fake_provider.completion_count(),
|
||||||
|
MAX_CONCURRENT_COMPLETION_REQUESTS
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the first completion request that is in flight and mark it as completed.
|
||||||
|
let completion = fake_provider
|
||||||
|
.running_completions()
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
fake_provider.finish_completion(&completion);
|
||||||
|
|
||||||
|
// Ensure that the number of in-flight completion requests is reduced.
|
||||||
|
assert_eq!(
|
||||||
|
fake_provider.completion_count(),
|
||||||
|
MAX_CONCURRENT_COMPLETION_REQUESTS - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.background_executor().run_until_parked();
|
||||||
|
|
||||||
|
// Ensure that another completion request was allowed to acquire the lock.
|
||||||
|
assert_eq!(
|
||||||
|
fake_provider.completion_count(),
|
||||||
|
MAX_CONCURRENT_COMPLETION_REQUESTS
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mark all completion requests as finished that are in flight.
|
||||||
|
for request in fake_provider.running_completions() {
|
||||||
|
fake_provider.finish_completion(&request);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(fake_provider.completion_count(), 0);
|
||||||
|
|
||||||
|
// Wait until the background tasks acquire the lock again.
|
||||||
|
cx.background_executor().run_until_parked();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
fake_provider.completion_count(),
|
||||||
|
MAX_CONCURRENT_COMPLETION_REQUESTS - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
// Finish all remaining completion requests.
|
||||||
|
for request in fake_provider.running_completions() {
|
||||||
|
fake_provider.finish_completion(&request);
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.background_executor().run_until_parked();
|
||||||
|
|
||||||
|
assert_eq!(fake_provider.completion_count(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
assistant_settings::AnthropicModel, CompletionProvider, LanguageModel, LanguageModelRequest,
|
assistant_settings::AnthropicModel, CompletionProvider, LanguageModel, LanguageModelRequest,
|
||||||
Role,
|
Role,
|
||||||
};
|
};
|
||||||
use crate::{count_open_ai_tokens, LanguageModelRequestMessage};
|
use crate::{count_open_ai_tokens, LanguageModelCompletionProvider, LanguageModelRequestMessage};
|
||||||
use anthropic::{stream_completion, Request, RequestMessage};
|
use anthropic::{stream_completion, Request, RequestMessage};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use editor::{Editor, EditorElement, EditorStyle};
|
use editor::{Editor, EditorElement, EditorStyle};
|
||||||
@@ -26,50 +26,22 @@ pub struct AnthropicCompletionProvider {
|
|||||||
settings_version: usize,
|
settings_version: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnthropicCompletionProvider {
|
impl LanguageModelCompletionProvider for AnthropicCompletionProvider {
|
||||||
pub fn new(
|
fn available_models(&self, _cx: &AppContext) -> Vec<LanguageModel> {
|
||||||
model: AnthropicModel,
|
|
||||||
api_url: String,
|
|
||||||
http_client: Arc<dyn HttpClient>,
|
|
||||||
low_speed_timeout: Option<Duration>,
|
|
||||||
settings_version: usize,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
api_key: None,
|
|
||||||
api_url,
|
|
||||||
model,
|
|
||||||
http_client,
|
|
||||||
low_speed_timeout,
|
|
||||||
settings_version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(
|
|
||||||
&mut self,
|
|
||||||
model: AnthropicModel,
|
|
||||||
api_url: String,
|
|
||||||
low_speed_timeout: Option<Duration>,
|
|
||||||
settings_version: usize,
|
|
||||||
) {
|
|
||||||
self.model = model;
|
|
||||||
self.api_url = api_url;
|
|
||||||
self.low_speed_timeout = low_speed_timeout;
|
|
||||||
self.settings_version = settings_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn available_models(&self) -> impl Iterator<Item = AnthropicModel> {
|
|
||||||
AnthropicModel::iter()
|
AnthropicModel::iter()
|
||||||
|
.map(LanguageModel::Anthropic)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn settings_version(&self) -> usize {
|
fn settings_version(&self) -> usize {
|
||||||
self.settings_version
|
self.settings_version
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_authenticated(&self) -> bool {
|
fn is_authenticated(&self) -> bool {
|
||||||
self.api_key.is_some()
|
self.api_key.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
if self.is_authenticated() {
|
if self.is_authenticated() {
|
||||||
Task::ready(Ok(()))
|
Task::ready(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
@@ -85,36 +57,36 @@ impl AnthropicCompletionProvider {
|
|||||||
String::from_utf8(api_key)?
|
String::from_utf8(api_key)?
|
||||||
};
|
};
|
||||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
if let CompletionProvider::Anthropic(provider) = provider {
|
provider.update_current_as::<_, AnthropicCompletionProvider>(|provider| {
|
||||||
provider.api_key = Some(api_key);
|
provider.api_key = Some(api_key);
|
||||||
}
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
let delete_credentials = cx.delete_credentials(&self.api_url);
|
let delete_credentials = cx.delete_credentials(&self.api_url);
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
delete_credentials.await.log_err();
|
delete_credentials.await.log_err();
|
||||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
if let CompletionProvider::Anthropic(provider) = provider {
|
provider.update_current_as::<_, AnthropicCompletionProvider>(|provider| {
|
||||||
provider.api_key = None;
|
provider.api_key = None;
|
||||||
}
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
||||||
cx.new_view(|cx| AuthenticationPrompt::new(self.api_url.clone(), cx))
|
cx.new_view(|cx| AuthenticationPrompt::new(self.api_url.clone(), cx))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn model(&self) -> AnthropicModel {
|
fn model(&self) -> LanguageModel {
|
||||||
self.model.clone()
|
LanguageModel::Anthropic(self.model.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_tokens(
|
fn count_tokens(
|
||||||
&self,
|
&self,
|
||||||
request: LanguageModelRequest,
|
request: LanguageModelRequest,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
@@ -122,7 +94,7 @@ impl AnthropicCompletionProvider {
|
|||||||
count_open_ai_tokens(request, cx.background_executor())
|
count_open_ai_tokens(request, cx.background_executor())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complete(
|
fn complete(
|
||||||
&self,
|
&self,
|
||||||
request: LanguageModelRequest,
|
request: LanguageModelRequest,
|
||||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||||
@@ -167,12 +139,48 @@ impl AnthropicCompletionProvider {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnthropicCompletionProvider {
|
||||||
|
pub fn new(
|
||||||
|
model: AnthropicModel,
|
||||||
|
api_url: String,
|
||||||
|
http_client: Arc<dyn HttpClient>,
|
||||||
|
low_speed_timeout: Option<Duration>,
|
||||||
|
settings_version: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
api_key: None,
|
||||||
|
api_url,
|
||||||
|
model,
|
||||||
|
http_client,
|
||||||
|
low_speed_timeout,
|
||||||
|
settings_version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
model: AnthropicModel,
|
||||||
|
api_url: String,
|
||||||
|
low_speed_timeout: Option<Duration>,
|
||||||
|
settings_version: usize,
|
||||||
|
) {
|
||||||
|
self.model = model;
|
||||||
|
self.api_url = api_url;
|
||||||
|
self.low_speed_timeout = low_speed_timeout;
|
||||||
|
self.settings_version = settings_version;
|
||||||
|
}
|
||||||
|
|
||||||
fn to_anthropic_request(&self, mut request: LanguageModelRequest) -> Request {
|
fn to_anthropic_request(&self, mut request: LanguageModelRequest) -> Request {
|
||||||
preprocess_anthropic_request(&mut request);
|
preprocess_anthropic_request(&mut request);
|
||||||
|
|
||||||
let model = match request.model {
|
let model = match request.model {
|
||||||
LanguageModel::Anthropic(model) => model,
|
LanguageModel::Anthropic(model) => model,
|
||||||
_ => self.model(),
|
_ => self.model.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut system_message = String::new();
|
let mut system_message = String::new();
|
||||||
@@ -236,7 +244,7 @@ pub fn preprocess_anthropic_request(request: &mut LanguageModelRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !system_message.is_empty() {
|
if !system_message.is_empty() {
|
||||||
request.messages.insert(
|
new_messages.insert(
|
||||||
0,
|
0,
|
||||||
LanguageModelRequestMessage {
|
LanguageModelRequestMessage {
|
||||||
role: Role::System,
|
role: Role::System,
|
||||||
@@ -278,9 +286,9 @@ impl AuthenticationPrompt {
|
|||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
write_credentials.await?;
|
write_credentials.await?;
|
||||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
if let CompletionProvider::Anthropic(provider) = provider {
|
provider.update_current_as::<_, AnthropicCompletionProvider>(|provider| {
|
||||||
provider.api_key = Some(api_key);
|
provider.api_key = Some(api_key);
|
||||||
}
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
@@ -349,7 +357,7 @@ impl Render for AuthenticationPrompt {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(Label::new("Click on").size(LabelSize::Small))
|
.child(Label::new("Click on").size(LabelSize::Small))
|
||||||
.child(Icon::new(IconName::Ai).size(IconSize::XSmall))
|
.child(Icon::new(IconName::ZedAssistant).size(IconSize::XSmall))
|
||||||
.child(
|
.child(
|
||||||
Label::new("in the status bar to close this panel.").size(LabelSize::Small),
|
Label::new("in the status bar to close this panel.").size(LabelSize::Small),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assistant_settings::CloudModel, count_open_ai_tokens, CompletionProvider, LanguageModel,
|
assistant_settings::CloudModel, count_open_ai_tokens, CompletionProvider, LanguageModel,
|
||||||
LanguageModelRequest,
|
LanguageModelCompletionProvider, LanguageModelRequest,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use client::{proto, Client};
|
use client::{proto, Client};
|
||||||
@@ -30,11 +30,9 @@ impl CloudCompletionProvider {
|
|||||||
let maintain_client_status = cx.spawn(|mut cx| async move {
|
let maintain_client_status = cx.spawn(|mut cx| async move {
|
||||||
while let Some(status) = status_rx.next().await {
|
while let Some(status) = status_rx.next().await {
|
||||||
let _ = cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
let _ = cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
if let CompletionProvider::Cloud(provider) = provider {
|
provider.update_current_as::<_, Self>(|provider| {
|
||||||
provider.status = status;
|
provider.status = status;
|
||||||
} else {
|
});
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -51,44 +49,53 @@ impl CloudCompletionProvider {
|
|||||||
self.model = model;
|
self.model = model;
|
||||||
self.settings_version = settings_version;
|
self.settings_version = settings_version;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn available_models(&self) -> impl Iterator<Item = CloudModel> {
|
impl LanguageModelCompletionProvider for CloudCompletionProvider {
|
||||||
|
fn available_models(&self, _cx: &AppContext) -> Vec<LanguageModel> {
|
||||||
let mut custom_model = if let CloudModel::Custom(custom_model) = self.model.clone() {
|
let mut custom_model = if let CloudModel::Custom(custom_model) = self.model.clone() {
|
||||||
Some(custom_model)
|
Some(custom_model)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
CloudModel::iter().filter_map(move |model| {
|
CloudModel::iter()
|
||||||
if let CloudModel::Custom(_) = model {
|
.filter_map(move |model| {
|
||||||
Some(CloudModel::Custom(custom_model.take()?))
|
if let CloudModel::Custom(_) = model {
|
||||||
} else {
|
Some(CloudModel::Custom(custom_model.take()?))
|
||||||
Some(model)
|
} else {
|
||||||
}
|
Some(model)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
.map(LanguageModel::Cloud)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn settings_version(&self) -> usize {
|
fn settings_version(&self) -> usize {
|
||||||
self.settings_version
|
self.settings_version
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn model(&self) -> CloudModel {
|
fn is_authenticated(&self) -> bool {
|
||||||
self.model.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_authenticated(&self) -> bool {
|
|
||||||
self.status.is_connected()
|
self.status.is_connected()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
cx.spawn(move |cx| async move { client.authenticate_and_connect(true, &cx).await })
|
cx.spawn(move |cx| async move { client.authenticate_and_connect(true, &cx).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
||||||
cx.new_view(|_cx| AuthenticationPrompt).into()
|
cx.new_view(|_cx| AuthenticationPrompt).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_tokens(
|
fn reset_credentials(&self, _cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
Task::ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(&self) -> LanguageModel {
|
||||||
|
LanguageModel::Cloud(self.model.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_tokens(
|
||||||
&self,
|
&self,
|
||||||
request: LanguageModelRequest,
|
request: LanguageModelRequest,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
@@ -101,7 +108,10 @@ impl CloudCompletionProvider {
|
|||||||
count_open_ai_tokens(request, cx.background_executor())
|
count_open_ai_tokens(request, cx.background_executor())
|
||||||
}
|
}
|
||||||
LanguageModel::Cloud(
|
LanguageModel::Cloud(
|
||||||
CloudModel::Claude3Opus | CloudModel::Claude3Sonnet | CloudModel::Claude3Haiku,
|
CloudModel::Claude3_5Sonnet
|
||||||
|
| CloudModel::Claude3Opus
|
||||||
|
| CloudModel::Claude3Sonnet
|
||||||
|
| CloudModel::Claude3Haiku,
|
||||||
) => {
|
) => {
|
||||||
// Can't find a tokenizer for Claude 3, so for now just use the same as OpenAI's as an approximation.
|
// Can't find a tokenizer for Claude 3, so for now just use the same as OpenAI's as an approximation.
|
||||||
count_open_ai_tokens(request, cx.background_executor())
|
count_open_ai_tokens(request, cx.background_executor())
|
||||||
@@ -125,7 +135,7 @@ impl CloudCompletionProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complete(
|
fn complete(
|
||||||
&self,
|
&self,
|
||||||
mut request: LanguageModelRequest,
|
mut request: LanguageModelRequest,
|
||||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||||
@@ -158,6 +168,10 @@ impl CloudCompletionProvider {
|
|||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AuthenticationPrompt;
|
struct AuthenticationPrompt;
|
||||||
|
|||||||
@@ -1,29 +1,107 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use collections::HashMap;
|
||||||
use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
|
use futures::{channel::mpsc, future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
|
||||||
|
use gpui::{AnyView, AppContext, Task};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use ui::WindowContext;
|
||||||
|
|
||||||
|
use crate::{LanguageModel, LanguageModelCompletionProvider, LanguageModelRequest};
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct FakeCompletionProvider {
|
pub struct FakeCompletionProvider {
|
||||||
current_completion_tx: Arc<parking_lot::Mutex<Option<mpsc::UnboundedSender<String>>>>,
|
current_completion_txs: Arc<parking_lot::Mutex<HashMap<String, mpsc::UnboundedSender<String>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeCompletionProvider {
|
impl FakeCompletionProvider {
|
||||||
pub fn complete(&self) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
#[cfg(test)]
|
||||||
let (tx, rx) = mpsc::unbounded();
|
pub fn setup_test(cx: &mut AppContext) -> Self {
|
||||||
*self.current_completion_tx.lock() = Some(tx);
|
use crate::CompletionProvider;
|
||||||
async move { Ok(rx.map(Ok).boxed()) }.boxed()
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
|
let this = Self::default();
|
||||||
|
let provider = CompletionProvider::new(Arc::new(RwLock::new(this.clone())), None);
|
||||||
|
cx.set_global(provider);
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_completion(&self, chunk: String) {
|
pub fn running_completions(&self) -> Vec<LanguageModelRequest> {
|
||||||
self.current_completion_tx
|
self.current_completion_txs
|
||||||
.lock()
|
.lock()
|
||||||
.as_ref()
|
.keys()
|
||||||
|
.map(|k| serde_json::from_str(k).unwrap())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn completion_count(&self) -> usize {
|
||||||
|
self.current_completion_txs.lock().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_completion(&self, request: &LanguageModelRequest, chunk: String) {
|
||||||
|
let json = serde_json::to_string(request).unwrap();
|
||||||
|
self.current_completion_txs
|
||||||
|
.lock()
|
||||||
|
.get(&json)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unbounded_send(chunk)
|
.unbounded_send(chunk)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_completion(&self) {
|
pub fn finish_completion(&self, request: &LanguageModelRequest) {
|
||||||
self.current_completion_tx.lock().take();
|
self.current_completion_txs
|
||||||
|
.lock()
|
||||||
|
.remove(&serde_json::to_string(request).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageModelCompletionProvider for FakeCompletionProvider {
|
||||||
|
fn available_models(&self, _cx: &AppContext) -> Vec<LanguageModel> {
|
||||||
|
vec![LanguageModel::default()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings_version(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_authenticated(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authenticate(&self, _cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
Task::ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authentication_prompt(&self, _cx: &mut WindowContext) -> AnyView {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_credentials(&self, _cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
Task::ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(&self) -> LanguageModel {
|
||||||
|
LanguageModel::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_tokens(
|
||||||
|
&self,
|
||||||
|
_request: LanguageModelRequest,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) -> BoxFuture<'static, Result<usize>> {
|
||||||
|
futures::future::ready(Ok(0)).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete(
|
||||||
|
&self,
|
||||||
|
_request: LanguageModelRequest,
|
||||||
|
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||||
|
let (tx, rx) = mpsc::unbounded();
|
||||||
|
self.current_completion_txs
|
||||||
|
.lock()
|
||||||
|
.insert(serde_json::to_string(&_request).unwrap(), tx);
|
||||||
|
async move { Ok(rx.map(Ok).boxed()) }.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
358
crates/assistant/src/completion_provider/ollama.rs
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
use crate::LanguageModelCompletionProvider;
|
||||||
|
use crate::{
|
||||||
|
assistant_settings::OllamaModel, CompletionProvider, LanguageModel, LanguageModelRequest, Role,
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use futures::StreamExt as _;
|
||||||
|
use futures::{future::BoxFuture, stream::BoxStream, FutureExt};
|
||||||
|
use gpui::{AnyView, AppContext, Task};
|
||||||
|
use http::HttpClient;
|
||||||
|
use ollama::{
|
||||||
|
get_models, preload_model, stream_chat_completion, ChatMessage, ChatOptions, ChatRequest,
|
||||||
|
Role as OllamaRole,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
use ui::{prelude::*, ButtonLike, ElevationIndex};
|
||||||
|
|
||||||
|
const OLLAMA_DOWNLOAD_URL: &str = "https://ollama.com/download";
|
||||||
|
const OLLAMA_LIBRARY_URL: &str = "https://ollama.com/library";
|
||||||
|
|
||||||
|
pub struct OllamaCompletionProvider {
|
||||||
|
api_url: String,
|
||||||
|
model: OllamaModel,
|
||||||
|
http_client: Arc<dyn HttpClient>,
|
||||||
|
low_speed_timeout: Option<Duration>,
|
||||||
|
settings_version: usize,
|
||||||
|
available_models: Vec<OllamaModel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageModelCompletionProvider for OllamaCompletionProvider {
|
||||||
|
fn available_models(&self, _cx: &AppContext) -> Vec<LanguageModel> {
|
||||||
|
self.available_models
|
||||||
|
.iter()
|
||||||
|
.map(|m| LanguageModel::Ollama(m.clone()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings_version(&self) -> usize {
|
||||||
|
self.settings_version
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_authenticated(&self) -> bool {
|
||||||
|
!self.available_models.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
if self.is_authenticated() {
|
||||||
|
Task::ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
self.fetch_models(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
||||||
|
let fetch_models = Box::new(move |cx: &mut WindowContext| {
|
||||||
|
cx.update_global::<CompletionProvider, _>(|provider, cx| {
|
||||||
|
provider
|
||||||
|
.update_current_as::<_, OllamaCompletionProvider>(|provider| {
|
||||||
|
provider.fetch_models(cx)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| Task::ready(Ok(())))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.new_view(|cx| DownloadOllamaMessage::new(fetch_models, cx))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
self.fetch_models(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(&self) -> LanguageModel {
|
||||||
|
LanguageModel::Ollama(self.model.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_tokens(
|
||||||
|
&self,
|
||||||
|
request: LanguageModelRequest,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) -> BoxFuture<'static, Result<usize>> {
|
||||||
|
// There is no endpoint for this _yet_ in Ollama
|
||||||
|
// see: https://github.com/ollama/ollama/issues/1716 and https://github.com/ollama/ollama/issues/3582
|
||||||
|
let token_count = request
|
||||||
|
.messages
|
||||||
|
.iter()
|
||||||
|
.map(|msg| msg.content.chars().count())
|
||||||
|
.sum::<usize>()
|
||||||
|
/ 4;
|
||||||
|
|
||||||
|
async move { Ok(token_count) }.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete(
|
||||||
|
&self,
|
||||||
|
request: LanguageModelRequest,
|
||||||
|
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||||
|
let request = self.to_ollama_request(request);
|
||||||
|
|
||||||
|
let http_client = self.http_client.clone();
|
||||||
|
let api_url = self.api_url.clone();
|
||||||
|
let low_speed_timeout = self.low_speed_timeout;
|
||||||
|
async move {
|
||||||
|
let request =
|
||||||
|
stream_chat_completion(http_client.as_ref(), &api_url, request, low_speed_timeout);
|
||||||
|
let response = request.await?;
|
||||||
|
let stream = response
|
||||||
|
.filter_map(|response| async move {
|
||||||
|
match response {
|
||||||
|
Ok(delta) => {
|
||||||
|
let content = match delta.message {
|
||||||
|
ChatMessage::User { content } => content,
|
||||||
|
ChatMessage::Assistant { content } => content,
|
||||||
|
ChatMessage::System { content } => content,
|
||||||
|
};
|
||||||
|
Some(Ok(content))
|
||||||
|
}
|
||||||
|
Err(error) => Some(Err(error)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
Ok(stream)
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OllamaCompletionProvider {
|
||||||
|
pub fn new(
|
||||||
|
model: OllamaModel,
|
||||||
|
api_url: String,
|
||||||
|
http_client: Arc<dyn HttpClient>,
|
||||||
|
low_speed_timeout: Option<Duration>,
|
||||||
|
settings_version: usize,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> Self {
|
||||||
|
cx.spawn({
|
||||||
|
let api_url = api_url.clone();
|
||||||
|
let client = http_client.clone();
|
||||||
|
let model = model.name.clone();
|
||||||
|
|
||||||
|
|_| async move {
|
||||||
|
if model.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
preload_model(client.as_ref(), &api_url, &model).await
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
api_url,
|
||||||
|
model,
|
||||||
|
http_client,
|
||||||
|
low_speed_timeout,
|
||||||
|
settings_version,
|
||||||
|
available_models: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
model: OllamaModel,
|
||||||
|
api_url: String,
|
||||||
|
low_speed_timeout: Option<Duration>,
|
||||||
|
settings_version: usize,
|
||||||
|
cx: &AppContext,
|
||||||
|
) {
|
||||||
|
cx.spawn({
|
||||||
|
let api_url = api_url.clone();
|
||||||
|
let client = self.http_client.clone();
|
||||||
|
let model = model.name.clone();
|
||||||
|
|
||||||
|
|_| async move { preload_model(client.as_ref(), &api_url, &model).await }
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
|
||||||
|
if model.name.is_empty() {
|
||||||
|
self.select_first_available_model()
|
||||||
|
} else {
|
||||||
|
self.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.api_url = api_url;
|
||||||
|
self.low_speed_timeout = low_speed_timeout;
|
||||||
|
self.settings_version = settings_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_first_available_model(&mut self) {
|
||||||
|
if let Some(model) = self.available_models.first() {
|
||||||
|
self.model = model.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_models(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
|
let http_client = self.http_client.clone();
|
||||||
|
let api_url = self.api_url.clone();
|
||||||
|
|
||||||
|
// As a proxy for the server being "authenticated", we'll check if its up by fetching the models
|
||||||
|
cx.spawn(|mut cx| async move {
|
||||||
|
let models = get_models(http_client.as_ref(), &api_url, None).await?;
|
||||||
|
|
||||||
|
let mut models: Vec<OllamaModel> = models
|
||||||
|
.into_iter()
|
||||||
|
// Since there is no metadata from the Ollama API
|
||||||
|
// indicating which models are embedding models,
|
||||||
|
// simply filter out models with "-embed" in their name
|
||||||
|
.filter(|model| !model.name.contains("-embed"))
|
||||||
|
.map(|model| OllamaModel::new(&model.name))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
models.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
|
|
||||||
|
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
|
provider.update_current_as::<_, OllamaCompletionProvider>(|provider| {
|
||||||
|
provider.available_models = models;
|
||||||
|
|
||||||
|
if !provider.available_models.is_empty() && provider.model.name.is_empty() {
|
||||||
|
provider.select_first_available_model()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_ollama_request(&self, request: LanguageModelRequest) -> ChatRequest {
|
||||||
|
let model = match request.model {
|
||||||
|
LanguageModel::Ollama(model) => model,
|
||||||
|
_ => self.model.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
ChatRequest {
|
||||||
|
model: model.name,
|
||||||
|
messages: request
|
||||||
|
.messages
|
||||||
|
.into_iter()
|
||||||
|
.map(|msg| match msg.role {
|
||||||
|
Role::User => ChatMessage::User {
|
||||||
|
content: msg.content,
|
||||||
|
},
|
||||||
|
Role::Assistant => ChatMessage::Assistant {
|
||||||
|
content: msg.content,
|
||||||
|
},
|
||||||
|
Role::System => ChatMessage::System {
|
||||||
|
content: msg.content,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
keep_alive: model.keep_alive.unwrap_or_default(),
|
||||||
|
stream: true,
|
||||||
|
options: Some(ChatOptions {
|
||||||
|
num_ctx: Some(model.max_tokens),
|
||||||
|
stop: Some(request.stop),
|
||||||
|
temperature: Some(request.temperature),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Role> for ollama::Role {
|
||||||
|
fn from(val: Role) -> Self {
|
||||||
|
match val {
|
||||||
|
Role::User => OllamaRole::User,
|
||||||
|
Role::Assistant => OllamaRole::Assistant,
|
||||||
|
Role::System => OllamaRole::System,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DownloadOllamaMessage {
|
||||||
|
retry_connection: Box<dyn Fn(&mut WindowContext) -> Task<Result<()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DownloadOllamaMessage {
|
||||||
|
pub fn new(
|
||||||
|
retry_connection: Box<dyn Fn(&mut WindowContext) -> Task<Result<()>>>,
|
||||||
|
_cx: &mut ViewContext<Self>,
|
||||||
|
) -> Self {
|
||||||
|
Self { retry_connection }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_download_button(&self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
ButtonLike::new("download_ollama_button")
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Large)
|
||||||
|
.layer(ElevationIndex::ModalSurface)
|
||||||
|
.child(Label::new("Get Ollama"))
|
||||||
|
.on_click(move |_, cx| cx.open_url(OLLAMA_DOWNLOAD_URL))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_retry_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
ButtonLike::new("retry_ollama_models")
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Large)
|
||||||
|
.layer(ElevationIndex::ModalSurface)
|
||||||
|
.child(Label::new("Retry"))
|
||||||
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
|
let connected = (this.retry_connection)(cx);
|
||||||
|
|
||||||
|
cx.spawn(|_this, _cx| async move {
|
||||||
|
connected.await?;
|
||||||
|
anyhow::Ok(())
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_next_steps(&self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
v_flex()
|
||||||
|
.p_4()
|
||||||
|
.size_full()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
Label::new("Once Ollama is on your machine, make sure to download a model or two.")
|
||||||
|
.size(LabelSize::Large),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
h_flex().w_full().p_4().justify_center().gap_2().child(
|
||||||
|
ButtonLike::new("view-models")
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Large)
|
||||||
|
.layer(ElevationIndex::ModalSurface)
|
||||||
|
.child(Label::new("View Available Models"))
|
||||||
|
.on_click(move |_, cx| cx.open_url(OLLAMA_LIBRARY_URL)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for DownloadOllamaMessage {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
v_flex()
|
||||||
|
.p_4()
|
||||||
|
.size_full()
|
||||||
|
.gap_2()
|
||||||
|
.child(Label::new("To use Ollama models via the assistant, Ollama must be running on your machine with at least one model downloaded.").size(LabelSize::Large))
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.p_4()
|
||||||
|
.justify_center()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
self.render_download_button(cx)
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
self.render_retry_button(cx)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.child(self.render_next_steps(cx))
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::assistant_settings::CloudModel;
|
use crate::assistant_settings::CloudModel;
|
||||||
|
use crate::assistant_settings::{AssistantProvider, AssistantSettings};
|
||||||
|
use crate::LanguageModelCompletionProvider;
|
||||||
use crate::{
|
use crate::{
|
||||||
assistant_settings::OpenAiModel, CompletionProvider, LanguageModel, LanguageModelRequest, Role,
|
assistant_settings::OpenAiModel, CompletionProvider, LanguageModel, LanguageModelRequest, Role,
|
||||||
};
|
};
|
||||||
@@ -56,19 +58,75 @@ impl OpenAiCompletionProvider {
|
|||||||
self.settings_version = settings_version;
|
self.settings_version = settings_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_models(&self) -> impl Iterator<Item = OpenAiModel> {
|
fn to_open_ai_request(&self, request: LanguageModelRequest) -> Request {
|
||||||
OpenAiModel::iter()
|
let model = match request.model {
|
||||||
|
LanguageModel::OpenAi(model) => model,
|
||||||
|
_ => self.model.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Request {
|
||||||
|
model,
|
||||||
|
messages: request
|
||||||
|
.messages
|
||||||
|
.into_iter()
|
||||||
|
.map(|msg| match msg.role {
|
||||||
|
Role::User => RequestMessage::User {
|
||||||
|
content: msg.content,
|
||||||
|
},
|
||||||
|
Role::Assistant => RequestMessage::Assistant {
|
||||||
|
content: Some(msg.content),
|
||||||
|
tool_calls: Vec::new(),
|
||||||
|
},
|
||||||
|
Role::System => RequestMessage::System {
|
||||||
|
content: msg.content,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
stream: true,
|
||||||
|
stop: request.stop,
|
||||||
|
temperature: request.temperature,
|
||||||
|
tools: Vec::new(),
|
||||||
|
tool_choice: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageModelCompletionProvider for OpenAiCompletionProvider {
|
||||||
|
fn available_models(&self, cx: &AppContext) -> Vec<LanguageModel> {
|
||||||
|
if let AssistantProvider::OpenAi {
|
||||||
|
available_models, ..
|
||||||
|
} = &AssistantSettings::get_global(cx).provider
|
||||||
|
{
|
||||||
|
if !available_models.is_empty() {
|
||||||
|
return available_models
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(LanguageModel::OpenAi)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let available_models = if matches!(self.model, OpenAiModel::Custom { .. }) {
|
||||||
|
vec![self.model.clone()]
|
||||||
|
} else {
|
||||||
|
OpenAiModel::iter()
|
||||||
|
.filter(|model| !matches!(model, OpenAiModel::Custom { .. }))
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
available_models
|
||||||
|
.into_iter()
|
||||||
|
.map(LanguageModel::OpenAi)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn settings_version(&self) -> usize {
|
fn settings_version(&self) -> usize {
|
||||||
self.settings_version
|
self.settings_version
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_authenticated(&self) -> bool {
|
fn is_authenticated(&self) -> bool {
|
||||||
self.api_key.is_some()
|
self.api_key.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
fn authenticate(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
if self.is_authenticated() {
|
if self.is_authenticated() {
|
||||||
Task::ready(Ok(()))
|
Task::ready(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
@@ -84,36 +142,36 @@ impl OpenAiCompletionProvider {
|
|||||||
String::from_utf8(api_key)?
|
String::from_utf8(api_key)?
|
||||||
};
|
};
|
||||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
if let CompletionProvider::OpenAi(provider) = provider {
|
provider.update_current_as::<_, Self>(|provider| {
|
||||||
provider.api_key = Some(api_key);
|
provider.api_key = Some(api_key);
|
||||||
}
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
fn reset_credentials(&self, cx: &AppContext) -> Task<Result<()>> {
|
||||||
let delete_credentials = cx.delete_credentials(&self.api_url);
|
let delete_credentials = cx.delete_credentials(&self.api_url);
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
delete_credentials.await.log_err();
|
delete_credentials.await.log_err();
|
||||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
if let CompletionProvider::OpenAi(provider) = provider {
|
provider.update_current_as::<_, Self>(|provider| {
|
||||||
provider.api_key = None;
|
provider.api_key = None;
|
||||||
}
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView {
|
||||||
cx.new_view(|cx| AuthenticationPrompt::new(self.api_url.clone(), cx))
|
cx.new_view(|cx| AuthenticationPrompt::new(self.api_url.clone(), cx))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn model(&self) -> OpenAiModel {
|
fn model(&self) -> LanguageModel {
|
||||||
self.model.clone()
|
LanguageModel::OpenAi(self.model.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_tokens(
|
fn count_tokens(
|
||||||
&self,
|
&self,
|
||||||
request: LanguageModelRequest,
|
request: LanguageModelRequest,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
@@ -121,7 +179,7 @@ impl OpenAiCompletionProvider {
|
|||||||
count_open_ai_tokens(request, cx.background_executor())
|
count_open_ai_tokens(request, cx.background_executor())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complete(
|
fn complete(
|
||||||
&self,
|
&self,
|
||||||
request: LanguageModelRequest,
|
request: LanguageModelRequest,
|
||||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||||
@@ -154,36 +212,8 @@ impl OpenAiCompletionProvider {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_open_ai_request(&self, request: LanguageModelRequest) -> Request {
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
let model = match request.model {
|
self
|
||||||
LanguageModel::OpenAi(model) => model,
|
|
||||||
_ => self.model(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Request {
|
|
||||||
model,
|
|
||||||
messages: request
|
|
||||||
.messages
|
|
||||||
.into_iter()
|
|
||||||
.map(|msg| match msg.role {
|
|
||||||
Role::User => RequestMessage::User {
|
|
||||||
content: msg.content,
|
|
||||||
},
|
|
||||||
Role::Assistant => RequestMessage::Assistant {
|
|
||||||
content: Some(msg.content),
|
|
||||||
tool_calls: Vec::new(),
|
|
||||||
},
|
|
||||||
Role::System => RequestMessage::System {
|
|
||||||
content: msg.content,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
stream: true,
|
|
||||||
stop: request.stop,
|
|
||||||
temperature: request.temperature,
|
|
||||||
tools: Vec::new(),
|
|
||||||
tool_choice: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,9 +240,11 @@ pub fn count_open_ai_tokens(
|
|||||||
|
|
||||||
match request.model {
|
match request.model {
|
||||||
LanguageModel::Anthropic(_)
|
LanguageModel::Anthropic(_)
|
||||||
|
| LanguageModel::Cloud(CloudModel::Claude3_5Sonnet)
|
||||||
| LanguageModel::Cloud(CloudModel::Claude3Opus)
|
| LanguageModel::Cloud(CloudModel::Claude3Opus)
|
||||||
| LanguageModel::Cloud(CloudModel::Claude3Sonnet)
|
| LanguageModel::Cloud(CloudModel::Claude3Sonnet)
|
||||||
| LanguageModel::Cloud(CloudModel::Claude3Haiku) => {
|
| LanguageModel::Cloud(CloudModel::Claude3Haiku)
|
||||||
|
| LanguageModel::OpenAi(OpenAiModel::Custom { .. }) => {
|
||||||
// Tiktoken doesn't yet support these models, so we manually use the
|
// Tiktoken doesn't yet support these models, so we manually use the
|
||||||
// same tokenizer as GPT-4.
|
// same tokenizer as GPT-4.
|
||||||
tiktoken_rs::num_tokens_from_messages("gpt-4", &messages)
|
tiktoken_rs::num_tokens_from_messages("gpt-4", &messages)
|
||||||
@@ -263,9 +295,9 @@ impl AuthenticationPrompt {
|
|||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
write_credentials.await?;
|
write_credentials.await?;
|
||||||
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
cx.update_global::<CompletionProvider, _>(|provider, _cx| {
|
||||||
if let CompletionProvider::OpenAi(provider) = provider {
|
provider.update_current_as::<_, OpenAiCompletionProvider>(|provider| {
|
||||||
provider.api_key = Some(api_key);
|
provider.api_key = Some(api_key);
|
||||||
}
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
@@ -336,7 +368,7 @@ impl Render for AuthenticationPrompt {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(Label::new("Click on").size(LabelSize::Small))
|
.child(Label::new("Click on").size(LabelSize::Small))
|
||||||
.child(Icon::new(IconName::Ai).size(IconSize::XSmall))
|
.child(Icon::new(IconName::ZedAssistant).size(IconSize::XSmall))
|
||||||
.child(
|
.child(
|
||||||
Label::new("in the status bar to close this panel.").size(LabelSize::Small),
|
Label::new("in the status bar to close this panel.").size(LabelSize::Small),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
use crate::{assistant_settings::OpenAiModel, MessageId, MessageMetadata};
|
use crate::{assistant_settings::OpenAiModel, MessageId, MessageMetadata};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use assistant_slash_command::SlashCommandOutputSection;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use fuzzy::StringMatchCandidate;
|
use fuzzy::StringMatchCandidate;
|
||||||
use gpui::{AppContext, Model, ModelContext, Task};
|
use gpui::{AppContext, Model, ModelContext, Task};
|
||||||
|
use paths::contexts_dir;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{cmp::Reverse, ffi::OsStr, path::PathBuf, sync::Arc, time::Duration};
|
use std::{cmp::Reverse, ffi::OsStr, path::PathBuf, sync::Arc, time::Duration};
|
||||||
use ui::Context;
|
use ui::Context;
|
||||||
use util::{paths::CONTEXTS_DIR, ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct SavedMessage {
|
pub struct SavedMessage {
|
||||||
@@ -26,10 +28,22 @@ pub struct SavedContext {
|
|||||||
pub messages: Vec<SavedMessage>,
|
pub messages: Vec<SavedMessage>,
|
||||||
pub message_metadata: HashMap<MessageId, MessageMetadata>,
|
pub message_metadata: HashMap<MessageId, MessageMetadata>,
|
||||||
pub summary: String,
|
pub summary: String,
|
||||||
|
pub slash_command_output_sections: Vec<SlashCommandOutputSection<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SavedContext {
|
impl SavedContext {
|
||||||
pub const VERSION: &'static str = "0.2.0";
|
pub const VERSION: &'static str = "0.3.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SavedContextV0_2_0 {
|
||||||
|
pub id: Option<String>,
|
||||||
|
pub zed: String,
|
||||||
|
pub version: String,
|
||||||
|
pub text: String,
|
||||||
|
pub messages: Vec<SavedMessage>,
|
||||||
|
pub message_metadata: HashMap<MessageId, MessageMetadata>,
|
||||||
|
pub summary: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -62,7 +76,7 @@ impl ContextStore {
|
|||||||
pub fn new(fs: Arc<dyn Fs>, cx: &mut AppContext) -> Task<Result<Model<Self>>> {
|
pub fn new(fs: Arc<dyn Fs>, cx: &mut AppContext) -> Task<Result<Model<Self>>> {
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
|
const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100);
|
||||||
let (mut events, _) = fs.watch(&CONTEXTS_DIR, CONTEXT_WATCH_DURATION).await;
|
let (mut events, _) = fs.watch(contexts_dir(), CONTEXT_WATCH_DURATION).await;
|
||||||
|
|
||||||
let this = cx.new_model(|cx: &mut ModelContext<Self>| Self {
|
let this = cx.new_model(|cx: &mut ModelContext<Self>| Self {
|
||||||
contexts_metadata: Vec::new(),
|
contexts_metadata: Vec::new(),
|
||||||
@@ -99,6 +113,20 @@ impl ContextStore {
|
|||||||
SavedContext::VERSION => {
|
SavedContext::VERSION => {
|
||||||
Ok(serde_json::from_value::<SavedContext>(saved_context_json)?)
|
Ok(serde_json::from_value::<SavedContext>(saved_context_json)?)
|
||||||
}
|
}
|
||||||
|
"0.2.0" => {
|
||||||
|
let saved_context =
|
||||||
|
serde_json::from_value::<SavedContextV0_2_0>(saved_context_json)?;
|
||||||
|
Ok(SavedContext {
|
||||||
|
id: saved_context.id,
|
||||||
|
zed: saved_context.zed,
|
||||||
|
version: saved_context.version,
|
||||||
|
text: saved_context.text,
|
||||||
|
messages: saved_context.messages,
|
||||||
|
message_metadata: saved_context.message_metadata,
|
||||||
|
summary: saved_context.summary,
|
||||||
|
slash_command_output_sections: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
"0.1.0" => {
|
"0.1.0" => {
|
||||||
let saved_context =
|
let saved_context =
|
||||||
serde_json::from_value::<SavedContextV0_1_0>(saved_context_json)?;
|
serde_json::from_value::<SavedContextV0_1_0>(saved_context_json)?;
|
||||||
@@ -110,6 +138,7 @@ impl ContextStore {
|
|||||||
messages: saved_context.messages,
|
messages: saved_context.messages,
|
||||||
message_metadata: saved_context.message_metadata,
|
message_metadata: saved_context.message_metadata,
|
||||||
summary: saved_context.summary,
|
summary: saved_context.summary,
|
||||||
|
slash_command_output_sections: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(anyhow!("unrecognized saved context version: {}", version)),
|
_ => Err(anyhow!("unrecognized saved context version: {}", version)),
|
||||||
@@ -152,9 +181,9 @@ impl ContextStore {
|
|||||||
fn reload(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
fn reload(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||||
let fs = self.fs.clone();
|
let fs = self.fs.clone();
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
fs.create_dir(&CONTEXTS_DIR).await?;
|
fs.create_dir(contexts_dir()).await?;
|
||||||
|
|
||||||
let mut paths = fs.read_dir(&CONTEXTS_DIR).await?;
|
let mut paths = fs.read_dir(contexts_dir()).await?;
|
||||||
let mut contexts = Vec::<SavedContextMetadata>::new();
|
let mut contexts = Vec::<SavedContextMetadata>::new();
|
||||||
while let Some(path) = paths.next().await {
|
while let Some(path) = paths.next().await {
|
||||||
let path = path?;
|
let path = path?;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
use crate::{assistant_settings::AssistantSettings, CompletionProvider, ToggleModelSelector};
|
use crate::{assistant_settings::AssistantSettings, CompletionProvider, ToggleModelSelector};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use settings::update_settings_file;
|
use settings::update_settings_file;
|
||||||
use ui::{popover_menu, prelude::*, ButtonLike, ContextMenu, PopoverMenuHandle, Tooltip};
|
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct ModelSelector {
|
pub struct ModelSelector {
|
||||||
@@ -19,11 +19,11 @@ impl ModelSelector {
|
|||||||
|
|
||||||
impl RenderOnce for ModelSelector {
|
impl RenderOnce for ModelSelector {
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
popover_menu("model-switcher")
|
PopoverMenu::new("model-switcher")
|
||||||
.with_handle(self.handle)
|
.with_handle(self.handle)
|
||||||
.menu(move |cx| {
|
.menu(move |cx| {
|
||||||
ContextMenu::build(cx, |mut menu, cx| {
|
ContextMenu::build(cx, |mut menu, cx| {
|
||||||
for model in CompletionProvider::global(cx).available_models() {
|
for model in CompletionProvider::global(cx).available_models(cx) {
|
||||||
menu = menu.custom_entry(
|
menu = menu.custom_entry(
|
||||||
{
|
{
|
||||||
let model = model.clone();
|
let model = model.clone();
|
||||||
@@ -49,6 +49,7 @@ impl RenderOnce for ModelSelector {
|
|||||||
})
|
})
|
||||||
.trigger(
|
.trigger(
|
||||||
ButtonLike::new("active-model")
|
ButtonLike::new("active-model")
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.w_full()
|
.w_full()
|
||||||
@@ -67,18 +68,15 @@ impl RenderOnce for ModelSelector {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div().child(
|
Icon::new(IconName::ChevronDown)
|
||||||
Icon::new(IconName::ChevronDown)
|
.color(Color::Muted)
|
||||||
.color(Color::Muted)
|
.size(IconSize::XSmall),
|
||||||
.size(IconSize::XSmall),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.style(ButtonStyle::Subtle)
|
|
||||||
.tooltip(move |cx| {
|
.tooltip(move |cx| {
|
||||||
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
|
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.anchor(gpui::AnchorCorner::BottomRight)
|
.attach(gpui::AnchorCorner::BottomLeft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,90 +6,130 @@ pub fn generate_content_prompt(
|
|||||||
language_name: Option<&str>,
|
language_name: Option<&str>,
|
||||||
buffer: BufferSnapshot,
|
buffer: BufferSnapshot,
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
project_name: Option<String>,
|
_project_name: Option<String>,
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
let mut prompt = String::new();
|
let mut prompt = String::new();
|
||||||
|
|
||||||
let content_type = match language_name {
|
let content_type = match language_name {
|
||||||
None | Some("Markdown" | "Plain Text") => {
|
None | Some("Markdown" | "Plain Text") => {
|
||||||
writeln!(prompt, "You are an expert engineer.")?;
|
|
||||||
"Text"
|
|
||||||
}
|
|
||||||
Some(language_name) => {
|
|
||||||
writeln!(prompt, "You are an expert {language_name} engineer.")?;
|
|
||||||
writeln!(
|
writeln!(
|
||||||
prompt,
|
prompt,
|
||||||
"Your answer MUST always and only be valid {}.",
|
"Here's a file of text that I'm going to ask you to make an edit to."
|
||||||
language_name
|
|
||||||
)?;
|
)?;
|
||||||
"Code"
|
"text"
|
||||||
|
}
|
||||||
|
Some(language_name) => {
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"Here's a file of {language_name} that I'm going to ask you to make an edit to."
|
||||||
|
)?;
|
||||||
|
"code"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(project_name) = project_name {
|
const MAX_CTX: usize = 50000;
|
||||||
writeln!(
|
let mut is_truncated = false;
|
||||||
prompt,
|
if range.is_empty() {
|
||||||
"You are currently working inside the '{project_name}' project in code editor Zed."
|
prompt.push_str("The point you'll need to insert at is marked with <insert_here></insert_here>.\n\n<document>");
|
||||||
)?;
|
} else {
|
||||||
|
prompt.push_str("The section you'll need to rewrite is marked with <rewrite_this></rewrite_this> tags.\n\n<document>");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include file content.
|
// Include file content.
|
||||||
for chunk in buffer.text_for_range(0..range.start) {
|
let before_range = 0..range.start;
|
||||||
prompt.push_str(chunk);
|
let truncated_before = if before_range.len() > MAX_CTX {
|
||||||
}
|
is_truncated = true;
|
||||||
|
range.start - MAX_CTX..range.start
|
||||||
if range.is_empty() {
|
|
||||||
prompt.push_str("<|START|>");
|
|
||||||
} else {
|
} else {
|
||||||
prompt.push_str("<|START|");
|
before_range
|
||||||
}
|
};
|
||||||
|
let mut non_rewrite_len = truncated_before.len();
|
||||||
for chunk in buffer.text_for_range(range.clone()) {
|
for chunk in buffer.text_for_range(truncated_before) {
|
||||||
prompt.push_str(chunk);
|
prompt.push_str(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !range.is_empty() {
|
if !range.is_empty() {
|
||||||
prompt.push_str("|END|>");
|
prompt.push_str("<rewrite_this>\n");
|
||||||
|
for chunk in buffer.text_for_range(range.clone()) {
|
||||||
|
prompt.push_str(chunk);
|
||||||
|
}
|
||||||
|
prompt.push_str("\n<rewrite_this>");
|
||||||
|
} else {
|
||||||
|
prompt.push_str("<insert_here></insert_here>");
|
||||||
}
|
}
|
||||||
|
let after_range = range.end..buffer.len();
|
||||||
for chunk in buffer.text_for_range(range.end..buffer.len()) {
|
let truncated_after = if after_range.len() > MAX_CTX {
|
||||||
|
is_truncated = true;
|
||||||
|
range.end..range.end + MAX_CTX
|
||||||
|
} else {
|
||||||
|
after_range
|
||||||
|
};
|
||||||
|
non_rewrite_len += truncated_after.len();
|
||||||
|
for chunk in buffer.text_for_range(truncated_after) {
|
||||||
prompt.push_str(chunk);
|
prompt.push_str(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt.push('\n');
|
write!(prompt, "</document>\n\n").unwrap();
|
||||||
|
|
||||||
|
if is_truncated {
|
||||||
|
writeln!(prompt, "The context around the relevant section has been truncated (possibly in the middle of a line) for brevity.\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
if range.is_empty() {
|
if range.is_empty() {
|
||||||
writeln!(
|
writeln!(
|
||||||
prompt,
|
prompt,
|
||||||
"Assume the cursor is located where the `<|START|>` span is."
|
"You can't replace {content_type}, your answer will be inserted in place of the `<insert_here></insert_here>` tags. Don't include the insert_here tags in your output.",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
writeln!(
|
writeln!(
|
||||||
prompt,
|
prompt,
|
||||||
"{content_type} can't be replaced, so assume your answer will be inserted at the cursor.",
|
"Generate {content_type} based on the following prompt:\n\n<prompt>\n{user_prompt}\n</prompt>",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
writeln!(
|
writeln!(prompt, "Match the indentation in the original file in the inserted {content_type}, don't include any indentation on blank lines.\n").unwrap();
|
||||||
prompt,
|
prompt.push_str("Immediately start with the following format with no remarks:\n\n```\n{{INSERTED_CODE}}\n```");
|
||||||
"Generate {content_type} based on the users prompt: {user_prompt}",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
} else {
|
||||||
writeln!(prompt, "Modify the user's selected {content_type} based upon the users prompt: '{user_prompt}'").unwrap();
|
writeln!(prompt, "Edit the section of {content_type} in <rewrite_this></rewrite_this> tags based on the following prompt:'").unwrap();
|
||||||
writeln!(prompt, "You must reply with only the adjusted {content_type} (within the '<|START|' and '|END|>' spans) not the entire file.").unwrap();
|
writeln!(prompt, "\n<prompt>\n{user_prompt}\n</prompt>\n").unwrap();
|
||||||
writeln!(
|
let rewrite_len = range.end - range.start;
|
||||||
|
if rewrite_len < 20000 && rewrite_len * 2 < non_rewrite_len {
|
||||||
|
writeln!(prompt, "And here's the section to rewrite based on that prompt again for reference:\n\n<rewrite_this>\n").unwrap();
|
||||||
|
for chunk in buffer.text_for_range(range.clone()) {
|
||||||
|
prompt.push_str(chunk);
|
||||||
|
}
|
||||||
|
writeln!(prompt, "\n</rewrite_this>\n").unwrap();
|
||||||
|
}
|
||||||
|
writeln!(prompt, "Only make changes that are necessary to fulfill the prompt, leave everything else as-is. All surrounding {content_type} will be preserved.\n").unwrap();
|
||||||
|
write!(
|
||||||
prompt,
|
prompt,
|
||||||
"Double check that you only return code and not the '<|START|' and '|END|'> spans"
|
"Start at the indentation level in the original file in the rewritten {content_type}. "
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
prompt.push_str("Don't stop until you've rewritten the entire section, even if you have no more changes to make, always write out the whole section with no unnecessary elisions.");
|
||||||
|
prompt.push_str("\n\nImmediately start with the following format with no remarks:\n\n```\n{{REWRITTEN_CODE}}\n```");
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(prompt, "Never make remarks about the output.").unwrap();
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"Do not return anything else, except the generated {content_type}."
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(prompt)
|
Ok(prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generate_terminal_assistant_prompt(
|
||||||
|
user_prompt: &str,
|
||||||
|
shell: Option<&str>,
|
||||||
|
working_directory: Option<&str>,
|
||||||
|
) -> String {
|
||||||
|
let mut prompt = String::new();
|
||||||
|
writeln!(&mut prompt, "You are an expert terminal user.").unwrap();
|
||||||
|
writeln!(&mut prompt, "You will be given a description of a command and you need to respond with a command that matches the description.").unwrap();
|
||||||
|
writeln!(&mut prompt, "Do not include markdown blocks or any other text formatting in your response, always respond with a single command that can be executed in the given shell.").unwrap();
|
||||||
|
if let Some(shell) = shell {
|
||||||
|
writeln!(&mut prompt, "Current shell is '{shell}'.").unwrap();
|
||||||
|
}
|
||||||
|
if let Some(working_directory) = working_directory {
|
||||||
|
writeln!(
|
||||||
|
&mut prompt,
|
||||||
|
"Current working directory is '{working_directory}'."
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
writeln!(&mut prompt, "Here is the description of the command:").unwrap();
|
||||||
|
prompt.push_str(user_prompt);
|
||||||
|
prompt
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use anyhow::Result;
|
|||||||
pub use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandRegistry};
|
pub use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandRegistry};
|
||||||
use editor::{CompletionProvider, Editor};
|
use editor::{CompletionProvider, Editor};
|
||||||
use fuzzy::{match_strings, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatchCandidate};
|
||||||
use gpui::{Model, Task, ViewContext, WeakView, WindowContext};
|
use gpui::{AppContext, Model, Task, ViewContext, WeakView, WindowContext};
|
||||||
use language::{Anchor, Buffer, CodeLabel, Documentation, LanguageServerId, ToPoint};
|
use language::{Anchor, Buffer, CodeLabel, Documentation, HighlightId, LanguageServerId, ToPoint};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rope::Point;
|
use rope::Point;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -14,17 +14,21 @@ use std::{
|
|||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use ui::ActiveTheme;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub mod active_command;
|
pub mod active_command;
|
||||||
pub mod default_command;
|
pub mod default_command;
|
||||||
|
pub mod diagnostics_command;
|
||||||
|
pub mod docs_command;
|
||||||
pub mod fetch_command;
|
pub mod fetch_command;
|
||||||
pub mod file_command;
|
pub mod file_command;
|
||||||
|
pub mod now_command;
|
||||||
pub mod project_command;
|
pub mod project_command;
|
||||||
pub mod prompt_command;
|
pub mod prompt_command;
|
||||||
pub mod rustdoc_command;
|
|
||||||
pub mod search_command;
|
pub mod search_command;
|
||||||
pub mod tabs_command;
|
pub mod tabs_command;
|
||||||
|
pub mod term_command;
|
||||||
|
|
||||||
pub(crate) struct SlashCommandCompletionProvider {
|
pub(crate) struct SlashCommandCompletionProvider {
|
||||||
commands: Arc<SlashCommandRegistry>,
|
commands: Arc<SlashCommandRegistry>,
|
||||||
@@ -166,7 +170,7 @@ impl SlashCommandCompletionProvider {
|
|||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|command_argument| {
|
.map(|command_argument| {
|
||||||
let confirm =
|
let confirm = if command_argument.run_command {
|
||||||
editor
|
editor
|
||||||
.clone()
|
.clone()
|
||||||
.zip(workspace.clone())
|
.zip(workspace.clone())
|
||||||
@@ -174,7 +178,7 @@ impl SlashCommandCompletionProvider {
|
|||||||
Arc::new({
|
Arc::new({
|
||||||
let command_range = command_range.clone();
|
let command_range = command_range.clone();
|
||||||
let command_name = command_name.clone();
|
let command_name = command_name.clone();
|
||||||
let command_argument = command_argument.clone();
|
let command_argument = command_argument.new_text.clone();
|
||||||
move |cx: &mut WindowContext| {
|
move |cx: &mut WindowContext| {
|
||||||
editor
|
editor
|
||||||
.update(cx, |editor, cx| {
|
.update(cx, |editor, cx| {
|
||||||
@@ -190,15 +194,24 @@ impl SlashCommandCompletionProvider {
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}) as Arc<_>
|
}) as Arc<_>
|
||||||
});
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut new_text = command_argument.new_text.clone();
|
||||||
|
if !command_argument.run_command {
|
||||||
|
new_text.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
project::Completion {
|
project::Completion {
|
||||||
old_range: argument_range.clone(),
|
old_range: argument_range.clone(),
|
||||||
label: CodeLabel::plain(command_argument.clone(), None),
|
label: CodeLabel::plain(command_argument.label, None),
|
||||||
new_text: command_argument.clone(),
|
new_text,
|
||||||
documentation: None,
|
documentation: None,
|
||||||
server_id: LanguageServerId(0),
|
server_id: LanguageServerId(0),
|
||||||
lsp_completion: Default::default(),
|
lsp_completion: Default::default(),
|
||||||
show_new_completions_on_confirm: false,
|
show_new_completions_on_confirm: !command_argument.run_command,
|
||||||
confirm,
|
confirm,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -216,6 +229,7 @@ impl CompletionProvider for SlashCommandCompletionProvider {
|
|||||||
&self,
|
&self,
|
||||||
buffer: &Model<Buffer>,
|
buffer: &Model<Buffer>,
|
||||||
buffer_position: Anchor,
|
buffer_position: Anchor,
|
||||||
|
_: editor::CompletionContext,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Task<Result<Vec<project::Completion>>> {
|
) -> Task<Result<Vec<project::Completion>>> {
|
||||||
let Some((name, argument, command_range, argument_range)) =
|
let Some((name, argument, command_range, argument_range)) =
|
||||||
@@ -344,3 +358,19 @@ impl SlashCommandLine {
|
|||||||
call
|
call
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_label_for_command(
|
||||||
|
command_name: &str,
|
||||||
|
arguments: &[&str],
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> CodeLabel {
|
||||||
|
let mut label = CodeLabel::default();
|
||||||
|
label.push_str(command_name, None);
|
||||||
|
label.push_str(" ", None);
|
||||||
|
label.push_str(
|
||||||
|
&arguments.join(" "),
|
||||||
|
cx.theme().syntax().highlight_id("comment").map(HighlightId),
|
||||||
|
);
|
||||||
|
label.filter_range = 0..command_name.len();
|
||||||
|
label
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput};
|
use super::{
|
||||||
|
diagnostics_command::write_single_file_diagnostics,
|
||||||
|
file_command::{build_entry_output_section, codeblock_fence_for_path},
|
||||||
|
SlashCommand, SlashCommandOutput,
|
||||||
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_slash_command::SlashCommandOutputSection;
|
use assistant_slash_command::ArgumentCompletion;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use gpui::{AppContext, Task, WeakView};
|
use gpui::{AppContext, Task, WeakView};
|
||||||
use language::LspAdapterDelegate;
|
use language::LspAdapterDelegate;
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::sync::atomic::AtomicBool;
|
||||||
use ui::{IntoElement, WindowContext};
|
use std::sync::Arc;
|
||||||
|
use ui::WindowContext;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct ActiveSlashCommand;
|
pub(crate) struct ActiveSlashCommand;
|
||||||
@@ -24,12 +29,12 @@ impl SlashCommand for ActiveSlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
&self,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_query: String,
|
||||||
_cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut AppContext,
|
_cx: &mut AppContext,
|
||||||
) -> Task<Result<Vec<String>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
Task::ready(Err(anyhow!("this command does not require argument")))
|
Task::ready(Err(anyhow!("this command does not require argument")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,46 +62,38 @@ impl SlashCommand for ActiveSlashCommand {
|
|||||||
|
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
let path = snapshot.resolve_file_path(cx, true);
|
let path = snapshot.resolve_file_path(cx, true);
|
||||||
let text = cx.background_executor().spawn({
|
let task = cx.background_executor().spawn({
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
async move {
|
async move {
|
||||||
let path = path
|
let mut output = String::new();
|
||||||
.as_ref()
|
output.push_str(&codeblock_fence_for_path(path.as_deref(), None));
|
||||||
.map(|path| path.to_string_lossy())
|
|
||||||
.unwrap_or_else(|| Cow::Borrowed("untitled"));
|
|
||||||
|
|
||||||
let mut output = String::with_capacity(path.len() + snapshot.len() + 9);
|
|
||||||
output.push_str("```");
|
|
||||||
output.push_str(&path);
|
|
||||||
output.push('\n');
|
|
||||||
for chunk in snapshot.as_rope().chunks() {
|
for chunk in snapshot.as_rope().chunks() {
|
||||||
output.push_str(chunk);
|
output.push_str(chunk);
|
||||||
}
|
}
|
||||||
if !output.ends_with('\n') {
|
if !output.ends_with('\n') {
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
}
|
}
|
||||||
output.push_str("```");
|
output.push_str("```\n");
|
||||||
output
|
let has_diagnostics =
|
||||||
|
write_single_file_diagnostics(&mut output, path.as_deref(), &snapshot);
|
||||||
|
if output.ends_with('\n') {
|
||||||
|
output.pop();
|
||||||
|
}
|
||||||
|
(output, has_diagnostics)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cx.foreground_executor().spawn(async move {
|
cx.foreground_executor().spawn(async move {
|
||||||
let text = text.await;
|
let (text, has_diagnostics) = task.await;
|
||||||
let range = 0..text.len();
|
let range = 0..text.len();
|
||||||
Ok(SlashCommandOutput {
|
Ok(SlashCommandOutput {
|
||||||
text,
|
text,
|
||||||
sections: vec![SlashCommandOutputSection {
|
sections: vec![build_entry_output_section(
|
||||||
range,
|
range,
|
||||||
render_placeholder: Arc::new(move |id, unfold, _| {
|
path.as_deref(),
|
||||||
FilePlaceholder {
|
false,
|
||||||
id,
|
None,
|
||||||
path: path.clone(),
|
)],
|
||||||
line_range: None,
|
run_commands_in_text: has_diagnostics,
|
||||||
unfold,
|
|
||||||
}
|
|
||||||
.into_any_element()
|
|
||||||
}),
|
|
||||||
}],
|
|
||||||
run_commands_in_text: false,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::{prompt_command::PromptPlaceholder, SlashCommand, SlashCommandOutput};
|
use super::{SlashCommand, SlashCommandOutput};
|
||||||
use crate::prompt_library::PromptStore;
|
use crate::prompt_library::PromptStore;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_slash_command::SlashCommandOutputSection;
|
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||||
use gpui::{AppContext, Task, WeakView};
|
use gpui::{AppContext, Task, WeakView};
|
||||||
use language::LspAdapterDelegate;
|
use language::LspAdapterDelegate;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -31,12 +31,12 @@ impl SlashCommand for DefaultSlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
&self,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_query: String,
|
||||||
_cancellation_flag: Arc<AtomicBool>,
|
_cancellation_flag: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut AppContext,
|
_cx: &mut AppContext,
|
||||||
) -> Task<Result<Vec<String>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
Task::ready(Err(anyhow!("this command does not require argument")))
|
Task::ready(Err(anyhow!("this command does not require argument")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ impl SlashCommand for DefaultSlashCommand {
|
|||||||
let prompts = store.default_prompt_metadata();
|
let prompts = store.default_prompt_metadata();
|
||||||
|
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
writeln!(text, "Default Prompt:").unwrap();
|
text.push('\n');
|
||||||
for prompt in prompts {
|
for prompt in prompts {
|
||||||
if let Some(title) = prompt.title {
|
if let Some(title) = prompt.title {
|
||||||
writeln!(text, "/prompt {}", title).unwrap();
|
writeln!(text, "/prompt {}", title).unwrap();
|
||||||
@@ -61,17 +61,15 @@ impl SlashCommand for DefaultSlashCommand {
|
|||||||
}
|
}
|
||||||
text.pop();
|
text.pop();
|
||||||
|
|
||||||
|
if text.is_empty() {
|
||||||
|
text.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
Ok(SlashCommandOutput {
|
Ok(SlashCommandOutput {
|
||||||
sections: vec![SlashCommandOutputSection {
|
sections: vec![SlashCommandOutputSection {
|
||||||
range: 0..text.len(),
|
range: 0..text.len(),
|
||||||
render_placeholder: Arc::new(move |id, unfold, _cx| {
|
icon: IconName::Library,
|
||||||
PromptPlaceholder {
|
label: "Default".into(),
|
||||||
title: "Default".into(),
|
|
||||||
id,
|
|
||||||
unfold,
|
|
||||||
}
|
|
||||||
.into_any_element()
|
|
||||||
}),
|
|
||||||
}],
|
}],
|
||||||
text,
|
text,
|
||||||
run_commands_in_text: true,
|
run_commands_in_text: true,
|
||||||
|
|||||||
497
crates/assistant/src/slash_command/diagnostics_command.rs
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
use super::{create_label_for_command, SlashCommand, SlashCommandOutput};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||||
|
use fuzzy::{PathMatch, StringMatchCandidate};
|
||||||
|
use gpui::{AppContext, Model, Task, View, WeakView};
|
||||||
|
use language::{
|
||||||
|
Anchor, BufferSnapshot, DiagnosticEntry, DiagnosticSeverity, LspAdapterDelegate,
|
||||||
|
OffsetRangeExt, ToOffset,
|
||||||
|
};
|
||||||
|
use project::{DiagnosticSummary, PathMatchCandidateSet, Project};
|
||||||
|
use rope::Point;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::{
|
||||||
|
ops::Range,
|
||||||
|
sync::{atomic::AtomicBool, Arc},
|
||||||
|
};
|
||||||
|
use ui::prelude::*;
|
||||||
|
use util::paths::PathMatcher;
|
||||||
|
use util::ResultExt;
|
||||||
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
pub(crate) struct DiagnosticsSlashCommand;
|
||||||
|
|
||||||
|
impl DiagnosticsSlashCommand {
|
||||||
|
fn search_paths(
|
||||||
|
&self,
|
||||||
|
query: String,
|
||||||
|
cancellation_flag: Arc<AtomicBool>,
|
||||||
|
workspace: &View<Workspace>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Task<Vec<PathMatch>> {
|
||||||
|
if query.is_empty() {
|
||||||
|
let workspace = workspace.read(cx);
|
||||||
|
let entries = workspace.recent_navigation_history(Some(10), cx);
|
||||||
|
let path_prefix: Arc<str> = "".into();
|
||||||
|
Task::ready(
|
||||||
|
entries
|
||||||
|
.into_iter()
|
||||||
|
.map(|(entry, _)| PathMatch {
|
||||||
|
score: 0.,
|
||||||
|
positions: Vec::new(),
|
||||||
|
worktree_id: entry.worktree_id.to_usize(),
|
||||||
|
path: entry.path.clone(),
|
||||||
|
path_prefix: path_prefix.clone(),
|
||||||
|
distance_to_relative_ancestor: 0,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let worktrees = workspace.read(cx).visible_worktrees(cx).collect::<Vec<_>>();
|
||||||
|
let candidate_sets = worktrees
|
||||||
|
.into_iter()
|
||||||
|
.map(|worktree| {
|
||||||
|
let worktree = worktree.read(cx);
|
||||||
|
PathMatchCandidateSet {
|
||||||
|
snapshot: worktree.snapshot(),
|
||||||
|
include_ignored: worktree
|
||||||
|
.root_entry()
|
||||||
|
.map_or(false, |entry| entry.is_ignored),
|
||||||
|
include_root_name: true,
|
||||||
|
candidates: project::Candidates::Entries,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let executor = cx.background_executor().clone();
|
||||||
|
cx.foreground_executor().spawn(async move {
|
||||||
|
fuzzy::match_path_sets(
|
||||||
|
candidate_sets.as_slice(),
|
||||||
|
query.as_str(),
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
100,
|
||||||
|
&cancellation_flag,
|
||||||
|
executor,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlashCommand for DiagnosticsSlashCommand {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"diagnostics".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label(&self, cx: &AppContext) -> language::CodeLabel {
|
||||||
|
create_label_for_command("diagnostics", &[INCLUDE_WARNINGS_ARGUMENT], cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
"Insert diagnostics".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn menu_text(&self) -> String {
|
||||||
|
"Insert Diagnostics".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requires_argument(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_argument(
|
||||||
|
self: Arc<Self>,
|
||||||
|
query: String,
|
||||||
|
cancellation_flag: Arc<AtomicBool>,
|
||||||
|
workspace: Option<WeakView<Workspace>>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
|
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
|
||||||
|
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||||
|
};
|
||||||
|
let query = query.split_whitespace().last().unwrap_or("").to_string();
|
||||||
|
|
||||||
|
let paths = self.search_paths(query.clone(), cancellation_flag.clone(), &workspace, cx);
|
||||||
|
let executor = cx.background_executor().clone();
|
||||||
|
cx.background_executor().spawn(async move {
|
||||||
|
let mut matches: Vec<String> = paths
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|path_match| {
|
||||||
|
format!(
|
||||||
|
"{}{}",
|
||||||
|
path_match.path_prefix,
|
||||||
|
path_match.path.to_string_lossy()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
matches.extend(
|
||||||
|
fuzzy::match_strings(
|
||||||
|
&Options::match_candidates_for_args(),
|
||||||
|
&query,
|
||||||
|
false,
|
||||||
|
10,
|
||||||
|
&cancellation_flag,
|
||||||
|
executor,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|candidate| candidate.string),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(matches
|
||||||
|
.into_iter()
|
||||||
|
.map(|completion| ArgumentCompletion {
|
||||||
|
label: completion.clone(),
|
||||||
|
new_text: completion,
|
||||||
|
run_command: true,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
self: Arc<Self>,
|
||||||
|
argument: Option<&str>,
|
||||||
|
workspace: WeakView<Workspace>,
|
||||||
|
_delegate: Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
|
let Some(workspace) = workspace.upgrade() else {
|
||||||
|
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = Options::parse(argument);
|
||||||
|
|
||||||
|
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
|
||||||
|
cx.spawn(move |_| async move {
|
||||||
|
let Some((text, sections)) = task.await? else {
|
||||||
|
return Ok(SlashCommandOutput::default());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SlashCommandOutput {
|
||||||
|
text,
|
||||||
|
sections: sections
|
||||||
|
.into_iter()
|
||||||
|
.map(|(range, placeholder_type)| SlashCommandOutputSection {
|
||||||
|
range,
|
||||||
|
icon: match placeholder_type {
|
||||||
|
PlaceholderType::Root(_, _) => IconName::ExclamationTriangle,
|
||||||
|
PlaceholderType::File(_) => IconName::File,
|
||||||
|
PlaceholderType::Diagnostic(DiagnosticType::Error, _) => {
|
||||||
|
IconName::XCircle
|
||||||
|
}
|
||||||
|
PlaceholderType::Diagnostic(DiagnosticType::Warning, _) => {
|
||||||
|
IconName::ExclamationTriangle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: match placeholder_type {
|
||||||
|
PlaceholderType::Root(summary, source) => {
|
||||||
|
let mut label = String::new();
|
||||||
|
label.push_str("Diagnostics");
|
||||||
|
if let Some(source) = source {
|
||||||
|
write!(label, " ({})", source).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if summary.error_count > 0 || summary.warning_count > 0 {
|
||||||
|
label.push(':');
|
||||||
|
|
||||||
|
if summary.error_count > 0 {
|
||||||
|
write!(label, " {} errors", summary.error_count).unwrap();
|
||||||
|
if summary.warning_count > 0 {
|
||||||
|
label.push_str(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if summary.warning_count > 0 {
|
||||||
|
write!(label, " {} warnings", summary.warning_count)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label.into()
|
||||||
|
}
|
||||||
|
PlaceholderType::File(file_path) => file_path.into(),
|
||||||
|
PlaceholderType::Diagnostic(_, message) => message.into(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
run_commands_in_text: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Options {
|
||||||
|
include_warnings: bool,
|
||||||
|
path_matcher: Option<PathMatcher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const INCLUDE_WARNINGS_ARGUMENT: &str = "--include-warnings";
|
||||||
|
|
||||||
|
impl Options {
|
||||||
|
fn parse(arguments_line: Option<&str>) -> Self {
|
||||||
|
arguments_line
|
||||||
|
.map(|arguments_line| {
|
||||||
|
let args = arguments_line.split_whitespace().collect::<Vec<_>>();
|
||||||
|
let mut include_warnings = false;
|
||||||
|
let mut path_matcher = None;
|
||||||
|
for arg in args {
|
||||||
|
if arg == INCLUDE_WARNINGS_ARGUMENT {
|
||||||
|
include_warnings = true;
|
||||||
|
} else {
|
||||||
|
path_matcher = PathMatcher::new(&[arg.to_owned()]).log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
include_warnings,
|
||||||
|
path_matcher,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_candidates_for_args() -> [StringMatchCandidate; 1] {
|
||||||
|
[StringMatchCandidate::new(
|
||||||
|
0,
|
||||||
|
INCLUDE_WARNINGS_ARGUMENT.to_string(),
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_diagnostics(
|
||||||
|
project: Model<Project>,
|
||||||
|
options: Options,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Task<Result<Option<(String, Vec<(Range<usize>, PlaceholderType)>)>>> {
|
||||||
|
let error_source = if let Some(path_matcher) = &options.path_matcher {
|
||||||
|
debug_assert_eq!(path_matcher.sources().len(), 1);
|
||||||
|
Some(path_matcher.sources().first().cloned().unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let glob_is_exact_file_match = if let Some(path) = options
|
||||||
|
.path_matcher
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|pm| pm.sources().first())
|
||||||
|
{
|
||||||
|
PathBuf::try_from(path)
|
||||||
|
.ok()
|
||||||
|
.and_then(|path| {
|
||||||
|
project.read(cx).worktrees().find_map(|worktree| {
|
||||||
|
let worktree = worktree.read(cx);
|
||||||
|
let worktree_root_path = Path::new(worktree.root_name());
|
||||||
|
let relative_path = path.strip_prefix(worktree_root_path).ok()?;
|
||||||
|
worktree.absolutize(&relative_path).ok()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let project_handle = project.downgrade();
|
||||||
|
let diagnostic_summaries: Vec<_> = project
|
||||||
|
.read(cx)
|
||||||
|
.diagnostic_summaries(false, cx)
|
||||||
|
.flat_map(|(path, _, summary)| {
|
||||||
|
let worktree = project.read(cx).worktree_for_id(path.worktree_id, cx)?;
|
||||||
|
let mut path_buf = PathBuf::from(worktree.read(cx).root_name());
|
||||||
|
path_buf.push(&path.path);
|
||||||
|
Some((path, path_buf, summary))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
cx.spawn(|mut cx| async move {
|
||||||
|
let mut text = String::new();
|
||||||
|
if let Some(error_source) = error_source.as_ref() {
|
||||||
|
writeln!(text, "diagnostics: {}", error_source).unwrap();
|
||||||
|
} else {
|
||||||
|
writeln!(text, "diagnostics").unwrap();
|
||||||
|
}
|
||||||
|
let mut sections: Vec<(Range<usize>, PlaceholderType)> = Vec::new();
|
||||||
|
|
||||||
|
let mut project_summary = DiagnosticSummary::default();
|
||||||
|
for (project_path, path, summary) in diagnostic_summaries {
|
||||||
|
if let Some(path_matcher) = &options.path_matcher {
|
||||||
|
if !path_matcher.is_match(&path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project_summary.error_count += summary.error_count;
|
||||||
|
if options.include_warnings {
|
||||||
|
project_summary.warning_count += summary.warning_count;
|
||||||
|
} else if summary.error_count == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_end = text.len();
|
||||||
|
let file_path = path.to_string_lossy().to_string();
|
||||||
|
if !glob_is_exact_file_match {
|
||||||
|
writeln!(&mut text, "{file_path}").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(buffer) = project_handle
|
||||||
|
.update(&mut cx, |project, cx| project.open_buffer(project_path, cx))?
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
{
|
||||||
|
collect_buffer_diagnostics(
|
||||||
|
&mut text,
|
||||||
|
&mut sections,
|
||||||
|
cx.read_model(&buffer, |buffer, _| buffer.snapshot())?,
|
||||||
|
options.include_warnings,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !glob_is_exact_file_match {
|
||||||
|
sections.push((
|
||||||
|
last_end..text.len().saturating_sub(1),
|
||||||
|
PlaceholderType::File(file_path),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No diagnostics found
|
||||||
|
if sections.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
sections.push((
|
||||||
|
0..text.len(),
|
||||||
|
PlaceholderType::Root(project_summary, error_source),
|
||||||
|
));
|
||||||
|
Ok(Some((text, sections)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer_has_error_diagnostics(snapshot: &BufferSnapshot) -> bool {
|
||||||
|
for (_, group) in snapshot.diagnostic_groups(None) {
|
||||||
|
let entry = &group.entries[group.primary_ix];
|
||||||
|
if entry.diagnostic.severity == DiagnosticSeverity::ERROR {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_single_file_diagnostics(
|
||||||
|
output: &mut String,
|
||||||
|
path: Option<&Path>,
|
||||||
|
snapshot: &BufferSnapshot,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(path) = path {
|
||||||
|
if buffer_has_error_diagnostics(&snapshot) {
|
||||||
|
output.push_str("/diagnostics ");
|
||||||
|
output.push_str(&path.to_string_lossy());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_buffer_diagnostics(
|
||||||
|
text: &mut String,
|
||||||
|
sections: &mut Vec<(Range<usize>, PlaceholderType)>,
|
||||||
|
snapshot: BufferSnapshot,
|
||||||
|
include_warnings: bool,
|
||||||
|
) {
|
||||||
|
for (_, group) in snapshot.diagnostic_groups(None) {
|
||||||
|
let entry = &group.entries[group.primary_ix];
|
||||||
|
collect_diagnostic(text, sections, entry, &snapshot, include_warnings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_diagnostic(
|
||||||
|
text: &mut String,
|
||||||
|
sections: &mut Vec<(Range<usize>, PlaceholderType)>,
|
||||||
|
entry: &DiagnosticEntry<Anchor>,
|
||||||
|
snapshot: &BufferSnapshot,
|
||||||
|
include_warnings: bool,
|
||||||
|
) {
|
||||||
|
const EXCERPT_EXPANSION_SIZE: u32 = 2;
|
||||||
|
const MAX_MESSAGE_LENGTH: usize = 2000;
|
||||||
|
|
||||||
|
let ty = match entry.diagnostic.severity {
|
||||||
|
DiagnosticSeverity::WARNING => {
|
||||||
|
if !include_warnings {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DiagnosticType::Warning
|
||||||
|
}
|
||||||
|
DiagnosticSeverity::ERROR => DiagnosticType::Error,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let prev_len = text.len();
|
||||||
|
|
||||||
|
let range = entry.range.to_point(snapshot);
|
||||||
|
let diagnostic_row_number = range.start.row + 1;
|
||||||
|
|
||||||
|
let start_row = range.start.row.saturating_sub(EXCERPT_EXPANSION_SIZE);
|
||||||
|
let end_row = (range.end.row + EXCERPT_EXPANSION_SIZE).min(snapshot.max_point().row) + 1;
|
||||||
|
let excerpt_range =
|
||||||
|
Point::new(start_row, 0).to_offset(&snapshot)..Point::new(end_row, 0).to_offset(&snapshot);
|
||||||
|
|
||||||
|
text.push_str("```");
|
||||||
|
if let Some(language_name) = snapshot.language().map(|l| l.code_fence_block_name()) {
|
||||||
|
text.push_str(&language_name);
|
||||||
|
}
|
||||||
|
text.push('\n');
|
||||||
|
|
||||||
|
let mut buffer_text = String::new();
|
||||||
|
for chunk in snapshot.text_for_range(excerpt_range) {
|
||||||
|
buffer_text.push_str(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, line) in buffer_text.lines().enumerate() {
|
||||||
|
let line_number = start_row + i as u32 + 1;
|
||||||
|
writeln!(text, "{}", line).unwrap();
|
||||||
|
|
||||||
|
if line_number == diagnostic_row_number {
|
||||||
|
text.push_str("//");
|
||||||
|
let prev_len = text.len();
|
||||||
|
write!(text, " {}: ", ty.as_str()).unwrap();
|
||||||
|
let padding = text.len() - prev_len;
|
||||||
|
|
||||||
|
let message = util::truncate(&entry.diagnostic.message, MAX_MESSAGE_LENGTH)
|
||||||
|
.replace('\n', format!("\n//{:padding$}", "").as_str());
|
||||||
|
|
||||||
|
writeln!(text, "{message}").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(text, "```").unwrap();
|
||||||
|
sections.push((
|
||||||
|
prev_len..text.len().saturating_sub(1),
|
||||||
|
PlaceholderType::Diagnostic(ty, entry.diagnostic.message.clone()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum PlaceholderType {
|
||||||
|
Root(DiagnosticSummary, Option<String>),
|
||||||
|
File(String),
|
||||||
|
Diagnostic(DiagnosticType, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum DiagnosticType {
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagnosticType {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
DiagnosticType::Warning => "warning",
|
||||||
|
DiagnosticType::Error => "error",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
373
crates/assistant/src/slash_command/docs_command.rs
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use assistant_slash_command::{
|
||||||
|
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||||
|
};
|
||||||
|
use gpui::{AppContext, Model, Task, WeakView};
|
||||||
|
use indexed_docs::{
|
||||||
|
IndexedDocsRegistry, IndexedDocsStore, LocalProvider, PackageName, ProviderId, RustdocIndexer,
|
||||||
|
};
|
||||||
|
use language::LspAdapterDelegate;
|
||||||
|
use project::{Project, ProjectPath};
|
||||||
|
use ui::prelude::*;
|
||||||
|
use util::{maybe, ResultExt};
|
||||||
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
pub(crate) struct DocsSlashCommand;
|
||||||
|
|
||||||
|
impl DocsSlashCommand {
|
||||||
|
pub const NAME: &'static str = "docs";
|
||||||
|
|
||||||
|
fn path_to_cargo_toml(project: Model<Project>, cx: &mut AppContext) -> Option<Arc<Path>> {
|
||||||
|
let worktree = project.read(cx).worktrees().next()?;
|
||||||
|
let worktree = worktree.read(cx);
|
||||||
|
let entry = worktree.entry_for_path("Cargo.toml")?;
|
||||||
|
let path = ProjectPath {
|
||||||
|
worktree_id: worktree.id(),
|
||||||
|
path: entry.path.clone(),
|
||||||
|
};
|
||||||
|
Some(Arc::from(
|
||||||
|
project.read(cx).absolute_path(&path, cx)?.as_path(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that the rustdoc provider is registered.
|
||||||
|
///
|
||||||
|
/// Ideally we would do this sooner, but we need to wait until we're able to
|
||||||
|
/// access the workspace so we can read the project.
|
||||||
|
fn ensure_rustdoc_provider_is_registered(
|
||||||
|
&self,
|
||||||
|
workspace: Option<WeakView<Workspace>>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) {
|
||||||
|
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
|
||||||
|
if indexed_docs_registry
|
||||||
|
.get_provider_store(ProviderId::rustdoc())
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
let index_provider_deps = maybe!({
|
||||||
|
let workspace = workspace.ok_or_else(|| anyhow!("no workspace"))?;
|
||||||
|
let workspace = workspace
|
||||||
|
.upgrade()
|
||||||
|
.ok_or_else(|| anyhow!("workspace was dropped"))?;
|
||||||
|
let project = workspace.read(cx).project().clone();
|
||||||
|
let fs = project.read(cx).fs().clone();
|
||||||
|
let cargo_workspace_root = Self::path_to_cargo_toml(project, cx)
|
||||||
|
.and_then(|path| path.parent().map(|path| path.to_path_buf()))
|
||||||
|
.ok_or_else(|| anyhow!("no Cargo workspace root found"))?;
|
||||||
|
|
||||||
|
anyhow::Ok((fs, cargo_workspace_root))
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((fs, cargo_workspace_root)) = index_provider_deps.log_err() {
|
||||||
|
indexed_docs_registry.register_provider(Box::new(RustdocIndexer::new(Box::new(
|
||||||
|
LocalProvider::new(fs, cargo_workspace_root),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlashCommand for DocsSlashCommand {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
Self::NAME.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
"insert docs".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn menu_text(&self) -> String {
|
||||||
|
"Insert Documentation".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requires_argument(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_argument(
|
||||||
|
self: Arc<Self>,
|
||||||
|
query: String,
|
||||||
|
_cancel: Arc<AtomicBool>,
|
||||||
|
workspace: Option<WeakView<Workspace>>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
|
self.ensure_rustdoc_provider_is_registered(workspace, cx);
|
||||||
|
|
||||||
|
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
|
||||||
|
let args = DocsSlashCommandArgs::parse(&query);
|
||||||
|
let store = args
|
||||||
|
.provider()
|
||||||
|
.ok_or_else(|| anyhow!("no docs provider specified"))
|
||||||
|
.and_then(|provider| IndexedDocsStore::try_global(provider, cx));
|
||||||
|
cx.background_executor().spawn(async move {
|
||||||
|
fn build_completions(
|
||||||
|
provider: ProviderId,
|
||||||
|
items: Vec<String>,
|
||||||
|
) -> Vec<ArgumentCompletion> {
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| ArgumentCompletion {
|
||||||
|
label: item.clone(),
|
||||||
|
new_text: format!("{provider} {item}"),
|
||||||
|
run_command: true,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
match args {
|
||||||
|
DocsSlashCommandArgs::NoProvider => {
|
||||||
|
let providers = indexed_docs_registry.list_providers();
|
||||||
|
Ok(providers
|
||||||
|
.into_iter()
|
||||||
|
.map(|provider| ArgumentCompletion {
|
||||||
|
label: provider.to_string(),
|
||||||
|
new_text: provider.to_string(),
|
||||||
|
run_command: false,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider,
|
||||||
|
package,
|
||||||
|
index,
|
||||||
|
} => {
|
||||||
|
let store = store?;
|
||||||
|
|
||||||
|
if index {
|
||||||
|
// We don't need to hold onto this task, as the `IndexedDocsStore` will hold it
|
||||||
|
// until it completes.
|
||||||
|
let _ = store.clone().index(package.as_str().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = store.search(package).await;
|
||||||
|
Ok(build_completions(provider, items))
|
||||||
|
}
|
||||||
|
DocsSlashCommandArgs::SearchItemDocs {
|
||||||
|
provider,
|
||||||
|
item_path,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let store = store?;
|
||||||
|
let items = store.search(item_path).await;
|
||||||
|
Ok(build_completions(provider, items))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
self: Arc<Self>,
|
||||||
|
argument: Option<&str>,
|
||||||
|
_workspace: WeakView<Workspace>,
|
||||||
|
_delegate: Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
|
let Some(argument) = argument else {
|
||||||
|
return Task::ready(Err(anyhow!("missing argument")));
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = DocsSlashCommandArgs::parse(argument);
|
||||||
|
let text = cx.background_executor().spawn({
|
||||||
|
let store = args
|
||||||
|
.provider()
|
||||||
|
.ok_or_else(|| anyhow!("no docs provider specified"))
|
||||||
|
.and_then(|provider| IndexedDocsStore::try_global(provider, cx));
|
||||||
|
async move {
|
||||||
|
match args {
|
||||||
|
DocsSlashCommandArgs::NoProvider => bail!("no docs provider specified"),
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider, package, ..
|
||||||
|
} => {
|
||||||
|
let store = store?;
|
||||||
|
let item_docs = store.load(package.clone()).await?;
|
||||||
|
|
||||||
|
anyhow::Ok((provider, package, item_docs.to_string()))
|
||||||
|
}
|
||||||
|
DocsSlashCommandArgs::SearchItemDocs {
|
||||||
|
provider,
|
||||||
|
item_path,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let store = store?;
|
||||||
|
let item_docs = store.load(item_path.clone()).await?;
|
||||||
|
|
||||||
|
anyhow::Ok((provider, item_path, item_docs.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.foreground_executor().spawn(async move {
|
||||||
|
let (provider, path, text) = text.await?;
|
||||||
|
let range = 0..text.len();
|
||||||
|
Ok(SlashCommandOutput {
|
||||||
|
text,
|
||||||
|
sections: vec![SlashCommandOutputSection {
|
||||||
|
range,
|
||||||
|
icon: IconName::FileRust,
|
||||||
|
label: format!("docs ({provider}): {path}",).into(),
|
||||||
|
}],
|
||||||
|
run_commands_in_text: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_item_path_delimiter(char: char) -> bool {
|
||||||
|
!char.is_alphanumeric() && char != '-' && char != '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) enum DocsSlashCommandArgs {
|
||||||
|
NoProvider,
|
||||||
|
SearchPackageDocs {
|
||||||
|
provider: ProviderId,
|
||||||
|
package: String,
|
||||||
|
index: bool,
|
||||||
|
},
|
||||||
|
SearchItemDocs {
|
||||||
|
provider: ProviderId,
|
||||||
|
package: String,
|
||||||
|
item_path: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DocsSlashCommandArgs {
|
||||||
|
pub fn parse(argument: &str) -> Self {
|
||||||
|
let Some((provider, argument)) = argument.split_once(' ') else {
|
||||||
|
return Self::NoProvider;
|
||||||
|
};
|
||||||
|
|
||||||
|
let provider = ProviderId(provider.into());
|
||||||
|
|
||||||
|
if let Some((package, rest)) = argument.split_once(is_item_path_delimiter) {
|
||||||
|
if rest.trim().is_empty() {
|
||||||
|
Self::SearchPackageDocs {
|
||||||
|
provider,
|
||||||
|
package: package.to_owned(),
|
||||||
|
index: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::SearchItemDocs {
|
||||||
|
provider,
|
||||||
|
package: package.to_owned(),
|
||||||
|
item_path: argument.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::SearchPackageDocs {
|
||||||
|
provider,
|
||||||
|
package: argument.to_owned(),
|
||||||
|
index: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn provider(&self) -> Option<ProviderId> {
|
||||||
|
match self {
|
||||||
|
Self::NoProvider => None,
|
||||||
|
Self::SearchPackageDocs { provider, .. } | Self::SearchItemDocs { provider, .. } => {
|
||||||
|
Some(provider.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn package(&self) -> Option<PackageName> {
|
||||||
|
match self {
|
||||||
|
Self::NoProvider => None,
|
||||||
|
Self::SearchPackageDocs { package, .. } | Self::SearchItemDocs { package, .. } => {
|
||||||
|
Some(package.as_str().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_docs_slash_command_args() {
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse(""),
|
||||||
|
DocsSlashCommandArgs::NoProvider
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("rustdoc"),
|
||||||
|
DocsSlashCommandArgs::NoProvider
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("rustdoc "),
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider: ProviderId("rustdoc".into()),
|
||||||
|
package: "".into(),
|
||||||
|
index: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("gleam "),
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider: ProviderId("gleam".into()),
|
||||||
|
package: "".into(),
|
||||||
|
index: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("rustdoc gpui"),
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider: ProviderId("rustdoc".into()),
|
||||||
|
package: "gpui".into(),
|
||||||
|
index: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("gleam gleam_stdlib"),
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider: ProviderId("gleam".into()),
|
||||||
|
package: "gleam_stdlib".into(),
|
||||||
|
index: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Adding an item path delimiter indicates we can start indexing.
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("rustdoc gpui:"),
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider: ProviderId("rustdoc".into()),
|
||||||
|
package: "gpui".into(),
|
||||||
|
index: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("gleam gleam_stdlib/"),
|
||||||
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
|
provider: ProviderId("gleam".into()),
|
||||||
|
package: "gleam_stdlib".into(),
|
||||||
|
index: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("rustdoc gpui::foo::bar::Baz"),
|
||||||
|
DocsSlashCommandArgs::SearchItemDocs {
|
||||||
|
provider: ProviderId("rustdoc".into()),
|
||||||
|
package: "gpui".into(),
|
||||||
|
item_path: "gpui::foo::bar::Baz".into()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
DocsSlashCommandArgs::parse("gleam gleam_stdlib/gleam/int"),
|
||||||
|
DocsSlashCommandArgs::SearchItemDocs {
|
||||||
|
provider: ProviderId("gleam".into()),
|
||||||
|
package: "gleam_stdlib".into(),
|
||||||
|
item_path: "gleam_stdlib/gleam/int".into()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,27 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
|
use assistant_slash_command::{
|
||||||
|
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||||
|
};
|
||||||
use futures::AsyncReadExt;
|
use futures::AsyncReadExt;
|
||||||
use gpui::{AppContext, Task, WeakView};
|
use gpui::{AppContext, Task, WeakView};
|
||||||
use html_to_markdown::convert_html_to_markdown;
|
use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
|
||||||
use http::{AsyncBody, HttpClient, HttpClientWithUrl};
|
use http::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||||
use language::LspAdapterDelegate;
|
use language::LspAdapterDelegate;
|
||||||
use ui::{prelude::*, ButtonLike, ElevationIndex};
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
|
enum ContentType {
|
||||||
|
Html,
|
||||||
|
Plaintext,
|
||||||
|
Json,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct FetchSlashCommand;
|
pub(crate) struct FetchSlashCommand;
|
||||||
|
|
||||||
impl FetchSlashCommand {
|
impl FetchSlashCommand {
|
||||||
@@ -37,7 +48,53 @@ impl FetchSlashCommand {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_html_to_markdown(&body[..])
|
let Some(content_type) = response.headers().get("content-type") else {
|
||||||
|
bail!("missing Content-Type header");
|
||||||
|
};
|
||||||
|
let content_type = content_type
|
||||||
|
.to_str()
|
||||||
|
.context("invalid Content-Type header")?;
|
||||||
|
let content_type = match content_type {
|
||||||
|
"text/html" => ContentType::Html,
|
||||||
|
"text/plain" => ContentType::Plaintext,
|
||||||
|
"application/json" => ContentType::Json,
|
||||||
|
_ => ContentType::Html,
|
||||||
|
};
|
||||||
|
|
||||||
|
match content_type {
|
||||||
|
ContentType::Html => {
|
||||||
|
let mut handlers: Vec<TagHandler> = vec![
|
||||||
|
Rc::new(RefCell::new(markdown::WebpageChromeRemover)),
|
||||||
|
Rc::new(RefCell::new(markdown::ParagraphHandler)),
|
||||||
|
Rc::new(RefCell::new(markdown::HeadingHandler)),
|
||||||
|
Rc::new(RefCell::new(markdown::ListHandler)),
|
||||||
|
Rc::new(RefCell::new(markdown::TableHandler::new())),
|
||||||
|
Rc::new(RefCell::new(markdown::StyledTextHandler)),
|
||||||
|
];
|
||||||
|
if url.contains("wikipedia.org") {
|
||||||
|
use html_to_markdown::structure::wikipedia;
|
||||||
|
|
||||||
|
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaChromeRemover)));
|
||||||
|
handlers.push(Rc::new(RefCell::new(wikipedia::WikipediaInfoboxHandler)));
|
||||||
|
handlers.push(Rc::new(
|
||||||
|
RefCell::new(wikipedia::WikipediaCodeHandler::new()),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
handlers.push(Rc::new(RefCell::new(markdown::CodeHandler)));
|
||||||
|
}
|
||||||
|
|
||||||
|
convert_html_to_markdown(&body[..], &mut handlers)
|
||||||
|
}
|
||||||
|
ContentType::Plaintext => Ok(std::str::from_utf8(&body)?.to_owned()),
|
||||||
|
ContentType::Json => {
|
||||||
|
let json: serde_json::Value = serde_json::from_slice(&body)?;
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"```json\n{}\n```",
|
||||||
|
serde_json::to_string_pretty(&json)?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,12 +116,12 @@ impl SlashCommand for FetchSlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
&self,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_query: String,
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut AppContext,
|
_cx: &mut AppContext,
|
||||||
) -> Task<Result<Vec<String>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
Task::ready(Ok(Vec::new()))
|
Task::ready(Ok(Vec::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,37 +155,11 @@ impl SlashCommand for FetchSlashCommand {
|
|||||||
text,
|
text,
|
||||||
sections: vec![SlashCommandOutputSection {
|
sections: vec![SlashCommandOutputSection {
|
||||||
range,
|
range,
|
||||||
render_placeholder: Arc::new(move |id, unfold, _cx| {
|
icon: IconName::AtSign,
|
||||||
FetchPlaceholder {
|
label: format!("fetch {}", url).into(),
|
||||||
id,
|
|
||||||
unfold,
|
|
||||||
url: url.clone(),
|
|
||||||
}
|
|
||||||
.into_any_element()
|
|
||||||
}),
|
|
||||||
}],
|
}],
|
||||||
run_commands_in_text: false,
|
run_commands_in_text: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
struct FetchPlaceholder {
|
|
||||||
pub id: ElementId,
|
|
||||||
pub unfold: Arc<dyn Fn(&mut WindowContext)>,
|
|
||||||
pub url: SharedString,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for FetchPlaceholder {
|
|
||||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
|
||||||
let unfold = self.unfold;
|
|
||||||
|
|
||||||
ButtonLike::new(self.id)
|
|
||||||
.style(ButtonStyle::Filled)
|
|
||||||
.layer(ElevationIndex::ElevatedSurface)
|
|
||||||
.child(Icon::new(IconName::AtSign))
|
|
||||||
.child(Label::new(format!("fetch {url}", url = self.url)))
|
|
||||||
.on_click(move |_, cx| unfold(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
use super::{SlashCommand, SlashCommandOutput};
|
use super::{diagnostics_command::write_single_file_diagnostics, SlashCommand, SlashCommandOutput};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_slash_command::SlashCommandOutputSection;
|
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||||
use fuzzy::PathMatch;
|
use fuzzy::PathMatch;
|
||||||
use gpui::{AppContext, RenderOnce, SharedString, Task, View, WeakView};
|
use gpui::{AppContext, Model, Task, View, WeakView};
|
||||||
use language::{LineEnding, LspAdapterDelegate};
|
use language::{BufferSnapshot, LineEnding, LspAdapterDelegate};
|
||||||
use project::PathMatchCandidateSet;
|
use project::{PathMatchCandidateSet, Project};
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt::Write,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{atomic::AtomicBool, Arc},
|
||||||
};
|
};
|
||||||
use ui::{prelude::*, ButtonLike, ElevationIndex};
|
use ui::prelude::*;
|
||||||
|
use util::{paths::PathMatcher, ResultExt};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct FileSlashCommand;
|
pub(crate) struct FileSlashCommand;
|
||||||
@@ -58,7 +60,7 @@ impl FileSlashCommand {
|
|||||||
.root_entry()
|
.root_entry()
|
||||||
.map_or(false, |entry| entry.is_ignored),
|
.map_or(false, |entry| entry.is_ignored),
|
||||||
include_root_name: true,
|
include_root_name: true,
|
||||||
directories_only: false,
|
candidates: project::Candidates::Entries,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -98,12 +100,12 @@ impl SlashCommand for FileSlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
&self,
|
self: Arc<Self>,
|
||||||
query: String,
|
query: String,
|
||||||
cancellation_flag: Arc<AtomicBool>,
|
cancellation_flag: Arc<AtomicBool>,
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
) -> Task<Result<Vec<String>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
|
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
|
||||||
return Task::ready(Err(anyhow!("workspace was dropped")));
|
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||||
};
|
};
|
||||||
@@ -114,11 +116,17 @@ impl SlashCommand for FileSlashCommand {
|
|||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|path_match| {
|
.map(|path_match| {
|
||||||
format!(
|
let text = format!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
path_match.path_prefix,
|
path_match.path_prefix,
|
||||||
path_match.path.to_string_lossy()
|
path_match.path.to_string_lossy()
|
||||||
)
|
);
|
||||||
|
|
||||||
|
ArgumentCompletion {
|
||||||
|
label: text.clone(),
|
||||||
|
new_text: text,
|
||||||
|
run_command: true,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
})
|
})
|
||||||
@@ -139,88 +147,225 @@ impl SlashCommand for FileSlashCommand {
|
|||||||
return Task::ready(Err(anyhow!("missing path")));
|
return Task::ready(Err(anyhow!("missing path")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = PathBuf::from(argument);
|
let task = collect_files(workspace.read(cx).project().clone(), argument, cx);
|
||||||
let abs_path = workspace
|
|
||||||
.read(cx)
|
|
||||||
.visible_worktrees(cx)
|
|
||||||
.find_map(|worktree| {
|
|
||||||
let worktree = worktree.read(cx);
|
|
||||||
let worktree_root_path = Path::new(worktree.root_name());
|
|
||||||
let relative_path = path.strip_prefix(worktree_root_path).ok()?;
|
|
||||||
worktree.absolutize(&relative_path).ok()
|
|
||||||
});
|
|
||||||
|
|
||||||
let Some(abs_path) = abs_path else {
|
|
||||||
return Task::ready(Err(anyhow!("missing path")));
|
|
||||||
};
|
|
||||||
|
|
||||||
let fs = workspace.read(cx).app_state().fs.clone();
|
|
||||||
let argument = argument.to_string();
|
|
||||||
let text = cx.background_executor().spawn(async move {
|
|
||||||
let mut content = fs.load(&abs_path).await?;
|
|
||||||
LineEnding::normalize(&mut content);
|
|
||||||
let mut output = String::with_capacity(argument.len() + content.len() + 9);
|
|
||||||
output.push_str("```");
|
|
||||||
output.push_str(&argument);
|
|
||||||
output.push('\n');
|
|
||||||
output.push_str(&content);
|
|
||||||
if !output.ends_with('\n') {
|
|
||||||
output.push('\n');
|
|
||||||
}
|
|
||||||
output.push_str("```");
|
|
||||||
anyhow::Ok(output)
|
|
||||||
});
|
|
||||||
cx.foreground_executor().spawn(async move {
|
cx.foreground_executor().spawn(async move {
|
||||||
let text = text.await?;
|
let (text, ranges) = task.await?;
|
||||||
let range = 0..text.len();
|
|
||||||
Ok(SlashCommandOutput {
|
Ok(SlashCommandOutput {
|
||||||
text,
|
text,
|
||||||
sections: vec![SlashCommandOutputSection {
|
sections: ranges
|
||||||
range,
|
.into_iter()
|
||||||
render_placeholder: Arc::new(move |id, unfold, _cx| {
|
.map(|(range, path, entry_type)| {
|
||||||
FilePlaceholder {
|
build_entry_output_section(
|
||||||
path: Some(path.clone()),
|
range,
|
||||||
line_range: None,
|
Some(&path),
|
||||||
id,
|
entry_type == EntryType::Directory,
|
||||||
unfold,
|
None,
|
||||||
}
|
)
|
||||||
.into_any_element()
|
})
|
||||||
}),
|
.collect(),
|
||||||
}],
|
run_commands_in_text: true,
|
||||||
run_commands_in_text: false,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub struct FilePlaceholder {
|
enum EntryType {
|
||||||
pub path: Option<PathBuf>,
|
File,
|
||||||
pub line_range: Option<Range<u32>>,
|
Directory,
|
||||||
pub id: ElementId,
|
|
||||||
pub unfold: Arc<dyn Fn(&mut WindowContext)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderOnce for FilePlaceholder {
|
fn collect_files(
|
||||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
project: Model<Project>,
|
||||||
let unfold = self.unfold;
|
glob_input: &str,
|
||||||
let title = if let Some(path) = self.path.as_ref() {
|
cx: &mut AppContext,
|
||||||
SharedString::from(path.to_string_lossy().to_string())
|
) -> Task<Result<(String, Vec<(Range<usize>, PathBuf, EntryType)>)>> {
|
||||||
} else {
|
let Ok(matcher) = PathMatcher::new(&[glob_input.to_owned()]) else {
|
||||||
SharedString::from("untitled")
|
return Task::ready(Err(anyhow!("invalid path")));
|
||||||
};
|
};
|
||||||
|
|
||||||
ButtonLike::new(self.id)
|
let project_handle = project.downgrade();
|
||||||
.style(ButtonStyle::Filled)
|
let snapshots = project
|
||||||
.layer(ElevationIndex::ElevatedSurface)
|
.read(cx)
|
||||||
.child(Icon::new(IconName::File))
|
.worktrees()
|
||||||
.child(Label::new(title))
|
.map(|worktree| worktree.read(cx).snapshot())
|
||||||
.when_some(self.line_range, |button, line_range| {
|
.collect::<Vec<_>>();
|
||||||
button.child(Label::new(":")).child(Label::new(format!(
|
cx.spawn(|mut cx| async move {
|
||||||
"{}-{}",
|
let mut text = String::new();
|
||||||
line_range.start, line_range.end
|
let mut ranges = Vec::new();
|
||||||
)))
|
for snapshot in snapshots {
|
||||||
})
|
let worktree_id = snapshot.id();
|
||||||
.on_click(move |_, cx| unfold(cx))
|
let mut directory_stack: Vec<(Arc<Path>, String, usize)> = Vec::new();
|
||||||
|
let mut folded_directory_names_stack = Vec::new();
|
||||||
|
let mut is_top_level_directory = true;
|
||||||
|
for entry in snapshot.entries(false, 0) {
|
||||||
|
let mut path_including_worktree_name = PathBuf::new();
|
||||||
|
path_including_worktree_name.push(snapshot.root_name());
|
||||||
|
path_including_worktree_name.push(&entry.path);
|
||||||
|
if !matcher.is_match(&path_including_worktree_name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some((dir, _, _)) = directory_stack.last() {
|
||||||
|
if entry.path.starts_with(dir) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let (_, entry_name, start) = directory_stack.pop().unwrap();
|
||||||
|
ranges.push((
|
||||||
|
start..text.len().saturating_sub(1),
|
||||||
|
PathBuf::from(entry_name),
|
||||||
|
EntryType::Directory,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = entry
|
||||||
|
.path
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_str()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if entry.is_dir() {
|
||||||
|
// Auto-fold directories that contain no files
|
||||||
|
let mut child_entries = snapshot.child_entries(&entry.path);
|
||||||
|
if let Some(child) = child_entries.next() {
|
||||||
|
if child_entries.next().is_none() && child.kind.is_dir() {
|
||||||
|
if is_top_level_directory {
|
||||||
|
is_top_level_directory = false;
|
||||||
|
folded_directory_names_stack.push(
|
||||||
|
path_including_worktree_name.to_string_lossy().to_string(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
folded_directory_names_stack.push(filename.to_string());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Skip empty directories
|
||||||
|
folded_directory_names_stack.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let prefix_paths = folded_directory_names_stack.drain(..).as_slice().join("/");
|
||||||
|
let entry_start = text.len();
|
||||||
|
if prefix_paths.is_empty() {
|
||||||
|
if is_top_level_directory {
|
||||||
|
text.push_str(&path_including_worktree_name.to_string_lossy());
|
||||||
|
is_top_level_directory = false;
|
||||||
|
} else {
|
||||||
|
text.push_str(&filename);
|
||||||
|
}
|
||||||
|
directory_stack.push((entry.path.clone(), filename, entry_start));
|
||||||
|
} else {
|
||||||
|
let entry_name = format!("{}/{}", prefix_paths, &filename);
|
||||||
|
text.push_str(&entry_name);
|
||||||
|
directory_stack.push((entry.path.clone(), entry_name, entry_start));
|
||||||
|
}
|
||||||
|
text.push('\n');
|
||||||
|
} else if entry.is_file() {
|
||||||
|
let Some(open_buffer_task) = project_handle
|
||||||
|
.update(&mut cx, |project, cx| {
|
||||||
|
project.open_buffer((worktree_id, &entry.path), cx)
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(buffer) = open_buffer_task.await.log_err() {
|
||||||
|
let snapshot = cx.read_model(&buffer, |buffer, _| buffer.snapshot())?;
|
||||||
|
let prev_len = text.len();
|
||||||
|
collect_file_content(&mut text, &snapshot, filename.clone());
|
||||||
|
text.push('\n');
|
||||||
|
if !write_single_file_diagnostics(
|
||||||
|
&mut text,
|
||||||
|
Some(&path_including_worktree_name),
|
||||||
|
&snapshot,
|
||||||
|
) {
|
||||||
|
text.pop();
|
||||||
|
}
|
||||||
|
ranges.push((
|
||||||
|
prev_len..text.len(),
|
||||||
|
PathBuf::from(filename),
|
||||||
|
EntryType::File,
|
||||||
|
));
|
||||||
|
text.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some((dir, _, start)) = directory_stack.pop() {
|
||||||
|
let mut root_path = PathBuf::new();
|
||||||
|
root_path.push(snapshot.root_name());
|
||||||
|
root_path.push(&dir);
|
||||||
|
ranges.push((start..text.len(), root_path, EntryType::Directory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((text, ranges))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_file_content(buffer: &mut String, snapshot: &BufferSnapshot, filename: String) {
|
||||||
|
let mut content = snapshot.text();
|
||||||
|
LineEnding::normalize(&mut content);
|
||||||
|
buffer.reserve(filename.len() + content.len() + 9);
|
||||||
|
buffer.push_str(&codeblock_fence_for_path(
|
||||||
|
Some(&PathBuf::from(filename)),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
buffer.push_str(&content);
|
||||||
|
if !buffer.ends_with('\n') {
|
||||||
|
buffer.push('\n');
|
||||||
|
}
|
||||||
|
buffer.push_str("```");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn codeblock_fence_for_path(path: Option<&Path>, row_range: Option<Range<u32>>) -> String {
|
||||||
|
let mut text = String::new();
|
||||||
|
write!(text, "```").unwrap();
|
||||||
|
|
||||||
|
if let Some(path) = path {
|
||||||
|
if let Some(extension) = path.extension().and_then(|ext| ext.to_str()) {
|
||||||
|
write!(text, "{} ", extension).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(text, "{}", path.display()).unwrap();
|
||||||
|
} else {
|
||||||
|
write!(text, "untitled").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(row_range) = row_range {
|
||||||
|
write!(text, ":{}-{}", row_range.start + 1, row_range.end + 1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
text.push('\n');
|
||||||
|
text
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_entry_output_section(
|
||||||
|
range: Range<usize>,
|
||||||
|
path: Option<&Path>,
|
||||||
|
is_directory: bool,
|
||||||
|
line_range: Option<Range<u32>>,
|
||||||
|
) -> SlashCommandOutputSection<usize> {
|
||||||
|
let mut label = if let Some(path) = path {
|
||||||
|
path.to_string_lossy().to_string()
|
||||||
|
} else {
|
||||||
|
"untitled".to_string()
|
||||||
|
};
|
||||||
|
if let Some(line_range) = line_range {
|
||||||
|
write!(label, ":{}-{}", line_range.start, line_range.end).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let icon = if is_directory {
|
||||||
|
IconName::Folder
|
||||||
|
} else {
|
||||||
|
IconName::File
|
||||||
|
};
|
||||||
|
|
||||||
|
SlashCommandOutputSection {
|
||||||
|
range,
|
||||||
|
icon,
|
||||||
|
label: label.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
64
crates/assistant/src/slash_command/now_command.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use assistant_slash_command::{
|
||||||
|
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||||
|
};
|
||||||
|
use chrono::Local;
|
||||||
|
use gpui::{AppContext, Task, WeakView};
|
||||||
|
use language::LspAdapterDelegate;
|
||||||
|
use ui::prelude::*;
|
||||||
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
pub(crate) struct NowSlashCommand;
|
||||||
|
|
||||||
|
impl SlashCommand for NowSlashCommand {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"now".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
"insert the current date and time".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn menu_text(&self) -> String {
|
||||||
|
"Insert current date and time".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requires_argument(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_argument(
|
||||||
|
self: Arc<Self>,
|
||||||
|
_query: String,
|
||||||
|
_cancel: Arc<AtomicBool>,
|
||||||
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
|
_cx: &mut AppContext,
|
||||||
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
|
Task::ready(Ok(Vec::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
self: Arc<Self>,
|
||||||
|
_argument: Option<&str>,
|
||||||
|
_workspace: WeakView<Workspace>,
|
||||||
|
_delegate: Arc<dyn LspAdapterDelegate>,
|
||||||
|
_cx: &mut WindowContext,
|
||||||
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
|
let now = Local::now();
|
||||||
|
let text = format!("Today is {now}.", now = now.to_rfc2822());
|
||||||
|
let range = 0..text.len();
|
||||||
|
|
||||||
|
Task::ready(Ok(SlashCommandOutput {
|
||||||
|
text,
|
||||||
|
sections: vec![SlashCommandOutputSection {
|
||||||
|
range,
|
||||||
|
icon: IconName::CountdownTimer,
|
||||||
|
label: now.to_rfc2822().into(),
|
||||||
|
}],
|
||||||
|
run_commands_in_text: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{SlashCommand, SlashCommandOutput};
|
use super::{SlashCommand, SlashCommandOutput};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use assistant_slash_command::SlashCommandOutputSection;
|
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{AppContext, Model, Task, WeakView};
|
use gpui::{AppContext, Model, Task, WeakView};
|
||||||
use language::LspAdapterDelegate;
|
use language::LspAdapterDelegate;
|
||||||
@@ -10,7 +10,7 @@ use std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{atomic::AtomicBool, Arc},
|
||||||
};
|
};
|
||||||
use ui::{prelude::*, ButtonLike, ElevationIndex};
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct ProjectSlashCommand;
|
pub(crate) struct ProjectSlashCommand;
|
||||||
@@ -102,12 +102,12 @@ impl SlashCommand for ProjectSlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
&self,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_query: String,
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut AppContext,
|
_cx: &mut AppContext,
|
||||||
) -> Task<Result<Vec<String>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
Task::ready(Err(anyhow!("this command does not require argument")))
|
Task::ready(Err(anyhow!("this command does not require argument")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,15 +138,8 @@ impl SlashCommand for ProjectSlashCommand {
|
|||||||
text,
|
text,
|
||||||
sections: vec![SlashCommandOutputSection {
|
sections: vec![SlashCommandOutputSection {
|
||||||
range,
|
range,
|
||||||
render_placeholder: Arc::new(move |id, unfold, _cx| {
|
icon: IconName::FileTree,
|
||||||
ButtonLike::new(id)
|
label: "Project".into(),
|
||||||
.style(ButtonStyle::Filled)
|
|
||||||
.layer(ElevationIndex::ElevatedSurface)
|
|
||||||
.child(Icon::new(IconName::FileTree))
|
|
||||||
.child(Label::new("Project"))
|
|
||||||
.on_click(move |_, cx| unfold(cx))
|
|
||||||
.into_any_element()
|
|
||||||
}),
|
|
||||||
}],
|
}],
|
||||||
run_commands_in_text: false,
|
run_commands_in_text: false,
|
||||||
})
|
})
|
||||||
|
|||||||