Compare commits
11 Commits
auto-comma
...
v0.141.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eadb45467d | ||
|
|
bfa2682316 | ||
|
|
8a8e29573f | ||
|
|
3e609e4793 | ||
|
|
fce2e00745 | ||
|
|
20edf6905c | ||
|
|
d3c2699b1e | ||
|
|
fbeaa70e97 | ||
|
|
11b45702c9 | ||
|
|
c844ba3cdc | ||
|
|
3562cad225 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -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
445
Cargo.lock
generated
@@ -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"
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -188,8 +188,7 @@
|
||||
"focus": false
|
||||
}
|
||||
],
|
||||
"cmd->": "assistant::QuoteSelection",
|
||||
"cmd-alt-e": "editor::SelectEnclosingSymbol"
|
||||
"cmd->": "assistant::QuoteSelection"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)>,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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, _| {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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())?;
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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, _| {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -268,7 +268,6 @@ gpui::actions!(
|
||||
SelectAllMatches,
|
||||
SelectDown,
|
||||
SelectLargerSyntaxNode,
|
||||
SelectEnclosingSymbol,
|
||||
SelectLeft,
|
||||
SelectLine,
|
||||
SelectRight,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"],
|
||||
);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -51,7 +51,6 @@ fn main() {
|
||||
kind: WindowKind::PopUp,
|
||||
is_movable: false,
|
||||
app_id: None,
|
||||
window_min_size: Size::default(),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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?
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user