Detailed changes
@@ -34,6 +34,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
[[package]]
name = "aes"
version = "0.8.4"
@@ -93,7 +99,7 @@ dependencies = [
"miow",
"parking_lot",
"piper",
- "polling 3.7.2",
+ "polling 3.7.3",
"regex-automata 0.4.7",
"rustix-openpty",
"serde",
@@ -123,12 +129,13 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "alsa"
-version = "0.9.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce"
+checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
dependencies = [
"alsa-sys",
"bitflags 2.6.0",
+ "cfg-if",
"libc",
]
@@ -284,7 +291,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -535,7 +542,7 @@ checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
dependencies = [
"async-task",
"concurrent-queue",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
"futures-lite 2.3.0",
"slab",
]
@@ -571,7 +578,7 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
dependencies = [
"async-channel 2.3.1",
"async-executor",
- "async-io 2.3.3",
+ "async-io 2.3.4",
"async-lock 3.4.0",
"blocking",
"futures-lite 2.3.0",
@@ -600,9 +607,9 @@ dependencies = [
[[package]]
name = "async-io"
-version = "2.3.3"
+version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964"
+checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8"
dependencies = [
"async-lock 3.4.0",
"cfg-if",
@@ -610,11 +617,11 @@ dependencies = [
"futures-io",
"futures-lite 2.3.0",
"parking",
- "polling 3.7.2",
- "rustix 0.38.34",
+ "polling 3.7.3",
+ "rustix 0.38.35",
"slab",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -666,7 +673,7 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
dependencies = [
- "async-io 2.3.3",
+ "async-io 2.3.4",
"blocking",
"futures-lite 2.3.0",
]
@@ -693,18 +700,18 @@ dependencies = [
"cfg-if",
"event-listener 3.1.0",
"futures-lite 1.13.0",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"windows-sys 0.48.0",
]
[[package]]
name = "async-process"
-version = "2.2.3"
+version = "2.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a"
+checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374"
dependencies = [
"async-channel 2.3.1",
- "async-io 2.3.3",
+ "async-io 2.3.4",
"async-lock 3.4.0",
"async-signal",
"async-task",
@@ -712,9 +719,9 @@ dependencies = [
"cfg-if",
"event-listener 5.3.1",
"futures-lite 2.3.0",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -736,25 +743,25 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
name = "async-signal"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32"
+checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
dependencies = [
- "async-io 2.3.3",
+ "async-io 2.3.4",
"async-lock 3.4.0",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"signal-hook-registry",
"slab",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -804,7 +811,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -856,7 +863,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -1018,7 +1025,7 @@ dependencies = [
"aws-smithy-types",
"aws-types",
"bytes 1.7.1",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
"hex",
"http 0.2.12",
"ring",
@@ -1031,9 +1038,9 @@ dependencies = [
[[package]]
name = "aws-credential-types"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9"
+checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da"
dependencies = [
"aws-smithy-async",
"aws-smithy-runtime-api",
@@ -1043,20 +1050,21 @@ dependencies = [
[[package]]
name = "aws-runtime"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6"
+checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f"
dependencies = [
"aws-credential-types",
"aws-sigv4",
"aws-smithy-async",
"aws-smithy-eventstream",
"aws-smithy-http",
+ "aws-smithy-runtime",
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
"bytes 1.7.1",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
"http 0.2.12",
"http-body 0.4.6",
"once_cell",
@@ -1068,9 +1076,9 @@ dependencies = [
[[package]]
name = "aws-sdk-s3"
-version = "1.46.0"
+version = "1.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4abf69a87be33b6f125a93d5046b5f7395c26d1f449bf8d3927f5577463b6de0"
+checksum = "cca49303c05d2a740b8a4552fac63a4db6ead84f7e7eeed04761fd3014c26f25"
dependencies = [
"ahash 0.8.11",
"aws-credential-types",
@@ -1087,7 +1095,7 @@ dependencies = [
"aws-smithy-xml",
"aws-types",
"bytes 1.7.1",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
"hex",
"hmac",
"http 0.2.12",
@@ -1103,9 +1111,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
-version = "1.37.0"
+version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1074e818fbe4f9169242d78448b15be8916a79daa38ea1231f2e2e10d993fcd2"
+checksum = "e5879bec6e74b648ce12f6085e7245417bc5f6d672781028384d2e494be3eb6d"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1125,9 +1133,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
-version = "1.38.0"
+version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29755c51e33fa3f678598f64324a169cf4b7d3c4865d2709d4308f53366a92a4"
+checksum = "4ef4cd9362f638c22a3b959fd8df292e7e47fdf170270f86246b97109b5f2f7d"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1147,9 +1155,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
-version = "1.37.0"
+version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e52dc3fd7dfa6c01a69cf3903e00aa467261639138a05b06cd92314d2c8fb07"
+checksum = "0b1e2735d2ab28b35ecbb5496c9d41857f52a0d6a0075bbf6a8af306045ea6f6"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1242,9 +1250,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http"
-version = "0.60.9"
+version = "0.60.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e"
+checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728"
dependencies = [
"aws-smithy-eventstream",
"aws-smithy-runtime-api",
@@ -1282,16 +1290,16 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime"
-version = "1.6.3"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225"
+checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87"
dependencies = [
"aws-smithy-async",
"aws-smithy-http",
"aws-smithy-runtime-api",
"aws-smithy-types",
"bytes 1.7.1",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
"h2",
"http 0.2.12",
"http-body 0.4.6",
@@ -1326,9 +1334,9 @@ dependencies = [
[[package]]
name = "aws-smithy-types"
-version = "1.2.2"
+version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8"
+checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543"
dependencies = [
"base64-simd",
"bytes 1.7.1",
@@ -1457,8 +1465,8 @@ dependencies = [
"cc",
"cfg-if",
"libc",
- "miniz_oxide",
- "object 0.36.2",
+ "miniz_oxide 0.7.4",
+ "object 0.36.4",
"rustc-demangle",
]
@@ -1541,7 +1549,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
- "syn 2.0.72",
+ "syn 2.0.76",
"which 4.4.2",
]
@@ -1562,7 +1570,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -1618,9 +1626,9 @@ dependencies = [
[[package]]
name = "bitstream-io"
-version = "2.5.0"
+version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499"
+checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
[[package]]
name = "bitvec"
@@ -1671,7 +1679,7 @@ source = "git+https://github.com/kvark/blade?rev=b37a9a994709d256f4634efd29281c7
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -1742,7 +1750,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
"syn_derive",
]
@@ -1812,22 +1820,22 @@ dependencies = [
[[package]]
name = "bytemuck"
-version = "1.16.3"
+version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83"
+checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
-version = "1.7.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b"
+checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -1900,8 +1908,8 @@ checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
dependencies = [
"bitflags 2.6.0",
"log",
- "polling 3.7.2",
- "rustix 0.38.34",
+ "polling 3.7.3",
+ "rustix 0.38.35",
"slab",
"thiserror",
]
@@ -1913,16 +1921,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
dependencies = [
"calloop",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"wayland-backend",
"wayland-client",
]
[[package]]
name = "camino"
-version = "1.1.7"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
+checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
dependencies = [
"serde",
]
@@ -1947,7 +1955,7 @@ checksum = "f83ae11f116bcbafc5327c6af250341db96b5930046732e1905f7dc65887e0e1"
dependencies = [
"cap-primitives",
"cap-std",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"smallvec",
]
@@ -1963,7 +1971,7 @@ dependencies = [
"io-lifetimes 2.0.3",
"ipnet",
"maybe-owned",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"windows-sys 0.52.0",
"winx",
]
@@ -1987,7 +1995,7 @@ dependencies = [
"cap-primitives",
"io-extras",
"io-lifetimes 2.0.3",
- "rustix 0.38.34",
+ "rustix 0.38.35",
]
[[package]]
@@ -2000,7 +2008,7 @@ dependencies = [
"cap-primitives",
"iana-time-zone",
"once_cell",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"winx",
]
@@ -2078,12 +2086,13 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.1.7"
+version = "1.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
dependencies = [
"jobserver",
"libc",
+ "shlex",
]
[[package]]
@@ -2247,9 +2256,9 @@ dependencies = [
[[package]]
name = "clap_complete"
-version = "4.5.23"
+version = "4.5.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "531d7959c5bbb6e266cecdd0f20213639c3a5c3e4d615f97db87661745f781ff"
+checksum = "6d7db6eca8c205649e8d3ccd05aa5042b1800a784e56bc7c43524fde8abbfa9b"
dependencies = [
"clap",
]
@@ -2263,7 +2272,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -2901,18 +2910,18 @@ dependencies = [
[[package]]
name = "cpp_demangle"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119"
+checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d"
dependencies = [
"cfg-if",
]
[[package]]
name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
dependencies = [
"libc",
]
@@ -3185,7 +3194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
dependencies = [
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -3365,7 +3374,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -3537,9 +3546,9 @@ dependencies = [
[[package]]
name = "dwrote"
-version = "0.11.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
+checksum = "2da3498378ed373237bdef1eddcc64e7be2d3ba4841f4c22a998e81cadeea83c"
dependencies = [
"lazy_static",
"libc",
@@ -3685,6 +3694,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
+[[package]]
+name = "embedded-io"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
+
[[package]]
name = "emojis"
version = "0.6.3"
@@ -3733,7 +3748,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -3838,9 +3853,9 @@ dependencies = [
[[package]]
name = "euclid"
-version = "0.22.10"
+version = "0.22.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20"
+checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48"
dependencies = [
"num-traits",
]
@@ -3903,7 +3918,7 @@ dependencies = [
"flume",
"half",
"lebe",
- "miniz_oxide",
+ "miniz_oxide 0.7.4",
"rayon-core",
"smallvec",
"zune-inflate",
@@ -4042,9 +4057,9 @@ dependencies = [
[[package]]
name = "fastrand"
-version = "2.1.0"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fd-lock"
@@ -4053,7 +4068,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
dependencies = [
"cfg-if",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"windows-sys 0.52.0",
]
@@ -4165,14 +4180,14 @@ dependencies = [
[[package]]
name = "filetime"
-version = "0.2.23"
+version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.4.1",
- "windows-sys 0.52.0",
+ "libredox",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -4183,12 +4198,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flate2"
-version = "1.0.31"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920"
+checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
dependencies = [
"crc32fast",
- "miniz_oxide",
+ "miniz_oxide 0.8.0",
]
[[package]]
@@ -4304,7 +4319,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -4386,7 +4401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb"
dependencies = [
"io-lifetimes 2.0.3",
- "rustix 0.38.34",
+ "rustix 0.38.35",
"windows-sys 0.52.0",
]
@@ -4532,7 +4547,7 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
dependencies = [
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
"futures-core",
"futures-io",
"parking",
@@ -4547,7 +4562,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -4666,7 +4681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
dependencies = [
"fallible-iterator",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"stable_deref_trait",
]
@@ -4972,7 +4987,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"slab",
"tokio",
"tokio-util",
@@ -5241,7 +5256,7 @@ dependencies = [
"markup5ever",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -5595,9 +5610,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.3.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
@@ -5624,7 +5639,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -5709,7 +5724,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -5759,7 +5774,7 @@ dependencies = [
"fnv",
"lazy_static",
"libc",
- "mio 1.0.1",
+ "mio 1.0.2",
"rand 0.8.5",
"serde",
"tempfile",
@@ -5784,11 +5799,11 @@ dependencies = [
[[package]]
name = "is-terminal"
-version = "0.4.12"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
dependencies = [
- "hermit-abi 0.3.9",
+ "hermit-abi 0.4.0",
"libc",
"windows-sys 0.52.0",
]
@@ -5924,9 +5939,9 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
[[package]]
name = "js-sys"
-version = "0.3.69"
+version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
dependencies = [
"wasm-bindgen",
]
@@ -6285,6 +6300,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
+ "redox_syscall 0.5.3",
]
[[package]]
@@ -6300,9 +6316,9 @@ dependencies = [
[[package]]
name = "libz-sys"
-version = "1.1.18"
+version = "1.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
+checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
dependencies = [
"cc",
"libc",
@@ -6336,7 +6352,7 @@ checksum = "cb26336e6dc7cc76e7927d2c9e7e3bb376d7af65a6f56a0b16c47d18a9b1abc5"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -6689,7 +6705,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
dependencies = [
- "rustix 0.38.34",
+ "rustix 0.38.35",
]
[[package]]
@@ -6774,6 +6790,15 @@ dependencies = [
"simd-adler32",
]
+[[package]]
+name = "miniz_oxide"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+dependencies = [
+ "adler2",
+]
+
[[package]]
name = "mint"
version = "0.5.9"
@@ -6794,9 +6819,9 @@ dependencies = [
[[package]]
name = "mio"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
"hermit-abi 0.3.9",
"libc",
@@ -6858,7 +6883,7 @@ dependencies = [
"cfg_aliases 0.1.1",
"codespan-reporting",
"hexf-parse",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"log",
"rustc-hash",
"spirv",
@@ -7142,7 +7167,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -7224,7 +7249,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -7269,15 +7294,15 @@ checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
dependencies = [
"crc32fast",
"hashbrown 0.14.5",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"memchr",
]
[[package]]
name = "object"
-version = "0.36.2"
+version = "0.36.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
+checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a"
dependencies = [
"memchr",
]
@@ -7332,7 +7357,7 @@ checksum = "8fc6ce4692fbfd044ce22ca07dcab1a30fa12432ca2aa5b1294eca50d3332a24"
dependencies = [
"aes",
"async-fs 2.1.2",
- "async-io 2.3.3",
+ "async-io 2.3.4",
"async-lock 3.4.0",
"async-net 2.0.0",
"blocking",
@@ -7423,7 +7448,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -7509,7 +7534,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -7551,12 +7576,14 @@ dependencies = [
"language",
"log",
"menu",
+ "pretty_assertions",
"project",
"schemars",
"search",
"serde",
"serde_json",
"settings",
+ "smol",
"theme",
"util",
"workspace",
@@ -7616,7 +7643,7 @@ dependencies = [
"by_address",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -7807,7 +7834,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -7828,7 +7855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
]
[[package]]
@@ -7881,7 +7908,7 @@ dependencies = [
"phf_shared 0.11.2",
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -7941,7 +7968,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.72",
+ "syn 2.0.76",
]
[[package]]
@@ -30,10 +30,15 @@ search.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
+smol.workspace = true
theme.workspace = true
util.workspace = true
worktree.workspace = true
workspace.workspace = true
+[dev-dependencies]
+search = { workspace = true, features = ["test-support"] }
+pretty_assertions.workspace = true
+
[package.metadata.cargo-machete]
ignored = ["log"]
@@ -3,9 +3,10 @@ mod outline_panel_settings;
use std::{
cell::OnceCell,
cmp,
+ hash::Hash,
ops::Range,
path::{Path, PathBuf},
- sync::{atomic::AtomicBool, Arc},
+ sync::{atomic::AtomicBool, Arc, OnceLock},
time::Duration,
u32,
};
@@ -16,7 +17,7 @@ use db::kvp::KEY_VALUE_STORE;
use editor::{
display_map::ToDisplayPoint,
items::{entry_git_aware_label_color, entry_label_color},
- scroll::{Autoscroll, ScrollAnchor},
+ scroll::{Autoscroll, AutoscrollStrategy, ScrollAnchor},
AnchorRangeExt, Bias, DisplayPoint, Editor, EditorEvent, EditorMode, ExcerptId, ExcerptRange,
MultiBufferSnapshot, RangeToAnchorExt,
};
@@ -39,8 +40,9 @@ use project::{File, Fs, Item, Project};
use search::{BufferSearchBar, ProjectSearchView};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
+use smol::channel;
use theme::SyntaxTheme;
-use util::{RangeExt, ResultExt, TryFutureExt};
+use util::{debug_panic, RangeExt, ResultExt, TryFutureExt};
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
item::ItemHandle,
@@ -50,7 +52,7 @@ use workspace::{
HighlightedLabel, Icon, IconButton, IconButtonShape, IconName, IconSize, Label,
LabelCommon, ListItem, Selectable, Spacing, StyledExt, StyledTypography, Tooltip,
},
- OpenInTerminal, Workspace,
+ OpenInTerminal, WeakItemHandle, Workspace,
};
use worktree::{Entry, ProjectEntryId, WorktreeId};
@@ -83,6 +85,7 @@ const OUTLINE_PANEL_KEY: &str = "OutlinePanel";
const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
type Outline = OutlineItem<language::Anchor>;
+type HighlightStyleData = Arc<OnceLock<Vec<(Range<usize>, HighlightStyle)>>>;
pub struct OutlinePanel {
fs: Arc<dyn Fs>,
@@ -112,8 +115,140 @@ pub struct OutlinePanel {
cached_entries: Vec<CachedEntry>,
filter_editor: View<Editor>,
mode: ItemsDisplayMode,
- search: Option<(SearchKind, String)>,
- search_matches: Vec<Range<editor::Anchor>>,
+}
+
+enum ItemsDisplayMode {
+ Search(SearchState),
+ Outline,
+}
+
+struct SearchState {
+ kind: SearchKind,
+ query: String,
+ matches: Vec<(Range<editor::Anchor>, OnceCell<Arc<SearchData>>)>,
+ highlight_search_match_tx: channel::Sender<HighlightArguments>,
+ _search_match_highlighter: Task<()>,
+ _search_match_notify: Task<()>,
+}
+
+struct HighlightArguments {
+ multi_buffer_snapshot: MultiBufferSnapshot,
+ search_data: Arc<SearchData>,
+}
+
+impl SearchState {
+ fn new(
+ kind: SearchKind,
+ query: String,
+ new_matches: Vec<Range<editor::Anchor>>,
+ theme: Arc<SyntaxTheme>,
+ cx: &mut ViewContext<'_, OutlinePanel>,
+ ) -> Self {
+ let (highlight_search_match_tx, highlight_search_match_rx) = channel::unbounded();
+ let (notify_tx, notify_rx) = channel::bounded::<()>(1);
+ Self {
+ kind,
+ query,
+ matches: new_matches
+ .into_iter()
+ .map(|range| (range, OnceCell::new()))
+ .collect(),
+ highlight_search_match_tx,
+ _search_match_highlighter: cx.background_executor().spawn(async move {
+ while let Some(highlight_arguments) = highlight_search_match_rx.recv().await.ok() {
+ let highlight_data = &highlight_arguments.search_data.highlights_data;
+ if highlight_data.get().is_some() {
+ continue;
+ }
+ let mut left_whitespaces_count = 0;
+ let mut non_whitespace_symbol_occurred = false;
+ let context_offset_range = highlight_arguments
+ .search_data
+ .context_range
+ .to_offset(&highlight_arguments.multi_buffer_snapshot);
+ let mut offset = context_offset_range.start;
+ let mut context_text = String::new();
+ let mut highlight_ranges = Vec::new();
+ for mut chunk in highlight_arguments
+ .multi_buffer_snapshot
+ .chunks(context_offset_range.start..context_offset_range.end, true)
+ {
+ if !non_whitespace_symbol_occurred {
+ for c in chunk.text.chars() {
+ if c.is_whitespace() {
+ left_whitespaces_count += c.len_utf8();
+ } else {
+ non_whitespace_symbol_occurred = true;
+ break;
+ }
+ }
+ }
+
+ if chunk.text.len() > context_offset_range.end - offset {
+ chunk.text = &chunk.text[0..(context_offset_range.end - offset)];
+ offset = context_offset_range.end;
+ } else {
+ offset += chunk.text.len();
+ }
+ let style = chunk
+ .syntax_highlight_id
+ .and_then(|highlight| highlight.style(&theme));
+ if let Some(style) = style {
+ let start = context_text.len();
+ let end = start + chunk.text.len();
+ highlight_ranges.push((start..end, style));
+ }
+ context_text.push_str(chunk.text);
+ if offset >= context_offset_range.end {
+ break;
+ }
+ }
+
+ highlight_ranges.iter_mut().for_each(|(range, _)| {
+ range.start = range.start.saturating_sub(left_whitespaces_count);
+ range.end = range.end.saturating_sub(left_whitespaces_count);
+ });
+ if highlight_data.set(highlight_ranges).ok().is_some() {
+ notify_tx.try_send(()).ok();
+ }
+
+ let trimmed_text = context_text[left_whitespaces_count..].to_owned();
+ debug_assert_eq!(
+ trimmed_text, highlight_arguments.search_data.context_text,
+ "Highlighted text that does not match the buffer text"
+ );
+ }
+ }),
+ _search_match_notify: cx.spawn(|outline_panel, mut cx| async move {
+ while let Some(()) = notify_rx.recv().await.ok() {
+ let update_result = outline_panel.update(&mut cx, |_, cx| {
+ cx.notify();
+ });
+ if update_result.is_err() {
+ break;
+ }
+ }
+ }),
+ }
+ }
+
+ fn highlight_search_match(
+ &mut self,
+ match_range: &Range<editor::Anchor>,
+ multi_buffer_snapshot: &MultiBufferSnapshot,
+ ) {
+ if let Some((_, search_data)) = self.matches.iter().find(|(range, _)| range == match_range)
+ {
+ let search_data = search_data
+ .get_or_init(|| Arc::new(SearchData::new(match_range, multi_buffer_snapshot)));
+ self.highlight_search_match_tx
+ .send_blocking(HighlightArguments {
+ multi_buffer_snapshot: multi_buffer_snapshot.clone(),
+ search_data: Arc::clone(search_data),
+ })
+ .ok();
+ }
+ }
}
#[derive(Debug)]
@@ -137,12 +272,6 @@ impl SelectedEntry {
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum ItemsDisplayMode {
- Search,
- Outline,
-}
-
#[derive(Debug, Clone, Copy, Default)]
struct FsChildren {
files: usize,
@@ -218,12 +347,11 @@ enum PanelEntry {
#[derive(Clone, Debug)]
struct SearchEntry {
match_range: Range<editor::Anchor>,
- same_line_matches: Vec<Range<editor::Anchor>>,
kind: SearchKind,
- render_data: Option<OnceCell<SearchData>>,
+ render_data: Arc<SearchData>,
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum SearchKind {
Project,
Buffer,
@@ -233,8 +361,10 @@ enum SearchKind {
struct SearchData {
context_range: Range<editor::Anchor>,
context_text: String,
- highlight_ranges: Vec<(Range<usize>, HighlightStyle)>,
+ truncated_left: bool,
+ truncated_right: bool,
search_match_indices: Vec<Range<usize>>,
+ highlights_data: HighlightStyleData,
}
impl PartialEq for PanelEntry {
@@ -262,87 +392,103 @@ impl PartialEq for PanelEntry {
impl Eq for PanelEntry {}
+const SEARCH_MATCH_CONTEXT_SIZE: u32 = 40;
+const TRUNCATED_CONTEXT_MARK: &str = "…";
+
impl SearchData {
fn new(
- kind: SearchKind,
match_range: &Range<editor::Anchor>,
multi_buffer_snapshot: &MultiBufferSnapshot,
- theme: &SyntaxTheme,
) -> Self {
let match_point_range = match_range.to_point(&multi_buffer_snapshot);
- let entire_row_range_start = language::Point::new(match_point_range.start.row, 0);
- let entire_row_range_end = multi_buffer_snapshot.clip_point(
- language::Point::new(match_point_range.end.row, u32::MAX),
+ let context_left_border = multi_buffer_snapshot.clip_point(
+ language::Point::new(
+ match_point_range.start.row,
+ match_point_range
+ .start
+ .column
+ .saturating_sub(SEARCH_MATCH_CONTEXT_SIZE),
+ ),
+ Bias::Left,
+ );
+ let context_right_border = multi_buffer_snapshot.clip_point(
+ language::Point::new(
+ match_point_range.end.row,
+ match_point_range.end.column + SEARCH_MATCH_CONTEXT_SIZE,
+ ),
Bias::Right,
);
- let entire_row_range =
- (entire_row_range_start..entire_row_range_end).to_anchors(&multi_buffer_snapshot);
- let entire_row_offset_range = entire_row_range.to_offset(&multi_buffer_snapshot);
+
+ let context_anchor_range =
+ (context_left_border..context_right_border).to_anchors(&multi_buffer_snapshot);
+ let context_offset_range = context_anchor_range.to_offset(&multi_buffer_snapshot);
let match_offset_range = match_range.to_offset(&multi_buffer_snapshot);
+
let mut search_match_indices = vec![
- match_offset_range.start - entire_row_offset_range.start
- ..match_offset_range.end - entire_row_offset_range.start,
+ multi_buffer_snapshot.clip_offset(
+ match_offset_range.start - context_offset_range.start,
+ Bias::Left,
+ )
+ ..multi_buffer_snapshot.clip_offset(
+ match_offset_range.end - context_offset_range.start,
+ Bias::Right,
+ ),
];
- let mut left_whitespaces_count = 0;
- let mut non_whitespace_symbol_occurred = false;
- let mut offset = entire_row_offset_range.start;
- let mut entire_row_text = String::new();
- let mut highlight_ranges = Vec::new();
- for mut chunk in multi_buffer_snapshot.chunks(
- entire_row_offset_range.start..entire_row_offset_range.end,
- true,
- ) {
- if !non_whitespace_symbol_occurred {
- for c in chunk.text.chars() {
- if c.is_whitespace() {
- left_whitespaces_count += 1;
- } else {
- non_whitespace_symbol_occurred = true;
- break;
- }
- }
- }
-
- if chunk.text.len() > entire_row_offset_range.end - offset {
- chunk.text = &chunk.text[0..(entire_row_offset_range.end - offset)];
- offset = entire_row_offset_range.end;
- } else {
- offset += chunk.text.len();
- }
- let style = chunk
- .syntax_highlight_id
- .and_then(|highlight| highlight.style(theme));
- if let Some(style) = style {
- let start = entire_row_text.len();
- let end = start + chunk.text.len();
- highlight_ranges.push((start..end, style));
- }
- entire_row_text.push_str(chunk.text);
- if offset >= entire_row_offset_range.end {
- break;
- }
- }
-
- if let SearchKind::Buffer = kind {
- left_whitespaces_count = 0;
- }
- highlight_ranges.iter_mut().for_each(|(range, _)| {
- range.start = range.start.saturating_sub(left_whitespaces_count);
- range.end = range.end.saturating_sub(left_whitespaces_count);
- });
+ let entire_context_text = multi_buffer_snapshot
+ .text_for_range(context_offset_range.clone())
+ .collect::<String>();
+ let left_whitespaces_offset = entire_context_text
+ .chars()
+ .take_while(|c| c.is_whitespace())
+ .map(|c| c.len_utf8())
+ .sum::<usize>();
+
+ let mut extended_context_left_border = context_left_border;
+ extended_context_left_border.column = extended_context_left_border.column.saturating_sub(1);
+ let extended_context_left_border =
+ multi_buffer_snapshot.clip_point(extended_context_left_border, Bias::Left);
+ let mut extended_context_right_border = context_right_border;
+ extended_context_right_border.column += 1;
+ let extended_context_right_border =
+ multi_buffer_snapshot.clip_point(extended_context_right_border, Bias::Right);
+
+ let truncated_left = left_whitespaces_offset == 0
+ && extended_context_left_border < context_left_border
+ && multi_buffer_snapshot
+ .chars_at(extended_context_left_border)
+ .last()
+ .map_or(false, |c| !c.is_whitespace());
+ let truncated_right = entire_context_text
+ .chars()
+ .last()
+ .map_or(true, |c| !c.is_whitespace())
+ && extended_context_right_border > context_right_border
+ && multi_buffer_snapshot
+ .chars_at(extended_context_right_border)
+ .next()
+ .map_or(false, |c| !c.is_whitespace());
search_match_indices.iter_mut().for_each(|range| {
- range.start = range.start.saturating_sub(left_whitespaces_count);
- range.end = range.end.saturating_sub(left_whitespaces_count);
+ range.start = multi_buffer_snapshot.clip_offset(
+ range.start.saturating_sub(left_whitespaces_offset),
+ Bias::Left,
+ );
+ range.end = multi_buffer_snapshot.clip_offset(
+ range.end.saturating_sub(left_whitespaces_offset),
+ Bias::Right,
+ );
});
+
let trimmed_row_offset_range =
- entire_row_offset_range.start + left_whitespaces_count..entire_row_offset_range.end;
- let trimmed_text = entire_row_text[left_whitespaces_count..].to_owned();
+ context_offset_range.start + left_whitespaces_offset..context_offset_range.end;
+ let trimmed_text = entire_context_text[left_whitespaces_offset..].to_owned();
Self {
- highlight_ranges,
+ highlights_data: Arc::default(),
search_match_indices,
context_range: trimmed_row_offset_range.to_anchors(&multi_buffer_snapshot),
context_text: trimmed_text,
+ truncated_left,
+ truncated_right,
}
}
}
@@ -376,7 +522,27 @@ impl PartialEq for FsEntry {
}
}
+impl Hash for FsEntry {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ match self {
+ Self::ExternalFile(buffer_id, _) => {
+ buffer_id.hash(state);
+ }
+ Self::Directory(worktree_id, entry) => {
+ worktree_id.hash(state);
+ entry.id.hash(state);
+ }
+ Self::File(worktree_id, entry, buffer_id, _) => {
+ worktree_id.hash(state);
+ entry.id.hash(state);
+ buffer_id.hash(state);
+ }
+ }
+ }
+}
+
struct ActiveItem {
+ item_handle: Box<dyn WeakItemHandle>,
active_editor: WeakView<Editor>,
_buffer_search_subscription: Subscription,
_editor_subscrpiption: Subscription,
@@ -464,11 +630,15 @@ impl OutlinePanel {
.expect("have a &mut Workspace"),
move |outline_panel, workspace, event, cx| {
if let workspace::Event::ActiveItemChanged = event {
- if let Some(new_active_editor) =
+ if let Some((new_active_item, new_active_editor)) =
workspace_active_editor(workspace.read(cx), cx)
{
- if outline_panel.should_replace_active_editor(&new_active_editor) {
- outline_panel.replace_active_editor(new_active_editor, cx);
+ if outline_panel.should_replace_active_item(new_active_item.as_ref()) {
+ outline_panel.replace_active_editor(
+ new_active_item,
+ new_active_editor,
+ cx,
+ );
}
} else {
outline_panel.clear_previous(cx);
@@ -502,8 +672,6 @@ impl OutlinePanel {
focus_handle,
filter_editor,
fs_entries: Vec::new(),
- search_matches: Vec::new(),
- search: None,
fs_entries_depth: HashMap::default(),
fs_children_count: HashMap::default(),
collapsed_entries: HashSet::default(),
@@ -528,8 +696,8 @@ impl OutlinePanel {
filter_update_subscription,
],
};
- if let Some(editor) = workspace_active_editor(workspace, cx) {
- outline_panel.replace_active_editor(editor, cx);
+ if let Some((item, editor)) = workspace_active_editor(workspace, cx) {
+ outline_panel.replace_active_editor(item, editor, cx);
}
outline_panel
});
@@ -691,12 +859,22 @@ impl OutlinePanel {
};
if let Some((offset, anchor)) = scroll_target {
+ self.workspace
+ .update(cx, |workspace, cx| match self.active_item() {
+ Some(active_item) => {
+ workspace.activate_item(active_item.as_ref(), true, change_selection, cx)
+ }
+ None => workspace.activate_item(&active_editor, true, change_selection, cx),
+ });
+
self.select_entry(entry.clone(), true, cx);
if change_selection {
active_editor.update(cx, |editor, cx| {
- editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
- s.select_ranges(Some(anchor..anchor))
- });
+ editor.change_selections(
+ Some(Autoscroll::Strategy(AutoscrollStrategy::Top)),
+ cx,
+ |s| s.select_ranges(Some(anchor..anchor)),
+ );
});
active_editor.focus_handle(cx).focus(cx);
} else {
@@ -705,20 +883,6 @@ impl OutlinePanel {
});
self.focus_handle.focus(cx);
}
-
- if let PanelEntry::Search(_) = entry {
- if let Some(active_project_search) =
- self.active_project_search(Some(&active_editor), cx)
- {
- self.workspace.update(cx, |workspace, cx| {
- workspace.activate_item(&active_project_search, true, change_selection, cx)
- });
- }
- } else {
- self.workspace.update(cx, |workspace, cx| {
- workspace.activate_item(&active_editor, true, change_selection, cx)
- });
- };
}
}
@@ -751,7 +915,7 @@ impl OutlinePanel {
}) {
self.select_entry(entry_to_select, true, cx);
} else {
- self.select_first(&SelectFirst {}, cx)
+ self.select_last(&SelectLast, cx)
}
}
@@ -778,7 +942,7 @@ impl OutlinePanel {
PanelEntry::FoldedDirs(dirs_worktree_id, dirs) => {
dirs_worktree_id == worktree_id
&& dirs
- .first()
+ .last()
.map_or(false, |dir| dir.path.as_ref() == parent_path)
}
_ => false,
@@ -1672,15 +1836,23 @@ impl OutlinePanel {
)
}
+ #[allow(clippy::too_many_arguments)]
fn render_search_match(
- &self,
+ &mut self,
+ multi_buffer_snapshot: Option<&MultiBufferSnapshot>,
match_range: &Range<editor::Anchor>,
- search_data: &SearchData,
+ search_data: &Arc<SearchData>,
kind: SearchKind,
depth: usize,
string_match: Option<&StringMatch>,
cx: &mut ViewContext<Self>,
) -> Stateful<Div> {
+ if let ItemsDisplayMode::Search(search_state) = &mut self.mode {
+ if let Some(multi_buffer_snapshot) = multi_buffer_snapshot {
+ search_state.highlight_search_match(match_range, multi_buffer_snapshot);
+ }
+ }
+
let search_matches = string_match
.iter()
.flat_map(|string_match| string_match.ranges())
@@ -1696,14 +1868,29 @@ impl OutlinePanel {
annotation_range: None,
range: search_data.context_range.clone(),
text: search_data.context_text.clone(),
- highlight_ranges: search_data.highlight_ranges.clone(),
+ highlight_ranges: search_data
+ .highlights_data
+ .get()
+ .cloned()
+ .unwrap_or_default(),
name_ranges: search_data.search_match_indices.clone(),
body_range: Some(search_data.context_range.clone()),
},
match_ranges.into_iter().cloned(),
cx,
- )
- .into_any_element();
+ );
+ let truncated_contents_label = || Label::new(TRUNCATED_CONTEXT_MARK);
+ let entire_label = h_flex()
+ .justify_center()
+ .p_0()
+ .when(search_data.truncated_left, |parent| {
+ parent.child(truncated_contents_label())
+ })
+ .child(label_element)
+ .when(search_data.truncated_right, |parent| {
+ parent.child(truncated_contents_label())
+ })
+ .into_any_element();
let is_active = match self.selected_entry() {
Some(PanelEntry::Search(SearchEntry {
@@ -1716,14 +1903,13 @@ impl OutlinePanel {
PanelEntry::Search(SearchEntry {
kind,
match_range: match_range.clone(),
- same_line_matches: Vec::new(),
- render_data: Some(OnceCell::new()),
+ render_data: Arc::clone(search_data),
}),
ElementId::from(SharedString::from(format!("search-{match_range:?}"))),
depth,
None,
is_active,
- label_element,
+ entire_label,
cx,
)
}
@@ -2156,20 +2342,24 @@ impl OutlinePanel {
fn replace_active_editor(
&mut self,
+ new_active_item: Box<dyn ItemHandle>,
new_active_editor: View<Editor>,
cx: &mut ViewContext<Self>,
) {
self.clear_previous(cx);
let buffer_search_subscription = cx.subscribe(
&new_active_editor,
- |outline_panel: &mut Self, _, _: &SearchEvent, cx: &mut ViewContext<'_, Self>| {
- outline_panel.update_search_matches(cx);
+ |outline_panel: &mut Self, _, e: &SearchEvent, cx: &mut ViewContext<'_, Self>| {
+ if matches!(e, SearchEvent::MatchesInvalidated) {
+ outline_panel.update_search_matches(cx);
+ };
outline_panel.autoscroll(cx);
},
);
self.active_item = Some(ActiveItem {
_buffer_search_subscription: buffer_search_subscription,
_editor_subscrpiption: subscribe_for_editor_events(&new_active_editor, cx),
+ item_handle: new_active_item.downgrade_item(),
active_editor: new_active_editor.downgrade(),
});
let new_entries =
@@ -2192,9 +2382,8 @@ impl OutlinePanel {
self.outline_fetch_tasks.clear();
self.excerpts.clear();
self.cached_entries = Vec::new();
- self.search_matches.clear();
- self.search = None;
self.pinned = false;
+ self.mode = ItemsDisplayMode::Outline;
}
fn location_for_editor_selection(
@@ -2218,12 +2407,12 @@ impl OutlinePanel {
let buffer_id = buffer.read(cx).remote_id();
let selection_display_point = selection.to_display_point(&editor_snapshot);
- match self.mode {
- ItemsDisplayMode::Search => self
- .search_matches
+ match &self.mode {
+ ItemsDisplayMode::Search(search_state) => search_state
+ .matches
.iter()
.rev()
- .min_by_key(|&match_range| {
+ .min_by_key(|&(match_range, _)| {
let match_display_range =
match_range.clone().to_display_points(&editor_snapshot);
let start_distance = if selection_display_point < match_display_range.start {
@@ -2238,17 +2427,12 @@ impl OutlinePanel {
};
start_distance + end_distance
})
- .and_then(|closest_range| {
+ .and_then(|(closest_range, _)| {
self.cached_entries.iter().find_map(|cached_entry| {
- if let PanelEntry::Search(SearchEntry {
- match_range,
- same_line_matches,
- ..
- }) = &cached_entry.entry
+ if let PanelEntry::Search(SearchEntry { match_range, .. }) =
+ &cached_entry.entry
{
- if match_range == closest_range
- || same_line_matches.contains(&closest_range)
- {
+ if match_range == closest_range {
Some(cached_entry.entry.clone())
} else {
None
@@ -2623,17 +2807,26 @@ impl OutlinePanel {
cx.spawn(|outline_panel, mut cx| async move {
let mut entries = Vec::new();
let mut match_candidates = Vec::new();
+ let mut added_contexts = HashSet::default();
let Ok(()) = outline_panel.update(&mut cx, |outline_panel, cx| {
let auto_fold_dirs = OutlinePanelSettings::get_global(cx).auto_fold_dirs;
let mut folded_dirs_entry = None::<(usize, WorktreeId, Vec<Entry>)>;
let track_matches = query.is_some();
- let mut parent_dirs = Vec::<(&Path, bool, bool, usize)>::new();
- for entry in &outline_panel.fs_entries {
- let is_expanded = outline_panel.is_expanded(entry);
- let (depth, should_add) = match entry {
+ #[derive(Debug)]
+ struct ParentStats {
+ path: Arc<Path>,
+ folded: bool,
+ expanded: bool,
+ depth: usize,
+ }
+ let mut parent_dirs = Vec::<ParentStats>::new();
+ for entry in outline_panel.fs_entries.clone() {
+ let is_expanded = outline_panel.is_expanded(&entry);
+ let (depth, should_add) = match &entry {
FsEntry::Directory(worktree_id, dir_entry) => {
+ let mut should_add = true;
let is_root = project
.read(cx)
.worktree_for_id(*worktree_id, cx)
@@ -2653,16 +2846,16 @@ impl OutlinePanel {
.get(&(*worktree_id, dir_entry.id))
.copied()
.unwrap_or(0);
- while let Some(&(previous_path, ..)) = parent_dirs.last() {
- if dir_entry.path.starts_with(previous_path) {
+ while let Some(parent) = parent_dirs.last() {
+ if dir_entry.path.starts_with(&parent.path) {
break;
}
parent_dirs.pop();
}
let auto_fold = match parent_dirs.last() {
- Some((parent_path, parent_folded, _, _)) => {
- *parent_folded
- && Some(*parent_path) == dir_entry.path.parent()
+ Some(parent) => {
+ parent.folded
+ && Some(parent.path.as_ref()) == dir_entry.path.parent()
&& outline_panel
.fs_children_count
.get(worktree_id)
@@ -2674,29 +2867,31 @@ impl OutlinePanel {
None => false,
};
let folded = folded || auto_fold;
- let (depth, parent_expanded) = match parent_dirs.last() {
- Some(&(_, previous_folded, previous_expanded, previous_depth)) => {
- let new_depth = if folded && previous_folded {
- previous_depth
+ let (depth, parent_expanded, parent_folded) = match parent_dirs.last() {
+ Some(parent) => {
+ let parent_folded = parent.folded;
+ let parent_expanded = parent.expanded;
+ let new_depth = if parent_folded {
+ parent.depth
} else {
- previous_depth + 1
+ parent.depth + 1
};
- parent_dirs.push((
- &dir_entry.path,
+ parent_dirs.push(ParentStats {
+ path: dir_entry.path.clone(),
folded,
- previous_expanded && is_expanded,
- new_depth,
- ));
- (new_depth, previous_expanded)
+ expanded: parent_expanded && is_expanded,
+ depth: new_depth,
+ });
+ (new_depth, parent_expanded, parent_folded)
}
None => {
- parent_dirs.push((
- &dir_entry.path,
+ parent_dirs.push(ParentStats {
+ path: dir_entry.path.clone(),
folded,
- is_expanded,
- fs_depth,
- ));
- (fs_depth, true)
+ expanded: is_expanded,
+ depth: fs_depth,
+ });
+ (fs_depth, true, false)
}
};
@@ -2712,27 +2907,51 @@ impl OutlinePanel {
folded_dirs_entry =
Some((folded_depth, folded_worktree_id, folded_dirs))
} else {
- if !is_singleton && (parent_expanded || query.is_some()) {
- let new_folded_dirs =
- PanelEntry::FoldedDirs(folded_worktree_id, folded_dirs);
- outline_panel.push_entry(
- &mut entries,
- &mut match_candidates,
- track_matches,
- new_folded_dirs,
- folded_depth,
- cx,
- );
+ if !is_singleton {
+ let start_of_collapsed_dir_sequence = !parent_expanded
+ && parent_dirs
+ .iter()
+ .rev()
+ .skip(folded_dirs.len() + 1)
+ .next()
+ .map_or(true, |parent| parent.expanded);
+ if start_of_collapsed_dir_sequence
+ || parent_expanded
+ || query.is_some()
+ {
+ if parent_folded {
+ folded_dirs.push(dir_entry.clone());
+ should_add = false;
+ }
+ let new_folded_dirs = PanelEntry::FoldedDirs(
+ folded_worktree_id,
+ folded_dirs,
+ );
+ outline_panel.push_entry(
+ &mut entries,
+ &mut match_candidates,
+ &mut added_contexts,
+ track_matches,
+ new_folded_dirs,
+ folded_depth,
+ cx,
+ );
+ }
}
- folded_dirs_entry =
+
+ folded_dirs_entry = if parent_folded {
+ None
+ } else {
Some((depth, *worktree_id, vec![dir_entry.clone()]))
+ };
}
} else if folded {
folded_dirs_entry =
Some((depth, *worktree_id, vec![dir_entry.clone()]));
}
- let should_add = parent_expanded && folded_dirs_entry.is_none();
+ let should_add =
+ should_add && parent_expanded && folded_dirs_entry.is_none();
(depth, should_add)
}
FsEntry::ExternalFile(..) => {
@@ -2742,16 +2961,15 @@ impl OutlinePanel {
let parent_expanded = parent_dirs
.iter()
.rev()
- .find(|(parent_path, ..)| {
- folded_dirs
- .iter()
- .all(|entry| entry.path.as_ref() != *parent_path)
+ .find(|parent| {
+ folded_dirs.iter().all(|entry| entry.path != parent.path)
})
- .map_or(true, |&(_, _, parent_expanded, _)| parent_expanded);
+ .map_or(true, |parent| parent.expanded);
if !is_singleton && (parent_expanded || query.is_some()) {
outline_panel.push_entry(
&mut entries,
&mut match_candidates,
+ &mut added_contexts,
track_matches,
PanelEntry::FoldedDirs(worktree_id, folded_dirs),
folded_depth,
@@ -2769,16 +2987,15 @@ impl OutlinePanel {
let parent_expanded = parent_dirs
.iter()
.rev()
- .find(|(parent_path, ..)| {
- folded_dirs
- .iter()
- .all(|entry| entry.path.as_ref() != *parent_path)
+ .find(|parent| {
+ folded_dirs.iter().all(|entry| entry.path != parent.path)
})
- .map_or(true, |&(_, _, parent_expanded, _)| parent_expanded);
+ .map_or(true, |parent| parent.expanded);
if !is_singleton && (parent_expanded || query.is_some()) {
outline_panel.push_entry(
&mut entries,
&mut match_candidates,
+ &mut added_contexts,
track_matches,
PanelEntry::FoldedDirs(worktree_id, folded_dirs),
folded_depth,
@@ -2792,16 +3009,16 @@ impl OutlinePanel {
.get(&(*worktree_id, file_entry.id))
.copied()
.unwrap_or(0);
- while let Some(&(previous_path, ..)) = parent_dirs.last() {
- if file_entry.path.starts_with(previous_path) {
+ while let Some(parent) = parent_dirs.last() {
+ if file_entry.path.starts_with(&parent.path) {
break;
}
parent_dirs.pop();
}
let (depth, should_add) = match parent_dirs.last() {
- Some(&(_, _, previous_expanded, previous_depth)) => {
- let new_depth = previous_depth + 1;
- (new_depth, previous_expanded)
+ Some(parent) => {
+ let new_depth = parent.depth + 1;
+ (new_depth, parent.expanded)
}
None => (fs_depth, true),
};
@@ -2815,6 +3032,7 @@ impl OutlinePanel {
outline_panel.push_entry(
&mut entries,
&mut match_candidates,
+ &mut added_contexts,
track_matches,
PanelEntry::Fs(entry.clone()),
depth,
@@ -2823,15 +3041,16 @@ impl OutlinePanel {
}
match outline_panel.mode {
- ItemsDisplayMode::Search => {
+ ItemsDisplayMode::Search(_) => {
if is_singleton || query.is_some() || (should_add && is_expanded) {
outline_panel.add_search_entries(
- entry,
- depth,
- track_matches,
- is_singleton,
&mut entries,
&mut match_candidates,
+ &mut added_contexts,
+ entry.clone(),
+ depth,
+ query.clone(),
+ is_singleton,
cx,
);
}
@@ -2839,7 +3058,7 @@ impl OutlinePanel {
ItemsDisplayMode::Outline => {
let excerpts_to_consider =
if is_singleton || query.is_some() || (should_add && is_expanded) {
- match entry {
+ match &entry {
FsEntry::File(_, _, buffer_id, entry_excerpts) => {
Some((*buffer_id, entry_excerpts))
}
@@ -2854,13 +3073,14 @@ impl OutlinePanel {
if let Some((buffer_id, entry_excerpts)) = excerpts_to_consider {
outline_panel.add_excerpt_entries(
buffer_id,
- entry_excerpts,
+ &entry_excerpts,
depth,
track_matches,
is_singleton,
query.as_deref(),
&mut entries,
&mut match_candidates,
+ &mut added_contexts,
cx,
);
}
@@ -2876,6 +3096,7 @@ impl OutlinePanel {
outline_panel.push_entry(
&mut entries,
&mut match_candidates,
+ &mut added_contexts,
track_matches,
PanelEntry::Fs(entry.clone()),
0,
@@ -2888,16 +3109,13 @@ impl OutlinePanel {
let parent_expanded = parent_dirs
.iter()
.rev()
- .find(|(parent_path, ..)| {
- folded_dirs
- .iter()
- .all(|entry| entry.path.as_ref() != *parent_path)
- })
- .map_or(true, |&(_, _, parent_expanded, _)| parent_expanded);
+ .find(|parent| folded_dirs.iter().all(|entry| entry.path != parent.path))
+ .map_or(true, |parent| parent.expanded);
if parent_expanded || query.is_some() {
outline_panel.push_entry(
&mut entries,
&mut match_candidates,
+ &mut added_contexts,
track_matches,
PanelEntry::FoldedDirs(worktree_id, folded_dirs),
folded_depth,
@@ -2909,6 +3127,19 @@ impl OutlinePanel {
return Vec::new();
};
+ outline_panel
+ .update(&mut cx, |outline_panel, _| {
+ if matches!(outline_panel.mode, ItemsDisplayMode::Search(_)) {
+ cleanup_fs_entries_without_search_children(
+ &outline_panel.collapsed_entries,
+ &mut entries,
+ &mut match_candidates,
+ &mut added_contexts,
+ );
+ }
+ })
+ .ok();
+
let Some(query) = query else {
return entries;
};
@@ -5,6 +5,14 @@ edition = "2021"
publish = false
license = "GPL-3.0-or-later"
+[features]
+test-support = [
+ "client/test-support",
+ "editor/test-support",
+ "gpui/test-support",
+ "workspace/test-support",
+]
+
[lints]
workspace = true
@@ -113,7 +113,7 @@ pub fn init(cx: &mut AppContext) {
.detach();
}
-struct ProjectSearch {
+pub struct ProjectSearch {
project: Model<Project>,
excerpts: Model<MultiBuffer>,
pending_search: Option<Task<Option<()>>>,
@@ -151,7 +151,7 @@ pub struct ProjectSearchView {
}
#[derive(Debug, Clone)]
-struct ProjectSearchSettings {
+pub struct ProjectSearchSettings {
search_options: SearchOptions,
filters_enabled: bool,
}
@@ -162,7 +162,7 @@ pub struct ProjectSearchBar {
}
impl ProjectSearch {
- fn new(project: Model<Project>, cx: &mut ModelContext<Self>) -> Self {
+ pub fn new(project: Model<Project>, cx: &mut ModelContext<Self>) -> Self {
let replica_id = project.read(cx).replica_id();
let capability = project.read(cx).capability();
@@ -605,7 +605,7 @@ impl ProjectSearchView {
});
}
- fn new(
+ pub fn new(
model: Model<ProjectSearch>,
cx: &mut ViewContext<Self>,
settings: Option<ProjectSearchSettings>,
@@ -751,9 +751,9 @@ impl ProjectSearchView {
});
}
- // Re-activate the most recently activated search in this pane or the most recent if it has been closed.
- // If no search exists in the workspace, create a new one.
- fn deploy_search(
+ /// Re-activate the most recently activated search in this pane or the most recent if it has been closed.
+ /// If no search exists in the workspace, create a new one.
+ pub fn deploy_search(
workspace: &mut Workspace,
action: &workspace::DeploySearch,
cx: &mut ViewContext<Workspace>,
@@ -1140,6 +1140,11 @@ impl ProjectSearchView {
return self.focus_results_editor(cx);
}
}
+
+ #[cfg(any(test, feature = "test-support"))]
+ pub fn results_editor(&self) -> &View<Editor> {
+ &self.results_editor
+ }
}
impl ProjectSearchBar {
@@ -1752,15 +1757,31 @@ fn register_workspace_action_for_present_search<A: Action>(
});
}
+#[cfg(any(test, feature = "test-support"))]
+pub fn perform_project_search(
+ search_view: &View<ProjectSearchView>,
+ text: impl Into<std::sync::Arc<str>>,
+ cx: &mut gpui::VisualTestContext,
+) {
+ search_view.update(cx, |search_view, cx| {
+ search_view
+ .query_editor
+ .update(cx, |query_editor, cx| query_editor.set_text(text, cx));
+ search_view.search(cx);
+ });
+ cx.run_until_parked();
+}
+
#[cfg(test)]
pub mod tests {
+ use std::sync::Arc;
+
use super::*;
use editor::{display_map::DisplayRow, DisplayPoint};
use gpui::{Action, TestAppContext, WindowHandle};
use project::FakeFs;
use serde_json::json;
use settings::SettingsStore;
- use std::sync::Arc;
use workspace::DeploySearch;
#[gpui::test]
@@ -117,7 +117,7 @@ pub struct RevealInProjectPanel {
pub entry_id: Option<u64>,
}
-#[derive(PartialEq, Clone, Deserialize)]
+#[derive(Default, PartialEq, Clone, Deserialize)]
pub struct DeploySearch {
#[serde(default)]
pub replace_enabled: bool,