Compare commits

..

11 Commits

Author SHA1 Message Date
Peter Tripp
eadb45467d zed 0.141.3 2024-06-28 09:23:38 -04:00
Nathan Sobo
bfa2682316 Fix a stupid bug that was dropping system prompts for Claude (#13626)
Release Notes:

- Fixed a bug that was causing system prompts to be dropped for
Anthropic models.

@JosephTLyons @notpeter We probably need to hot-fix this as I'm pretty
sure this affects the regular anthropic provider in addition to just the
feature-flagged cloud stuff. Wouldn't mind confirming that first so we
can communicate around it. 😬
2024-06-28 09:23:02 -04:00
gcp-cherry-pick-bot[bot]
8a8e29573f fix panics (cherry-pick #13554) (#13557)
Cherry-picked fix panics (#13554)

Release Notes:

- Fixed a panic when editing HTML near the end of a file
- Fixed a panic when editing settings.json from inside the .zed
directory

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-06-26 16:27:53 -06:00
Peter Tripp
3e609e4793 v0.141.x stable 2024-06-26 12:19:49 -04:00
Joseph T. Lyons
fce2e00745 Add metrics_id to editor_events (#13525)
Release Notes:

- N/A
2024-06-26 11:37:56 -04:00
Zed Bot
20edf6905c Bump to 0.141.2 for @osiewicz 2024-06-23 10:08:17 -07:00
Piotr Osiewicz
d3c2699b1e editor: Select first match in "Find all references" editor (#13424)
Previously we've placed cursor on the first line of the first excerpt in
the multibuffer, but alas,
https://x.com/fasterthanlime/status/1804883499809165473 happened (j/k,
this feedback is totally valid) and now we're gonna place it at the end
of the first reference. As a bonus, with the old configuration `editor:
select next` tripped over itself. Now it's possible (& feasible) to do a
"select next" in "find all references"; consecutive referenced ranges
will be selected.

Fixes #13419



Release Notes:

- Fixed a bug where "Find all references" editor had cursor placed on
the first line of the first excerpt in the multibuffer instead of having
it on the first reference.
2024-06-23 19:07:08 +02:00
gcp-cherry-pick-bot[bot]
fbeaa70e97 Fix: Picker select_last not scrolling to item index (cherry-pick #13393) (#13410)
Cherry-picked Fix: Picker select_last not scrolling to item index
(#13393)

Release Notes:

- Fix: Command palette not scrolling down to the last element

Co-authored-by: Vitor Ramos <ramos.vitor89@gmail.com>
2024-06-23 09:55:04 +02:00
Zed Bot
11b45702c9 Bump to 0.141.1 for @osiewicz 2024-06-21 02:05:45 -07:00
Piotr Osiewicz
c844ba3cdc YAML: set auto_indent_using_last_non_empty_line to false (fix wonky formatting) (#13351)
This makes us treat yaml like other indentation-sensitive languages
(e.g. Python) and not reformat it on pasting and what not.

Fixes #12236
Fixes #13338

Release Notes:

- Fixed spurious appliance of auto-formatting to YAML blocks.
2024-06-21 11:04:14 +02:00
Joseph T Lyons
3562cad225 v0.141.x preview 2024-06-19 12:09:07 -04:00
149 changed files with 2015 additions and 4962 deletions

View File

@@ -307,7 +307,7 @@ jobs:
exit 1
fi
- name: Create Linux .tar.gz bundle
- 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
@@ -315,7 +315,7 @@ jobs:
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 }}-x86_64-unknown-linux-gnu.tar.gz
path: target/release/zed-*.tar.gz
path: zed-*.tar.gz
- name: Upload app bundle to release
uses: softprops/action-gh-release@v1
@@ -348,12 +348,12 @@ jobs:
- 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
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 libzstd-dev libvulkan1 libgit2-dev
echo "/usr/lib/llvm-10/bin" >> $GITHUB_PATH
- uses: rui314/setup-mold@v1
with:
mold-version: 2.32.0
mold_version: 2.32.0
- name: rustup
run: |

445
Cargo.lock generated
View File

@@ -34,6 +34,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aes"
version = "0.8.4"
@@ -133,12 +139,6 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "aligned-vec"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
[[package]]
name = "allocator-api2"
version = "0.2.16"
@@ -284,17 +284,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.59",
]
[[package]]
name = "arrayref"
version = "0.3.7"
@@ -373,7 +362,6 @@ dependencies = [
"anthropic",
"anyhow",
"assistant_slash_command",
"async-watch",
"cargo_toml",
"chrono",
"client",
@@ -417,7 +405,6 @@ dependencies = [
"strsim 0.11.1",
"strum",
"telemetry_events",
"terminal_view",
"theme",
"tiktoken-rs",
"toml 0.8.10",
@@ -885,15 +872,6 @@ dependencies = [
"tungstenite 0.16.0",
]
[[package]]
name = "async-watch"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a078faf4e27c0c6cc0efb20e5da59dcccc04968ebf2801d8e0b2195124cdcdb2"
dependencies = [
"event-listener 2.5.3",
]
[[package]]
name = "async_zip"
version = "0.0.17"
@@ -997,29 +975,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "av1-grain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf"
dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"num-rational",
"v_frame",
]
[[package]]
name = "avif-serialize"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2"
dependencies = [
"arrayvec",
]
[[package]]
name = "aws-config"
version = "1.1.5"
@@ -1466,7 +1421,7 @@ dependencies = [
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"miniz_oxide 0.7.1",
"object",
"rustc-demangle",
]
@@ -1589,12 +1544,6 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -1610,12 +1559,6 @@ dependencies = [
"serde",
]
[[package]]
name = "bitstream-io"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "415f8399438eb5e4b2f73ed3152a3448b98149dda642a957ee704e1daa5cf1d8"
[[package]]
name = "bitvec"
version = "1.0.1"
@@ -1631,7 +1574,7 @@ dependencies = [
[[package]]
name = "blade-graphics"
version = "0.4.0"
source = "git+https://github.com/kvark/blade?rev=21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7#21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7"
source = "git+https://github.com/zed-industries/blade?rev=33fd51359d113c03b785e28f4a6cf75bacb0b26d#33fd51359d113c03b785e28f4a6cf75bacb0b26d"
dependencies = [
"ash",
"ash-window",
@@ -1661,7 +1604,7 @@ dependencies = [
[[package]]
name = "blade-macros"
version = "0.2.1"
source = "git+https://github.com/kvark/blade?rev=21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7#21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7"
source = "git+https://github.com/zed-industries/blade?rev=33fd51359d113c03b785e28f4a6cf75bacb0b26d#33fd51359d113c03b785e28f4a6cf75bacb0b26d"
dependencies = [
"proc-macro2",
"quote",
@@ -1671,7 +1614,7 @@ dependencies = [
[[package]]
name = "blade-util"
version = "0.1.0"
source = "git+https://github.com/kvark/blade?rev=21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7#21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7"
source = "git+https://github.com/zed-industries/blade?rev=33fd51359d113c03b785e28f4a6cf75bacb0b26d#33fd51359d113c03b785e28f4a6cf75bacb0b26d"
dependencies = [
"blade-graphics",
"bytemuck",
@@ -1797,12 +1740,6 @@ dependencies = [
"serde",
]
[[package]]
name = "built"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6a6c0b39c38fd754ac338b00a88066436389c0f029da5d37d1e01091d9b7c17"
[[package]]
name = "bumpalo"
version = "3.14.0"
@@ -1857,12 +1794,6 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "0.4.12"
@@ -2091,16 +2022,6 @@ dependencies = [
"nom",
]
[[package]]
name = "cfg-expr"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -2291,7 +2212,6 @@ dependencies = [
"fork",
"ipc-channel",
"once_cell",
"parking_lot",
"paths",
"plist",
"release_channel",
@@ -2359,7 +2279,6 @@ dependencies = [
"futures 0.3.28",
"gpui",
"http 0.1.0",
"isahc",
"lazy_static",
"log",
"once_cell",
@@ -3306,6 +3225,16 @@ dependencies = [
"util",
]
[[package]]
name = "deflate"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [
"adler32",
"byteorder",
]
[[package]]
name = "deflate64"
version = "0.1.8"
@@ -3888,22 +3817,6 @@ dependencies = [
"libc",
]
[[package]]
name = "exr"
version = "1.72.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
dependencies = [
"bit_field",
"flume",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "extension"
version = "0.1.0"
@@ -4186,7 +4099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
dependencies = [
"crc32fast",
"miniz_oxide",
"miniz_oxide 0.7.1",
]
[[package]]
@@ -4660,9 +4573,9 @@ dependencies = [
[[package]]
name = "gif"
version = "0.13.1"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
dependencies = [
"color_quant",
"weezl",
@@ -5091,12 +5004,6 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "heed"
version = "0.20.1"
@@ -5429,35 +5336,21 @@ dependencies = [
[[package]]
name = "image"
version = "0.25.1"
version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11"
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"exr",
"gif",
"image-webp",
"jpeg-decoder",
"num-iter",
"num-rational 0.3.2",
"num-traits",
"png",
"qoi",
"ravif",
"rayon",
"rgb",
"png 0.16.8",
"scoped_threadpool",
"tiff",
"zune-core",
"zune-jpeg",
]
[[package]]
name = "image-webp"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d"
dependencies = [
"byteorder-lite",
"thiserror",
]
[[package]]
@@ -5479,12 +5372,6 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "imgref"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -5598,17 +5485,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "interpolate_name"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.59",
]
[[package]]
name = "io-extras"
version = "0.18.1"
@@ -5810,9 +5686,12 @@ dependencies = [
[[package]]
name = "jpeg-decoder"
version = "0.3.1"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
dependencies = [
"rayon",
]
[[package]]
name = "js-sys"
@@ -6061,29 +5940,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "lebe"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libfuzzer-sys"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
dependencies = [
"arbitrary",
"cc",
"once_cell",
]
[[package]]
name = "libgit2-sys"
version = "0.17.0+1.8.1"
@@ -6276,15 +6138,6 @@ dependencies = [
"value-bag",
]
[[package]]
name = "loop9"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
dependencies = [
"imgref",
]
[[package]]
name = "lsp"
version = "0.1.0"
@@ -6460,16 +6313,6 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4"
[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
"rayon",
]
[[package]]
name = "md-5"
version = "0.10.5"
@@ -6577,6 +6420,25 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
@@ -6806,12 +6668,6 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "notifications"
version = "0.1.0"
@@ -6877,7 +6733,7 @@ dependencies = [
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-rational 0.4.1",
"num-traits",
]
@@ -6953,17 +6809,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.59",
]
[[package]]
name = "num-format"
version = "0.4.4"
@@ -6995,6 +6840,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
@@ -7103,7 +6959,7 @@ dependencies = [
"jni 0.20.0",
"ndk",
"ndk-context",
"num-derive 0.3.3",
"num-derive",
"num-traits",
"oboe-sys",
]
@@ -7236,9 +7092,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "300.3.0+3.3.0"
version = "300.2.3+3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eba8804a1c5765b18c4b3f907e6897ebabeedebc9830e1a0046c4a4cf44663e1"
checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843"
dependencies = [
"cc",
]
@@ -7502,7 +7358,7 @@ dependencies = [
[[package]]
name = "pathfinder_simd"
version = "0.5.3"
source = "git+https://github.com/servo/pathfinder.git?rev=4968e819c0d9b015437ffc694511e175801a17c7#4968e819c0d9b015437ffc694511e175801a17c7"
source = "git+https://github.com/servo/pathfinder.git?rev=30419d07660dc11a21e42ef4a7fa329600cff152#30419d07660dc11a21e42ef4a7fa329600cff152"
dependencies = [
"rustc_version",
]
@@ -7791,6 +7647,18 @@ dependencies = [
"plotters-backend",
]
[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"deflate",
"miniz_oxide 0.3.7",
]
[[package]]
name = "png"
version = "0.17.13"
@@ -7801,7 +7669,7 @@ dependencies = [
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
"miniz_oxide 0.7.1",
]
[[package]]
@@ -8225,21 +8093,6 @@ dependencies = [
"unicase",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.30.0"
@@ -8363,56 +8216,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "977b1e897f9d764566891689e642653e5ed90c6895106acd005eb4c1d0203991"
[[package]]
name = "rav1e"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
dependencies = [
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"av1-grain",
"bitstream-io",
"built",
"cfg-if",
"interpolate_name",
"itertools 0.12.1",
"libc",
"libfuzzer-sys",
"log",
"maybe-rayon",
"new_debug_unreachable",
"noop_proc_macro",
"num-derive 0.4.2",
"num-traits",
"once_cell",
"paste",
"profiling",
"rand 0.8.5",
"rand_chacha 0.3.1",
"simd_helpers",
"system-deps",
"thiserror",
"v_frame",
"wasm-bindgen",
]
[[package]]
name = "ravif"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rayon",
"rgb",
]
[[package]]
name = "raw-window-handle"
version = "0.5.2"
@@ -9212,6 +9015,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -9738,15 +9547,6 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simd_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
dependencies = [
"quote",
]
[[package]]
name = "simdutf8"
version = "0.1.4"
@@ -10555,19 +10355,6 @@ dependencies = [
"windows 0.52.0",
]
[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr",
"heck 0.5.0",
"pkg-config",
"toml 0.8.10",
"version-compare",
]
[[package]]
name = "system-interface"
version = "0.27.1"
@@ -10907,12 +10694,12 @@ dependencies = [
[[package]]
name = "tiff"
version = "0.9.1"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
dependencies = [
"flate2",
"jpeg-decoder",
"miniz_oxide 0.4.4",
"weezl",
]
@@ -10992,7 +10779,7 @@ dependencies = [
"bytemuck",
"cfg-if",
"log",
"png",
"png 0.17.13",
"tiny-skia-path",
]
@@ -11872,17 +11659,6 @@ dependencies = [
"sha1_smol",
]
[[package]]
name = "v_frame"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b"
dependencies = [
"aligned-vec",
"num-traits",
"wasm-bindgen",
]
[[package]]
name = "valuable"
version = "0.1.0"
@@ -11945,12 +11721,6 @@ dependencies = [
"workspace",
]
[[package]]
name = "version-compare"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]]
name = "version_check"
version = "0.9.4"
@@ -13301,7 +13071,6 @@ dependencies = [
"language",
"log",
"parking_lot",
"paths",
"postage",
"pretty_assertions",
"rand 0.8.5",
@@ -13549,7 +13318,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.142.0"
version = "0.141.3"
dependencies = [
"activity_indicator",
"anyhow",
@@ -13817,7 +13586,7 @@ dependencies = [
[[package]]
name = "zed_snippets"
version = "0.0.5"
version = "0.0.3"
dependencies = [
"serde_json",
"zed_extension_api 0.0.6",
@@ -13968,30 +13737,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]
[[package]]
name = "zune-jpeg"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448"
dependencies = [
"zune-core",
]
[[package]]
name = "zvariant"
version = "4.0.2"

View File

@@ -281,9 +281,9 @@ async-tar = "0.4.2"
async-trait = "0.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
bitflags = "2.4.2"
blade-graphics = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
blade-macros = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
blade-util = { git = "https://github.com/kvark/blade", rev = "21a56f780e21e4cb42c70a1dcf4b59842d1ad7f7" }
blade-graphics = { git = "https://github.com/zed-industries/blade", rev = "33fd51359d113c03b785e28f4a6cf75bacb0b26d" }
blade-macros = { git = "https://github.com/zed-industries/blade", rev = "33fd51359d113c03b785e28f4a6cf75bacb0b26d" }
blade-util = { git = "https://github.com/zed-industries/blade", rev = "33fd51359d113c03b785e28f4a6cf75bacb0b26d" }
cap-std = "3.0"
cargo_toml = "0.20"
chrono = { version = "0.4", features = ["serde"] }
@@ -308,11 +308,14 @@ heed = { version = "0.20.1", features = ["read-txn-no-tls"] }
hex = "0.4.3"
html5ever = "0.27.0"
ignore = "0.4.22"
image = "0.25.1"
image = "0.23"
indexmap = { version = "1.6.2", features = ["serde"] }
indoc = "1"
# We explicitly disable http2 support in isahc.
isahc = { version = "1.7.2", default-features = false, features = [ "text-decoding" ] }
isahc = { version = "1.7.2", default-features = false, features = [
"static-curl",
"text-decoding",
] }
itertools = "0.11.0"
lazy_static = "1.4.0"
libc = "0.2"
@@ -459,12 +462,11 @@ features = [
[patch.crates-io]
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.
pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "4968e819c0d9b015437ffc694511e175801a17c7" }
pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "30419d07660dc11a21e42ef4a7fa329600cff152" }
[profile.dev]
split-debuginfo = "unpacked"
debug = "limited"
codegen-units = 16
[profile.dev.package]
taffy = { opt-level = 3 }
@@ -483,11 +485,6 @@ codegen-units = 1
[profile.release.package]
zed = { codegen-units = 16 }
[profile.release-fast]
inherits = "release"
lto = false
codegen-units = 16
[workspace.lints.clippy]
dbg_macro = "deny"
todo = "deny"

View File

@@ -25,8 +25,7 @@
],
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::AddSelectionAbove",
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine",
"ctrl-shift-m": "markdown::OpenPreviewToTheSide"
"cmd-shift-backspace": "editor::DeleteToBeginningOfLine"
}
},
{

View File

@@ -1,4 +1,5 @@
[
// todo(linux): Review the editor bindings
// Standard Linux bindings
{
"bindings": {
@@ -42,8 +43,13 @@
"shift-tab": "editor::TabPrev",
"ctrl-k": "editor::CutToEndOfLine",
"ctrl-t": "editor::Transpose",
// "ctrl-backspace": "editor::DeleteToBeginningOfLine",
// "ctrl-delete": "editor::DeleteToEndOfLine",
"ctrl-backspace": "editor::DeleteToPreviousWordStart",
// "ctrl-w": "editor::DeleteToPreviousWordStart",
"ctrl-delete": "editor::DeleteToNextWordEnd",
// "alt-h": "editor::DeleteToPreviousWordStart",
// "alt-d": "editor::DeleteToNextWordEnd",
"ctrl-x": "editor::Cut",
"ctrl-insert": "editor::Copy",
"ctrl-c": "editor::Copy",
@@ -65,7 +71,13 @@
"left": "editor::MoveLeft",
"right": "editor::MoveRight",
"ctrl-left": "editor::MoveToPreviousWordStart",
// "alt-b": "editor::MoveToPreviousWordStart",
"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-end": "editor::MoveToEnd",
"shift-up": "editor::SelectUp",
@@ -76,6 +88,8 @@
"ctrl-shift-right": "editor::SelectToNextWordEnd",
"ctrl-shift-up": "editor::AddSelectionAbove",
"ctrl-shift-down": "editor::AddSelectionBelow",
// "ctrl-shift-up": "editor::SelectToStartOfParagraph",
// "ctrl-shift-down": "editor::SelectToEndOfParagraph",
"ctrl-shift-home": "editor::SelectToBeginning",
"ctrl-shift-end": "editor::SelectToEnd",
"ctrl-a": "editor::SelectAll",
@@ -152,8 +166,7 @@
// "focus": false
// }
// ],
"ctrl->": "assistant::QuoteSelection",
"ctrl-alt-e": "editor::SelectEnclosingSymbol"
"ctrl->": "assistant::QuoteSelection"
}
},
{

View File

@@ -188,8 +188,7 @@
"focus": false
}
],
"cmd->": "assistant::QuoteSelection",
"cmd-alt-e": "editor::SelectEnclosingSymbol"
"cmd->": "assistant::QuoteSelection"
}
},
{

View File

@@ -612,13 +612,13 @@
{
"context": "Editor && vim_mode == normal && !VimWaiting",
"bindings": {
"g c c": "vim::ToggleComments"
"g c c": "editor::ToggleComments"
}
},
{
"context": "Editor && vim_mode == visual",
"bindings": {
"g c": "vim::ToggleComments"
"g c": "editor::ToggleComments"
}
},
{
@@ -645,13 +645,11 @@
"escape": "vim::NormalBefore",
"ctrl-c": "vim::NormalBefore",
"ctrl-[": "vim::NormalBefore",
"tab": "vim::Tab",
"enter": "vim::Enter",
"backspace": "vim::UndoReplace"
}
},
{
"context": "Editor && vim_mode != replace && VimWaiting",
"context": "Editor && VimWaiting",
"bindings": {
"tab": "vim::Tab",
"enter": "vim::Enter",

View File

@@ -146,10 +146,6 @@
// opening parenthesis, bracket, brace, single or double quote characters.
// For example, when you type (, Zed will add a closing ) at the correct position.
"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.
// When set to `false`(default), skipping over and auto-removing of the closing characters
// happen only for auto-inserted characters.
@@ -231,8 +227,6 @@
"line_numbers": true,
// Whether to show code action buttons in the gutter.
"code_actions": true,
// Whether to show runnables buttons in the gutter.
"runnables": true,
// Whether to show fold buttons in the gutter.
"folds": true
},
@@ -241,8 +235,6 @@
"enabled": true,
/// The width of the indent guides in pixels, between 1 and 10.
"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.
/// This setting can take the following three values:
///
@@ -257,8 +249,6 @@
/// 2. "indent_aware"
"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.
"vertical_scroll_margin": 3,
// Scroll sensitivity multiplier. This multiplier is applied
@@ -312,14 +302,7 @@
"auto_reveal_entries": true,
/// Whether to fold directories automatically
/// when a directory has only one directory inside.
"auto_fold_dirs": false,
/// Scrollbar-related settings
"scrollbar": {
/// When to show the scrollbar in the project panel.
///
/// Default: always
"show": "always"
}
"auto_fold_dirs": false
},
"outline_panel": {
// Whether to show the outline panel button in the status bar
@@ -477,16 +460,16 @@
// or falling back to formatting via language server:
// "formatter": "auto"
"formatter": "auto",
// How to soft-wrap long lines of text.
// Possible values:
// How to soft-wrap long lines of text. This setting can take
// three values:
//
// 1. Do not soft wrap.
// "soft_wrap": "none",
// 2. Prefer a single line generally, unless an overly long line is encountered.
// "soft_wrap": "prefer_line",
// 3. Soft wrap lines that overflow the editor.
// 3. Soft wrap lines that overflow the editor:
// "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": "prefer_line",
// The column at which to soft-wrap lines, for buffers where soft-wrap

View File

@@ -12,8 +12,6 @@ pub const ANTHROPIC_API_URL: &'static str = "https://api.anthropic.com";
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, EnumIter)]
pub enum Model {
#[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")]
Claude3Opus,
#[serde(alias = "claude-3-sonnet", rename = "claude-3-sonnet-20240229")]
@@ -24,9 +22,7 @@ pub enum Model {
impl Model {
pub fn from_id(id: &str) -> Result<Self> {
if id.starts_with("claude-3-5-sonnet") {
Ok(Self::Claude3_5Sonnet)
} else if id.starts_with("claude-3-opus") {
if id.starts_with("claude-3-opus") {
Ok(Self::Claude3Opus)
} else if id.starts_with("claude-3-sonnet") {
Ok(Self::Claude3Sonnet)
@@ -39,7 +35,6 @@ impl Model {
pub fn id(&self) -> &'static str {
match self {
Model::Claude3_5Sonnet => "claude-3-5-sonnet-20240620",
Model::Claude3Opus => "claude-3-opus-20240229",
Model::Claude3Sonnet => "claude-3-sonnet-20240229",
Model::Claude3Haiku => "claude-3-opus-20240307",
@@ -48,7 +43,6 @@ impl Model {
pub fn display_name(&self) -> &'static str {
match self {
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
Self::Claude3Opus => "Claude 3 Opus",
Self::Claude3Sonnet => "Claude 3 Sonnet",
Self::Claude3Haiku => "Claude 3 Haiku",

View File

@@ -16,7 +16,6 @@ doctest = false
anyhow.workspace = true
anthropic = { workspace = true, features = ["schemars"] }
assistant_slash_command.workspace = true
async-watch.workspace = true
cargo_toml.workspace = true
chrono.workspace = true
client.workspace = true
@@ -56,7 +55,6 @@ smol.workspace = true
strsim = "0.11"
strum.workspace = true
telemetry_events.workspace = true
terminal_view.workspace = true
theme.workspace = true
tiktoken-rs.workspace = true
toml.workspace = true

View File

@@ -10,14 +10,14 @@ mod search;
mod slash_command;
mod streaming_diff;
pub use assistant_panel::{AssistantPanel, AssistantPanelEvent};
pub use assistant_panel::AssistantPanel;
use assistant_settings::{AnthropicModel, AssistantSettings, CloudModel, OllamaModel, OpenAiModel};
use assistant_slash_command::SlashCommandRegistry;
use client::{proto, Client};
use command_palette_hooks::CommandPaletteFilter;
pub(crate) use completion_provider::*;
pub(crate) use context_store::*;
use fs::Fs;
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
pub(crate) use inline_assistant::*;
pub(crate) use model_selector::*;
@@ -26,9 +26,8 @@ use semantic_index::{CloudEmbeddingProvider, SemanticIndex};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use slash_command::{
active_command, auto_command, default_command, diagnostics_command, fetch_command,
file_command, now_command, project_command, prompt_command, rustdoc_command, search_command,
tabs_command, term_command,
active_command, default_command, diagnostics_command, fetch_command, file_command, now_command,
project_command, prompt_command, rustdoc_command, search_command, tabs_command,
};
use std::{
fmt::{self, Display},
@@ -187,10 +186,7 @@ impl LanguageModelRequest {
LanguageModel::Anthropic(_) => {}
LanguageModel::Ollama(_) => {}
LanguageModel::Cloud(model) => match model {
CloudModel::Claude3Opus
| CloudModel::Claude3Sonnet
| CloudModel::Claude3Haiku
| CloudModel::Claude3_5Sonnet => {
CloudModel::Claude3Opus | CloudModel::Claude3Sonnet | CloudModel::Claude3Haiku => {
preprocess_anthropic_request(self);
}
_ => {}
@@ -265,7 +261,7 @@ impl Assistant {
}
}
pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
cx.set_global(Assistant::default());
AssistantSettings::register(cx);
@@ -289,7 +285,7 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
assistant_slash_command::init(cx);
register_slash_commands(cx);
assistant_panel::init(cx);
inline_assistant::init(fs.clone(), client.telemetry().clone(), cx);
inline_assistant::init(client.telemetry().clone(), cx);
RustdocStore::init_global(cx);
CommandPaletteFilter::update_global(cx, |filter, _cx| {
@@ -311,7 +307,6 @@ pub fn init(fs: Arc<dyn Fs>, client: Arc<Client>, cx: &mut AppContext) {
fn register_slash_commands(cx: &mut AppContext) {
let slash_command_registry = SlashCommandRegistry::global(cx);
slash_command_registry.register_command(auto_command::AutoCommand, true);
slash_command_registry.register_command(file_command::FileSlashCommand, true);
slash_command_registry.register_command(active_command::ActiveSlashCommand, true);
slash_command_registry.register_command(tabs_command::TabsSlashCommand, true);
@@ -319,31 +314,12 @@ fn register_slash_commands(cx: &mut AppContext) {
slash_command_registry.register_command(search_command::SearchSlashCommand, true);
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
slash_command_registry.register_command(default_command::DefaultSlashCommand, true);
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::DiagnosticsCommand, true);
slash_command_registry.register_command(rustdoc_command::RustdocSlashCommand, 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)]
#[ctor::ctor]
fn init_logger() {

View File

@@ -1,6 +1,5 @@
use crate::{
assistant_settings::{AssistantDockPosition, AssistantSettings},
humanize_token_count,
prompt_library::open_prompt_library,
search::*,
slash_command::{
@@ -18,9 +17,7 @@ use client::telemetry::Telemetry;
use collections::{BTreeSet, HashMap, HashSet};
use editor::{
actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},
display_map::{
BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease, RenderBlock, ToDisplayPoint,
},
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease, ToDisplayPoint},
scroll::{Autoscroll, AutoscrollStrategy},
Anchor, Editor, EditorEvent, RowExt, ToOffset as _, ToPoint,
};
@@ -90,10 +87,6 @@ pub fn init(cx: &mut AppContext) {
.detach();
}
pub enum AssistantPanelEvent {
ContextEdited,
}
pub struct AssistantPanel {
workspace: WeakView<Workspace>,
width: Option<Pixels>,
@@ -365,11 +358,11 @@ impl AssistantPanel {
return;
}
let Some(assistant_panel) = workspace.panel::<AssistantPanel>(cx) else {
let Some(assistant) = workspace.panel::<AssistantPanel>(cx) else {
return;
};
let context_editor = assistant_panel
let context_editor = assistant
.read(cx)
.active_context_editor()
.and_then(|editor| {
@@ -396,37 +389,25 @@ impl AssistantPanel {
return;
};
if assistant_panel.update(cx, |panel, cx| panel.is_authenticated(cx)) {
if assistant.update(cx, |assistant, cx| assistant.is_authenticated(cx)) {
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_editor,
Some(cx.view().downgrade()),
include_context.then_some(&assistant_panel),
include_context,
cx,
)
})
} else {
let assistant_panel = assistant_panel.downgrade();
let assistant = assistant.downgrade();
cx.spawn(|workspace, mut cx| async move {
assistant_panel
assistant
.update(&mut cx, |assistant, cx| assistant.authenticate(cx))?
.await?;
if assistant_panel
.update(&mut cx, |assistant, cx| assistant.is_authenticated(cx))?
{
if assistant.update(&mut cx, |assistant, cx| assistant.is_authenticated(cx))? {
cx.update(|cx| {
let assistant_panel = if include_context {
assistant_panel.upgrade()
} else {
None
};
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_editor,
Some(workspace),
assistant_panel.as_ref(),
cx,
)
assistant.assist(&active_editor, Some(workspace), include_context, cx)
})
})?
} else {
@@ -477,7 +458,7 @@ impl AssistantPanel {
_subscriptions: subscriptions,
});
self.show_saved_contexts = false;
cx.emit(AssistantPanelEvent::ContextEdited);
cx.notify();
}
@@ -489,7 +470,6 @@ impl AssistantPanel {
) {
match event {
ContextEditorEvent::TabContentChanged => cx.notify(),
ContextEditorEvent::Edited => cx.emit(AssistantPanelEvent::ContextEdited),
}
}
@@ -881,33 +861,18 @@ impl AssistantPanel {
context: &Model<Context>,
cx: &mut ViewContext<Self>,
) -> Option<impl IntoElement> {
let model = CompletionProvider::global(cx).model();
let token_count = context.read(cx).token_count()?;
let max_token_count = model.max_token_count();
let remaining_tokens = max_token_count as isize - token_count as isize;
let token_count_color = if remaining_tokens <= 0 {
let remaining_tokens = context.read(cx).remaining_tokens(cx)?;
let remaining_tokens_color = if remaining_tokens <= 0 {
Color::Error
} else if token_count as f32 / max_token_count as f32 >= 0.8 {
} else if remaining_tokens <= 500 {
Color::Warning
} else {
Color::Muted
};
Some(
h_flex()
.gap_0p5()
.child(
Label::new(humanize_token_count(token_count))
.size(LabelSize::Small)
.color(token_count_color),
)
.child(Label::new("/").size(LabelSize::Small).color(Color::Muted))
.child(
Label::new(humanize_token_count(max_token_count))
.size(LabelSize::Small)
.color(Color::Muted),
),
Label::new(remaining_tokens.to_string())
.size(LabelSize::Small)
.color(remaining_tokens_color),
)
}
}
@@ -1011,7 +976,6 @@ impl Panel for AssistantPanel {
}
impl EventEmitter<PanelEvent> for AssistantPanel {}
impl EventEmitter<AssistantPanelEvent> for AssistantPanel {}
impl FocusableView for AssistantPanel {
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
@@ -1572,6 +1536,11 @@ impl Context {
}
}
fn remaining_tokens(&self, cx: &AppContext) -> Option<isize> {
let model = CompletionProvider::global(cx).model();
Some(model.max_token_count() as isize - self.token_count? as isize)
}
fn completion_provider_changed(&mut self, cx: &mut ModelContext<Self>) {
self.count_remaining_tokens(cx);
}
@@ -2212,7 +2181,6 @@ struct PendingCompletion {
}
enum ContextEditorEvent {
Edited,
TabContentChanged,
}
@@ -2232,7 +2200,6 @@ pub struct ContextEditor {
blocks: HashSet<BlockId>,
scroll_position: Option<ScrollPosition>,
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
pending_slash_command_blocks: HashMap<Range<language::Anchor>, BlockId>,
_subscriptions: Vec<Subscription>,
}
@@ -2283,7 +2250,6 @@ impl ContextEditor {
editor.set_show_line_numbers(false, cx);
editor.set_show_git_diff_gutter(false, cx);
editor.set_show_code_actions(false, cx);
editor.set_show_runnables(false, cx);
editor.set_show_wrap_guides(false, cx);
editor.set_show_indent_guides(false, cx);
editor.set_completion_provider(Box::new(completion_provider));
@@ -2307,7 +2273,6 @@ impl ContextEditor {
fs,
workspace: workspace.downgrade(),
pending_slash_command_creases: HashMap::default(),
pending_slash_command_blocks: HashMap::default(),
_subscriptions,
};
this.update_message_headers(cx);
@@ -2569,8 +2534,7 @@ impl ContextEditor {
ContextEvent::PendingSlashCommandsUpdated { removed, updated } => {
self.editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).snapshot(cx);
let (excerpt_id, buffer_id, _) = buffer.as_singleton().unwrap();
let excerpt_id = *excerpt_id;
let excerpt_id = *buffer.as_singleton().unwrap().0;
editor.remove_creases(
removed
@@ -2579,16 +2543,6 @@ impl ContextEditor {
cx,
);
editor.remove_blocks(
HashSet::from_iter(
removed.iter().filter_map(|range| {
self.pending_slash_command_blocks.remove(range)
}),
),
None,
cx,
);
let crease_ids = editor.insert_creases(
updated.iter().map(|command| {
let workspace = self.workspace.clone();
@@ -2621,7 +2575,7 @@ impl ContextEditor {
move |row, _, _, _cx: &mut WindowContext| {
render_pending_slash_command_gutter_decoration(
row,
&command.status,
command.status.clone(),
confirm_command.clone(),
)
}
@@ -2655,43 +2609,12 @@ impl ContextEditor {
cx,
);
let block_ids = editor.insert_blocks(
updated
.iter()
.filter_map(|command| match &command.status {
PendingSlashCommandStatus::Error(error) => {
Some((command, error.clone()))
}
_ => None,
})
.map(|(command, error_message)| BlockProperties {
style: BlockStyle::Fixed,
position: Anchor {
buffer_id: Some(buffer_id),
excerpt_id,
text_anchor: command.source_range.start,
},
height: 1,
disposition: BlockDisposition::Below,
render: slash_command_error_block_renderer(error_message),
}),
None,
cx,
);
self.pending_slash_command_creases.extend(
updated
.iter()
.map(|command| command.source_range.clone())
.zip(crease_ids),
);
self.pending_slash_command_blocks.extend(
updated
.iter()
.map(|command| command.source_range.clone())
.zip(block_ids),
);
})
}
ContextEvent::SlashCommandFinished {
@@ -2805,7 +2728,6 @@ impl ContextEditor {
EditorEvent::SelectionsChanged { .. } => {
self.scroll_position = self.cursor_scroll_position(cx);
}
EditorEvent::BufferEdited => cx.emit(ContextEditorEvent::Edited),
_ => {}
}
}
@@ -3282,7 +3204,7 @@ fn render_slash_command_output_toggle(
fn render_pending_slash_command_gutter_decoration(
row: MultiBufferRow,
status: &PendingSlashCommandStatus,
status: PendingSlashCommandStatus,
confirm_command: Arc<dyn Fn(&mut WindowContext)>,
) -> AnyElement {
let mut icon = IconButton::new(
@@ -3300,7 +3222,11 @@ fn render_pending_slash_command_gutter_decoration(
PendingSlashCommandStatus::Running { .. } => {
icon = icon.selected(true);
}
PendingSlashCommandStatus::Error(_) => icon = icon.icon_color(Color::Error),
PendingSlashCommandStatus::Error(error) => {
icon = icon
.icon_color(Color::Error)
.tooltip(move |cx| Tooltip::text(format!("error: {error}"), cx));
}
}
icon.into_any_element()
@@ -3351,19 +3277,6 @@ fn make_lsp_adapter_delegate(
})
}
fn slash_command_error_block_renderer(message: String) -> RenderBlock {
Box::new(move |_| {
div()
.pl_6()
.child(
Label::new(format!("error: {}", message))
.single_line()
.color(Color::Error),
)
.into_any()
})
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -24,7 +24,6 @@ pub enum CloudModel {
Gpt4Turbo,
#[default]
Gpt4Omni,
Claude3_5Sonnet,
Claude3Opus,
Claude3Sonnet,
Claude3Haiku,
@@ -106,7 +105,6 @@ impl CloudModel {
Self::Gpt4 => "gpt-4",
Self::Gpt4Turbo => "gpt-4-turbo-preview",
Self::Gpt4Omni => "gpt-4o",
Self::Claude3_5Sonnet => "claude-3-5-sonnet",
Self::Claude3Opus => "claude-3-opus",
Self::Claude3Sonnet => "claude-3-sonnet",
Self::Claude3Haiku => "claude-3-haiku",
@@ -120,7 +118,6 @@ impl CloudModel {
Self::Gpt4 => "GPT 4",
Self::Gpt4Turbo => "GPT 4 Turbo",
Self::Gpt4Omni => "GPT 4 Omni",
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
Self::Claude3Opus => "Claude 3 Opus",
Self::Claude3Sonnet => "Claude 3 Sonnet",
Self::Claude3Haiku => "Claude 3 Haiku",
@@ -133,10 +130,7 @@ impl CloudModel {
Self::Gpt3Point5Turbo => 2048,
Self::Gpt4 => 4096,
Self::Gpt4Turbo | Self::Gpt4Omni => 128000,
Self::Claude3_5Sonnet
| Self::Claude3Opus
| Self::Claude3Sonnet
| Self::Claude3Haiku => 200000,
Self::Claude3Opus | Self::Claude3Sonnet | Self::Claude3Haiku => 200000,
Self::Custom(_) => 4096, // TODO: Make this configurable
}
}

View File

@@ -236,7 +236,7 @@ pub fn preprocess_anthropic_request(request: &mut LanguageModelRequest) {
}
if !system_message.is_empty() {
request.messages.insert(
new_messages.insert(
0,
LanguageModelRequestMessage {
role: Role::System,

View File

@@ -101,10 +101,7 @@ impl CloudCompletionProvider {
count_open_ai_tokens(request, cx.background_executor())
}
LanguageModel::Cloud(
CloudModel::Claude3_5Sonnet
| CloudModel::Claude3Opus
| CloudModel::Claude3Sonnet
| CloudModel::Claude3Haiku,
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.
count_open_ai_tokens(request, cx.background_executor())

View File

@@ -210,7 +210,6 @@ pub fn count_open_ai_tokens(
match request.model {
LanguageModel::Anthropic(_)
| LanguageModel::Cloud(CloudModel::Claude3_5Sonnet)
| LanguageModel::Cloud(CloudModel::Claude3Opus)
| LanguageModel::Cloud(CloudModel::Claude3Sonnet)
| LanguageModel::Cloud(CloudModel::Claude3Haiku) => {

File diff suppressed because it is too large Load Diff

View File

@@ -569,7 +569,7 @@ impl PromptLibrary {
let provider = CompletionProvider::global(cx);
if provider.is_authenticated() {
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(&prompt_editor, None, None, cx)
assistant.assist(&prompt_editor, None, false, cx)
})
} else {
for window in cx.windows() {

View File

@@ -33,32 +33,35 @@ pub fn generate_content_prompt(
)?;
}
writeln!(
prompt,
"The user has the following file open in the editor:"
)?;
// Include file content.
for chunk in buffer.text_for_range(0..range.start) {
prompt.push_str(chunk);
}
if range.is_empty() {
write!(prompt, "```")?;
if let Some(language_name) = language_name {
write!(prompt, "{language_name}")?;
}
prompt.push_str("<|START|>");
} else {
prompt.push_str("<|START|");
}
for chunk in buffer.as_rope().chunks_in_range(0..range.start) {
prompt.push_str(chunk);
}
prompt.push_str("<|CURSOR|>");
for chunk in buffer.as_rope().chunks_in_range(range.start..buffer.len()) {
prompt.push_str(chunk);
}
if !prompt.ends_with('\n') {
prompt.push('\n');
}
writeln!(prompt, "```")?;
prompt.push('\n');
for chunk in buffer.text_for_range(range.clone()) {
prompt.push_str(chunk);
}
if !range.is_empty() {
prompt.push_str("|END|>");
}
for chunk in buffer.text_for_range(range.end..buffer.len()) {
prompt.push_str(chunk);
}
prompt.push('\n');
if range.is_empty() {
writeln!(
prompt,
"Assume the cursor is located where the `<|CURSOR|>` span is."
"Assume the cursor is located where the `<|START|>` span is."
)
.unwrap();
writeln!(
@@ -72,42 +75,11 @@ pub fn generate_content_prompt(
)
.unwrap();
} else {
write!(prompt, "```")?;
for chunk in buffer.as_rope().chunks() {
prompt.push_str(chunk);
}
if !prompt.ends_with('\n') {
prompt.push('\n');
}
writeln!(prompt, "```")?;
prompt.push('\n');
writeln!(prompt, "Modify the user's selected {content_type} based upon the users prompt: '{user_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,
"In particular, the following piece of text is selected:"
)?;
write!(prompt, "```")?;
if let Some(language_name) = language_name {
write!(prompt, "{language_name}")?;
}
prompt.push('\n');
for chunk in buffer.text_for_range(range.clone()) {
prompt.push_str(chunk);
}
if !prompt.ends_with('\n') {
prompt.push('\n');
}
writeln!(prompt, "```")?;
prompt.push('\n');
writeln!(
prompt,
"Modify the user's selected {content_type} based upon the users prompt: {user_prompt}"
)
.unwrap();
writeln!(
prompt,
"You must reply with only the adjusted {content_type}, not the entire file."
"Double check that you only return code and not the '<|START|' and '|END|'> spans"
)
.unwrap();
}

View File

@@ -3,8 +3,8 @@ use anyhow::Result;
pub use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandRegistry};
use editor::{CompletionProvider, Editor};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{AppContext, Model, Task, ViewContext, WeakView, WindowContext};
use language::{Anchor, Buffer, CodeLabel, Documentation, HighlightId, LanguageServerId, ToPoint};
use gpui::{Model, Task, ViewContext, WeakView, WindowContext};
use language::{Anchor, Buffer, CodeLabel, Documentation, LanguageServerId, ToPoint};
use parking_lot::{Mutex, RwLock};
use rope::Point;
use std::{
@@ -14,11 +14,9 @@ use std::{
Arc,
},
};
use ui::ActiveTheme;
use workspace::Workspace;
pub mod active_command;
pub mod auto_command;
pub mod default_command;
pub mod diagnostics_command;
pub mod fetch_command;
@@ -29,7 +27,6 @@ pub mod prompt_command;
pub mod rustdoc_command;
pub mod search_command;
pub mod tabs_command;
pub mod term_command;
pub(crate) struct SlashCommandCompletionProvider {
commands: Arc<SlashCommandRegistry>,
@@ -350,19 +347,3 @@ impl SlashCommandLine {
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
}

View File

@@ -1,5 +1,4 @@
use super::{
diagnostics_command::write_single_file_diagnostics,
file_command::{build_entry_output_section, codeblock_fence_for_path},
SlashCommand, SlashCommandOutput,
};
@@ -61,28 +60,24 @@ impl SlashCommand for ActiveSlashCommand {
let snapshot = buffer.read(cx).snapshot();
let path = snapshot.resolve_file_path(cx, true);
let task = cx.background_executor().spawn({
let text = cx.background_executor().spawn({
let path = path.clone();
async move {
let mut output = String::new();
output.push_str(&codeblock_fence_for_path(path.as_deref(), None));
output.push('\n');
for chunk in snapshot.as_rope().chunks() {
output.push_str(chunk);
}
if !output.ends_with('\n') {
output.push('\n');
}
output.push_str("```\n");
let has_diagnostics =
write_single_file_diagnostics(&mut output, path.as_deref(), &snapshot);
if output.ends_with('\n') {
output.pop();
}
(output, has_diagnostics)
output.push_str("```");
output
}
});
cx.foreground_executor().spawn(async move {
let (text, has_diagnostics) = task.await;
let text = text.await;
let range = 0..text.len();
Ok(SlashCommandOutput {
text,
@@ -92,7 +87,7 @@ impl SlashCommand for ActiveSlashCommand {
false,
None,
)],
run_commands_in_text: has_diagnostics,
run_commands_in_text: false,
})
})
});

View File

@@ -1,193 +0,0 @@
use super::create_label_for_command;
use super::{SlashCommand, SlashCommandOutput};
use crate::{CompletionProvider, LanguageModelRequest, LanguageModelRequestMessage, Role};
use anyhow::{anyhow, Result};
use futures::StreamExt;
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LspAdapterDelegate};
use std::sync::{atomic::AtomicBool, Arc};
use ui::WindowContext;
use workspace::Workspace;
pub(crate) struct AutoCommand;
impl SlashCommand for AutoCommand {
fn name(&self) -> String {
"auto".into()
}
fn description(&self) -> String {
"Automatically infer what context to add, based on your prompt".into()
}
fn menu_text(&self) -> String {
"Automatically Infer Context".into()
}
fn label(&self, cx: &AppContext) -> CodeLabel {
create_label_for_command("auto", &["--prompt"], cx)
}
fn complete_argument(
self: Arc<Self>,
_query: String,
_cancellation_flag: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
) -> Task<Result<Vec<String>>> {
// There's no autocomplete for a prompt, since it's arbitrary text.
Task::ready(Ok(Vec::new()))
}
fn requires_argument(&self) -> bool {
true
}
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 prompt")));
};
let prompt = format!("{PROMPT_INSTRUCTIONS_BEFORE_SUMMARY}\n{SUMMARY}\n{PROMPT_INSTRUCTIONS_AFTER_SUMMARY}\n{argument}");
let request = LanguageModelRequest {
model: CompletionProvider::global(cx).model(),
messages: vec![LanguageModelRequestMessage {
role: Role::User,
content: prompt,
}],
stop: vec![],
temperature: 1.0,
};
let stream = CompletionProvider::global(cx).complete(request);
let mut wip_action: String = String::new();
let task: Task<Result<String>> = cx.spawn(|_cx| async move {
let mut actions_text = String::new();
let stream_completion = async {
let mut messages = stream.await?;
while let Some(message) = messages.next().await {
let text = message?;
chunked_line(&mut wip_action, &text, |line| {
actions_text.push('/');
actions_text.push_str(line);
actions_text.push('\n');
});
smol::future::yield_now().await;
}
anyhow::Ok(())
};
stream_completion.await?;
Ok(actions_text)
});
// As a convenience, append /auto's argument to the end of the prompt so you don't have to write it again.
let argument = argument.to_string();
cx.background_executor().spawn(async move {
let mut text = task.await?;
text.push_str(&argument);
Ok(SlashCommandOutput {
text,
sections: Vec::new(),
run_commands_in_text: true,
})
})
}
}
const PROMPT_INSTRUCTIONS_BEFORE_SUMMARY: &str = r#"
I'm going to give you a prompt. I don't want you to respond
to the prompt itself. I want you to figure out which of the following
actions on my project, if any, would help you answer the prompt.
Here are the actions:
## file
This action's parameter is a file path to one of the files
in the project. If you ask for this action, I will tell you
the full contents of the file, so you can learn all the
details of the file.
## search
This action's parameter is a string to search for across
the project. It will tell you which files this string
(or similar strings; it is a semantic search) appear in,
as well as some context of the lines surrounding each result.
---
That was the end of the list of actions.
Here is an XML summary of each of the files in my project:
"#;
const PROMPT_INSTRUCTIONS_AFTER_SUMMARY: &str = r#"
Actions have a cost, so only include actions that you think
will be helpful to you in doing a great job answering the
prompt in the future.
You must respond ONLY with a list of actions you would like to
perform. Each action should be on its own line, and followed by a space and then its parameter.
Actions can be performed more than once with different parameters.
Here is an example valid response:
```
file path/to/my/file.txt
file path/to/another/file.txt
search something to search for
search something else to search for
```
Once again, do not forget: you must respond ONLY in the format of
one action per line, and the action name should be followed by
its parameter. Your response must not include anything other
than a list of actions, with one action per line, in this format.
It is extremely important that you do not deviate from this format even slightly!
This is the end of my instructions for how to respond. The rest is the prompt:
"#;
const SUMMARY: &str = "";
fn chunked_line(wip: &mut String, chunk: &str, mut on_line_end: impl FnMut(&str)) {
// The first iteration of the loop should just push to wip
// and nothing else. We only push what we encountered in
// previous iterations of the loop.
//
// This correctly handles both the scenario where no
// newlines are encountered (the loop will only run once,
// and so will only push to wip), as well as the scenario
// where the chunk contains at least one newline but
// does not end in a newline (the last iteration of the
// loop will update wip but will not run anything).
let mut is_first_iteration = true;
for line in chunk.split('\n') {
if is_first_iteration {
is_first_iteration = false;
} else {
// Since this isn't the first iteration of the loop, we definitely hit a newline
// at the end of the previous iteration! Run the function on whatever wip we have.
on_line_end(wip);
wip.clear();
}
wip.push_str(line);
}
}

View File

@@ -1,4 +1,4 @@
use super::{create_label_for_command, SlashCommand, SlashCommandOutput};
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
use assistant_slash_command::SlashCommandOutputSection;
use fuzzy::{PathMatch, StringMatchCandidate};
@@ -10,7 +10,6 @@ use language::{
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},
@@ -58,7 +57,7 @@ impl DiagnosticsCommand {
include_ignored: worktree
.root_entry()
.map_or(false, |entry| entry.is_ignored),
include_root_name: true,
include_root_name: false,
candidates: project::Candidates::Entries,
}
})
@@ -86,10 +85,6 @@ impl SlashCommand for DiagnosticsCommand {
"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()
}
@@ -162,10 +157,7 @@ impl SlashCommand for DiagnosticsCommand {
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());
};
let (text, sections) = task.await?;
Ok(SlashCommandOutput {
text,
sections: sections
@@ -261,7 +253,7 @@ fn collect_diagnostics(
project: Model<Project>,
options: Options,
cx: &mut AppContext,
) -> Task<Result<Option<(String, Vec<(Range<usize>, PlaceholderType)>)>>> {
) -> Task<Result<(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())
@@ -269,37 +261,8 @@ fn collect_diagnostics(
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();
let diagnostic_summaries: Vec<_> = project.read(cx).diagnostic_summaries(false, cx).collect();
cx.spawn(|mut cx| async move {
let mut text = String::new();
@@ -311,9 +274,9 @@ fn collect_diagnostics(
let mut sections: Vec<(Range<usize>, PlaceholderType)> = Vec::new();
let mut project_summary = DiagnosticSummary::default();
for (project_path, path, summary) in diagnostic_summaries {
for (project_path, _, summary) in diagnostic_summaries {
if let Some(path_matcher) = &options.path_matcher {
if !path_matcher.is_match(&path) {
if !path_matcher.is_match(&project_path.path) {
continue;
}
}
@@ -326,10 +289,8 @@ fn collect_diagnostics(
}
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();
}
let file_path = project_path.path.to_string_lossy().to_string();
writeln!(&mut text, "{file_path}").unwrap();
if let Some(buffer) = project_handle
.update(&mut cx, |project, cx| project.open_buffer(project_path, cx))?
@@ -344,52 +305,20 @@ fn collect_diagnostics(
);
}
if !glob_is_exact_file_match {
sections.push((
last_end..text.len().saturating_sub(1),
PlaceholderType::File(file_path),
))
}
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)))
Ok((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)>,

View File

@@ -1,10 +1,11 @@
use super::{diagnostics_command::write_single_file_diagnostics, SlashCommand, SlashCommandOutput};
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
use assistant_slash_command::SlashCommandOutputSection;
use fs::Fs;
use fuzzy::PathMatch;
use gpui::{AppContext, Model, Task, View, WeakView};
use language::{BufferSnapshot, LineEnding, LspAdapterDelegate};
use project::{PathMatchCandidateSet, Project};
use language::{LineEnding, LspAdapterDelegate};
use project::{PathMatchCandidateSet, Worktree};
use std::{
fmt::Write,
ops::Range,
@@ -141,7 +142,13 @@ impl SlashCommand for FileSlashCommand {
return Task::ready(Err(anyhow!("missing path")));
};
let task = collect_files(workspace.read(cx).project().clone(), argument, cx);
let fs = workspace.read(cx).app_state().fs.clone();
let task = collect_files(
workspace.read(cx).visible_worktrees(cx).collect(),
argument,
fs,
cx,
);
cx.foreground_executor().spawn(async move {
let (text, ranges) = task.await?;
@@ -158,7 +165,7 @@ impl SlashCommand for FileSlashCommand {
)
})
.collect(),
run_commands_in_text: true,
run_commands_in_text: false,
})
})
}
@@ -171,33 +178,62 @@ enum EntryType {
}
fn collect_files(
project: Model<Project>,
worktrees: Vec<Model<Worktree>>,
glob_input: &str,
fs: Arc<dyn Fs>,
cx: &mut AppContext,
) -> Task<Result<(String, Vec<(Range<usize>, PathBuf, EntryType)>)>> {
let Ok(matcher) = PathMatcher::new(&[glob_input.to_owned()]) else {
return Task::ready(Err(anyhow!("invalid path")));
};
let project_handle = project.downgrade();
let snapshots = project
.read(cx)
.worktrees()
let path = PathBuf::try_from(glob_input).ok();
let file_path = if let Some(path) = &path {
worktrees.iter().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()
})
} else {
None
};
if let Some(abs_path) = file_path {
if abs_path.is_file() {
let filename = path
.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default();
return cx.background_executor().spawn(async move {
let mut text = String::new();
collect_file_content(&mut text, fs, filename.clone(), abs_path.clone().into())
.await?;
let text_range = 0..text.len();
Ok((
text,
vec![(text_range, path.unwrap_or_default(), EntryType::File)],
))
});
}
}
let snapshots = worktrees
.iter()
.map(|worktree| worktree.read(cx).snapshot())
.collect::<Vec<_>>();
cx.spawn(|mut cx| async move {
cx.background_executor().spawn(async move {
let mut text = String::new();
let mut ranges = Vec::new();
for snapshot in snapshots {
let worktree_id = snapshot.id();
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) {
let mut path_buf = PathBuf::new();
path_buf.push(snapshot.root_name());
path_buf.push(&entry.path);
if !matcher.is_match(&path_buf) {
continue;
}
@@ -228,9 +264,8 @@ fn collect_files(
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(),
);
folded_directory_names_stack
.push(path_buf.to_string_lossy().to_string());
} else {
folded_directory_names_stack.push(filename.to_string());
}
@@ -245,7 +280,7 @@ fn collect_files(
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());
text.push_str(&path_buf.to_string_lossy());
is_top_level_directory = false;
} else {
text.push_str(&filename);
@@ -258,26 +293,15 @@ fn collect_files(
}
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())?;
if let Some(abs_path) = snapshot.absolutize(&entry.path).log_err() {
let prev_len = text.len();
collect_file_content(&mut text, &snapshot, filename.clone());
text.push('\n');
if !write_single_file_diagnostics(
collect_file_content(
&mut text,
Some(&path_including_worktree_name),
&snapshot,
) {
text.pop();
}
fs.clone(),
filename.clone(),
abs_path.into(),
)
.await?;
ranges.push((
prev_len..text.len(),
PathBuf::from(filename),
@@ -299,8 +323,13 @@ fn collect_files(
})
}
fn collect_file_content(buffer: &mut String, snapshot: &BufferSnapshot, filename: String) {
let mut content = snapshot.text();
async fn collect_file_content(
buffer: &mut String,
fs: Arc<dyn Fs>,
filename: String,
abs_path: Arc<Path>,
) -> Result<()> {
let mut content = fs.load(&abs_path).await?;
LineEnding::normalize(&mut content);
buffer.reserve(filename.len() + content.len() + 9);
buffer.push_str(&codeblock_fence_for_path(
@@ -312,6 +341,7 @@ fn collect_file_content(buffer: &mut String, snapshot: &BufferSnapshot, filename
buffer.push('\n');
}
buffer.push_str("```");
anyhow::Ok(())
}
pub fn codeblock_fence_for_path(path: Option<&Path>, row_range: Option<Range<u32>>) -> String {

View File

@@ -1,12 +1,11 @@
use super::{
create_label_for_command,
file_command::{build_entry_output_section, codeblock_fence_for_path},
SlashCommand, SlashCommandOutput,
};
use anyhow::Result;
use assistant_slash_command::SlashCommandOutputSection;
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LineEnding, LspAdapterDelegate};
use language::{CodeLabel, HighlightId, LineEnding, LspAdapterDelegate};
use semantic_index::SemanticIndex;
use std::{
fmt::Write,
@@ -25,7 +24,14 @@ impl SlashCommand for SearchSlashCommand {
}
fn label(&self, cx: &AppContext) -> CodeLabel {
create_label_for_command("search", &["--n"], cx)
let mut label = CodeLabel::default();
label.push_str("search ", None);
label.push_str(
"--n",
cx.theme().syntax().highlight_id("comment").map(HighlightId),
);
label.filter_range = 0.."search".len();
label
}
fn description(&self) -> String {

View File

@@ -1,5 +1,4 @@
use super::{
diagnostics_command::write_single_file_diagnostics,
file_command::{build_entry_output_section, codeblock_fence_for_path},
SlashCommand, SlashCommandOutput,
};
@@ -78,7 +77,6 @@ impl SlashCommand for TabsSlashCommand {
let mut sections = Vec::new();
let mut text = String::new();
let mut has_diagnostics = false;
for (full_path, buffer, _) in open_buffers {
let section_start_ix = text.len();
text.push_str(&codeblock_fence_for_path(full_path.as_deref(), None));
@@ -88,14 +86,7 @@ impl SlashCommand for TabsSlashCommand {
if !text.ends_with('\n') {
text.push('\n');
}
writeln!(text, "```").unwrap();
if write_single_file_diagnostics(&mut text, full_path.as_deref(), &buffer) {
has_diagnostics = true;
}
if !text.ends_with('\n') {
text.push('\n');
}
writeln!(text, "```\n").unwrap();
let section_end_ix = text.len() - 1;
sections.push(build_entry_output_section(
section_start_ix..section_end_ix,
@@ -108,7 +99,7 @@ impl SlashCommand for TabsSlashCommand {
Ok(SlashCommandOutput {
text,
sections,
run_commands_in_text: has_diagnostics,
run_commands_in_text: false,
})
}),
Err(error) => Task::ready(Err(error)),

View File

@@ -1,105 +0,0 @@
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use anyhow::Result;
use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LspAdapterDelegate};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
use ui::prelude::*;
use workspace::Workspace;
use super::create_label_for_command;
pub(crate) struct TermSlashCommand;
const LINE_COUNT_ARG: &str = "--line-count";
impl SlashCommand for TermSlashCommand {
fn name(&self) -> String {
"term".into()
}
fn label(&self, cx: &AppContext) -> CodeLabel {
create_label_for_command("term", &[LINE_COUNT_ARG], cx)
}
fn description(&self) -> String {
"insert terminal output".into()
}
fn menu_text(&self) -> String {
"Insert terminal output".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<String>>> {
Task::ready(Ok(vec![LINE_COUNT_ARG.to_string()]))
}
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::anyhow!("workspace was dropped")));
};
let Some(terminal_panel) = workspace.read(cx).panel::<TerminalPanel>(cx) else {
return Task::ready(Err(anyhow::anyhow!("no terminal panel open")));
};
let Some(active_terminal) = terminal_panel
.read(cx)
.pane()
.read(cx)
.active_item()
.and_then(|t| t.downcast::<TerminalView>())
else {
return Task::ready(Err(anyhow::anyhow!("no active terminal")));
};
let line_count = argument.and_then(|a| parse_argument(a)).unwrap_or(20);
let lines = active_terminal
.read(cx)
.model()
.read(cx)
.last_n_non_empty_lines(line_count);
let mut text = String::new();
text.push_str("Terminal output:\n");
text.push_str(&lines.join("\n"));
let range = 0..text.len();
Task::ready(Ok(SlashCommandOutput {
text,
sections: vec![SlashCommandOutputSection {
range,
icon: IconName::Terminal,
label: "Terminal".into(),
}],
run_commands_in_text: false,
}))
}
}
fn parse_argument(argument: &str) -> Option<usize> {
let mut args = argument.split(' ');
if args.next() == Some(LINE_COUNT_ARG) {
if let Some(line_count) = args.next().and_then(|s| s.parse::<usize>().ok()) {
return Some(line_count);
}
}
None
}

View File

@@ -50,7 +50,6 @@ pub type RenderFoldPlaceholder = Arc<
+ Fn(ElementId, Arc<dyn Fn(&mut WindowContext)>, &mut WindowContext) -> AnyElement,
>;
#[derive(Default)]
pub struct SlashCommandOutput {
pub text: String,
pub sections: Vec<SlashCommandOutputSection<usize>>,

View File

@@ -141,13 +141,8 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut AppContext) {
let auto_updater = cx.new_model(|cx| {
let updater = AutoUpdater::new(version, http_client);
let poll_for_updates = ReleaseChannel::try_global(cx)
.map(|channel| channel.poll_for_updates())
.unwrap_or(false);
if option_env!("ZED_UPDATE_EXPLANATION").is_none()
&& env::var("ZED_UPDATE_EXPLANATION").is_err()
&& poll_for_updates
{
let mut update_subscription = AutoUpdateSetting::get_global(cx)
.0
@@ -191,13 +186,6 @@ pub fn check(_: &Check, cx: &mut WindowContext) {
return;
}
if !ReleaseChannel::try_global(cx)
.map(|channel| channel.poll_for_updates())
.unwrap_or(false)
{
return;
}
if let Some(updater) = AutoUpdater::get(cx) {
updater.update(cx, |updater, cx| updater.poll(cx));
} else {

View File

@@ -114,6 +114,7 @@ impl ActiveCall {
async fn handle_incoming_call(
this: Model<Self>,
envelope: TypedEnvelope<proto::IncomingCall>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::Ack> {
let user_store = this.update(&mut cx, |this, _| this.user_store.clone())?;
@@ -141,6 +142,7 @@ impl ActiveCall {
async fn handle_call_canceled(
this: Model<Self>,
envelope: TypedEnvelope<proto::CallCanceled>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, _| {

View File

@@ -697,6 +697,7 @@ impl Room {
async fn handle_room_updated(
this: Model<Self>,
envelope: TypedEnvelope<proto::RoomUpdated>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let room = envelope

View File

@@ -138,6 +138,7 @@ impl ChannelBuffer {
async fn handle_update_channel_buffer(
this: Model<Self>,
update_channel_buffer: TypedEnvelope<proto::UpdateChannelBuffer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let ops = update_channel_buffer
@@ -159,6 +160,7 @@ impl ChannelBuffer {
async fn handle_update_channel_buffer_collaborators(
this: Model<Self>,
message: TypedEnvelope<proto::UpdateChannelBufferCollaborators>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {

View File

@@ -528,6 +528,7 @@ impl ChannelChat {
async fn handle_message_sent(
this: Model<Self>,
message: TypedEnvelope<proto::ChannelMessageSent>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let user_store = this.update(&mut cx, |this, _| this.user_store.clone())?;
@@ -552,6 +553,7 @@ impl ChannelChat {
async fn handle_message_removed(
this: Model<Self>,
message: TypedEnvelope<proto::RemoveChannelMessage>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -563,6 +565,7 @@ impl ChannelChat {
async fn handle_message_updated(
this: Model<Self>,
message: TypedEnvelope<proto::ChannelMessageUpdate>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let user_store = this.update(&mut cx, |this, _| this.user_store.clone())?;

View File

@@ -888,6 +888,7 @@ impl ChannelStore {
async fn handle_update_channels(
this: Model<Self>,
message: TypedEnvelope<proto::UpdateChannels>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, _| {
@@ -901,6 +902,7 @@ impl ChannelStore {
async fn handle_update_user_channels(
this: Model<Self>,
message: TypedEnvelope<proto::UpdateUserChannels>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {

View File

@@ -21,7 +21,6 @@ anyhow.workspace = true
clap.workspace = true
ipc-channel = "0.18"
once_cell.workspace = true
parking_lot.workspace = true
paths.workspace = true
release_channel.workspace = true
serde.workspace = true

View File

@@ -3,12 +3,10 @@
use anyhow::{Context, Result};
use clap::Parser;
use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
use parking_lot::Mutex;
use std::{
env, fs, io,
path::{Path, PathBuf},
process::ExitStatus,
sync::Arc,
thread::{self, JoinHandle},
};
use util::paths::PathLikeWithPosition;
@@ -56,7 +54,7 @@ struct Args {
fn parse_path_with_position(
argument_str: &str,
) -> Result<PathLikeWithPosition<PathBuf>, std::convert::Infallible> {
PathLikeWithPosition::parse_str(argument_str, |_, path_str| {
PathLikeWithPosition::parse_str(argument_str, |path_str| {
Ok(Path::new(path_str).to_path_buf())
})
}
@@ -125,34 +123,26 @@ fn main() -> Result<()> {
None
};
let exit_status = Arc::new(Mutex::new(None));
let sender: JoinHandle<anyhow::Result<()>> = thread::spawn(move || {
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
let (tx, rx) = (handshake.requests, handshake.responses);
tx.send(CliRequest::Open {
paths,
wait: args.wait,
open_new_workspace,
dev_server_token: args.dev_server_token,
})?;
let sender: JoinHandle<anyhow::Result<()>> = thread::spawn({
let exit_status = exit_status.clone();
move || {
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
let (tx, rx) = (handshake.requests, handshake.responses);
tx.send(CliRequest::Open {
paths,
wait: args.wait,
open_new_workspace,
dev_server_token: args.dev_server_token,
})?;
while let Ok(response) = rx.recv() {
match response {
CliResponse::Ping => {}
CliResponse::Stdout { message } => println!("{message}"),
CliResponse::Stderr { message } => eprintln!("{message}"),
CliResponse::Exit { status } => {
exit_status.lock().replace(status);
return Ok(());
}
}
while let Ok(response) = rx.recv() {
match response {
CliResponse::Ping => {}
CliResponse::Stdout { message } => println!("{message}"),
CliResponse::Stderr { message } => eprintln!("{message}"),
CliResponse::Exit { status } => std::process::exit(status),
}
Ok(())
}
Ok(())
});
if args.foreground {
@@ -162,9 +152,6 @@ fn main() -> Result<()> {
sender.join().unwrap()?;
}
if let Some(exit_status) = exit_status.lock().take() {
std::process::exit(exit_status);
}
Ok(())
}

View File

@@ -19,6 +19,7 @@ test-support = ["clock/test-support", "collections/test-support", "gpui/test-sup
anyhow.workspace = true
async-recursion = "0.3"
async-tungstenite = { version = "0.16", features = ["async-std", "async-native-tls"] }
async-native-tls = { version = "0.5.0", features = ["vendored"] }
chrono = { workspace = true, features = ["serde"] }
clock.workspace = true
collections.workspace = true
@@ -67,5 +68,10 @@ windows.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
cocoa.workspace = true
isahc = { workspace = true, features = ["static-curl"] }
async-native-tls = { version = "0.5.0", features = ["vendored"] }
[target.'cfg(target_os = "linux")'.dependencies]
async-native-tls = {"version" = "0.5.0", features = ["vendored"]}
# This is an indirect dependency of async-tungstenite that is included
# here so we can vendor libssl with the feature flag.
[package.metadata.cargo-machete]
ignored = ["async-native-tls"]

View File

@@ -689,22 +689,6 @@ impl Client {
entity: WeakModel<E>,
handler: H,
) -> Subscription
where
M: EnvelopedMessage,
E: 'static,
H: 'static + Sync + Fn(Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
F: 'static + Future<Output = Result<()>>,
{
self.add_message_handler_impl(entity, move |model, message, _, cx| {
handler(model, message, cx)
})
}
fn add_message_handler_impl<M, E, H, F>(
self: &Arc<Self>,
entity: WeakModel<E>,
handler: H,
) -> Subscription
where
M: EnvelopedMessage,
E: 'static,
@@ -753,11 +737,19 @@ impl Client {
where
M: RequestMessage,
E: 'static,
H: 'static + Sync + Fn(Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
H: 'static
+ Sync
+ Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F
+ Send
+ Sync,
F: 'static + Future<Output = Result<M::Response>>,
{
self.add_message_handler_impl(model, move |handle, envelope, this, cx| {
Self::respond_to_request(envelope.receipt(), handler(handle, envelope, cx), this)
self.add_message_handler(model, move |handle, envelope, this, cx| {
Self::respond_to_request(
envelope.receipt(),
handler(handle, envelope, this.clone(), cx),
this,
)
})
}
@@ -765,11 +757,11 @@ impl Client {
where
M: EntityMessage,
E: 'static,
H: 'static + Fn(Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
H: 'static + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F + Send + Sync,
F: 'static + Future<Output = Result<()>>,
{
self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, _, cx| {
handler(subscriber.downcast::<E>().unwrap(), message, cx)
self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
handler(subscriber.downcast::<E>().unwrap(), message, client, cx)
})
}
@@ -816,13 +808,13 @@ impl Client {
where
M: EntityMessage + RequestMessage,
E: 'static,
H: 'static + Fn(Model<E>, TypedEnvelope<M>, AsyncAppContext) -> F + Send + Sync,
H: 'static + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F + Send + Sync,
F: 'static + Future<Output = Result<M::Response>>,
{
self.add_entity_message_handler::<M, E, _, _>(move |entity, envelope, client, cx| {
self.add_model_message_handler(move |entity, envelope, client, cx| {
Self::respond_to_request::<M, _>(
envelope.receipt(),
handler(entity.downcast::<E>().unwrap(), envelope, cx),
handler(entity, envelope, client.clone(), cx),
client,
)
})
@@ -1920,7 +1912,7 @@ mod tests {
let (done_tx1, mut done_rx1) = smol::channel::unbounded();
let (done_tx2, mut done_rx2) = smol::channel::unbounded();
client.add_model_message_handler(
move |model: Model<TestModel>, _: TypedEnvelope<proto::JoinProject>, mut cx| {
move |model: Model<TestModel>, _: TypedEnvelope<proto::JoinProject>, _, mut cx| {
match model.update(&mut cx, |model, _| model.id).unwrap() {
1 => done_tx1.try_send(()).unwrap(),
2 => done_tx2.try_send(()).unwrap(),
@@ -1982,7 +1974,7 @@ mod tests {
let (done_tx2, mut done_rx2) = smol::channel::unbounded();
let subscription1 = client.add_message_handler(
model.downgrade(),
move |_, _: TypedEnvelope<proto::Ping>, _| {
move |_, _: TypedEnvelope<proto::Ping>, _, _| {
done_tx1.try_send(()).unwrap();
async { Ok(()) }
},
@@ -1990,7 +1982,7 @@ mod tests {
drop(subscription1);
let _subscription2 = client.add_message_handler(
model.downgrade(),
move |_, _: TypedEnvelope<proto::Ping>, _| {
move |_, _: TypedEnvelope<proto::Ping>, _, _| {
done_tx2.try_send(()).unwrap();
async { Ok(()) }
},
@@ -2016,7 +2008,7 @@ mod tests {
let (done_tx, mut done_rx) = smol::channel::unbounded();
let subscription = client.add_message_handler(
model.clone().downgrade(),
move |model: Model<TestModel>, _: TypedEnvelope<proto::Ping>, mut cx| {
move |model: Model<TestModel>, _: TypedEnvelope<proto::Ping>, _, mut cx| {
model
.update(&mut cx, |model, _| model.subscription.take())
.unwrap();

View File

@@ -611,6 +611,7 @@ impl Telemetry {
let request_body = EventRequestBody {
installation_id: state.installation_id.as_deref().map(Into::into),
metrics_id: state.metrics_id.as_deref().map(Into::into),
session_id: state.session_id.clone(),
is_staff: state.is_staff,
app_version: state.app_version.clone(),

View File

@@ -242,6 +242,7 @@ impl UserStore {
async fn handle_update_invite_info(
this: Model<Self>,
message: TypedEnvelope<proto::UpdateInviteInfo>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -257,6 +258,7 @@ impl UserStore {
async fn handle_show_contacts(
this: Model<Self>,
_: TypedEnvelope<proto::ShowContacts>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |_, cx| cx.emit(Event::ShowContacts))?;
@@ -270,6 +272,7 @@ impl UserStore {
async fn handle_update_contacts(
this: Model<Self>,
message: TypedEnvelope<proto::UpdateContacts>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, _| {

View File

@@ -122,11 +122,6 @@ spec:
secretKeyRef:
name: anthropic
key: api_key
- name: GOOGLE_AI_API_KEY
valueFrom:
secretKeyRef:
name: google-ai
key: api_key
- name: BLOB_STORE_ACCESS_KEY
valueFrom:
secretKeyRef:

View File

@@ -664,6 +664,7 @@ where
#[derive(Serialize, Debug, clickhouse::Row)]
pub struct EditorEventRow {
installation_id: String,
metrics_id: String,
operation: String,
app_version: String,
file_extension: String,
@@ -713,6 +714,7 @@ impl EditorEventRow {
os_version: body.os_version.clone().unwrap_or_default(),
architecture: body.architecture.clone(),
installation_id: body.installation_id.clone().unwrap_or_default(),
metrics_id: body.metrics_id.clone().unwrap_or_default(),
session_id: body.session_id.clone(),
is_staff: body.is_staff,
time: time.timestamp_millis(),

View File

@@ -2583,13 +2583,14 @@ async fn rejoin_dev_server_projects(
)
.await?
};
notify_rejoined_projects(&mut rejoined_projects, &session)?;
response.send(proto::RejoinRemoteProjectsResponse {
rejoined_projects: rejoined_projects
.iter()
.into_iter()
.map(|project| project.to_proto())
.collect(),
})?;
notify_rejoined_projects(&mut rejoined_projects, &session)
})
}
async fn reconnect_dev_server(
@@ -4502,7 +4503,6 @@ async fn complete_with_google_ai(
session.http_client.clone(),
google_ai::API_URL,
api_key.as_ref(),
&request.model.clone(),
crate::ai::language_model_request_to_google_ai(request)?,
)
.await

View File

@@ -73,7 +73,6 @@ impl ConnectionPool {
pub fn reset(&mut self) {
self.connections.clear();
self.connected_users.clear();
self.connected_dev_servers.clear();
self.channels.clear();
}

View File

@@ -504,29 +504,6 @@ async fn test_dev_server_reconnect(
.unwrap();
}
#[gpui::test]
async fn test_dev_server_restart(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
let (server, client1) = TestServer::start1(cx1).await;
let (_dev_server, remote_workspace) =
create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await;
let cx = VisualTestContext::from_window(remote_workspace.into(), cx1).as_mut();
server.reset().await;
cx.run_until_parked();
cx.simulate_keystrokes("cmd-p 1 enter");
remote_workspace
.update(cx, |ws, cx| {
ws.active_item_as::<Editor>(cx)
.unwrap()
.update(cx, |ed, cx| {
assert_eq!(ed.text(cx).to_string(), "remote\nremote\nremote");
})
})
.unwrap();
}
#[gpui::test]
async fn test_create_dev_server_project_path_validation(
cx1: &mut gpui::TestAppContext,

View File

@@ -1204,7 +1204,7 @@ async fn test_share_project(
buffer_a.read_with(cx_a, |buffer, _| {
buffer
.snapshot()
.selections_in_range(text::Anchor::MIN..text::Anchor::MAX, false)
.remote_selections_in_range(text::Anchor::MIN..text::Anchor::MAX)
.count()
== 1
});
@@ -1245,7 +1245,7 @@ async fn test_share_project(
buffer_a.read_with(cx_a, |buffer, _| {
buffer
.snapshot()
.selections_in_range(text::Anchor::MIN..text::Anchor::MAX, false)
.remote_selections_in_range(text::Anchor::MIN..text::Anchor::MAX)
.count()
== 0
});

View File

@@ -124,6 +124,5 @@ fn notification_window_options(
display_id: Some(screen.id()),
window_background: WindowBackgroundAppearance::default(),
app_id: Some(app_id.to_owned()),
window_min_size: Size::default(),
}
}

View File

@@ -124,6 +124,7 @@ impl Store {
async fn handle_dev_server_projects_update(
this: Model<Self>,
envelope: TypedEnvelope<proto::DevServerProjectsUpdate>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {

View File

@@ -137,7 +137,7 @@ impl ProjectDiagnosticsEditor {
this.summary = project.read(cx).diagnostic_summary(false, cx);
cx.emit(EditorEvent::TitleChanged);
if this.editor.focus_handle(cx).contains_focused(cx) || this.focus_handle.contains_focused(cx) {
if this.editor.read(cx).is_focused(cx) || this.focus_handle.is_focused(cx) {
log::debug!("diagnostics updated for server {language_server_id}, path {path:?}. recording change");
} else {
log::debug!("diagnostics updated for server {language_server_id}, path {path:?}. updating excerpts");

View File

@@ -268,7 +268,6 @@ gpui::actions!(
SelectAllMatches,
SelectDown,
SelectLargerSyntaxNode,
SelectEnclosingSymbol,
SelectLeft,
SelectLine,
SelectRight,

View File

@@ -169,7 +169,7 @@ impl DisplayMap {
let (wrap_snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(tab_snapshot.clone(), edits, cx));
let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits).snapshot;
let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits);
DisplaySnapshot {
buffer_snapshot: self.buffer.read(cx).snapshot(cx),
@@ -348,25 +348,6 @@ impl DisplayMap {
block_map.remove(ids);
}
pub fn row_for_block(
&mut self,
block_id: BlockId,
cx: &mut ModelContext<Self>,
) -> Option<DisplayRow> {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
let block_map = self.block_map.read(snapshot, edits);
let block_row = block_map.row_for_block(block_id)?;
Some(DisplayRow(block_row.0))
}
pub fn highlight_text(
&mut self,
type_id: TypeId,
@@ -1002,23 +983,8 @@ impl DisplaySnapshot {
break;
}
}
let mut row_before_line_breaks = end.unwrap_or(max_point);
while row_before_line_breaks.row > start.row
&& self
.buffer_snapshot
.is_line_blank(MultiBufferRow(row_before_line_breaks.row))
{
row_before_line_breaks.row -= 1;
}
row_before_line_breaks = Point::new(
row_before_line_breaks.row,
self.buffer_snapshot
.line_len(MultiBufferRow(row_before_line_breaks.row)),
);
Some((start..row_before_line_breaks, self.fold_placeholder.clone()))
let end = end.unwrap_or(max_point);
Some((start..end, self.fold_placeholder.clone()))
} else {
None
}

View File

@@ -37,11 +37,6 @@ pub struct BlockMap {
excerpt_footer_height: u8,
}
pub struct BlockMapReader<'a> {
blocks: &'a Vec<Arc<Block>>,
pub snapshot: BlockSnapshot,
}
pub struct BlockMapWriter<'a>(&'a mut BlockMap);
#[derive(Clone)]
@@ -251,15 +246,12 @@ impl BlockMap {
map
}
pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapReader {
pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockSnapshot {
self.sync(&wrap_snapshot, edits);
*self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
BlockMapReader {
blocks: &self.blocks,
snapshot: BlockSnapshot {
wrap_snapshot,
transforms: self.transforms.borrow().clone(),
},
BlockSnapshot {
wrap_snapshot,
transforms: self.transforms.borrow().clone(),
}
}
@@ -614,62 +606,6 @@ impl std::ops::DerefMut for BlockPoint {
}
}
impl<'a> Deref for BlockMapReader<'a> {
type Target = BlockSnapshot;
fn deref(&self) -> &Self::Target {
&self.snapshot
}
}
impl<'a> DerefMut for BlockMapReader<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.snapshot
}
}
impl<'a> BlockMapReader<'a> {
pub fn row_for_block(&self, block_id: BlockId) -> Option<BlockRow> {
let block = self.blocks.iter().find(|block| block.id == block_id)?;
let buffer_row = block
.position
.to_point(self.wrap_snapshot.buffer_snapshot())
.row;
let wrap_row = self
.wrap_snapshot
.make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
.row();
let start_wrap_row = WrapRow(
self.wrap_snapshot
.prev_row_boundary(WrapPoint::new(wrap_row, 0)),
);
let end_wrap_row = WrapRow(
self.wrap_snapshot
.next_row_boundary(WrapPoint::new(wrap_row, 0))
.unwrap_or(self.wrap_snapshot.max_point().row() + 1),
);
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
cursor.seek(&start_wrap_row, Bias::Left, &());
while let Some(transform) = cursor.item() {
if cursor.start().0 > end_wrap_row {
break;
}
if let Some(BlockType::Custom(id)) =
transform.block.as_ref().map(|block| block.block_type())
{
if id == block_id {
return Some(cursor.start().1);
}
}
cursor.next(&());
}
None
}
}
impl<'a> BlockMapWriter<'a> {
pub fn insert(
&mut self,
@@ -1848,15 +1784,6 @@ mod tests {
expected_block_positions
);
for (block_row, block) in expected_block_positions {
if let BlockType::Custom(block_id) = block.block_type() {
assert_eq!(
blocks_snapshot.row_for_block(block_id),
Some(BlockRow(block_row))
);
}
}
let mut expected_longest_rows = Vec::new();
let mut longest_line_len = -1_isize;
for (row, line) in expected_lines.iter().enumerate() {

View File

@@ -462,8 +462,11 @@ impl InlayMap {
if buffer_edits.is_empty() {
if snapshot.buffer.edit_count() != buffer_snapshot.edit_count()
|| snapshot.buffer.non_text_state_update_count()
!= buffer_snapshot.non_text_state_update_count()
|| snapshot.buffer.parse_count() != buffer_snapshot.parse_count()
|| snapshot.buffer.diagnostics_update_count()
!= buffer_snapshot.diagnostics_update_count()
|| snapshot.buffer.git_diff_update_count()
!= buffer_snapshot.git_diff_update_count()
|| snapshot.buffer.trailing_excerpt_update_count()
!= buffer_snapshot.trailing_excerpt_update_count()
{

View File

@@ -457,9 +457,6 @@ pub struct Editor {
pub display_map: Model<DisplayMap>,
pub selections: SelectionsCollection,
pub scroll_manager: ScrollManager,
/// When inline assist editors are linked, they all render cursors because
/// typing enters text into each of them, even the ones that aren't focused.
pub(crate) show_cursor_when_unfocused: bool,
columnar_selection_tail: Option<Anchor>,
add_selections_state: Option<AddSelectionsState>,
select_next_state: Option<SelectNextState>,
@@ -484,7 +481,6 @@ pub struct Editor {
show_line_numbers: Option<bool>,
show_git_diff_gutter: Option<bool>,
show_code_actions: Option<bool>,
show_runnables: Option<bool>,
show_wrap_guides: Option<bool>,
show_indent_guides: Option<bool>,
placeholder_text: Option<Arc<str>>,
@@ -536,7 +532,6 @@ pub struct Editor {
next_editor_action_id: EditorActionId,
editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
use_autoclose: bool,
use_auto_surround: bool,
auto_replace_emoji_shortcode: bool,
show_git_blame_gutter: bool,
show_git_blame_inline: bool,
@@ -567,7 +562,6 @@ pub struct EditorSnapshot {
show_line_numbers: Option<bool>,
show_git_diff_gutter: Option<bool>,
show_code_actions: Option<bool>,
show_runnables: Option<bool>,
render_git_blame_gutter: bool,
pub display_snapshot: DisplaySnapshot,
pub placeholder_text: Option<Arc<str>>,
@@ -1640,7 +1634,7 @@ impl Editor {
clone
}
pub fn new(
fn new(
mode: EditorMode,
buffer: Model<MultiBuffer>,
project: Option<Model<Project>>,
@@ -1757,7 +1751,6 @@ impl Editor {
let mut this = Self {
focus_handle,
show_cursor_when_unfocused: false,
last_focused_descendant: None,
buffer: buffer.clone(),
display_map: display_map.clone(),
@@ -1785,7 +1778,6 @@ impl Editor {
show_line_numbers: None,
show_git_diff_gutter: None,
show_code_actions: None,
show_runnables: None,
show_wrap_guides: None,
show_indent_guides,
placeholder_text: None,
@@ -1819,7 +1811,6 @@ impl Editor {
use_modal_editing: mode == EditorMode::Full,
read_only: false,
use_autoclose: true,
use_auto_surround: true,
auto_replace_emoji_shortcode: false,
leader_peer_id: None,
remote_id: None,
@@ -2035,7 +2026,6 @@ impl Editor {
show_line_numbers: self.show_line_numbers,
show_git_diff_gutter: self.show_git_diff_gutter,
show_code_actions: self.show_code_actions,
show_runnables: self.show_runnables,
render_git_blame_gutter: self.render_git_blame_gutter(cx),
display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
scroll_anchor: self.scroll_manager.anchor(),
@@ -2198,10 +2188,6 @@ impl Editor {
self.use_autoclose = autoclose;
}
pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
self.use_auto_surround = auto_surround;
}
pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
self.auto_replace_emoji_shortcode = auto_replace;
}
@@ -2228,7 +2214,7 @@ impl Editor {
// Copy selections to primary selection buffer
#[cfg(target_os = "linux")]
if local {
let selections = self.selections.all::<usize>(cx);
let selections = &self.selections.disjoint;
let buffer_handle = self.buffer.read(cx).read(cx);
let mut text = String::new();
@@ -2564,47 +2550,14 @@ impl Editor {
}
}
let point_to_delete: Option<usize> = {
let selected_points: Vec<Selection<Point>> =
self.selections.disjoint_in_range(start..end, cx);
if !add || click_count > 1 {
None
} else if selected_points.len() > 0 {
Some(selected_points[0].id)
} else {
let clicked_point_already_selected =
self.selections.disjoint.iter().find(|selection| {
selection.start.to_point(buffer) == start.to_point(buffer)
|| selection.end.to_point(buffer) == end.to_point(buffer)
});
if let Some(selection) = clicked_point_already_selected {
Some(selection.id)
} else {
None
}
}
};
let selections_count = self.selections.count();
self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
if let Some(point_to_delete) = point_to_delete {
s.delete(point_to_delete);
if selections_count == 1 {
s.set_pending_anchor_range(start..end, mode);
}
} else {
if !add {
s.clear_disjoint();
} else if click_count > 1 {
s.delete(newest_selection.id)
}
s.set_pending_anchor_range(start..end, mode);
if !add {
s.clear_disjoint();
} else if click_count > 1 {
s.delete(newest_selection.id)
}
s.set_pending_anchor_range(start..end, mode);
});
}
@@ -2896,6 +2849,9 @@ impl Editor {
let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
let end_offset = start_offset + end_difference;
let start_offset = start_offset + start_difference;
if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
continue;
}
let start = buffer_snapshot.anchor_after(start_offset);
let end = buffer_snapshot.anchor_after(end_offset);
linked_edits
@@ -2934,7 +2890,7 @@ impl Editor {
// `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
// and they are removing the character that triggered IME popup.
for (pair, enabled) in scope.brackets() {
if !pair.close && !pair.surround {
if !pair.close {
continue;
}
@@ -2952,10 +2908,9 @@ impl Editor {
}
if let Some(bracket_pair) = bracket_pair {
let snapshot_settings = snapshot.settings_at(selection.start, cx);
let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
let auto_surround =
self.use_auto_surround && snapshot_settings.use_auto_surround;
let autoclose = self.use_autoclose
&& snapshot.settings_at(selection.start, cx).use_autoclose;
if selection.is_empty() {
if is_bracket_pair_start {
let prefix_len = bracket_pair.start.len() - text.len();
@@ -2977,7 +2932,6 @@ impl Editor {
&bracket_pair.start[..prefix_len],
));
if autoclose
&& bracket_pair.close
&& following_text_allows_autoclose
&& preceding_text_matches_prefix
{
@@ -3029,8 +2983,7 @@ impl Editor {
}
// If an opening bracket is 1 character long and is typed while
// text is selected, then surround that text with the bracket pair.
else if auto_surround
&& bracket_pair.surround
else if autoclose
&& is_bracket_pair_start
&& bracket_pair.start.chars().count() == 1
{
@@ -8226,58 +8179,6 @@ impl Editor {
});
}
pub fn select_enclosing_symbol(
&mut self,
_: &SelectEnclosingSymbol,
cx: &mut ViewContext<Self>,
) {
let buffer = self.buffer.read(cx).snapshot(cx);
let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
fn update_selection(
selection: &Selection<usize>,
buffer_snap: &MultiBufferSnapshot,
) -> Option<Selection<usize>> {
let cursor = selection.head();
let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
for symbol in symbols.iter().rev() {
let start = symbol.range.start.to_offset(&buffer_snap);
let end = symbol.range.end.to_offset(&buffer_snap);
let new_range = start..end;
if start < selection.start || end > selection.end {
return Some(Selection {
id: selection.id,
start: new_range.start,
end: new_range.end,
goal: SelectionGoal::None,
reversed: selection.reversed,
});
}
}
None
}
let mut selected_larger_symbol = false;
let new_selections = old_selections
.iter()
.map(|selection| match update_selection(selection, &buffer) {
Some(new_selection) => {
if new_selection.range() != selection.range() {
selected_larger_symbol = true;
}
new_selection
}
None => selection.clone(),
})
.collect::<Vec<_>>();
if selected_larger_symbol {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select(new_selections);
});
}
}
pub fn select_larger_syntax_node(
&mut self,
_: &SelectLargerSyntaxNode,
@@ -8340,10 +8241,6 @@ impl Editor {
}
fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
if !EditorSettings::get_global(cx).gutter.runnables {
self.clear_tasks();
return Task::ready(());
}
let project = self.project.clone();
cx.spawn(|this, mut cx| async move {
let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
@@ -10063,15 +9960,6 @@ impl Editor {
}
}
pub fn row_for_block(
&self,
block_id: BlockId,
cx: &mut ViewContext<Self>,
) -> Option<DisplayRow> {
self.display_map
.update(cx, |map, cx| map.row_for_block(block_id, cx))
}
pub fn insert_creases(
&mut self,
creases: impl IntoIterator<Item = Crease>,
@@ -10270,11 +10158,6 @@ impl Editor {
cx.notify();
}
pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
self.show_runnables = Some(show_runnables);
cx.notify();
}
pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
self.show_wrap_guides = Some(show_wrap_guides);
cx.notify();
@@ -11015,11 +10898,6 @@ impl Editor {
&& self.focus_handle.is_focused(cx)
}
pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
self.show_cursor_when_unfocused = is_enabled;
cx.notify();
}
fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
cx.notify();
}
@@ -11139,7 +11017,6 @@ impl Editor {
}
fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
self.tasks_update_task = Some(self.refresh_runnables(cx));
self.refresh_inline_completion(true, cx);
self.refresh_inlay_hints(
InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
@@ -11840,7 +11717,7 @@ impl EditorSnapshot {
.map(|(_, collaborator)| (collaborator.replica_id, collaborator))
.collect::<HashMap<_, _>>();
self.buffer_snapshot
.selections_in_range(range, false)
.remote_selections_in_range(range)
.filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
let collaborator = collaborators_by_replica_id.get(&replica_id)?;
let participant_index = participant_indices.get(&collaborator.user_id).copied();
@@ -11895,7 +11772,7 @@ impl EditorSnapshot {
let gutter_settings = EditorSettings::get_global(cx).gutter;
let show_line_numbers = self
.show_line_numbers
.unwrap_or(gutter_settings.line_numbers);
.unwrap_or_else(|| gutter_settings.line_numbers);
let line_gutter_width = if show_line_numbers {
// Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
let min_width_for_number_on_gutter = em_width * 4.0;
@@ -11906,16 +11783,14 @@ impl EditorSnapshot {
let show_code_actions = self
.show_code_actions
.unwrap_or(gutter_settings.code_actions);
let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
.unwrap_or_else(|| gutter_settings.code_actions);
let git_blame_entries_width = self
.render_git_blame_gutter
.then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
left_padding += if show_code_actions || show_runnables {
left_padding += if show_code_actions {
em_width * 3.0
} else if show_git_gutter && show_line_numbers {
em_width * 2.0
@@ -12322,12 +12197,9 @@ impl ViewInputHandler for Editor {
// Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
let use_autoclose = this.use_autoclose;
let use_auto_surround = this.use_auto_surround;
this.set_use_autoclose(false);
this.set_use_auto_surround(false);
this.handle_input(text, cx);
this.set_use_autoclose(use_autoclose);
this.set_use_auto_surround(use_auto_surround);
if let Some(new_selected_range) = new_selected_range_utf16 {
let snapshot = this.buffer.read(cx).read(cx);

View File

@@ -15,7 +15,6 @@ pub struct EditorSettings {
pub toolbar: Toolbar,
pub scrollbar: Scrollbar,
pub gutter: Gutter,
pub scroll_beyond_last_line: ScrollBeyondLastLine,
pub vertical_scroll_margin: f32,
pub scroll_sensitivity: f32,
pub relative_line_numbers: bool,
@@ -85,7 +84,6 @@ pub struct Scrollbar {
pub struct Gutter {
pub line_numbers: bool,
pub code_actions: bool,
pub runnables: bool,
pub folds: bool,
}
@@ -117,22 +115,6 @@ pub enum MultiCursorModifier {
CmdOrCtrl,
}
/// Whether the editor will scroll beyond the last line.
///
/// Default: one_page
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ScrollBeyondLastLine {
/// The editor will not scroll beyond the last line.
Off,
/// The editor will scroll beyond the last line by one page.
OnePage,
/// The editor will scroll beyond the last line by the same number of lines as vertical_scroll_margin.
VerticalScrollMargin,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent {
/// Whether the cursor blinks in the editor.
@@ -175,10 +157,6 @@ pub struct EditorSettingsContent {
pub scrollbar: Option<ScrollbarContent>,
/// Gutter related settings
pub gutter: Option<GutterContent>,
/// Whether the editor will scroll beyond the last line.
///
/// Default: one_page
pub scroll_beyond_last_line: Option<ScrollBeyondLastLine>,
/// The number of lines to keep above/below the cursor when auto-scrolling.
///
/// Default: 3.
@@ -277,10 +255,6 @@ pub struct GutterContent {
///
/// Default: true
pub code_actions: Option<bool>,
/// Whether to show runnable buttons in the gutter.
///
/// Default: true
pub runnables: Option<bool>,
/// Whether to show fold buttons in the gutter.
///
/// Default: true

View File

@@ -436,57 +436,6 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
);
}
#[gpui::test]
fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let editor = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
build_editor(buffer, cx)
});
_ = editor.update(cx, |view, cx| {
view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, cx);
});
_ = editor.update(cx, |view, cx| {
view.end_selection(cx);
});
_ = editor.update(cx, |view, cx| {
view.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, cx);
});
_ = editor.update(cx, |view, cx| {
view.end_selection(cx);
});
assert_eq!(
editor
.update(cx, |view, cx| view.selections.display_ranges(cx))
.unwrap(),
[
DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
]
);
_ = editor.update(cx, |view, cx| {
view.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, cx);
});
_ = editor.update(cx, |view, cx| {
view.end_selection(cx);
});
assert_eq!(
editor
.update(cx, |view, cx| view.selections.display_ranges(cx))
.unwrap(),
[DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
);
}
#[gpui::test]
fn test_canceling_pending_selection(cx: &mut TestAppContext) {
init_test(cx, |_| {});
@@ -909,175 +858,6 @@ fn test_fold_action(cx: &mut TestAppContext) {
});
}
#[gpui::test]
fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let view = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple(
&"
class Foo:
# Hello!
def a():
print(1)
def b():
print(2)
def c():
print(3)
"
.unindent(),
cx,
);
build_editor(buffer.clone(), cx)
});
_ = view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| {
s.select_display_ranges([
DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(10), 0)
]);
});
view.fold(&Fold, cx);
assert_eq!(
view.display_text(cx),
"
class Foo:
# Hello!
def a():
print(1)
def b():⋯
def c():⋯
"
.unindent(),
);
view.fold(&Fold, cx);
assert_eq!(
view.display_text(cx),
"
class Foo:⋯
"
.unindent(),
);
view.unfold_lines(&UnfoldLines, cx);
assert_eq!(
view.display_text(cx),
"
class Foo:
# Hello!
def a():
print(1)
def b():⋯
def c():⋯
"
.unindent(),
);
view.unfold_lines(&UnfoldLines, cx);
assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
});
}
#[gpui::test]
fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let view = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple(
&"
class Foo:
# Hello!
def a():
print(1)
def b():
print(2)
def c():
print(3)
"
.unindent(),
cx,
);
build_editor(buffer.clone(), cx)
});
_ = view.update(cx, |view, cx| {
view.change_selections(None, cx, |s| {
s.select_display_ranges([
DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(11), 0)
]);
});
view.fold(&Fold, cx);
assert_eq!(
view.display_text(cx),
"
class Foo:
# Hello!
def a():
print(1)
def b():⋯
def c():⋯
"
.unindent(),
);
view.fold(&Fold, cx);
assert_eq!(
view.display_text(cx),
"
class Foo:⋯
"
.unindent(),
);
view.unfold_lines(&UnfoldLines, cx);
assert_eq!(
view.display_text(cx),
"
class Foo:
# Hello!
def a():
print(1)
def b():⋯
def c():⋯
"
.unindent(),
);
view.unfold_lines(&UnfoldLines, cx);
assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
});
}
#[gpui::test]
fn test_move_cursor(cx: &mut TestAppContext) {
init_test(cx, |_| {});
@@ -4855,14 +4635,12 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
start: "{".to_string(),
end: "}".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: false,
surround: false,
newline: true,
},
],
@@ -4906,7 +4684,7 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
}
#[gpui::test]
async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
@@ -4919,44 +4697,32 @@ async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "/*".to_string(),
end: " */".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "[".to_string(),
end: "]".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "\"".to_string(),
end: "\"".to_string(),
close: true,
surround: true,
newline: false,
},
BracketPair {
start: "<".to_string(),
end: ">".to_string(),
close: false,
surround: true,
newline: true,
},
],
..Default::default()
},
@@ -5084,16 +4850,6 @@ async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
cx.assert_editor_state("a\"ˇ\"");
cx.update_editor(|view, cx| view.handle_input("\"", cx));
cx.assert_editor_state("a\"\"ˇ");
// Don't autoclose pair if autoclose is disabled
cx.set_state("ˇ");
cx.update_editor(|view, cx| view.handle_input("<", cx));
cx.assert_editor_state("");
// Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
cx.set_state("«aˇ» b");
cx.update_editor(|view, cx| view.handle_input("<", cx));
cx.assert_editor_state("<«aˇ»> b");
}
#[gpui::test]
@@ -5112,21 +4868,18 @@ async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut gpui::TestA
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "[".to_string(),
end: "]".to_string(),
close: false,
surround: false,
newline: true,
},
],
@@ -5540,14 +5293,12 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "/* ".to_string(),
end: "*/".to_string(),
close: true,
surround: true,
..Default::default()
},
],
@@ -5696,7 +5447,6 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
}],
..Default::default()
@@ -5808,21 +5558,18 @@ async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut gpui::TestAppC
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "[".to_string(),
end: "]".to_string(),
close: false,
surround: true,
newline: true,
},
],
@@ -7790,14 +7537,12 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
},
BracketPair {
start: "/* ".to_string(),
end: " */".to_string(),
close: true,
surround: true,
newline: true,
},
],
@@ -8599,7 +8344,6 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
}],
disabled_scopes_by_bracket_ix: Vec::new(),
@@ -11859,7 +11603,6 @@ fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -
settings: IndentGuideSettings {
enabled: true,
line_width: 1,
active_line_width: 1,
..Default::default()
},
}

View File

@@ -1,4 +1,3 @@
use crate::editor_settings::ScrollBeyondLastLine;
use crate::{
blame_entry_tooltip::{blame_entry_relative_timestamp, BlameEntryTooltip},
display_map::{
@@ -276,7 +275,6 @@ impl EditorElement {
register_action(view, cx, Editor::toggle_comments);
register_action(view, cx, Editor::select_larger_syntax_node);
register_action(view, cx, Editor::select_smaller_syntax_node);
register_action(view, cx, Editor::select_enclosing_symbol);
register_action(view, cx, Editor::move_to_enclosing_bracket);
register_action(view, cx, Editor::undo_selection);
register_action(view, cx, Editor::redo_selection);
@@ -860,28 +858,6 @@ impl EditorElement {
}
selections.extend(remote_selections.into_values());
} else if !editor.is_focused(cx) && editor.show_cursor_when_unfocused {
let player = if editor.read_only(cx) {
cx.theme().players().read_only()
} else {
self.style.local_player
};
let layouts = snapshot
.buffer_snapshot
.selections_in_range(&(start_anchor..end_anchor), true)
.map(move |(_, line_mode, cursor_shape, selection)| {
SelectionLayout::new(
selection,
line_mode,
cursor_shape,
&snapshot.display_snapshot,
false,
false,
None,
)
})
.collect::<Vec<_>>();
selections.push((player, layouts));
}
(selections, active_rows, newest_selection_head)
}
@@ -1113,17 +1089,11 @@ impl EditorElement {
point(bounds.lower_right().x, bounds.lower_left().y),
);
let settings = EditorSettings::get_global(cx);
let scroll_beyond_last_line: f32 = match settings.scroll_beyond_last_line {
ScrollBeyondLastLine::OnePage => rows_per_page,
ScrollBeyondLastLine::Off => 1.0,
ScrollBeyondLastLine::VerticalScrollMargin => 1.0 + settings.vertical_scroll_margin,
};
let total_rows = snapshot.max_point().row().as_f32() + scroll_beyond_last_line;
let height = bounds.size.height;
let total_rows = snapshot.max_point().row().as_f32() + rows_per_page;
let px_per_row = height / total_rows;
let thumb_height = (rows_per_page * px_per_row).max(ScrollbarLayout::MIN_THUMB_HEIGHT);
let row_height = (height - thumb_height) / (total_rows - rows_per_page).max(0.0);
let row_height = (height - thumb_height) / snapshot.max_point().row().as_f32();
Some(ScrollbarLayout {
hitbox: cx.insert_hitbox(track_bounds, false),
@@ -2814,12 +2784,7 @@ impl EditorElement {
)),
};
let requested_line_width = if indent_guide.active {
settings.active_line_width
} else {
settings.line_width
}
.clamp(1, 10);
let requested_line_width = settings.line_width.clamp(1, 10);
let mut line_indicator_width = 0.;
if let Some(color) = line_color {
cx.paint_quad(fill(
@@ -3654,12 +3619,12 @@ impl EditorElement {
let forbid_vertical_scroll = editor.scroll_manager.forbid_vertical_scroll();
if forbid_vertical_scroll {
scroll_position.y = current_scroll_position.y;
if scroll_position == current_scroll_position {
return;
}
}
if scroll_position != current_scroll_position {
editor.scroll(scroll_position, axis, cx);
cx.stop_propagation();
}
editor.scroll(scroll_position, axis, cx);
cx.stop_propagation();
});
}
}
@@ -4644,29 +4609,13 @@ impl Element for EditorElement {
let content_origin =
text_hitbox.origin + point(gutter_dimensions.margin, Pixels::ZERO);
let height_in_lines = bounds.size.height / line_height;
let max_scroll_top = if matches!(snapshot.mode, EditorMode::AutoHeight { .. }) {
(snapshot.max_point().row().as_f32() - height_in_lines + 1.).max(0.)
} else {
let settings = EditorSettings::get_global(cx);
let max_row = snapshot.max_point().row().as_f32();
match settings.scroll_beyond_last_line {
ScrollBeyondLastLine::OnePage => max_row,
ScrollBeyondLastLine::Off => (max_row - height_in_lines + 1.0).max(0.0),
ScrollBeyondLastLine::VerticalScrollMargin => {
(max_row - height_in_lines + 1.0 + settings.vertical_scroll_margin)
.max(0.0)
}
}
};
let mut autoscroll_containing_element = false;
let mut autoscroll_horizontally = false;
self.editor.update(cx, |editor, cx| {
autoscroll_containing_element =
editor.autoscroll_requested() || editor.has_pending_selection();
autoscroll_horizontally =
editor.autoscroll_vertically(bounds, line_height, max_scroll_top, cx);
editor.autoscroll_vertically(bounds, line_height, cx);
snapshot = editor.snapshot(cx);
});
@@ -4674,6 +4623,7 @@ impl Element for EditorElement {
// The scroll position is a fractional point, the whole number of which represents
// the top of the window in terms of display rows.
let start_row = DisplayRow(scroll_position.y as u32);
let height_in_lines = bounds.size.height / line_height;
let max_row = snapshot.max_point().row();
let end_row = cmp::min(
(scroll_position.y + height_in_lines).ceil() as u32,
@@ -4857,7 +4807,7 @@ impl Element for EditorElement {
let scroll_max = point(
((scroll_width - text_hitbox.size.width) / em_width).max(0.0),
max_scroll_top,
max_row.as_f32(),
);
self.editor.update(cx, |editor, cx| {
@@ -4981,18 +4931,14 @@ impl Element for EditorElement {
}
}
let test_indicators = if gutter_settings.runnables {
self.layout_run_indicators(
line_height,
scroll_pixel_position,
&gutter_dimensions,
&gutter_hitbox,
&snapshot,
cx,
)
} else {
vec![]
};
let test_indicators = self.layout_run_indicators(
line_height,
scroll_pixel_position,
&gutter_dimensions,
&gutter_hitbox,
&snapshot,
cx,
);
if !cx.has_active_drag() {
self.layout_hover_popovers(

View File

@@ -55,14 +55,12 @@ mod tests {
start: "{".to_string(),
end: "}".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: false,
surround: false,
newline: true,
},
],

View File

@@ -165,16 +165,10 @@ pub fn indent_guides_in_range(
.indent_guides_in_range(start_anchor..end_anchor, ignore_disabled_for_language, cx)
.into_iter()
.filter(|indent_guide| {
let start =
MultiBufferRow(indent_guide.multibuffer_row_range.start.0.saturating_sub(1));
// Filter out indent guides that are inside a fold
let is_folded = snapshot.is_line_folded(start);
let line_indent = snapshot.line_indent_for_buffer_row(start);
let contained_in_fold =
line_indent.len(indent_guide.tab_size) <= indent_guide.indent_level();
!(is_folded && contained_in_fold)
!snapshot.is_line_folded(MultiBufferRow(
indent_guide.multibuffer_row_range.start.0.saturating_sub(1),
))
})
.collect()
}

View File

@@ -1201,22 +1201,20 @@ impl SearchableItem for Editor {
for (excerpt_id, search_buffer, search_range) in
buffer.excerpts_in_ranges(search_within_ranges)
{
if !search_range.is_empty() {
ranges.extend(
query
.search(&search_buffer, Some(search_range.clone()))
.await
.into_iter()
.map(|match_range| {
let start = search_buffer
.anchor_after(search_range.start + match_range.start);
let end = search_buffer
.anchor_before(search_range.start + match_range.end);
buffer.anchor_in_excerpt(excerpt_id, start).unwrap()
..buffer.anchor_in_excerpt(excerpt_id, end).unwrap()
}),
);
}
ranges.extend(
query
.search(&search_buffer, Some(search_range.clone()))
.await
.into_iter()
.map(|match_range| {
let start = search_buffer
.anchor_after(search_range.start + match_range.start);
let end = search_buffer
.anchor_before(search_range.start + match_range.end);
buffer.anchor_in_excerpt(excerpt_id, start).unwrap()
..buffer.anchor_in_excerpt(excerpt_id, end).unwrap()
}),
);
}
};

View File

@@ -1,7 +1,7 @@
use crate::{
Copy, CopyPermalinkToLine, Cut, DisplayPoint, Editor, EditorMode, FindAllReferences,
GoToDefinition, GoToImplementation, GoToTypeDefinition, Paste, Rename, RevealInFinder,
SelectMode, ToggleCodeActions,
Copy, Cut, DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition,
GoToImplementation, GoToTypeDefinition, Paste, Rename, RevealInFinder, SelectMode,
ToggleCodeActions,
};
use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
use workspace::OpenInTerminal;
@@ -91,8 +91,7 @@ pub fn deploy_context_menu(
.action("Paste", Box::new(Paste))
.separator()
.action("Reveal in Finder", Box::new(RevealInFinder))
.action("Open in Terminal", Box::new(OpenInTerminal))
.action("Copy Permalink", Box::new(CopyPermalinkToLine));
.action("Open in Terminal", Box::new(OpenInTerminal));
match focus {
Some(focus) => builder.context(focus),
None => builder,

View File

@@ -2,7 +2,6 @@ mod actions;
pub(crate) mod autoscroll;
pub(crate) mod scroll_amount;
use crate::editor_settings::ScrollBeyondLastLine;
use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint},
hover_popover::hide_hover,
@@ -200,20 +199,8 @@ impl ScrollManager {
0,
)
} else {
let scroll_top = scroll_position.y;
let scroll_top = match EditorSettings::get_global(cx).scroll_beyond_last_line {
ScrollBeyondLastLine::OnePage => scroll_top,
ScrollBeyondLastLine::Off => scroll_top
.min((map.max_buffer_row().as_f32()) - self.visible_line_count.unwrap() + 1.0),
ScrollBeyondLastLine::VerticalScrollMargin => scroll_top.min(
(map.max_buffer_row().as_f32()) - self.visible_line_count.unwrap()
+ 1.0
+ self.vertical_scroll_margin,
),
};
let scroll_top_buffer_point =
DisplayPoint::new(DisplayRow(scroll_top as u32), 0).to_point(&map);
DisplayPoint::new(DisplayRow(scroll_position.y as u32), 0).to_point(&map);
let top_anchor = map
.buffer_snapshot
.anchor_at(scroll_top_buffer_point, Bias::Right);
@@ -223,7 +210,7 @@ impl ScrollManager {
anchor: top_anchor,
offset: point(
scroll_position.x.max(0.),
scroll_top - top_anchor.to_display_point(&map).row().as_f32(),
scroll_position.y - top_anchor.to_display_point(&map).row().as_f32(),
),
},
scroll_top_buffer_point.row,

View File

@@ -69,7 +69,6 @@ impl Editor {
&mut self,
bounds: Bounds<Pixels>,
line_height: Pixels,
max_scroll_top: f32,
cx: &mut ViewContext<Editor>,
) -> bool {
let viewport_height = bounds.size.height;
@@ -85,6 +84,11 @@ impl Editor {
}
}
}
let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
(display_map.max_point().row().as_f32() - visible_lines + 1.).max(0.)
} else {
display_map.max_point().row().as_f32()
};
if scroll_position.y > max_scroll_top {
scroll_position.y = max_scroll_top;
}

View File

@@ -181,7 +181,6 @@ impl EditorLspTestContext {
start: "{".to_string(),
end: "}".to_string(),
close: true,
surround: true,
newline: true,
}],
disabled_scopes_by_bracket_ix: Default::default(),

View File

@@ -793,18 +793,17 @@ impl PickerDelegate for FileFinderDelegate {
cx.notify();
Task::ready(())
} else {
let query =
PathLikeWithPosition::parse_str(&raw_query, |normalized_query, path_like_str| {
Ok::<_, std::convert::Infallible>(FileSearchQuery {
raw_query: normalized_query.to_owned(),
file_query_end: if path_like_str == raw_query {
None
} else {
Some(path_like_str.len())
},
})
let query = PathLikeWithPosition::parse_str(raw_query, |path_like_str| {
Ok::<_, std::convert::Infallible>(FileSearchQuery {
raw_query: raw_query.to_owned(),
file_query_end: if path_like_str == raw_query {
None
} else {
Some(path_like_str.len())
},
})
.expect("infallible");
})
.expect("infallible");
if Path::new(query.path_like.path_query()).is_absolute() {
self.lookup_absolute_path(query, cx)

View File

@@ -1855,9 +1855,9 @@ fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
}
fn test_path_like(test_str: &str) -> PathLikeWithPosition<FileSearchQuery> {
PathLikeWithPosition::parse_str(test_str, |normalized_query, path_like_str| {
PathLikeWithPosition::parse_str(test_str, |path_like_str| {
Ok::<_, std::convert::Infallible>(FileSearchQuery {
raw_query: normalized_query.to_owned(),
raw_query: test_str.to_owned(),
file_query_end: if path_like_str == test_str {
None
} else {

View File

@@ -337,7 +337,7 @@ impl PickerDelegate for NewPathDelegate {
gpui::PromptLevel::Critical,
&format!("{} already exists. Do you want to replace it?", m.relative_path()),
Some(
"A file or folder with the same name already exists. Replacing it will overwrite its current contents.",
"A file or folder with the same name already eixsts. Replacing it will overwrite its current contents.",
),
&["Replace", "Cancel"],
);

View File

@@ -11,11 +11,10 @@ pub async fn stream_generate_content(
client: Arc<dyn HttpClient>,
api_url: &str,
api_key: &str,
model: &str,
request: GenerateContentRequest,
) -> Result<BoxStream<'static, Result<GenerateContentResponse>>> {
let uri = format!(
"{}/v1beta/models/{model}:streamGenerateContent?alt=sse&key={}",
"{}/v1beta/models/gemini-pro:streamGenerateContent?alt=sse&key={}",
api_url, api_key
);

View File

@@ -42,7 +42,7 @@ futures.workspace = true
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5a5c4d4" }
gpui_macros.workspace = true
http.workspace = true
image = "0.25.1"
image = "0.23"
itertools.workspace = true
lazy_static.workspace = true
linkme = "0.3"
@@ -81,9 +81,6 @@ collections = { workspace = true, features = ["test-support"] }
util = { workspace = true, features = ["test-support"] }
http = { workspace = true, features = ["test-support"] }
[build-dependencies]
embed-resource = "2.4"
[target.'cfg(target_os = "macos")'.build-dependencies]
bindgen = "0.65.1"
cbindgen = "0.26.0"
@@ -138,7 +135,6 @@ xim = { git = "https://github.com/npmania/xim-rs", rev = "27132caffc5b9bc9c432ca
"x11rb-xcb",
"x11rb-client",
] }
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5a5c4d4", features = ["source-fontconfig-dlopen"] }
x11-clipboard = "0.9.2"
[target.'cfg(windows)'.dependencies]
@@ -146,6 +142,9 @@ windows.workspace = true
windows-core = "0.57"
clipboard-win = "3.1.1"
[target.'cfg(windows)'.build-dependencies]
embed-resource = "2.4"
[[example]]
name = "hello_world"
path = "examples/hello_world.rs"

View File

@@ -3,25 +3,18 @@
//TODO: consider generating shader code for WGSL
//TODO: deprecate "runtime-shaders" and "macos-blade"
use std::env;
fn main() {
let target = env::var("CARGO_CFG_TARGET_OS");
#[cfg(target_os = "macos")]
macos::build();
match target.as_deref() {
Ok("macos") => {
#[cfg(target_os = "macos")]
macos::build();
}
Ok("windows") => {
let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml");
let rc_file = std::path::Path::new("resources/windows/gpui.rc");
println!("cargo:rerun-if-changed={}", manifest.display());
println!("cargo:rerun-if-changed={}", rc_file.display());
embed_resource::compile(rc_file, embed_resource::NONE);
}
_ => (),
};
#[cfg(target_os = "windows")]
{
let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml");
let rc_file = std::path::Path::new("resources/windows/gpui.rc");
println!("cargo:rerun-if-changed={}", manifest.display());
println!("cargo:rerun-if-changed={}", rc_file.display());
embed_resource::compile(rc_file, embed_resource::NONE);
}
}
#[cfg(target_os = "macos")]

View File

@@ -51,7 +51,6 @@ fn main() {
kind: WindowKind::PopUp,
is_movable: false,
app_id: None,
window_min_size: Size::default(),
}
};

View File

@@ -1,6 +1,6 @@
use crate::{size, DevicePixels, Result, SharedString, Size};
use image::RgbaImage;
use image::{Bgra, ImageBuffer};
use std::{
borrow::Cow,
fmt,
@@ -40,12 +40,12 @@ pub(crate) struct RenderImageParams {
pub struct ImageData {
/// The ID associated with this image
pub id: ImageId,
data: RgbaImage,
data: ImageBuffer<Bgra<u8>, Vec<u8>>,
}
impl ImageData {
/// Create a new image from the given data.
pub fn new(data: RgbaImage) -> Self {
pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Self {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
Self {

View File

@@ -2493,11 +2493,6 @@ impl ScrollHandle {
self.0.borrow().bounds
}
/// Set the bounds into which this child is painted
pub(super) fn set_bounds(&self, bounds: Bounds<Pixels>) {
self.0.borrow_mut().bounds = bounds;
}
/// Get the bounds for a specific child.
pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
self.0.borrow().child_bounds.get(ix).cloned()

View File

@@ -384,13 +384,7 @@ impl Asset for Image {
};
let data = if let Ok(format) = image::guess_format(&bytes) {
let mut data = image::load_from_memory_with_format(&bytes, format)?.into_rgba8();
// Convert from RGBA to BGRA.
for pixel in data.chunks_exact_mut(4) {
pixel.swap(0, 2);
}
let data = image::load_from_memory_with_format(&bytes, format)?.into_bgra8();
ImageData::new(data)
} else {
let pixmap =

View File

@@ -79,37 +79,31 @@ pub struct UniformListFrameState {
/// A handle for controlling the scroll position of a uniform list.
/// This should be stored in your view and passed to the uniform_list on each frame.
#[derive(Clone, Debug, Default)]
pub struct UniformListScrollHandle(pub Rc<RefCell<UniformListScrollState>>);
#[derive(Clone, Debug, Default)]
#[allow(missing_docs)]
pub struct UniformListScrollState {
pub base_handle: ScrollHandle,
pub deferred_scroll_to_item: Option<usize>,
pub last_item_height: Option<Pixels>,
#[derive(Clone, Default)]
pub struct UniformListScrollHandle {
base_handle: ScrollHandle,
deferred_scroll_to_item: Rc<RefCell<Option<usize>>>,
}
impl UniformListScrollHandle {
/// Create a new scroll handle to bind to a uniform list.
pub fn new() -> Self {
Self(Rc::new(RefCell::new(UniformListScrollState {
Self {
base_handle: ScrollHandle::new(),
deferred_scroll_to_item: None,
last_item_height: None,
})))
deferred_scroll_to_item: Rc::new(RefCell::new(None)),
}
}
/// Scroll the list to the given item index.
pub fn scroll_to_item(&mut self, ix: usize) {
self.0.borrow_mut().deferred_scroll_to_item = Some(ix);
self.deferred_scroll_to_item.replace(Some(ix));
}
/// Get the index of the topmost visible child.
pub fn logical_scroll_top_index(&self) -> usize {
let this = self.0.borrow();
this.deferred_scroll_to_item
.unwrap_or_else(|| this.base_handle.logical_scroll_top().0)
self.deferred_scroll_to_item
.borrow()
.unwrap_or_else(|| self.base_handle.logical_scroll_top().0)
}
}
@@ -201,11 +195,10 @@ impl Element for UniformList {
let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
let shared_scroll_to_item = self.scroll_handle.as_mut().and_then(|handle| {
let mut handle = handle.0.borrow_mut();
handle.last_item_height = Some(item_height);
handle.deferred_scroll_to_item.take()
});
let shared_scroll_to_item = self
.scroll_handle
.as_mut()
.and_then(|handle| handle.deferred_scroll_to_item.take());
self.interactivity.prepaint(
global_id,
@@ -221,10 +214,6 @@ impl Element for UniformList {
bounds.lower_right() - point(border.right + padding.right, border.bottom),
);
if let Some(handle) = self.scroll_handle.as_mut() {
handle.0.borrow_mut().base_handle.set_bounds(bounds);
}
if self.item_count > 0 {
let content_height =
item_height * self.item_count + padding.top + padding.bottom;
@@ -337,7 +326,7 @@ impl UniformList {
/// Track and render scroll state of this list with reference to the given scroll handle.
pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
self.interactivity.tracked_scroll_handle = Some(handle.0.borrow().base_handle.clone());
self.interactivity.tracked_scroll_handle = Some(handle.base_handle.clone());
self.scroll_handle = Some(handle);
self
}

View File

@@ -2287,15 +2287,6 @@ impl Pixels {
pub fn abs(&self) -> Self {
Self(self.0.abs())
}
/// Returns the f64 value of `Pixels`.
///
/// # Returns
///
/// A f64 value of the `Pixels`.
pub fn to_f64(self) -> f64 {
self.0 as f64
}
}
impl Mul<Pixels> for Pixels {

View File

@@ -567,9 +567,6 @@ pub struct WindowOptions {
/// Application identifier of the window. Can by used by desktop environments to group applications together.
pub app_id: Option<String>,
/// Window minimum size
pub window_min_size: Size<Pixels>,
}
/// The variables that can be configured when creating a new window
@@ -597,9 +594,6 @@ pub(crate) struct WindowParams {
pub display_id: Option<DisplayId>,
pub window_background: WindowBackgroundAppearance,
#[cfg_attr(target_os = "linux", allow(dead_code))]
pub window_min_size: Size<Pixels>,
}
/// Represents the status of how a window should be opened.
@@ -648,7 +642,6 @@ impl Default for WindowOptions {
display_id: None,
window_background: WindowBackgroundAppearance::default(),
app_id: None,
window_min_size: Size::default(),
}
}
}

View File

@@ -583,11 +583,19 @@ impl Keystroke {
let key_utf8 = state.key_get_utf8(keycode);
let key_sym = state.key_get_one_sym(keycode);
// The logic here tries to replicate the logic in `../mac/events.rs`
// "Consumed" modifiers are modifiers that have been used to translate a key, for example
// pressing "shift" and "1" on US layout produces the key `!` but "consumes" the shift.
// Notes:
// - macOS gets the key character directly ("."), xkb gives us the key name ("period")
// - macOS logic removes consumed shift modifier for symbols: "{", not "shift-{"
// - macOS logic keeps consumed shift modifiers for letters: "shift-a", not "a" or "A"
let mut handle_consumed_modifiers = true;
let key = match key_sym {
Keysym::Return => "enter".to_owned(),
Keysym::Prior => "pageup".to_owned(),
Keysym::Next => "pagedown".to_owned(),
Keysym::ISO_Left_Tab => "tab".to_owned(),
Keysym::comma => ",".to_owned(),
Keysym::period => ".".to_owned(),
@@ -625,22 +633,30 @@ impl Keystroke {
Keysym::equal => "=".to_owned(),
Keysym::plus => "+".to_owned(),
_ => xkb::keysym_get_name(key_sym).to_lowercase(),
};
if modifiers.shift {
// we only include the shift for upper-case letters by convention,
// so don't include for numbers and symbols, but do include for
// tab/enter, etc.
if key.chars().count() == 1 && key_utf8 == key {
modifiers.shift = false;
Keysym::ISO_Left_Tab => {
handle_consumed_modifiers = false;
"tab".to_owned()
}
}
_ => {
handle_consumed_modifiers = false;
xkb::keysym_get_name(key_sym).to_lowercase()
}
};
// Ignore control characters (and DEL) for the purposes of ime_key
let ime_key =
(key_utf32 >= 32 && key_utf32 != 127 && !key_utf8.is_empty()).then_some(key_utf8);
if handle_consumed_modifiers {
let mod_shift_index = state.get_keymap().mod_get_index(xkb::MOD_NAME_SHIFT);
let is_shift_consumed = state.mod_index_is_consumed(keycode, mod_shift_index);
if modifiers.shift && is_shift_consumed {
modifiers.shift = false;
}
}
Keystroke {
modifiers,
key,

View File

@@ -671,12 +671,12 @@ impl LinuxClient for WaylandClient {
return;
};
if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() {
state.clipboard.set_primary(item.text);
let serial = state.serial_tracker.get(SerialKind::KeyPress);
let serial = state.serial_tracker.get(SerialKind::KeyEnter);
let data_source = primary_selection_manager.create_source(&state.globals.qh, ());
data_source.offer(state.clipboard.self_mime());
data_source.offer(TEXT_MIME_TYPE.to_string());
primary_selection.set_selection(Some(&data_source), serial);
state.clipboard.set_primary(item.text);
}
}
@@ -689,12 +689,12 @@ impl LinuxClient for WaylandClient {
return;
};
if state.mouse_focused_window.is_some() || state.keyboard_focused_window.is_some() {
state.clipboard.set(item.text);
let serial = state.serial_tracker.get(SerialKind::KeyPress);
let serial = state.serial_tracker.get(SerialKind::KeyEnter);
let data_source = data_device_manager.create_data_source(&state.globals.qh, ());
data_source.offer(state.clipboard.self_mime());
data_source.offer(TEXT_MIME_TYPE.to_string());
data_device.set_selection(Some(&data_source), serial);
state.clipboard.set(item.text);
}
}

View File

@@ -1,5 +1,4 @@
use std::cell::RefCell;
use std::collections::HashSet;
use std::ffi::OsString;
use std::ops::Deref;
use std::rc::{Rc, Weak};
@@ -299,24 +298,7 @@ impl X11Client {
{
let xcb_connection = xcb_connection.clone();
move |_readiness, _, client| {
let mut events = Vec::new();
let mut windows_to_refresh = HashSet::new();
while let Some(event) = xcb_connection.poll_for_event()? {
if let Event::Expose(event) = event {
windows_to_refresh.insert(event.window);
} else {
events.push(event);
}
}
for window in windows_to_refresh.into_iter() {
if let Some(window) = client.get_window(window) {
window.refresh();
}
}
for event in events.into_iter() {
let mut state = client.0.borrow_mut();
if state.ximc.is_none() || state.xim_handler.is_none() {
drop(state);

View File

@@ -270,11 +270,6 @@ impl X11WindowState {
);
let mut bounds = params.bounds.to_device_pixels(scale_factor);
if bounds.size.width.0 == 0 || bounds.size.height.0 == 0 {
log::warn!("Window bounds contain a zero value. height={}, width={}. Falling back to defaults.", bounds.size.height.0, bounds.size.width.0);
bounds.size.width = 800.into();
bounds.size.height = 600.into();
}
xcb_connection
.create_window(
@@ -291,10 +286,7 @@ impl X11WindowState {
&win_aux,
)
.unwrap()
.check().with_context(|| {
format!("CreateWindow request to X server failed. depth: {}, x_window: {}, visual_set.root: {}, bounds.origin.x.0: {}, bounds.origin.y.0: {}, bounds.size.width.0: {}, bounds.size.height.0: {}",
visual.depth, x_window, visual_set.root, bounds.origin.x.0 + 2, bounds.origin.y.0, bounds.size.width.0, bounds.size.height.0)
})?;
.check()?;
let reply = xcb_connection
.get_geometry(x_window)

View File

@@ -310,7 +310,7 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
}
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Debug)]
#[derive(Clone)]
enum ImeInput {
InsertText(String, Option<Range<usize>>),
SetMarkedText(String, Option<Range<usize>>, Option<Range<usize>>),
@@ -344,7 +344,6 @@ struct MacWindowState {
// Whether the next left-mouse click is also the focusing click.
first_mouse: bool,
fullscreen_restore_bounds: Bounds<Pixels>,
ime_composing: bool,
}
impl MacWindowState {
@@ -505,7 +504,6 @@ impl MacWindow {
focus,
show,
display_id,
window_min_size,
}: WindowParams,
executor: ForegroundExecutor,
renderer_context: renderer::Context,
@@ -625,7 +623,6 @@ impl MacWindow {
external_files_dragged: false,
first_mouse: false,
fullscreen_restore_bounds: Bounds::default(),
ime_composing: false,
})));
(*native_window).set_ivar(
@@ -647,11 +644,6 @@ impl MacWindow {
native_window.setMovable_(is_movable as BOOL);
native_window.setContentMinSize_(NSSize {
width: window_min_size.width.to_f64(),
height: window_min_size.height.to_f64(),
});
if titlebar.map_or(true, |titlebar| titlebar.appears_transparent) {
native_window.setTitlebarAppearsTransparent_(YES);
native_window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden);
@@ -1242,7 +1234,6 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
let mut lock = window_state.lock();
let previous_keydown_inserted_text = lock.previous_keydown_inserted_text.take();
let mut last_inserts = lock.last_ime_inputs.take().unwrap();
let ime_composing = std::mem::take(&mut lock.ime_composing);
let mut callback = lock.event_callback.take();
drop(lock);
@@ -1257,8 +1248,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
let is_composing =
with_input_handler(this, |input_handler| input_handler.marked_text_range())
.flatten()
.is_some()
|| ime_composing;
.is_some();
if let Some((text, range)) = last_insert {
if !is_composing {
@@ -1921,7 +1911,7 @@ fn send_to_input_handler(window: &Object, ime: ImeInput) {
let mut lock = window_state.lock();
if let Some(mut input_handler) = lock.input_handler.take() {
match ime {
match ime.clone() {
ImeInput::InsertText(text, range) => {
if let Some(ime_input) = lock.last_ime_inputs.as_mut() {
ime_input.push((text, range));
@@ -1932,7 +1922,6 @@ fn send_to_input_handler(window: &Object, ime: ImeInput) {
input_handler.replace_text_in_range(range, &text)
}
ImeInput::SetMarkedText(text, range, marked_range) => {
lock.ime_composing = true;
drop(lock);
input_handler.replace_and_mark_text_in_range(range, &text, marked_range)
}
@@ -1942,15 +1931,6 @@ fn send_to_input_handler(window: &Object, ime: ImeInput) {
}
}
window_state.lock().input_handler = Some(input_handler);
} else {
match ime {
ImeInput::InsertText(text, range) => {
if let Some(ime_input) = lock.last_ime_inputs.as_mut() {
ime_input.push((text, range));
}
}
_ => {}
}
}
}
}

View File

@@ -267,8 +267,14 @@ fn handle_syskeydown_msg(
) -> Option<isize> {
// we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
// shortcuts.
let keystroke = parse_syskeydown_msg_keystroke(wparam)?;
let mut func = state_ptr.state.borrow_mut().callbacks.input.take()?;
let Some(keystroke) = parse_syskeydown_msg_keystroke(wparam) else {
return None;
};
let mut lock = state_ptr.state.borrow_mut();
let Some(mut func) = lock.callbacks.input.take() else {
return None;
};
drop(lock);
let event = KeyDownEvent {
keystroke,
is_held: lparam.0 & (0x1 << 30) > 0,
@@ -286,8 +292,14 @@ fn handle_syskeydown_msg(
fn handle_syskeyup_msg(wparam: WPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
// we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
// shortcuts.
let keystroke = parse_syskeydown_msg_keystroke(wparam)?;
let mut func = state_ptr.state.borrow_mut().callbacks.input.take()?;
let Some(keystroke) = parse_syskeydown_msg_keystroke(wparam) else {
return None;
};
let mut lock = state_ptr.state.borrow_mut();
let Some(mut func) = lock.callbacks.input.take() else {
return None;
};
drop(lock);
let event = KeyUpEvent { keystroke };
let result = if func(PlatformInput::KeyUp(event)).default_prevented {
Some(0)
@@ -602,25 +614,35 @@ fn handle_ime_composition(
) -> Option<isize> {
let mut ime_input = None;
if lparam.0 as u32 & GCS_COMPSTR.0 > 0 {
let (comp_string, string_len) = parse_ime_compostion_string(handle)?;
let mut input_handler = state_ptr.state.borrow_mut().input_handler.take()?;
input_handler.replace_and_mark_text_in_range(
None,
&comp_string,
Some(string_len..string_len),
);
let Some((string, string_len)) = parse_ime_compostion_string(handle) else {
return None;
};
let mut lock = state_ptr.state.borrow_mut();
let Some(mut input_handler) = lock.input_handler.take() else {
return None;
};
drop(lock);
input_handler.replace_and_mark_text_in_range(None, string.as_str(), Some(0..string_len));
state_ptr.state.borrow_mut().input_handler = Some(input_handler);
ime_input = Some(comp_string);
ime_input = Some(string);
}
if lparam.0 as u32 & GCS_CURSORPOS.0 > 0 {
let comp_string = &ime_input?;
let Some(ref comp_string) = ime_input else {
return None;
};
let caret_pos = retrieve_composition_cursor_position(handle);
let mut input_handler = state_ptr.state.borrow_mut().input_handler.take()?;
input_handler.replace_and_mark_text_in_range(None, comp_string, Some(caret_pos..caret_pos));
let mut lock = state_ptr.state.borrow_mut();
let Some(mut input_handler) = lock.input_handler.take() else {
return None;
};
drop(lock);
input_handler.replace_and_mark_text_in_range(None, comp_string, Some(0..caret_pos));
state_ptr.state.borrow_mut().input_handler = Some(input_handler);
}
if lparam.0 as u32 & GCS_RESULTSTR.0 > 0 {
let comp_result = parse_ime_compostion_result(handle)?;
let Some(comp_result) = parse_ime_compostion_result(handle) else {
return None;
};
let mut lock = state_ptr.state.borrow_mut();
let Some(mut input_handler) = lock.input_handler.take() else {
return Some(1);
@@ -641,7 +663,11 @@ fn handle_calc_client_size(
lparam: LPARAM,
state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> {
if !state_ptr.hide_title_bar || state_ptr.state.borrow().is_fullscreen() || wparam.0 == 0 {
if !state_ptr.hide_title_bar || state_ptr.state.borrow().is_fullscreen() {
return None;
}
if wparam.0 == 0 {
return None;
}
@@ -1056,31 +1082,37 @@ fn parse_syskeydown_msg_keystroke(wparam: WPARAM) -> Option<Keystroke> {
}
let vk_code = wparam.loword();
let basic_key = basic_vkcode_to_string(vk_code, modifiers);
if basic_key.is_some() {
return basic_key;
}
let key = match VIRTUAL_KEY(vk_code) {
VK_BACK => "backspace",
VK_RETURN => "enter",
VK_TAB => "tab",
VK_UP => "up",
VK_DOWN => "down",
VK_RIGHT => "right",
VK_LEFT => "left",
VK_HOME => "home",
VK_END => "end",
VK_PRIOR => "pageup",
VK_NEXT => "pagedown",
VK_ESCAPE => "escape",
VK_INSERT => "insert",
VK_DELETE => "delete",
_ => return basic_vkcode_to_string(vk_code, modifiers),
}
.to_owned();
VK_BACK => Some("backspace"),
VK_RETURN => Some("enter"),
VK_TAB => Some("tab"),
VK_UP => Some("up"),
VK_DOWN => Some("down"),
VK_RIGHT => Some("right"),
VK_LEFT => Some("left"),
VK_HOME => Some("home"),
VK_END => Some("end"),
VK_PRIOR => Some("pageup"),
VK_NEXT => Some("pagedown"),
VK_ESCAPE => Some("escape"),
VK_INSERT => Some("insert"),
_ => None,
};
Some(Keystroke {
modifiers,
key,
ime_key: None,
})
if let Some(key) = key {
Some(Keystroke {
modifiers,
key: key.to_string(),
ime_key: None,
})
} else {
None
}
}
enum KeystrokeOrModifier {
@@ -1093,62 +1125,67 @@ fn parse_keydown_msg_keystroke(wparam: WPARAM) -> Option<KeystrokeOrModifier> {
let modifiers = current_modifiers();
let key = match VIRTUAL_KEY(vk_code) {
VK_BACK => "backspace",
VK_RETURN => "enter",
VK_TAB => "tab",
VK_UP => "up",
VK_DOWN => "down",
VK_RIGHT => "right",
VK_LEFT => "left",
VK_HOME => "home",
VK_END => "end",
VK_PRIOR => "pageup",
VK_NEXT => "pagedown",
VK_ESCAPE => "escape",
VK_INSERT => "insert",
VK_DELETE => "delete",
_ => {
if is_modifier(VIRTUAL_KEY(vk_code)) {
return Some(KeystrokeOrModifier::Modifier(modifiers));
}
if is_modifier(VIRTUAL_KEY(vk_code)) {
return Some(KeystrokeOrModifier::Modifier(modifiers));
}
if modifiers.control || modifiers.alt {
let basic_key = basic_vkcode_to_string(vk_code, modifiers);
if let Some(basic_key) = basic_key {
return Some(KeystrokeOrModifier::Keystroke(basic_key));
}
}
if vk_code >= VK_F1.0 && vk_code <= VK_F24.0 {
let offset = vk_code - VK_F1.0;
return Some(KeystrokeOrModifier::Keystroke(Keystroke {
modifiers,
key: format!("f{}", offset + 1),
ime_key: None,
}));
};
return None;
if modifiers.control || modifiers.alt {
let basic_key = basic_vkcode_to_string(vk_code, modifiers);
if let Some(basic_key) = basic_key {
return Some(KeystrokeOrModifier::Keystroke(basic_key));
}
}
.to_owned();
Some(KeystrokeOrModifier::Keystroke(Keystroke {
modifiers,
key,
ime_key: None,
}))
if vk_code >= VK_F1.0 && vk_code <= VK_F24.0 {
let offset = vk_code - VK_F1.0;
return Some(KeystrokeOrModifier::Keystroke(Keystroke {
modifiers,
key: format!("f{}", offset + 1),
ime_key: None,
}));
}
let key = match VIRTUAL_KEY(vk_code) {
VK_BACK => Some("backspace"),
VK_RETURN => Some("enter"),
VK_TAB => Some("tab"),
VK_UP => Some("up"),
VK_DOWN => Some("down"),
VK_RIGHT => Some("right"),
VK_LEFT => Some("left"),
VK_HOME => Some("home"),
VK_END => Some("end"),
VK_PRIOR => Some("pageup"),
VK_NEXT => Some("pagedown"),
VK_ESCAPE => Some("escape"),
VK_INSERT => Some("insert"),
VK_DELETE => Some("delete"),
_ => None,
};
if let Some(key) = key {
Some(KeystrokeOrModifier::Keystroke(Keystroke {
modifiers,
key: key.to_string(),
ime_key: None,
}))
} else {
None
}
}
fn parse_char_msg_keystroke(wparam: WPARAM) -> Option<Keystroke> {
let first_char = char::from_u32((wparam.0 as u16).into())?;
let src = [wparam.0 as u16];
let Ok(first_char) = char::decode_utf16(src).collect::<Vec<_>>()[0] else {
return None;
};
if first_char.is_control() {
None
} else {
let mut modifiers = current_modifiers();
// for characters that use 'shift' to type it is expected that the
// shift is not reported if the uppercase/lowercase are the same and instead only the key is reported
if first_char.to_ascii_uppercase() == first_char.to_ascii_lowercase() {
if first_char.to_lowercase().to_string() == first_char.to_uppercase().to_string() {
modifiers.shift = false;
}
let key = match first_char {
@@ -1225,25 +1262,56 @@ fn parse_ime_compostion_result(handle: HWND) -> Option<String> {
}
fn basic_vkcode_to_string(code: u16, modifiers: Modifiers) -> Option<Keystroke> {
let mapped_code = unsafe { MapVirtualKeyW(code as u32, MAPVK_VK_TO_CHAR) };
match code {
// VK_0 - VK_9
48..=57 => Some(Keystroke {
modifiers,
key: format!("{}", code - VK_0.0),
ime_key: None,
}),
// VK_A - VK_Z
65..=90 => Some(Keystroke {
modifiers,
key: format!("{}", (b'a' + code as u8 - VK_A.0 as u8) as char),
ime_key: None,
}),
// VK_F1 - VK_F24
112..=135 => Some(Keystroke {
modifiers,
key: format!("f{}", code - VK_F1.0 + 1),
ime_key: None,
}),
// OEM3: `/~, OEM_MINUS: -/_, OEM_PLUS: =/+, ...
_ => {
if let Some(key) = oemkey_vkcode_to_string(code) {
Some(Keystroke {
modifiers,
key,
ime_key: None,
})
} else {
None
}
}
}
}
let key = match mapped_code {
0 => None,
raw_code => char::from_u32(raw_code),
}?
.to_ascii_lowercase();
let key = if matches!(code as u32, 112..=135) {
format!("f{key}")
} else {
key.to_string()
};
Some(Keystroke {
modifiers,
key,
ime_key: None,
})
fn oemkey_vkcode_to_string(code: u16) -> Option<String> {
match code {
186 => Some(";".to_string()), // VK_OEM_1
187 => Some("=".to_string()), // VK_OEM_PLUS
188 => Some(",".to_string()), // VK_OEM_COMMA
189 => Some("-".to_string()), // VK_OEM_MINUS
190 => Some(".".to_string()), // VK_OEM_PERIOD
// https://kbdlayout.info/features/virtualkeys/VK_ABNT_C1
191 | 193 => Some("/".to_string()), // VK_OEM_2 VK_ABNT_C1
192 => Some("`".to_string()), // VK_OEM_3
219 => Some("[".to_string()), // VK_OEM_4
220 => Some("\\".to_string()), // VK_OEM_5
221 => Some("]".to_string()), // VK_OEM_6
222 => Some("'".to_string()), // VK_OEM_7
_ => None,
}
}
#[inline]

View File

@@ -93,16 +93,6 @@ struct WindowFocusEvent {
current_focus_path: SmallVec<[FocusId; 8]>,
}
impl WindowFocusEvent {
pub fn is_focus_in(&self, focus_id: FocusId) -> bool {
!self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id)
}
pub fn is_focus_out(&self, focus_id: FocusId) -> bool {
self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id)
}
}
/// This is provided when subscribing for `ViewContext::on_focus_out` events.
pub struct FocusOutEvent {
/// A weak focus handle representing what was blurred.
@@ -631,7 +621,6 @@ impl Window {
display_id,
window_background,
app_id,
window_min_size,
} = options;
let bounds = window_bounds
@@ -648,7 +637,6 @@ impl Window {
show,
display_id,
window_background,
window_min_size,
},
)?;
let display_id = platform_window.display().map(|display| display.id());
@@ -2895,53 +2883,6 @@ impl<'a> WindowContext<'a> {
));
}
/// Register a listener to be called when the given focus handle or one of its descendants receives focus.
/// This does not fire if the given focus handle - or one of its descendants - was previously focused.
/// Returns a subscription and persists until the subscription is dropped.
pub fn on_focus_in(
&mut self,
handle: &FocusHandle,
mut listener: impl FnMut(&mut WindowContext) + 'static,
) -> Subscription {
let focus_id = handle.id;
let (subscription, activate) =
self.window.new_focus_listener(Box::new(move |event, cx| {
if event.is_focus_in(focus_id) {
listener(cx);
}
true
}));
self.app.defer(move |_| activate());
subscription
}
/// Register a listener to be called when the given focus handle or one of its descendants loses focus.
/// Returns a subscription and persists until the subscription is dropped.
pub fn on_focus_out(
&mut self,
handle: &FocusHandle,
mut listener: impl FnMut(FocusOutEvent, &mut WindowContext) + 'static,
) -> Subscription {
let focus_id = handle.id;
let (subscription, activate) =
self.window.new_focus_listener(Box::new(move |event, cx| {
if let Some(blurred_id) = event.previous_focus_path.last().copied() {
if event.is_focus_out(focus_id) {
let event = FocusOutEvent {
blurred: WeakFocusHandle {
id: blurred_id,
handles: Arc::downgrade(&cx.window.focus_handles),
},
};
listener(event, cx)
}
}
true
}));
self.app.defer(move |_| activate());
subscription
}
fn reset_cursor_style(&self) {
// Set the cursor only if we're the active window.
if self.is_window_active() {
@@ -4168,7 +4109,9 @@ impl<'a, V: 'static> ViewContext<'a, V> {
let (subscription, activate) =
self.window.new_focus_listener(Box::new(move |event, cx| {
view.update(cx, |view, cx| {
if event.is_focus_in(focus_id) {
if !event.previous_focus_path.contains(&focus_id)
&& event.current_focus_path.contains(&focus_id)
{
listener(view, cx)
}
})
@@ -4232,7 +4175,9 @@ impl<'a, V: 'static> ViewContext<'a, V> {
self.window.new_focus_listener(Box::new(move |event, cx| {
view.update(cx, |view, cx| {
if let Some(blurred_id) = event.previous_focus_path.last().copied() {
if event.is_focus_out(focus_id) {
if event.previous_focus_path.contains(&focus_id)
&& !event.current_focus_path.contains(&focus_id)
{
let event = FocusOutEvent {
blurred: WeakFocusHandle {
id: blurred_id,

View File

@@ -119,6 +119,7 @@ impl DevServer {
async fn handle_dev_server_instructions(
this: Model<Self>,
envelope: TypedEnvelope<proto::DevServerInstructions>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let (added_projects, removed_projects_ids) = this.read_with(&mut cx, |this, _| {
@@ -161,6 +162,7 @@ impl DevServer {
async fn handle_validate_dev_server_project_request(
this: Model<Self>,
envelope: TypedEnvelope<proto::ValidateDevServerProjectRequest>,
_: Arc<Client>,
cx: AsyncAppContext,
) -> Result<proto::Ack> {
let expanded = shellexpand::tilde(&envelope.payload.path).to_string();
@@ -178,6 +180,7 @@ impl DevServer {
async fn handle_shutdown(
this: Model<Self>,
_envelope: TypedEnvelope<proto::ShutdownDevServer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {

View File

@@ -103,10 +103,14 @@ pub struct Buffer {
sync_parse_timeout: Duration,
syntax_map: Mutex<SyntaxMap>,
parsing_in_background: bool,
non_text_state_update_count: usize,
parse_count: usize,
diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>,
remote_selections: TreeMap<ReplicaId, SelectionSet>,
selections_update_count: usize,
diagnostics_update_count: usize,
diagnostics_timestamp: clock::Lamport,
file_update_count: usize,
git_diff_update_count: usize,
completion_triggers: Vec<String>,
completion_triggers_timestamp: clock::Lamport,
deferred_ops: OperationQueue<Operation>,
@@ -123,9 +127,13 @@ pub struct BufferSnapshot {
pub(crate) syntax: SyntaxSnapshot,
file: Option<Arc<dyn File>>,
diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>,
diagnostics_update_count: usize,
file_update_count: usize,
git_diff_update_count: usize,
remote_selections: TreeMap<ReplicaId, SelectionSet>,
selections_update_count: usize,
language: Option<Arc<Language>>,
non_text_state_update_count: usize,
parse_count: usize,
}
/// The kind and amount of indentation in a particular line. For now,
@@ -703,14 +711,18 @@ impl Buffer {
capability,
syntax_map: Mutex::new(SyntaxMap::new()),
parsing_in_background: false,
non_text_state_update_count: 0,
parse_count: 0,
sync_parse_timeout: Duration::from_millis(1),
autoindent_requests: Default::default(),
pending_autoindent: Default::default(),
language: None,
remote_selections: Default::default(),
selections_update_count: 0,
diagnostics: Default::default(),
diagnostics_update_count: 0,
diagnostics_timestamp: Default::default(),
file_update_count: 0,
git_diff_update_count: 0,
completion_triggers: Default::default(),
completion_triggers_timestamp: Default::default(),
deferred_ops: OperationQueue::new(),
@@ -733,8 +745,12 @@ impl Buffer {
file: self.file.clone(),
remote_selections: self.remote_selections.clone(),
diagnostics: self.diagnostics.clone(),
diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
git_diff_update_count: self.git_diff_update_count,
language: self.language.clone(),
non_text_state_update_count: self.non_text_state_update_count,
parse_count: self.parse_count,
selections_update_count: self.selections_update_count,
}
}
@@ -766,7 +782,7 @@ impl Buffer {
/// Assign a language to the buffer.
pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut ModelContext<Self>) {
self.non_text_state_update_count += 1;
self.parse_count += 1;
self.syntax_map.lock().clear();
self.language = language;
self.reparse(cx);
@@ -899,7 +915,7 @@ impl Buffer {
self.file = Some(new_file);
if file_changed {
self.non_text_state_update_count += 1;
self.file_update_count += 1;
cx.emit(Event::FileHandleChanged);
cx.notify();
}
@@ -953,7 +969,7 @@ impl Buffer {
let buffer_diff = diff.await;
this.update(&mut cx, |this, cx| {
this.git_diff = buffer_diff;
this.non_text_state_update_count += 1;
this.git_diff_update_count += 1;
cx.emit(Event::DiffUpdated);
})
.ok();
@@ -976,10 +992,29 @@ impl Buffer {
.or_else(|| self.language.clone())
}
/// An integer version number that accounts for all updates besides
/// the buffer's text itself (which is versioned via a version vector).
pub fn non_text_state_update_count(&self) -> usize {
self.non_text_state_update_count
/// The number of times the buffer was parsed.
pub fn parse_count(&self) -> usize {
self.parse_count
}
/// The number of times selections were updated.
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
}
/// The number of times diagnostics were updated.
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
/// The number of times the underlying file was updated.
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
/// The number of times the git diff status was updated.
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
}
/// Whether the buffer is being parsed in the background.
@@ -1089,7 +1124,7 @@ impl Buffer {
}
fn did_finish_parsing(&mut self, syntax_snapshot: SyntaxSnapshot, cx: &mut ModelContext<Self>) {
self.non_text_state_update_count += 1;
self.parse_count += 1;
self.syntax_map.lock().did_parse(syntax_snapshot);
self.request_autoindent(cx);
cx.emit(Event::Reparsed);
@@ -1666,8 +1701,6 @@ impl Buffer {
},
cx,
);
self.non_text_state_update_count += 1;
cx.notify();
}
/// Clears the selections, so that other replicas of the buffer do not see any selections for
@@ -1938,7 +1971,7 @@ impl Buffer {
},
);
self.text.lamport_clock.observe(lamport_timestamp);
self.non_text_state_update_count += 1;
self.selections_update_count += 1;
}
Operation::UpdateCompletionTriggers {
triggers,
@@ -1970,7 +2003,7 @@ impl Buffer {
};
}
self.diagnostics_timestamp = lamport_timestamp;
self.non_text_state_update_count += 1;
self.diagnostics_update_count += 1;
self.text.lamport_clock.observe(lamport_timestamp);
cx.notify();
cx.emit(Event::DiagnosticsUpdated);
@@ -3322,10 +3355,9 @@ impl BufferSnapshot {
/// Returns selections for remote peers intersecting the given range.
#[allow(clippy::type_complexity)]
pub fn selections_in_range(
pub fn remote_selections_in_range(
&self,
range: Range<Anchor>,
include_local: bool,
) -> impl Iterator<
Item = (
ReplicaId,
@@ -3336,9 +3368,8 @@ impl BufferSnapshot {
> + '_ {
self.remote_selections
.iter()
.filter(move |(replica_id, set)| {
(include_local || **replica_id != self.text.replica_id())
&& !set.selections.is_empty()
.filter(|(replica_id, set)| {
**replica_id != self.text.replica_id() && !set.selections.is_empty()
})
.map(move |(replica_id, set)| {
let start_ix = match set.selections.binary_search_by(|probe| {
@@ -3488,10 +3519,19 @@ impl BufferSnapshot {
.flat_map(move |(_, set)| set.group(group_id, self))
}
/// An integer version number that accounts for all updates besides
/// the buffer's text itself (which is versioned via a version vector).
pub fn non_text_state_update_count(&self) -> usize {
self.non_text_state_update_count
/// The number of times diagnostics were updated.
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
/// The number of times the buffer was parsed.
pub fn parse_count(&self) -> usize {
self.parse_count
}
/// The number of times selections were updated.
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
}
/// Returns a snapshot of underlying file.
@@ -3511,6 +3551,16 @@ impl BufferSnapshot {
None
}
}
/// The number of times the underlying file was updated.
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
/// The number of times the git diff status was updated.
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
}
}
fn indent_size_for_line(text: &text::BufferSnapshot, row: u32) -> IndentSize {
@@ -3542,8 +3592,12 @@ impl Clone for BufferSnapshot {
file: self.file.clone(),
remote_selections: self.remote_selections.clone(),
diagnostics: self.diagnostics.clone(),
selections_update_count: self.selections_update_count,
diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
git_diff_update_count: self.git_diff_update_count,
language: self.language.clone(),
non_text_state_update_count: self.non_text_state_update_count,
parse_count: self.parse_count,
}
}
}

View File

@@ -1782,14 +1782,12 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
start: "{".into(),
end: "}".into(),
close: true,
surround: true,
newline: false,
},
BracketPair {
start: "'".into(),
end: "'".into(),
close: true,
surround: true,
newline: false,
},
],
@@ -1912,14 +1910,12 @@ fn test_language_scope_at_with_rust(cx: &mut AppContext) {
start: "{".into(),
end: "}".into(),
close: true,
surround: true,
newline: false,
},
BracketPair {
start: "'".into(),
end: "'".into(),
close: true,
surround: true,
newline: false,
},
],
@@ -2416,7 +2412,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
for buffer in &buffers {
let buffer = buffer.read(cx).snapshot();
let actual_remote_selections = buffer
.selections_in_range(Anchor::MIN..Anchor::MAX, false)
.remote_selections_in_range(Anchor::MIN..Anchor::MAX)
.map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
.collect::<Vec<_>>();
let expected_remote_selections = active_selections

View File

@@ -61,7 +61,6 @@ use task::RunnableTag;
pub use task_context::{ContextProvider, RunnableRange};
use theme::SyntaxTheme;
use tree_sitter::{self, wasmtime, Query, QueryCursor, WasmStore};
use util::serde::default_true;
pub use buffer::Operation;
pub use buffer::*;
@@ -804,9 +803,6 @@ pub struct BracketPair {
pub end: String,
/// True if `end` should be automatically inserted right after `start` characters.
pub close: bool,
/// True if selected text should be surrounded by `start` and `end` characters.
#[serde(default = "default_true")]
pub surround: bool,
/// True if an extra newline should be inserted while the cursor is in the middle
/// of that bracket pair.
pub newline: bool,

View File

@@ -112,8 +112,6 @@ pub struct LanguageSettings {
pub inlay_hints: InlayHintSettings,
/// Whether to automatically close brackets.
pub use_autoclose: bool,
/// Whether to automatically surround text with brackets.
pub use_auto_surround: bool,
// Controls how the editor handles the autoclosed characters.
pub always_treat_brackets_as_autoclosed: bool,
/// Which code actions to run on save
@@ -317,11 +315,6 @@ pub struct LanguageSettingsContent {
///
/// Default: true
pub use_autoclose: Option<bool>,
/// Whether to automatically surround text with characters for you. For example,
/// when you select text and type (, Zed will automatically surround text with ().
///
/// Default: true
pub use_auto_surround: Option<bool>,
// Controls how the editor handles the autoclosed characters.
// When set to `false`(default), skipping over and auto-removing of the closing characters
// happen only for auto-inserted characters.
@@ -450,11 +443,6 @@ pub struct IndentGuideSettings {
/// Default: 1
#[serde(default = "line_width")]
pub line_width: u32,
/// The width of the active indent guide in pixels, between 1 and 10.
///
/// Default: 1
#[serde(default = "active_line_width")]
pub active_line_width: u32,
/// Determines how indent guides are colored.
///
/// Default: Fixed
@@ -471,10 +459,6 @@ fn line_width() -> u32 {
1
}
fn active_line_width() -> u32 {
line_width()
}
/// Determines how indent guides are colored.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
@@ -790,7 +774,6 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
merge(&mut settings.hard_tabs, src.hard_tabs);
merge(&mut settings.soft_wrap, src.soft_wrap);
merge(&mut settings.use_autoclose, src.use_autoclose);
merge(&mut settings.use_auto_surround, src.use_auto_surround);
merge(
&mut settings.always_treat_brackets_as_autoclosed,
src.always_treat_brackets_as_autoclosed,

View File

@@ -1,35 +1,27 @@
use anyhow::{anyhow, bail, Context, Result};
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use collections::HashMap;
use feature_flags::FeatureFlagAppExt;
use futures::StreamExt;
use gpui::{AppContext, AsyncAppContext};
use http::github::{latest_github_release, GitHubLspBinaryVersion};
use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime;
use project::ContextProviderWithTasks;
use serde_json::{json, Value};
use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
use smol::{
fs::{self},
io::BufReader,
};
use smol::fs;
use std::{
any::Any,
env::consts,
ffi::OsString,
path::{Path, PathBuf},
str::FromStr,
sync::{Arc, OnceLock},
};
use task::{TaskTemplate, TaskTemplates, VariableName};
use util::{fs::remove_matching, maybe, ResultExt};
use util::{maybe, ResultExt};
const SERVER_PATH: &str =
"node_modules/vscode-langservers-extracted/bin/vscode-json-language-server";
const SERVER_PATH: &str = "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver";
// Origin: https://github.com/SchemaStore/schemastore
const TSCONFIG_SCHEMA: &str = include_str!("json/schemas/tsconfig.json");
@@ -141,7 +133,7 @@ impl LspAdapter for JsonLspAdapter {
) -> Result<Box<dyn 'static + Send + Any>> {
Ok(Box::new(
self.node
.npm_package_latest_version("vscode-langservers-extracted")
.npm_package_latest_version("vscode-json-languageserver")
.await?,
) as Box<_>)
}
@@ -154,7 +146,7 @@ impl LspAdapter for JsonLspAdapter {
) -> Result<LanguageServerBinary> {
let latest_version = latest_version.downcast::<String>().unwrap();
let server_path = container_dir.join(SERVER_PATH);
let package_name = "vscode-langservers-extracted";
let package_name = "vscode-json-languageserver";
let should_install_language_server = self
.node
@@ -162,11 +154,9 @@ impl LspAdapter for JsonLspAdapter {
.await;
if should_install_language_server {
// TODO: the postinstall fails on Windows
self.node
.npm_install_packages(&container_dir, &[(package_name, latest_version.as_str())])
.await
.log_err();
.await?;
}
Ok(LanguageServerBinary {
@@ -250,145 +240,7 @@ async fn get_cached_server_binary(
.log_err()
}
#[inline]
fn schema_file_match(path: &Path) -> String {
fn schema_file_match(path: &Path) -> &Path {
path.strip_prefix(path.parent().unwrap().parent().unwrap())
.unwrap()
.display()
.to_string()
.replace('\\', "/")
}
pub(super) struct NodeVersionAdapter;
#[async_trait(?Send)]
impl LspAdapter for NodeVersionAdapter {
fn name(&self) -> LanguageServerName {
LanguageServerName("package-version-server".into())
}
async fn fetch_latest_server_version(
&self,
delegate: &dyn LspAdapterDelegate,
) -> Result<Box<dyn 'static + Send + Any>> {
let release = latest_github_release(
"zed-industries/package-version-server",
true,
false,
delegate.http_client(),
)
.await?;
let os = match consts::OS {
"macos" => "apple-darwin",
"linux" => "unknown-linux-gnu",
"windows" => "pc-windows-msvc",
other => bail!("Running on unsupported os: {other}"),
};
let suffix = if consts::OS == "windows" {
".zip"
} else {
".tar.gz"
};
let asset_name = format!("package-version-server-{}-{os}{suffix}", consts::ARCH);
let asset = release
.assets
.iter()
.find(|asset| asset.name == asset_name)
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
Ok(Box::new(GitHubLspBinaryVersion {
name: release.tag_name,
url: asset.browser_download_url.clone(),
}))
}
async fn fetch_server_binary(
&self,
latest_version: Box<dyn 'static + Send + Any>,
container_dir: PathBuf,
delegate: &dyn LspAdapterDelegate,
) -> Result<LanguageServerBinary> {
let version = latest_version.downcast::<GitHubLspBinaryVersion>().unwrap();
let destination_path =
container_dir.join(format!("package-version-server-{}", version.name));
let destination_container_path =
container_dir.join(format!("package-version-server-{}-tmp", version.name));
if fs::metadata(&destination_path).await.is_err() {
let mut response = delegate
.http_client()
.get(&version.url, Default::default(), true)
.await
.map_err(|err| anyhow!("error downloading release: {}", err))?;
if version.url.ends_with(".zip") {
node_runtime::extract_zip(
&destination_container_path,
BufReader::new(response.body_mut()),
)
.await?;
} else if version.url.ends_with(".tar.gz") {
let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
let archive = Archive::new(decompressed_bytes);
archive.unpack(&destination_container_path).await?;
}
fs::copy(
destination_container_path.join("package-version-server"),
&destination_path,
)
.await?;
// todo("windows")
#[cfg(not(windows))]
{
fs::set_permissions(
&destination_path,
<fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
)
.await?;
}
remove_matching(&container_dir, |entry| entry != destination_path).await;
}
Ok(LanguageServerBinary {
path: destination_path.join("package-version-server"),
env: None,
arguments: Default::default(),
})
}
async fn cached_server_binary(
&self,
container_dir: PathBuf,
_delegate: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
get_cached_version_server_binary(container_dir).await
}
async fn installation_test_binary(
&self,
container_dir: PathBuf,
) -> Option<LanguageServerBinary> {
get_cached_version_server_binary(container_dir)
.await
.map(|mut binary| {
binary.arguments = vec!["--version".into()];
binary
})
}
}
async fn get_cached_version_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
maybe!(async {
let mut last = None;
let mut entries = fs::read_dir(&container_dir).await?;
while let Some(entry) = entries.next().await {
last = Some(entry?.path());
}
anyhow::Ok(LanguageServerBinary {
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
env: None,
arguments: Default::default(),
})
})
.await
.log_err()
}

View File

@@ -117,13 +117,10 @@ pub fn init(
language!(
"json",
vec![
Arc::new(json::JsonLspAdapter::new(
node_runtime.clone(),
languages.clone(),
)),
Arc::new(json::NodeVersionAdapter)
],
vec![Arc::new(json::JsonLspAdapter::new(
node_runtime.clone(),
languages.clone(),
))],
json_task_context()
);
language!("markdown");

View File

@@ -201,18 +201,11 @@ impl LspAdapter for RustLspAdapter {
completion: &lsp::CompletionItem,
language: &Arc<Language>,
) -> Option<CodeLabel> {
let detail = completion
.detail
.as_ref()
.or(completion
.label_details
.as_ref()
.and_then(|detail| detail.detail.as_ref()))
.map(ToOwned::to_owned);
match completion.kind {
Some(lsp::CompletionItemKind::FIELD) if detail.is_some() => {
Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
let detail = completion.detail.as_ref().unwrap();
let name = &completion.label;
let text = format!("{}: {}", name, detail.unwrap());
let text = format!("{}: {}", name, detail);
let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
let runs = language.highlight_text(&source, 11..11 + text.len());
return Some(CodeLabel {
@@ -222,11 +215,12 @@ impl LspAdapter for RustLspAdapter {
});
}
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE)
if detail.is_some()
if completion.detail.is_some()
&& completion.insert_text_format != Some(lsp::InsertTextFormat::SNIPPET) =>
{
let detail = completion.detail.as_ref().unwrap();
let name = &completion.label;
let text = format!("{}: {}", name, detail.unwrap());
let text = format!("{}: {}", name, detail);
let source = Rope::from(format!("let {} = ();", text).as_str());
let runs = language.highlight_text(&source, 4..4 + text.len());
return Some(CodeLabel {
@@ -236,12 +230,12 @@ impl LspAdapter for RustLspAdapter {
});
}
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD)
if detail.is_some() =>
if completion.detail.is_some() =>
{
lazy_static! {
static ref REGEX: Regex = Regex::new("\\(…?\\)").unwrap();
}
let detail = detail.unwrap();
let detail = completion.detail.as_ref().unwrap();
const FUNCTION_PREFIXES: [&'static str; 2] = ["async fn", "fn"];
let prefix = FUNCTION_PREFIXES
.iter()
@@ -275,14 +269,9 @@ impl LspAdapter for RustLspAdapter {
_ => None,
};
let highlight_id = language.grammar()?.highlight_id_for_name(highlight_name?)?;
let mut label = completion.label.clone();
if let Some(detail) = detail.filter(|detail| detail.starts_with(" (")) {
use std::fmt::Write;
write!(label, "{detail}").ok()?;
}
let mut label = CodeLabel::plain(label, None);
let mut label = CodeLabel::plain(completion.label.clone(), None);
label.runs.push((
0..label.text.rfind('(').unwrap_or(completion.label.len()),
0..label.text.rfind('(').unwrap_or(label.text.len()),
highlight_id,
));
return Some(label);

View File

@@ -999,7 +999,7 @@ impl LanguageServer {
select! {
response = rx.fuse() => {
let elapsed = started.elapsed();
log::trace!("Took {elapsed:?} to receive response to {method:?} id {id}");
log::info!("Took {elapsed:?} to receive response to {method:?} id {id}");
cancel_on_drop.abort();
response?
}

View File

@@ -152,7 +152,11 @@ pub trait ToPointUtf16: 'static + fmt::Debug {
struct BufferState {
buffer: Model<Buffer>,
last_version: clock::Global,
last_non_text_state_update_count: usize,
last_parse_count: usize,
last_selections_update_count: usize,
last_diagnostics_update_count: usize,
last_file_update_count: usize,
last_git_diff_update_count: usize,
excerpts: Vec<Locator>,
_subscriptions: [gpui::Subscription; 2],
}
@@ -163,8 +167,10 @@ pub struct MultiBufferSnapshot {
singleton: bool,
excerpts: SumTree<Excerpt>,
excerpt_ids: SumTree<ExcerptIdMapping>,
parse_count: usize,
diagnostics_update_count: usize,
trailing_excerpt_update_count: usize,
non_text_state_update_count: usize,
git_diff_update_count: usize,
edit_count: usize,
is_dirty: bool,
has_conflict: bool,
@@ -390,7 +396,11 @@ impl MultiBuffer {
BufferState {
buffer: buffer_state.buffer.clone(),
last_version: buffer_state.last_version.clone(),
last_non_text_state_update_count: buffer_state.last_non_text_state_update_count,
last_parse_count: buffer_state.last_parse_count,
last_selections_update_count: buffer_state.last_selections_update_count,
last_diagnostics_update_count: buffer_state.last_diagnostics_update_count,
last_file_update_count: buffer_state.last_file_update_count,
last_git_diff_update_count: buffer_state.last_git_diff_update_count,
excerpts: buffer_state.excerpts.clone(),
_subscriptions: [
new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
@@ -1234,7 +1244,11 @@ impl MultiBuffer {
let mut buffers = self.buffers.borrow_mut();
let buffer_state = buffers.entry(buffer_id).or_insert_with(|| BufferState {
last_version: buffer_snapshot.version().clone(),
last_non_text_state_update_count: buffer_snapshot.non_text_state_update_count(),
last_parse_count: buffer_snapshot.parse_count(),
last_selections_update_count: buffer_snapshot.selections_update_count(),
last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
last_file_update_count: buffer_snapshot.file_update_count(),
last_git_diff_update_count: buffer_snapshot.git_diff_update_count(),
excerpts: Default::default(),
_subscriptions: [
cx.observe(&buffer, |_, _, cx| cx.notify()),
@@ -1809,7 +1823,9 @@ impl MultiBuffer {
fn sync(&self, cx: &AppContext) {
let mut snapshot = self.snapshot.borrow_mut();
let mut excerpts_to_edit = Vec::new();
let mut non_text_state_updated = false;
let mut reparsed = false;
let mut diagnostics_updated = false;
let mut git_diff_updated = false;
let mut is_dirty = false;
let mut has_conflict = false;
let mut edited = false;
@@ -1817,14 +1833,34 @@ impl MultiBuffer {
for buffer_state in buffers.values_mut() {
let buffer = buffer_state.buffer.read(cx);
let version = buffer.version();
let non_text_state_update_count = buffer.non_text_state_update_count();
let parse_count = buffer.parse_count();
let selections_update_count = buffer.selections_update_count();
let diagnostics_update_count = buffer.diagnostics_update_count();
let file_update_count = buffer.file_update_count();
let git_diff_update_count = buffer.git_diff_update_count();
let buffer_edited = version.changed_since(&buffer_state.last_version);
let buffer_non_text_state_updated =
non_text_state_update_count > buffer_state.last_non_text_state_update_count;
if buffer_edited || buffer_non_text_state_updated {
let buffer_reparsed = parse_count > buffer_state.last_parse_count;
let buffer_selections_updated =
selections_update_count > buffer_state.last_selections_update_count;
let buffer_diagnostics_updated =
diagnostics_update_count > buffer_state.last_diagnostics_update_count;
let buffer_file_updated = file_update_count > buffer_state.last_file_update_count;
let buffer_git_diff_updated =
git_diff_update_count > buffer_state.last_git_diff_update_count;
if buffer_edited
|| buffer_reparsed
|| buffer_selections_updated
|| buffer_diagnostics_updated
|| buffer_file_updated
|| buffer_git_diff_updated
{
buffer_state.last_version = version;
buffer_state.last_non_text_state_update_count = non_text_state_update_count;
buffer_state.last_parse_count = parse_count;
buffer_state.last_selections_update_count = selections_update_count;
buffer_state.last_diagnostics_update_count = diagnostics_update_count;
buffer_state.last_file_update_count = file_update_count;
buffer_state.last_git_diff_update_count = git_diff_update_count;
excerpts_to_edit.extend(
buffer_state
.excerpts
@@ -1834,15 +1870,23 @@ impl MultiBuffer {
}
edited |= buffer_edited;
non_text_state_updated |= buffer_non_text_state_updated;
reparsed |= buffer_reparsed;
diagnostics_updated |= buffer_diagnostics_updated;
git_diff_updated |= buffer_git_diff_updated;
is_dirty |= buffer.is_dirty();
has_conflict |= buffer.has_conflict();
}
if edited {
snapshot.edit_count += 1;
}
if non_text_state_updated {
snapshot.non_text_state_update_count += 1;
if reparsed {
snapshot.parse_count += 1;
}
if diagnostics_updated {
snapshot.diagnostics_update_count += 1;
}
if git_diff_updated {
snapshot.git_diff_update_count += 1;
}
snapshot.is_dirty = is_dirty;
snapshot.has_conflict = has_conflict;
@@ -3154,8 +3198,8 @@ impl MultiBufferSnapshot {
self.edit_count
}
pub fn non_text_state_update_count(&self) -> usize {
self.non_text_state_update_count
pub fn parse_count(&self) -> usize {
self.parse_count
}
/// Returns the smallest enclosing bracket ranges containing the given range or
@@ -3368,6 +3412,14 @@ impl MultiBufferSnapshot {
.collect()
}
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
}
pub fn trailing_excerpt_update_count(&self) -> usize {
self.trailing_excerpt_update_count
}
@@ -3765,42 +3817,59 @@ impl MultiBufferSnapshot {
ranges: impl IntoIterator<Item = Range<Anchor>>,
) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, Range<usize>)> {
let mut ranges = ranges.into_iter().map(|range| range.to_offset(self));
let mut cursor = self.excerpts.cursor::<usize>();
cursor.next(&());
let mut current_range = ranges.next();
iter::from_fn(move || {
let range = current_range.clone()?;
if range.start >= cursor.end(&()) {
let mut next_range = move |cursor: &mut Cursor<Excerpt, usize>| {
let range = ranges.next();
if let Some(range) = range.as_ref() {
cursor.seek_forward(&range.start, Bias::Right, &());
if range.start == self.len() {
cursor.prev(&());
}
range
};
let mut range = next_range(&mut cursor);
iter::from_fn(move || {
if range.is_none() {
return None;
}
if range.as_ref().unwrap().is_empty() || *cursor.start() >= range.as_ref().unwrap().end
{
range = next_range(&mut cursor);
if range.is_none() {
return None;
}
}
let excerpt = cursor.item()?;
let range_start_in_excerpt = cmp::max(range.start, *cursor.start());
let range_end_in_excerpt = if excerpt.has_trailing_newline {
cmp::min(range.end, cursor.end(&()) - 1)
} else {
cmp::min(range.end, cursor.end(&()))
};
let buffer_range = MultiBufferExcerpt::new(excerpt, *cursor.start())
.map_range_to_buffer(range_start_in_excerpt..range_end_in_excerpt);
cursor.item().map(|excerpt| {
let multibuffer_excerpt = MultiBufferExcerpt::new(&excerpt, *cursor.start());
if range.end > cursor.end(&()) {
cursor.next(&());
} else {
current_range = ranges.next();
}
let multibuffer_excerpt_range = multibuffer_excerpt
.map_range_from_buffer(excerpt.range.context.to_offset(&excerpt.buffer));
Some((excerpt.id, &excerpt.buffer, buffer_range))
let overlap_range = cmp::max(
range.as_ref().unwrap().start,
multibuffer_excerpt_range.start,
)
..cmp::min(range.as_ref().unwrap().end, multibuffer_excerpt_range.end);
let overlap_range = multibuffer_excerpt.map_range_to_buffer(overlap_range);
if multibuffer_excerpt_range.end <= range.as_ref().unwrap().end {
cursor.next(&());
} else {
range = next_range(&mut cursor);
}
(excerpt.id, &excerpt.buffer, overlap_range)
})
})
}
pub fn selections_in_range<'a>(
pub fn remote_selections_in_range<'a>(
&'a self,
range: &'a Range<Anchor>,
include_local: bool,
) -> impl 'a + Iterator<Item = (ReplicaId, bool, CursorShape, Selection<Anchor>)> {
let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
let start_locator = self.excerpt_locator_for_id(range.start.excerpt_id);
@@ -3819,7 +3888,7 @@ impl MultiBufferSnapshot {
excerpt
.buffer
.selections_in_range(query_range, include_local)
.remote_selections_in_range(query_range)
.flat_map(move |(replica_id, line_mode, cursor_shape, selections)| {
selections.map(move |selection| {
let mut start = Anchor {

View File

@@ -1,7 +1,6 @@
mod archive;
use anyhow::{anyhow, bail, Context, Result};
pub use archive::extract_zip;
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use futures::AsyncReadExt;

View File

@@ -209,6 +209,7 @@ impl NotificationStore {
async fn handle_new_notification(
this: Model<Self>,
envelope: TypedEnvelope<proto::AddNotification>,
_: Arc<Client>,
cx: AsyncAppContext,
) -> Result<()> {
Self::add_notifications(
@@ -227,6 +228,7 @@ impl NotificationStore {
async fn handle_delete_notification(
this: Model<Self>,
envelope: TypedEnvelope<proto::DeleteNotification>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -238,6 +240,7 @@ impl NotificationStore {
async fn handle_update_notification(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateNotification>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {

View File

@@ -224,12 +224,6 @@ pub fn default_prettier_dir() -> &'static PathBuf {
DEFAULT_PRETTIER_DIR.get_or_init(|| support_dir().join("prettier"))
}
/// Returns the relative path to a `.zed` folder within a project.
pub fn local_settings_folder_relative_path() -> &'static Path {
static LOCAL_SETTINGS_FOLDER_RELATIVE_PATH: OnceLock<&Path> = OnceLock::new();
LOCAL_SETTINGS_FOLDER_RELATIVE_PATH.get_or_init(|| Path::new(".zed"))
}
/// Returns the relative path to a `settings.json` file within a project.
pub fn local_settings_file_relative_path() -> &'static Path {
static LOCAL_SETTINGS_FILE_RELATIVE_PATH: OnceLock<&Path> = OnceLock::new();

View File

@@ -4028,6 +4028,7 @@ impl Project {
async fn handle_restart_language_servers(
project: Model<Self>,
envelope: TypedEnvelope<proto::RestartLanguageServers>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::Ack> {
project.update(&mut cx, |project, cx| {
@@ -8192,7 +8193,7 @@ impl Project {
}
};
if abs_path.ends_with(local_settings_file_relative_path()) {
if path.ends_with(local_settings_file_relative_path()) {
let settings_dir = Arc::from(
path.ancestors()
.nth(local_settings_file_relative_path().components().count())
@@ -8209,7 +8210,7 @@ impl Project {
},
)
});
} else if abs_path.ends_with(local_tasks_file_relative_path()) {
} else if path.ends_with(local_tasks_file_relative_path()) {
self.task_inventory().update(cx, |task_inventory, cx| {
if removed {
task_inventory.remove_local_static_source(&abs_path);
@@ -8229,7 +8230,7 @@ impl Project {
);
}
})
} else if abs_path.ends_with(local_vscode_tasks_file_relative_path()) {
} else if path.ends_with(local_vscode_tasks_file_relative_path()) {
self.task_inventory().update(cx, |task_inventory, cx| {
if removed {
task_inventory.remove_local_static_source(&abs_path);
@@ -8560,6 +8561,7 @@ impl Project {
async fn handle_blame_buffer(
this: Model<Self>,
envelope: TypedEnvelope<proto::BlameBuffer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::BlameBufferResponse> {
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
@@ -8590,6 +8592,7 @@ impl Project {
async fn handle_multi_lsp_query(
project: Model<Self>,
envelope: TypedEnvelope<proto::MultiLspQuery>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::MultiLspQueryResponse> {
let sender_id = envelope.original_sender_id()?;
@@ -8701,6 +8704,7 @@ impl Project {
async fn handle_unshare_project(
this: Model<Self>,
_: TypedEnvelope<proto::UnshareProject>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -8716,6 +8720,7 @@ impl Project {
async fn handle_add_collaborator(
this: Model<Self>,
mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let collaborator = envelope
@@ -8739,6 +8744,7 @@ impl Project {
async fn handle_update_project_collaborator(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let old_peer_id = envelope
@@ -8787,6 +8793,7 @@ impl Project {
async fn handle_remove_collaborator(
this: Model<Self>,
envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -8815,6 +8822,7 @@ impl Project {
async fn handle_update_project(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateProject>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -8829,6 +8837,7 @@ impl Project {
async fn handle_update_worktree(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateWorktree>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -8846,6 +8855,7 @@ impl Project {
async fn handle_update_worktree_settings(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -8869,6 +8879,7 @@ impl Project {
async fn handle_create_project_entry(
this: Model<Self>,
envelope: TypedEnvelope<proto::CreateProjectEntry>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ProjectEntryResponse> {
let worktree = this.update(&mut cx, |this, cx| {
@@ -8882,6 +8893,7 @@ impl Project {
async fn handle_rename_project_entry(
this: Model<Self>,
envelope: TypedEnvelope<proto::RenameProjectEntry>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ProjectEntryResponse> {
let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
@@ -8895,6 +8907,7 @@ impl Project {
async fn handle_copy_project_entry(
this: Model<Self>,
envelope: TypedEnvelope<proto::CopyProjectEntry>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ProjectEntryResponse> {
let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
@@ -8908,6 +8921,7 @@ impl Project {
async fn handle_delete_project_entry(
this: Model<Self>,
envelope: TypedEnvelope<proto::DeleteProjectEntry>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ProjectEntryResponse> {
let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
@@ -8922,6 +8936,7 @@ impl Project {
async fn handle_expand_project_entry(
this: Model<Self>,
envelope: TypedEnvelope<proto::ExpandProjectEntry>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ExpandProjectEntryResponse> {
let entry_id = ProjectEntryId::from_proto(envelope.payload.entry_id);
@@ -8934,6 +8949,7 @@ impl Project {
async fn handle_update_diagnostic_summary(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateDiagnosticSummary>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -8981,6 +8997,7 @@ impl Project {
async fn handle_start_language_server(
this: Model<Self>,
envelope: TypedEnvelope<proto::StartLanguageServer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let server = envelope
@@ -9005,6 +9022,7 @@ impl Project {
async fn handle_update_language_server(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateLanguageServer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -9067,6 +9085,7 @@ impl Project {
async fn handle_update_buffer(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateBuffer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::Ack> {
this.update(&mut cx, |this, cx| {
@@ -9104,6 +9123,7 @@ impl Project {
async fn handle_create_buffer_for_peer(
this: Model<Self>,
envelope: TypedEnvelope<proto::CreateBufferForPeer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -9189,6 +9209,7 @@ impl Project {
async fn handle_update_diff_base(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateDiffBase>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
@@ -9211,6 +9232,7 @@ impl Project {
async fn handle_update_buffer_file(
this: Model<Self>,
envelope: TypedEnvelope<proto::UpdateBufferFile>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let buffer_id = envelope.payload.buffer_id;
@@ -9241,6 +9263,7 @@ impl Project {
async fn handle_save_buffer(
this: Model<Self>,
envelope: TypedEnvelope<proto::SaveBuffer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::BufferSaved> {
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
@@ -9282,6 +9305,7 @@ impl Project {
async fn handle_reload_buffers(
this: Model<Self>,
envelope: TypedEnvelope<proto::ReloadBuffers>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ReloadBuffersResponse> {
let sender_id = envelope.original_sender_id()?;
@@ -9311,6 +9335,7 @@ impl Project {
async fn handle_synchronize_buffers(
this: Model<Self>,
envelope: TypedEnvelope<proto::SynchronizeBuffers>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::SynchronizeBuffersResponse> {
let project_id = envelope.payload.project_id;
@@ -9401,6 +9426,7 @@ impl Project {
async fn handle_format_buffers(
this: Model<Self>,
envelope: TypedEnvelope<proto::FormatBuffers>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::FormatBuffersResponse> {
let sender_id = envelope.original_sender_id()?;
@@ -9431,6 +9457,7 @@ impl Project {
async fn handle_apply_additional_edits_for_completion(
this: Model<Self>,
envelope: TypedEnvelope<proto::ApplyCompletionAdditionalEdits>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
let (buffer, completion) = this.update(&mut cx, |this, _| {
@@ -9482,6 +9509,7 @@ impl Project {
async fn handle_resolve_completion_documentation(
this: Model<Self>,
envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ResolveCompletionDocumentationResponse> {
let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?;
@@ -9549,6 +9577,7 @@ impl Project {
async fn handle_apply_code_action(
this: Model<Self>,
envelope: TypedEnvelope<proto::ApplyCodeAction>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ApplyCodeActionResponse> {
let sender_id = envelope.original_sender_id()?;
@@ -9580,6 +9609,7 @@ impl Project {
async fn handle_on_type_formatting(
this: Model<Self>,
envelope: TypedEnvelope<proto::OnTypeFormatting>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::OnTypeFormattingResponse> {
let on_type_formatting = this.update(&mut cx, |this, cx| {
@@ -9612,6 +9642,7 @@ impl Project {
async fn handle_inlay_hints(
this: Model<Self>,
envelope: TypedEnvelope<proto::InlayHints>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::InlayHintsResponse> {
let sender_id = envelope.original_sender_id()?;
@@ -9660,6 +9691,7 @@ impl Project {
async fn handle_resolve_inlay_hint(
this: Model<Self>,
envelope: TypedEnvelope<proto::ResolveInlayHint>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::ResolveInlayHintResponse> {
let proto_hint = envelope
@@ -9694,6 +9726,7 @@ impl Project {
async fn handle_task_context_for_location(
project: Model<Self>,
envelope: TypedEnvelope<proto::TaskContextForLocation>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::TaskContext> {
let location = envelope
@@ -9736,6 +9769,7 @@ impl Project {
async fn handle_task_templates(
project: Model<Self>,
envelope: TypedEnvelope<proto::TaskTemplates>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::TaskTemplatesResponse> {
let worktree = envelope.payload.worktree_id.map(WorktreeId::from_proto);
@@ -9901,6 +9935,7 @@ impl Project {
async fn handle_refresh_inlay_hints(
this: Model<Self>,
_: TypedEnvelope<proto::RefreshInlayHints>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::Ack> {
this.update(&mut cx, |_, cx| {
@@ -9912,6 +9947,7 @@ impl Project {
async fn handle_lsp_command<T: LspCommand>(
this: Model<Self>,
envelope: TypedEnvelope<T::ProtoRequest>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<<T::ProtoRequest as proto::RequestMessage>::Response>
where
@@ -9957,6 +9993,7 @@ impl Project {
async fn handle_get_project_symbols(
this: Model<Self>,
envelope: TypedEnvelope<proto::GetProjectSymbols>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::GetProjectSymbolsResponse> {
let symbols = this
@@ -9973,6 +10010,7 @@ impl Project {
async fn handle_search_project(
this: Model<Self>,
envelope: TypedEnvelope<proto::SearchProject>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::SearchProjectResponse> {
let peer_id = envelope.original_sender_id()?;
@@ -10012,6 +10050,7 @@ impl Project {
async fn handle_open_buffer_for_symbol(
this: Model<Self>,
envelope: TypedEnvelope<proto::OpenBufferForSymbol>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::OpenBufferForSymbolResponse> {
let peer_id = envelope.original_sender_id()?;
@@ -10077,6 +10116,7 @@ impl Project {
async fn handle_open_buffer_by_id(
this: Model<Self>,
envelope: TypedEnvelope<proto::OpenBufferById>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::OpenBufferResponse> {
let peer_id = envelope.original_sender_id()?;
@@ -10090,6 +10130,7 @@ impl Project {
async fn handle_open_buffer_by_path(
this: Model<Self>,
envelope: TypedEnvelope<proto::OpenBufferByPath>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::OpenBufferResponse> {
let peer_id = envelope.original_sender_id()?;
@@ -10111,6 +10152,7 @@ impl Project {
async fn handle_open_new_buffer(
this: Model<Self>,
envelope: TypedEnvelope<proto::OpenNewBuffer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::OpenBufferResponse> {
let buffer = this.update(&mut cx, |this, cx| this.create_local_buffer("", None, cx))?;
@@ -10505,6 +10547,7 @@ impl Project {
async fn handle_buffer_saved(
this: Model<Self>,
envelope: TypedEnvelope<proto::BufferSaved>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let version = deserialize_version(&envelope.payload.version);
@@ -10529,6 +10572,7 @@ impl Project {
async fn handle_buffer_reloaded(
this: Model<Self>,
envelope: TypedEnvelope<proto::BufferReloaded>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let payload = envelope.payload;

View File

@@ -1,15 +1,9 @@
mod project_panel_settings;
mod scrollbar;
use client::{ErrorCode, ErrorExt};
use scrollbar::ProjectPanelScrollbar;
use settings::{Settings, SettingsStore};
use db::kvp::KEY_VALUE_STORE;
use editor::{
items::entry_git_aware_label_color,
scroll::{Autoscroll, ScrollbarAutoHide},
Editor,
};
use editor::{items::entry_git_aware_label_color, scroll::Autoscroll, Editor};
use file_icons::FileIcons;
use anyhow::{anyhow, Result};
@@ -25,17 +19,15 @@ use gpui::{
};
use menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev};
use project::{Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings, ShowScrollbar};
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings};
use serde::{Deserialize, Serialize};
use std::{
cell::{Cell, OnceCell},
cell::OnceCell,
collections::HashSet,
ffi::OsStr,
ops::Range,
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
time::Duration,
};
use theme::ThemeSettings;
use ui::{prelude::*, v_flex, ContextMenu, Icon, KeyBinding, Label, ListItem, Tooltip};
@@ -71,9 +63,6 @@ pub struct ProjectPanel {
workspace: WeakView<Workspace>,
width: Option<Pixels>,
pending_serialization: Task<Option<()>>,
show_scrollbar: bool,
scrollbar_drag_thumb_offset: Rc<Cell<Option<f32>>>,
hide_scrollbar_task: Option<Task<()>>,
}
#[derive(Clone, Debug)]
@@ -199,10 +188,7 @@ impl ProjectPanel {
let project_panel = cx.new_view(|cx: &mut ViewContext<Self>| {
let focus_handle = cx.focus_handle();
cx.on_focus(&focus_handle, Self::focus_in).detach();
cx.on_focus_out(&focus_handle, |this, _, cx| {
this.hide_scrollbar(cx);
})
.detach();
cx.subscribe(&project, |this, project, event, cx| match event {
project::Event::ActiveEntryChanged(Some(entry_id)) => {
if ProjectPanelSettings::get_global(cx).auto_reveal_entries {
@@ -287,9 +273,6 @@ impl ProjectPanel {
workspace: workspace.weak_handle(),
width: None,
pending_serialization: Task::ready(None),
show_scrollbar: !Self::should_autohide_scrollbar(cx),
hide_scrollbar_task: None,
scrollbar_drag_thumb_offset: Default::default(),
};
this.update_visible_entries(None, cx);
@@ -2218,88 +2201,6 @@ impl ProjectPanel {
)
}
fn render_scrollbar(
&self,
items_count: usize,
cx: &mut ViewContext<Self>,
) -> Option<Stateful<Div>> {
let settings = ProjectPanelSettings::get_global(cx);
if settings.scrollbar.show == ShowScrollbar::Never {
return None;
}
let scroll_handle = self.scroll_handle.0.borrow();
let height = scroll_handle
.last_item_height
.filter(|_| self.show_scrollbar || self.scrollbar_drag_thumb_offset.get().is_some())?;
let total_list_length = height.0 as f64 * items_count as f64;
let current_offset = scroll_handle.base_handle.offset().y.0.min(0.).abs() as f64;
let mut percentage = current_offset / total_list_length;
let end_offset = (current_offset + scroll_handle.base_handle.bounds().size.height.0 as f64)
/ total_list_length;
// Uniform scroll handle might briefly report an offset greater than the length of a list;
// in such case we'll adjust the starting offset as well to keep the scrollbar thumb length stable.
let overshoot = (end_offset - 1.).clamp(0., 1.);
if overshoot > 0. {
percentage -= overshoot;
}
const MINIMUM_SCROLLBAR_PERCENTAGE_HEIGHT: f64 = 0.005;
if percentage + MINIMUM_SCROLLBAR_PERCENTAGE_HEIGHT > 1.0 || end_offset > total_list_length
{
return None;
}
if total_list_length < scroll_handle.base_handle.bounds().size.height.0 as f64 {
return None;
}
let end_offset = end_offset.clamp(percentage + MINIMUM_SCROLLBAR_PERCENTAGE_HEIGHT, 1.);
Some(
div()
.occlude()
.id("project-panel-scroll")
.on_mouse_move(cx.listener(|_, _, cx| {
cx.notify();
cx.stop_propagation()
}))
.on_hover(|_, cx| {
cx.stop_propagation();
})
.on_any_mouse_down(|_, cx| {
cx.stop_propagation();
})
.on_mouse_up(
MouseButton::Left,
cx.listener(|this, _, cx| {
if this.scrollbar_drag_thumb_offset.get().is_none()
&& !this.focus_handle.contains_focused(cx)
{
this.hide_scrollbar(cx);
cx.notify();
}
cx.stop_propagation();
}),
)
.on_scroll_wheel(cx.listener(|_, _, cx| {
cx.notify();
}))
.h_full()
.absolute()
.right_0()
.top_0()
.bottom_0()
.w_3()
.cursor_default()
.child(ProjectPanelScrollbar::new(
percentage as f32..end_offset as f32,
self.scroll_handle.clone(),
self.scrollbar_drag_thumb_offset.clone(),
cx.view().clone().into(),
items_count,
)),
)
}
fn dispatch_context(&self, cx: &ViewContext<Self>) -> KeyContext {
let mut dispatch_context = KeyContext::new_with_defaults();
dispatch_context.add("ProjectPanel");
@@ -2315,29 +2216,6 @@ impl ProjectPanel {
dispatch_context
}
fn should_autohide_scrollbar(cx: &AppContext) -> bool {
cx.try_global::<ScrollbarAutoHide>()
.map_or_else(|| cx.should_auto_hide_scrollbars(), |autohide| autohide.0)
}
fn hide_scrollbar(&mut self, cx: &mut ViewContext<Self>) {
const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
if !Self::should_autohide_scrollbar(cx) {
return;
}
self.hide_scrollbar_task = Some(cx.spawn(|panel, mut cx| async move {
cx.background_executor()
.timer(SCROLLBAR_SHOW_INTERVAL)
.await;
panel
.update(&mut cx, |panel, cx| {
panel.show_scrollbar = false;
cx.notify();
})
.log_err();
}))
}
fn reveal_entry(
&mut self,
project: Model<Project>,
@@ -2371,26 +2249,10 @@ impl Render for ProjectPanel {
let project = self.project.read(cx);
if has_worktree {
let items_count = self
.visible_entries
.iter()
.map(|(_, worktree_entries, _)| worktree_entries.len())
.sum();
h_flex()
.id("project-panel")
.group("project-panel")
.size_full()
.relative()
.on_hover(cx.listener(|this, hovered, cx| {
if *hovered {
this.show_scrollbar = true;
this.hide_scrollbar_task.take();
cx.notify();
} else if !this.focus_handle.contains_focused(cx) {
this.hide_scrollbar(cx);
}
}))
.key_context(self.dispatch_context(cx))
.on_action(cx.listener(Self::select_next))
.on_action(cx.listener(Self::select_prev))
@@ -2436,20 +2298,27 @@ impl Render for ProjectPanel {
)
.track_focus(&self.focus_handle)
.child(
uniform_list(cx.view().clone(), "entries", items_count, {
|this, range, cx| {
let mut items = Vec::new();
this.for_each_visible_entry(range, cx, |id, details, cx| {
items.push(this.render_entry(id, details, cx));
});
items
}
})
uniform_list(
cx.view().clone(),
"entries",
self.visible_entries
.iter()
.map(|(_, worktree_entries, _)| worktree_entries.len())
.sum(),
{
|this, range, cx| {
let mut items = Vec::new();
this.for_each_visible_entry(range, cx, |id, details, cx| {
items.push(this.render_entry(id, details, cx));
});
items
}
},
)
.size_full()
.with_sizing_behavior(ListSizingBehavior::Infer)
.track_scroll(self.scroll_handle.clone()),
)
.children(self.render_scrollbar(items_count, cx))
.children(self.context_menu.as_ref().map(|(menu, position, _)| {
deferred(
anchored()

View File

@@ -22,36 +22,6 @@ pub struct ProjectPanelSettings {
pub indent_size: f32,
pub auto_reveal_entries: bool,
pub auto_fold_dirs: bool,
pub scrollbar: ScrollbarSettings,
}
/// When to show the scrollbar in the project panel.
///
/// Default: always
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ShowScrollbar {
#[default]
/// Always show the scrollbar.
Always,
/// Never show the scrollbar.
Never,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ScrollbarSettings {
/// When to show the scrollbar in the project panel.
///
/// Default: always
pub show: ShowScrollbar,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct ScrollbarSettingsContent {
/// When to show the scrollbar in the project panel.
///
/// Default: always
pub show: Option<ShowScrollbar>,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
@@ -95,8 +65,6 @@ pub struct ProjectPanelSettingsContent {
///
/// Default: false
pub auto_fold_dirs: Option<bool>,
/// Scrollbar-related settings
pub scrollbar: Option<ScrollbarSettingsContent>,
}
impl Settings for ProjectPanelSettings {

Some files were not shown because too many files have changed in this diff Show More