Detailed changes
@@ -2368,9 +2368,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.3"
+version = "1.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
+checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2"
dependencies = [
"jobserver",
"libc",
@@ -3308,18 +3308,18 @@ dependencies = [
[[package]]
name = "cranelift-bforest"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f823c6662ea77699089ec8b6b4b8a23c1e1a9c6526a6420ede7ac957274a7ab4"
+checksum = "69792bd40d21be8059f7c709f44200ded3bbd073df7eb3fa3c282b387c7ffa5b"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-bitset"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fcbb4187005097204458a8e4309bb9e737933477e47b4609f81b07a5b4cdd25"
+checksum = "38da1eb6f7d8cdfa92f05acfae63c9a1d7a337e49ce7a2d0769c7fa03a2613a5"
dependencies = [
"serde",
"serde_derive",
@@ -3327,9 +3327,9 @@ dependencies = [
[[package]]
name = "cranelift-codegen"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cd1aaf8e88339f4f95afffd60d22033546ec7da4d79e805b85260a16668f78f"
+checksum = "709f5567a2bff9f06edf911a7cb5ebb091e4c81701714dc6ab574d08b4a69a0d"
dependencies = [
"bumpalo",
"cranelift-bforest",
@@ -3343,40 +3343,40 @@ dependencies = [
"hashbrown 0.14.5",
"log",
"regalloc2",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.1.1",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-codegen-meta"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e541b0418bbba3ce82040a445bd9a83bf3e0da604a95178d9e949dc8a7840af"
+checksum = "72d39a6b194c069fd091ca1f17b9d86ff1a4627ccad8806095828f61989a691f"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91fc96a709a30be39d53ecf89dbfe4edcc5adba528d4b65f7e58dc867ba70fab"
+checksum = "18f81aefad1f80ed4132ae33f40b92779eeb57edeb1e28bb24424a4098c963a2"
[[package]]
name = "cranelift-control"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c3bfcb035e0a501323896bb7ea3d7a5dd1fac3e92dda458ccd23960fde12c88"
+checksum = "6adbaac785ad4683c4f199686f9e15c1471f52ae2f4c013a3be039b4719db754"
dependencies = [
"arbitrary",
]
[[package]]
name = "cranelift-entity"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2f00b4eba51d73a8c343c45cfdeeffa1f74f423bba0e6b8e290e646777c2b81"
+checksum = "70b85ed43567e13782cd1b25baf42a8167ee57169a60dfd3d7307c6ca3839da0"
dependencies = [
"cranelift-bitset",
"serde",
@@ -3385,9 +3385,9 @@ dependencies = [
[[package]]
name = "cranelift-frontend"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52d5e18bf04660bb716dacf45809e2d4c85e7111701e27dbdb75b4634504ad8f"
+checksum = "8349f71373bb69c6f73992c6c1606236a66c8134e7a60e04e03fbd64b1aa7dcf"
dependencies = [
"cranelift-codegen",
"log",
@@ -3397,15 +3397,15 @@ dependencies = [
[[package]]
name = "cranelift-isle"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31f9901807b6d0fde1205f0e4db9d96dcf7ddfc1894c69eb2ff93c47ebf2439f"
+checksum = "464a6b958ce05e0c237c8b25508012b6c644e8c37348213a8c786ba29e28cfdb"
[[package]]
name = "cranelift-native"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "967d65a4077726a9afc3f4694e037f34b992cbe2b6c48ce519b714a0b0558f97"
+checksum = "ffc4acaf6894ee323ff4e9ce786bec09f0ebbe49941e8012f1c1052f1d965034"
dependencies = [
"cranelift-codegen",
"libc",
@@ -3414,9 +3414,9 @@ dependencies = [
[[package]]
name = "cranelift-wasm"
-version = "0.111.2"
+version = "0.112.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4899fd1ef6b1fe1df30f26ef864bd6e45040b8cf9f3cb3905d3e973c25698579"
+checksum = "b878860895cca97454ef8d8b12bfda9d0889dd49efee175dba78d54ff8363ec2"
dependencies = [
"cranelift-codegen",
"cranelift-entity",
@@ -3424,7 +3424,7 @@ dependencies = [
"itertools 0.12.1",
"log",
"smallvec",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
"wasmtime-types",
]
@@ -4060,6 +4060,7 @@ dependencies = [
"http_client",
"indoc",
"inline_completion",
+ "inventory",
"itertools 0.14.0",
"language",
"linkify",
@@ -4447,8 +4448,8 @@ dependencies = [
"serde_json",
"toml 0.8.20",
"util",
- "wasm-encoder 0.215.0",
- "wasmparser 0.215.0",
+ "wasm-encoder 0.217.1",
+ "wasmparser 0.217.1",
"wit-component",
]
@@ -4517,7 +4518,7 @@ dependencies = [
"toml 0.8.20",
"url",
"util",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
"wasmtime",
"wasmtime-wasi",
]
@@ -5699,15 +5700,6 @@ dependencies = [
"ahash 0.7.8",
]
-[[package]]
-name = "hashbrown"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
-dependencies = [
- "ahash 0.8.11",
-]
-
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -6912,6 +6904,7 @@ dependencies = [
"similar",
"smallvec",
"smol",
+ "streaming-iterator",
"strsim",
"sum_tree",
"task",
@@ -7886,7 +7879,9 @@ version = "0.1.0"
dependencies = [
"collections",
"convert_case 0.7.1",
+ "log",
"pretty_assertions",
+ "streaming-iterator",
"tree-sitter",
"tree-sitter-json",
]
@@ -10781,13 +10776,13 @@ dependencies = [
[[package]]
name = "regalloc2"
-version = "0.9.3"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6"
+checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0"
dependencies = [
- "hashbrown 0.13.2",
+ "hashbrown 0.14.5",
"log",
- "rustc-hash 1.1.0",
+ "rustc-hash 2.1.1",
"slice-group-by",
"smallvec",
]
@@ -11858,6 +11853,7 @@ dependencies = [
"settings",
"sha2",
"smol",
+ "streaming-iterator",
"tempfile",
"theme",
"tree-sitter",
@@ -12037,6 +12033,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"indoc",
+ "inventory",
"log",
"migrator",
"paths",
@@ -12049,6 +12046,7 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"smallvec",
+ "streaming-iterator",
"tree-sitter",
"tree-sitter-json",
"unindent",
@@ -12732,6 +12730,12 @@ dependencies = [
"ui",
]
+[[package]]
+name = "streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
+
[[package]]
name = "streaming_diff"
version = "0.1.0"
@@ -14031,13 +14035,14 @@ dependencies = [
[[package]]
name = "tree-sitter"
-version = "0.23.2"
+version = "0.24.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0203df02a3b6dd63575cc1d6e609edc2181c9a11867a271b25cfd2abff3ec5ca"
+checksum = "a5387dffa7ffc7d2dae12b50c6f7aab8ff79d6210147c6613561fc3d474c6f75"
dependencies = [
"cc",
"regex",
"regex-syntax 0.8.5",
+ "streaming-iterator",
"tree-sitter-language",
"wasmtime-c-api-impl",
]
@@ -14936,9 +14941,9 @@ dependencies = [
[[package]]
name = "wasm-encoder"
-version = "0.215.0"
+version = "0.217.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847"
+checksum = "10961fd76db420582926af70816dd205019d8152d9e51e1b939125dd1639f854"
dependencies = [
"leb128",
]
@@ -14985,9 +14990,9 @@ dependencies = [
[[package]]
name = "wasmparser"
-version = "0.215.0"
+version = "0.217.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e"
+checksum = "65a5a0689975b9fd93c02f5400cfd9669858b99607e54e7b892c6080cba598bb"
dependencies = [
"ahash 0.8.11",
"bitflags 2.8.0",
@@ -14999,20 +15004,20 @@ dependencies = [
[[package]]
name = "wasmprinter"
-version = "0.215.0"
+version = "0.217.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8e9a325d85053408209b3d2ce5eaddd0dd6864d1cff7a007147ba073157defc"
+checksum = "324c6782d7b81c01625335d252653b26ea68e835ddb4aef4cb1ed3ea40ae3a49"
dependencies = [
"anyhow",
"termcolor",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
]
[[package]]
name = "wasmtime"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e763074ccd6b251c78095fcd27707253b69cef961ea0a2ff76a8d246ddfadd1b"
+checksum = "f38dbf42dc56a6fe41ccd77211ea8ec90855de05e52cd00df5a0a3bca87d6147"
dependencies = [
"anyhow",
"async-trait",
@@ -15040,7 +15045,7 @@ dependencies = [
"smallvec",
"sptr",
"target-lexicon",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
"wasmtime-asm-macros",
"wasmtime-component-macro",
"wasmtime-component-util",
@@ -15056,18 +15061,18 @@ dependencies = [
[[package]]
name = "wasmtime-asm-macros"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f45004b6fa5d12dd95b427474e69bde05a6d31d33b39bd56054f9cd68e824283"
+checksum = "30e0c7f9983c2d60109a939d9ab0e0df301901085c3608e1c22c27c98390a027"
dependencies = [
"cfg-if",
]
[[package]]
name = "wasmtime-c-api-impl"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e038dd412700174019867608617127e7cc4f113f764dd10e7488dbf5f47b191"
+checksum = "ebfcdb4aa0f68020934099815cf6ef11dbbedaf070ef800b3f0a7f6ec7b7d005"
dependencies = [
"anyhow",
"log",
@@ -15079,9 +15084,9 @@ dependencies = [
[[package]]
name = "wasmtime-c-api-macros"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bde0ca2263811d980ab676bcb2a190c990737f58969a908976101ad208149a17"
+checksum = "842c213ad4546fb0178735910b96ee7da303e1d745c3f42f4178b0de1da138b6"
dependencies = [
"proc-macro2",
"quote",
@@ -15089,9 +15094,9 @@ dependencies = [
[[package]]
name = "wasmtime-component-macro"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74b72572d389586e429a9830ab68a5b3e2a567962b8a82f4249652ccc68ddab2"
+checksum = "0929ffffaca32dd8770b56848c94056036963ca05de25fb47cac644e20262168"
dependencies = [
"anyhow",
"proc-macro2",
@@ -15099,20 +15104,20 @@ dependencies = [
"syn 2.0.90",
"wasmtime-component-util",
"wasmtime-wit-bindgen",
- "wit-parser 0.215.0",
+ "wit-parser 0.217.1",
]
[[package]]
name = "wasmtime-component-util"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb3081af782040e8016373e603ee854496c82cdc0f32b13a6bc9700e15f582db"
+checksum = "fdc29d2b56629d66d2fd791d1b46471d0016e0d684ed2dc299e870d127082268"
[[package]]
name = "wasmtime-cranelift"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42c18ca178eee0947cd53b27d3a101dd2f79afec86fc3ce657545519c6bf011a"
+checksum = "f8c8af1197703f4de556a274384adf5db36a146f9892bc9607bad16881e75c80"
dependencies = [
"anyhow",
"cfg-if",
@@ -15125,18 +15130,19 @@ dependencies = [
"gimli 0.29.0",
"log",
"object",
+ "smallvec",
"target-lexicon",
"thiserror 1.0.69",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
"wasmtime-environ",
"wasmtime-versioned-export-macros",
]
[[package]]
name = "wasmtime-environ"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e80da0784d4dd0788479ce390cd4a54a893d24f2937d4046145704777aa7a131"
+checksum = "3f1b5af7bac868c5bce3b78a366a10677caacf6e6467c156301297e36ed31f3e"
dependencies = [
"anyhow",
"cpp_demangle",
@@ -15152,8 +15158,8 @@ dependencies = [
"serde",
"serde_derive",
"target-lexicon",
- "wasm-encoder 0.215.0",
- "wasmparser 0.215.0",
+ "wasm-encoder 0.217.1",
+ "wasmparser 0.217.1",
"wasmprinter",
"wasmtime-component-util",
"wasmtime-types",
@@ -15161,9 +15167,9 @@ dependencies = [
[[package]]
name = "wasmtime-fiber"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c3d366194ff87b8aeeb7348bb789d5dd9a9aca18b340b19dcf4ab96966e663"
+checksum = "665ccc1bb0f28496e6fa02e94c575ee9ad6e3202c7df8591e5dda78106d5aa4a"
dependencies = [
"anyhow",
"cc",
@@ -15176,9 +15182,9 @@ dependencies = [
[[package]]
name = "wasmtime-jit-icache-coherence"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c543f7ee7b1ec8f2215f88197a40f9fa3452dc98c5902c5c700d8ec9e9ea7021"
+checksum = "5d7314e32c624f645ad7d6b9fc3ac89eb7d2b9aa06695d6445cec087958ec27d"
dependencies = [
"anyhow",
"cfg-if",
@@ -15188,29 +15194,29 @@ dependencies = [
[[package]]
name = "wasmtime-slab"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcf7ded4156c76cc1cb348e5728096087e2c432714d1b285044c6da6a1e3d01a"
+checksum = "f75cba1a8cc327839f493cfc3036c9de3d077d59ab76296bc710ee5f95be5391"
[[package]]
name = "wasmtime-types"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c92a6f3c2a8704a60ae0278ea2635c986539539ce1b80080b0fe8ea7bc83da81"
+checksum = "c6d83a7816947a4974e2380c311eacb1db009b8bad86081dc726b705603c93c7"
dependencies = [
"anyhow",
"cranelift-entity",
"serde",
"serde_derive",
"smallvec",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
]
[[package]]
name = "wasmtime-versioned-export-macros"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6e2f847c118d5b26f0cc01d12a6d72fa450e32c42a4a3ce5d33afb4729ed6a"
+checksum = "6879a8e168aef3fe07335343b7fbede12fa494215e83322e173d4018e124a846"
dependencies = [
"proc-macro2",
"quote",
@@ -15219,9 +15225,9 @@ dependencies = [
[[package]]
name = "wasmtime-wasi"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f88f94e393084426f5055d57ce7ae6346ae623783ee6792f411282d6b9e1e5c3"
+checksum = "d042ea66b2834fb03b8a6968ef1a99a4b537211b00f7502a4d6a37f4eb2049b2"
dependencies = [
"anyhow",
"async-trait",
@@ -15250,16 +15256,16 @@ dependencies = [
[[package]]
name = "wasmtime-winch"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee3640cd34c67f505e88cef0da11368806204a24c68c35d671a48a59bb37f908"
+checksum = "6baca2a919a288df653246069868b4de80f07e9679a8ef9b78ad79fc658ffd12"
dependencies = [
"anyhow",
"cranelift-codegen",
"gimli 0.29.0",
"object",
"target-lexicon",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
"wasmtime-cranelift",
"wasmtime-environ",
"winch-codegen",
@@ -15267,14 +15273,14 @@ dependencies = [
[[package]]
name = "wasmtime-wit-bindgen"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c58b085b2d330e5057dddd31f3ca527569b90fcdd35f6d373420c304927a5190"
+checksum = "3f571f63ac1d532e986eb3973bbef3a45e4ae83de521a8d573b0fe0594dc9608"
dependencies = [
"anyhow",
"heck 0.4.1",
"indexmap",
- "wit-parser 0.215.0",
+ "wit-parser 0.217.1",
]
[[package]]
@@ -15493,9 +15499,9 @@ dependencies = [
[[package]]
name = "wiggle"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c72a4c92952216582f55eab27819a1fe8d3c54b292b7b8e5f849b23bfed96e78"
+checksum = "4c8fdcd81702e0f46a8ab2ed28a5bf824aabf4a1af1673af496a020aacd0b6f9"
dependencies = [
"anyhow",
"async-trait",
@@ -15508,9 +15514,9 @@ dependencies = [
[[package]]
name = "wiggle-generate"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb744fb938a9fc38207838829b4a43831c1de499e3526eaea71deeff4d9cbb83"
+checksum = "14f745361f0a9071aaabd05de1bb2b782d9f0597f30d9c0f20326224902e64d5"
dependencies = [
"anyhow",
"heck 0.4.1",
@@ -15523,9 +15529,9 @@ dependencies = [
[[package]]
name = "wiggle-macro"
-version = "24.0.2"
+version = "25.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cef395fff17bf8f9c1dee6c0e12801a3ba24928139af0ecb5ccb82ff87bf9d2"
+checksum = "bfbdae3574621921ed3c13325edc910388487759d10fb330f656cfc69bee38db"
dependencies = [
"proc-macro2",
"quote",
@@ -15566,9 +15572,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winch-codegen"
-version = "0.22.2"
+version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46d7fecc199486f048bb2d649dce68bf28712ae1183dd54fd4a0534989517b24"
+checksum = "01cd1dc56c5a45d509ff06e7ca8817eaa9ec3240096f07e71915d5d528658e8a"
dependencies = [
"anyhow",
"cranelift-codegen",
@@ -15576,7 +15582,7 @@ dependencies = [
"regalloc2",
"smallvec",
"target-lexicon",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
"wasmtime-cranelift",
"wasmtime-environ",
]
@@ -16128,9 +16134,9 @@ dependencies = [
[[package]]
name = "wit-parser"
-version = "0.215.0"
+version = "0.217.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "935a97eaffd57c3b413aa510f8f0b550a4a9fe7d59e79cd8b89a83dcb860321f"
+checksum = "e5aaf02882453eaeec4fe30f1e4263cfd8b8ea36dd00e1fe7d902d9cb498bccd"
dependencies = [
"anyhow",
"id-arena",
@@ -16141,7 +16147,7 @@ dependencies = [
"serde_derive",
"serde_json",
"unicode-xid",
- "wasmparser 0.215.0",
+ "wasmparser 0.217.1",
]
[[package]]
@@ -423,6 +423,7 @@ ignore = "0.4.22"
image = "0.25.1"
indexmap = { version = "2.7.0", features = ["serde"] }
indoc = "2"
+inventory = "0.3.19"
itertools = "0.14.0"
jsonwebtoken = "9.3"
jupyter-protocol = { version = "0.6.0" }
@@ -502,6 +503,7 @@ simplelog = "0.12.2"
smallvec = { version = "1.6", features = ["union"] }
smol = "2.0"
sqlformat = "0.2"
+streaming-iterator = "0.1"
strsim = "0.11"
strum = { version = "0.26.0", features = ["derive"] }
subtle = "2.5.0"
@@ -523,7 +525,7 @@ tiny_http = "0.8"
toml = "0.8"
tokio = { version = "1" }
tower-http = "0.4.4"
-tree-sitter = { version = "0.23", features = ["wasm"] }
+tree-sitter = { version = "0.24", features = ["wasm"] }
tree-sitter-bash = "0.23"
tree-sitter-c = "0.23"
tree-sitter-cpp = "0.23"
@@ -552,16 +554,16 @@ unicode-segmentation = "1.10"
unicode-script = "0.5.7"
url = "2.2"
uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] }
-wasmparser = "0.215"
-wasm-encoder = "0.215"
-wasmtime = { version = "24", default-features = false, features = [
+wasmparser = "0.217"
+wasm-encoder = "0.217"
+wasmtime = { version = "25", default-features = false, features = [
"async",
"demangle",
"runtime",
"cranelift",
"component-model",
] }
-wasmtime-wasi = "24"
+wasmtime-wasi = "25"
which = "6.0.0"
wit-component = "0.201"
zed_llm_client = "0.4"
@@ -49,6 +49,7 @@ gpui.workspace = true
http_client.workspace = true
indoc.workspace = true
inline_completion.workspace = true
+inventory.workspace = true
itertools.workspace = true
language.workspace = true
linkify.workspace = true
@@ -62,10 +62,10 @@ pub use editor_settings::{
CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
};
pub use editor_settings_controls::*;
+use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
pub use element::{
CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
};
-use element::{LineWithInvisibles, PositionMap};
use futures::{future, FutureExt};
use fuzzy::StringMatchCandidate;
@@ -190,6 +190,9 @@ pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
+pub(crate) const EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT: &str =
+ "edit_prediction_requires_modifier";
+
pub fn render_parsed_markdown(
element_id: impl Into<ElementId>,
parsed: &language::ParsedMarkdown,
@@ -1528,7 +1531,7 @@ impl Editor {
key_context.add("edit_prediction");
if showing_completions || self.edit_prediction_requires_modifier(cx) {
- key_context.add("edit_prediction_requires_modifier");
+ key_context.add(EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT);
}
}
@@ -5092,19 +5095,38 @@ impl Editor {
has_completion && self.edit_prediction_requires_modifier(cx)
}
- fn update_inline_completion_preview(
+ fn handle_modifiers_changed(
&mut self,
- modifiers: &Modifiers,
+ modifiers: Modifiers,
+ position_map: &PositionMap,
window: &mut Window,
cx: &mut Context<Self>,
) {
if !self.show_edit_predictions_in_menu(cx) {
+ let accept_binding =
+ AcceptEditPredictionBinding::resolve(self.focus_handle(cx), window);
+ if let Some(accept_keystroke) = accept_binding.keystroke() {
+ let was_previewing_inline_completion = self.previewing_inline_completion;
+ self.previewing_inline_completion = modifiers == accept_keystroke.modifiers
+ && accept_keystroke.modifiers.modified();
+ if self.previewing_inline_completion != was_previewing_inline_completion {
+ self.update_visible_inline_completion(window, cx);
+ }
+ }
+ }
+
+ let mouse_position = window.mouse_position();
+ if !position_map.text_hitbox.is_hovered(window) {
return;
}
- self.previewing_inline_completion = modifiers.alt;
- self.update_visible_inline_completion(window, cx);
- cx.notify();
+ self.update_hovered_link(
+ position_map.point_for_position(mouse_position),
+ &position_map.snapshot,
+ modifiers,
+ window,
+ cx,
+ )
}
fn update_visible_inline_completion(
@@ -9711,7 +9711,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
&r#"
<!-- Λ<script> -->
// Λvar x = new Y();
- // Λ</script>
+ <!-- Λ</script> -->
"#
.unindent(),
);
@@ -15,13 +15,14 @@ use crate::{
items::BufferSearchHighlights,
mouse_context_menu::{self, MenuPosition, MouseContextMenu},
scroll::{axis_pair, scroll_amount::ScrollAmount, AxisPair},
- BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
- DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
+ AcceptEditPrediction, BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayPoint,
+ DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GoToHunk,
GoToPrevHunk, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor,
InlineCompletion, JumpData, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point,
RevertSelectedHunks, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap,
- StickyHeaderExcerpt, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT,
+ StickyHeaderExcerpt, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR,
+ EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT, FILE_HEADER_HEIGHT,
GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
};
use client::ParticipantIndex;
@@ -34,11 +35,11 @@ use gpui::{
relative, size, svg, transparent_black, Action, AnyElement, App, AvailableSpace, Axis, Bounds,
ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase,
Edges, Element, ElementInputHandler, Entity, FocusHandle, Focusable as _, FontId,
- GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Keystroke, Length,
- ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
- ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
- StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement,
- WeakEntity, Window,
+ GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, KeyBindingContextPredicate,
+ Keystroke, Length, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
+ MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
+ SharedString, Size, StatefulInteractiveElement, Style, Styled, Subscription, TextRun,
+ TextStyleRefinement, WeakEntity, Window,
};
use itertools::Itertools;
use language::{
@@ -54,7 +55,7 @@ use multi_buffer::{
RowInfo, ToOffset,
};
use project::project_settings::{GitGutterSetting, ProjectSettings};
-use settings::Settings;
+use settings::{KeyBindingValidator, KeyBindingValidatorRegistration, Settings};
use smallvec::{smallvec, SmallVec};
use std::{
any::TypeId,
@@ -74,7 +75,7 @@ use ui::{
POPOVER_Y_PADDING,
};
use unicode_segmentation::UnicodeSegmentation;
-use util::{RangeExt, ResultExt};
+use util::{markdown::MarkdownString, RangeExt, ResultExt};
use workspace::{item::Item, notifications::NotifyTaskExt, Workspace};
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
@@ -511,35 +512,12 @@ impl EditorElement {
if editor.hover_state.focused(window, cx) {
return;
}
- Self::modifiers_changed(editor, event, &position_map, window, cx)
+ editor.handle_modifiers_changed(event.modifiers, &position_map, window, cx);
})
}
});
}
- fn modifiers_changed(
- editor: &mut Editor,
- event: &ModifiersChangedEvent,
- position_map: &PositionMap,
- window: &mut Window,
- cx: &mut Context<Editor>,
- ) {
- editor.update_inline_completion_preview(&event.modifiers, window, cx);
-
- let mouse_position = window.mouse_position();
- if !position_map.text_hitbox.is_hovered(window) {
- return;
- }
-
- editor.update_hovered_link(
- position_map.point_for_position(mouse_position),
- &position_map.snapshot,
- event.modifiers,
- window,
- cx,
- )
- }
-
fn mouse_left_down(
editor: &mut Editor,
event: &MouseDownEvent,
@@ -3190,49 +3168,8 @@ impl EditorElement {
);
let edit_prediction = if edit_prediction_popover_visible {
- let accept_keystroke: Option<Keystroke>;
-
- // TODO: load modifier from keymap.
- // `bindings_for_action_in` returns `None` in Linux, and is intermittent on macOS
- #[cfg(target_os = "macos")]
- {
- // let bindings = window.bindings_for_action_in(
- // &crate::AcceptEditPrediction,
- // &self.editor.focus_handle(cx),
- // );
-
- // let last_binding = bindings.last();
-
- // accept_keystroke = if let Some(binding) = last_binding {
- // match &binding.keystrokes() {
- // // TODO: no need to clone once this logic works on linux.
- // [keystroke] => Some(keystroke.clone()),
- // _ => None,
- // }
- // } else {
- // None
- // };
- accept_keystroke = Some(Keystroke {
- modifiers: gpui::Modifiers {
- alt: true,
- ..Default::default()
- },
- key: "tab".to_string(),
- key_char: None,
- });
- }
-
- #[cfg(not(target_os = "macos"))]
- {
- accept_keystroke = Some(Keystroke {
- modifiers: gpui::Modifiers {
- alt: true,
- ..Default::default()
- },
- key: "enter".to_string(),
- key_char: None,
- });
- }
+ let accept_binding =
+ AcceptEditPredictionBinding::resolve(self.editor.focus_handle(cx), window);
self.editor.update(cx, move |editor, cx| {
let mut element = editor.render_edit_prediction_cursor_popover(
@@ -3240,7 +3177,7 @@ impl EditorElement {
max_width,
cursor_point,
style,
- accept_keystroke.as_ref()?,
+ accept_binding.keystroke()?,
window,
cx,
)?;
@@ -5739,48 +5676,12 @@ fn inline_completion_accept_indicator(
label: impl Into<SharedString>,
icon: Option<IconName>,
previewing: bool,
- focus_handle: FocusHandle,
+ editor_focus_handle: FocusHandle,
window: &Window,
cx: &App,
) -> Option<AnyElement> {
- let use_hardcoded_linux_bindings;
-
- #[cfg(target_os = "macos")]
- {
- use_hardcoded_linux_bindings = false;
- }
-
- #[cfg(not(target_os = "macos"))]
- {
- use_hardcoded_linux_bindings = true;
- }
-
- let accept_keystroke = if use_hardcoded_linux_bindings {
- if previewing {
- Keystroke {
- modifiers: Default::default(),
- key: "enter".to_string(),
- key_char: None,
- }
- } else {
- Keystroke {
- modifiers: Default::default(),
- key: "tab".to_string(),
- key_char: None,
- }
- }
- } else {
- let bindings = window.bindings_for_action_in(&crate::AcceptEditPrediction, &focus_handle);
- if let Some(keystroke) = bindings
- .last()
- .and_then(|binding| binding.keystrokes().first())
- {
- // TODO: clone unnecessary once `use_hardcoded_linux_bindings` is removed.
- keystroke.clone()
- } else {
- return None;
- }
- };
+ let accept_binding = AcceptEditPredictionBinding::resolve(editor_focus_handle, window);
+ let accept_keystroke = accept_binding.keystroke()?;
let accept_key = h_flex()
.px_0p5()
@@ -5828,6 +5729,69 @@ fn inline_completion_accept_indicator(
)
}
+pub struct AcceptEditPredictionBinding(Option<gpui::KeyBinding>);
+
+impl AcceptEditPredictionBinding {
+ pub fn resolve(editor_focus_handle: FocusHandle, window: &Window) -> Self {
+ AcceptEditPredictionBinding(
+ window
+ .bindings_for_action_in(&AcceptEditPrediction, &editor_focus_handle)
+ .into_iter()
+ .next(),
+ )
+ }
+
+ pub fn keystroke(&self) -> Option<&Keystroke> {
+ if let Some(binding) = self.0.as_ref() {
+ match &binding.keystrokes() {
+ [keystroke] => Some(keystroke),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ }
+}
+
+struct AcceptEditPredictionsBindingValidator;
+
+inventory::submit! { KeyBindingValidatorRegistration(|| Box::new(AcceptEditPredictionsBindingValidator)) }
+
+impl KeyBindingValidator for AcceptEditPredictionsBindingValidator {
+ fn action_type_id(&self) -> TypeId {
+ TypeId::of::<AcceptEditPrediction>()
+ }
+
+ fn validate(&self, binding: &gpui::KeyBinding) -> Result<(), MarkdownString> {
+ use KeyBindingContextPredicate::*;
+
+ if binding.keystrokes().len() == 1 && binding.keystrokes()[0].modifiers.modified() {
+ return Ok(());
+ }
+ let required_predicate =
+ Not(Identifier(EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT.into()).into());
+ match binding.predicate() {
+ Some(predicate) if required_predicate.is_superset(&predicate) => {
+ return Ok(());
+ }
+ _ => {}
+ }
+ Err(MarkdownString(format!(
+ "{} can only be bound to a single keystroke with modifiers, so \
+ that holding down these modifiers can be used to preview \
+ completions inline when the completions menu is open.\n\n\
+ This restriction does not apply when the context requires {}, \
+ since these bindings will not be used when the completions menu \
+ is open.",
+ MarkdownString::inline_code(AcceptEditPrediction.name()),
+ MarkdownString::inline_code(&format!(
+ "!{}",
+ EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT
+ )),
+ )))
+ }
+}
+
#[allow(clippy::too_many_arguments)]
fn prepaint_gutter_button(
button: IconButton,
@@ -84,7 +84,7 @@ impl HostWorktree for WasmState {
latest::HostWorktree::which(self, delegate, binary_name).await
}
- fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
+ async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
Ok(())
}
}
@@ -92,7 +92,7 @@ impl HostWorktree for WasmState {
latest::HostWorktree::which(self, delegate, binary_name).await
}
- fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
+ async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
// We only ever hand out borrows of worktrees.
Ok(())
}
@@ -147,7 +147,7 @@ impl HostWorktree for WasmState {
latest::HostWorktree::which(self, delegate, binary_name).await
}
- fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
+ async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
// We only ever hand out borrows of worktrees.
Ok(())
}
@@ -240,7 +240,7 @@ impl HostKeyValueStore for WasmState {
kv_store.insert(key, value).await.to_wasmtime_result()
}
- fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
+ async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
// We only ever hand out borrows of key-value stores.
Ok(())
}
@@ -282,7 +282,7 @@ impl HostWorktree for WasmState {
latest::HostWorktree::which(self, delegate, binary_name).await
}
- fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
+ async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
// We only ever hand out borrows of worktrees.
Ok(())
}
@@ -350,7 +350,7 @@ impl http_client::HostHttpResponseStream for WasmState {
.to_wasmtime_result()
}
- fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
+ async fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
Ok(())
}
}
@@ -259,7 +259,7 @@ impl HostKeyValueStore for WasmState {
kv_store.insert(key, value).await.to_wasmtime_result()
}
- fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
+ async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
// We only ever hand out borrows of key-value stores.
Ok(())
}
@@ -275,7 +275,7 @@ impl HostProject for WasmState {
Ok(project.worktree_ids())
}
- fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
+ async fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
// We only ever hand out borrows of projects.
Ok(())
}
@@ -325,7 +325,7 @@ impl HostWorktree for WasmState {
Ok(delegate.which(binary_name).await)
}
- fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
+ async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
// We only ever hand out borrows of worktrees.
Ok(())
}
@@ -393,7 +393,7 @@ impl http_client::HostHttpResponseStream for WasmState {
.to_wasmtime_result()
}
- fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
+ async fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
Ok(())
}
}
@@ -79,7 +79,7 @@ futures.workspace = true
gpui_macros.workspace = true
http_client = { optional = true, workspace = true }
image = "0.25.1"
-inventory = "0.3.19"
+inventory.workspace = true
itertools.workspace = true
log.workspace = true
num_cpus = "1.13"
@@ -53,6 +53,7 @@ settings.workspace = true
similar.workspace = true
smallvec.workspace = true
smol.workspace = true
+streaming-iterator.workspace = true
strsim.workspace = true
sum_tree.workspace = true
task.workspace = true
@@ -146,7 +146,9 @@ impl<T> Outline<T> {
}
} else {
let mut name_ranges = outline_match.name_ranges.iter();
- let mut name_range = name_ranges.next().unwrap();
+ let Some(mut name_range) = name_ranges.next() else {
+ continue;
+ };
let mut preceding_ranges_len = 0;
for position in &mut string_match.positions {
while *position >= preceding_ranges_len + name_range.len() {
@@ -194,6 +196,40 @@ impl<T> Outline<T> {
#[cfg(test)]
mod tests {
use super::*;
+ use gpui::TestAppContext;
+
+ #[gpui::test]
+ async fn test_entries_with_no_names(cx: &mut TestAppContext) {
+ let outline = Outline::new(vec![
+ OutlineItem {
+ depth: 0,
+ range: Point::new(0, 0)..Point::new(5, 0),
+ text: "class Foo".to_string(),
+ highlight_ranges: vec![],
+ name_ranges: vec![6..9],
+ body_range: None,
+ annotation_range: None,
+ },
+ OutlineItem {
+ depth: 0,
+ range: Point::new(2, 0)..Point::new(2, 7),
+ text: "private".to_string(),
+ highlight_ranges: vec![],
+ name_ranges: vec![],
+ body_range: None,
+ annotation_range: None,
+ },
+ ]);
+ assert_eq!(
+ outline
+ .search(" ", cx.executor())
+ .await
+ .into_iter()
+ .map(|mat| mat.string)
+ .collect::<Vec<String>>(),
+ vec!["class Foo".to_string()]
+ );
+ }
#[test]
fn test_find_most_similar_with_low_similarity() {
@@ -14,6 +14,7 @@ use std::{
ops::{Deref, DerefMut, Range},
sync::Arc,
};
+use streaming_iterator::StreamingIterator;
use sum_tree::{Bias, SeekTarget, SumTree};
use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint};
use tree_sitter::{Node, Query, QueryCapture, QueryCaptures, QueryCursor, QueryMatches, Tree};
@@ -1143,7 +1144,7 @@ impl<'a> SyntaxMapMatches<'a> {
impl<'a> SyntaxMapCapturesLayer<'a> {
fn advance(&mut self) {
- self.next_capture = self.captures.next().map(|(mat, ix)| mat.captures[ix]);
+ self.next_capture = self.captures.next().map(|(mat, ix)| mat.captures[*ix]);
}
fn sort_key(&self) -> (usize, Reverse<usize>, usize) {
@@ -1280,7 +1281,8 @@ fn get_injections(
for query_range in changed_ranges {
query_cursor.set_byte_range(query_range.start.saturating_sub(1)..query_range.end + 1);
- for mat in query_cursor.matches(&config.query, node, TextProvider(text.as_rope())) {
+ let mut matches = query_cursor.matches(&config.query, node, TextProvider(text.as_rope()));
+ while let Some(mat) = matches.next() {
let content_ranges = mat
.nodes_for_capture_index(config.content_capture_ix)
.map(|node| node.range())
@@ -1554,7 +1556,8 @@ impl<'a> SyntaxLayer<'a> {
query_cursor.set_byte_range(offset.saturating_sub(1)..offset.saturating_add(1));
let mut smallest_match: Option<(u32, Range<usize>)> = None;
- for mat in query_cursor.matches(&config.query, self.node(), text) {
+ let mut matches = query_cursor.matches(&config.query, self.node(), text);
+ while let Some(mat) = matches.next() {
for capture in mat.captures {
let Some(override_entry) = config.values.get(&capture.index) else {
continue;
@@ -15,6 +15,8 @@ doctest = false
[dependencies]
collections.workspace = true
convert_case.workspace = true
+log.workspace = true
+streaming-iterator.workspace = true
tree-sitter-json.workspace = true
tree-sitter.workspace = true
@@ -1,6 +1,7 @@
use collections::HashMap;
use convert_case::{Case, Casing};
use std::{cmp::Reverse, ops::Range, sync::LazyLock};
+use streaming_iterator::StreamingIterator;
use tree_sitter::{Query, QueryMatch};
fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Option<String> {
@@ -11,10 +12,10 @@ fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Option<Str
let syntax_tree = parser.parse(&text, None).unwrap();
let mut cursor = tree_sitter::QueryCursor::new();
- let matches = cursor.matches(query, syntax_tree.root_node(), text.as_bytes());
+ let mut matches = cursor.matches(query, syntax_tree.root_node(), text.as_bytes());
let mut edits = vec![];
- for mat in matches {
+ while let Some(mat) = matches.next() {
if let Some((_, callback)) = patterns.get(mat.pattern_index) {
edits.extend(callback(&text, &mat, query));
}
@@ -28,11 +29,19 @@ fn migrate(text: &str, patterns: MigrationPatterns, query: &Query) -> Option<Str
if edits.is_empty() {
None
} else {
- let mut text = text.to_string();
- for (range, replacement) in edits.into_iter().rev() {
- text.replace_range(range, &replacement);
+ let mut new_text = text.to_string();
+ for (range, replacement) in edits.iter().rev() {
+ new_text.replace_range(range.clone(), replacement);
+ }
+ if new_text == text {
+ log::error!(
+ "Edits computed for configuration migration do not cause a change: {:?}",
+ edits
+ );
+ None
+ } else {
+ Some(new_text)
}
- Some(text)
}
}
@@ -42,6 +42,7 @@ serde_json.workspace = true
settings.workspace = true
sha2.workspace = true
smol.workspace = true
+streaming-iterator.workspace = true
theme.workspace = true
tree-sitter.workspace = true
ui.workspace = true
@@ -7,6 +7,7 @@ use std::{
path::Path,
sync::Arc,
};
+use streaming_iterator::StreamingIterator;
use tree_sitter::QueryCapture;
use util::ResultExt as _;
@@ -88,7 +89,7 @@ fn syntactic_ranges(
let mut ranges = with_query_cursor(|cursor| {
cursor
.matches(&outline.query, tree.root_node(), text.as_bytes())
- .filter_map(|mat| {
+ .filter_map_deref(|mat| {
mat.captures
.iter()
.find_map(|QueryCapture { node, index }| {
@@ -22,6 +22,7 @@ ec4rs.workspace = true
fs.workspace = true
futures.workspace = true
gpui.workspace = true
+inventory.workspace = true
log.workspace = true
paths.workspace = true
release_channel.workspace = true
@@ -32,6 +33,7 @@ serde_derive.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
smallvec.workspace = true
+streaming-iterator.workspace = true
tree-sitter-json.workspace = true
tree-sitter.workspace = true
util.workspace = true
@@ -1,5 +1,5 @@
use anyhow::{anyhow, Context as _, Result};
-use collections::{HashMap, IndexMap};
+use collections::{BTreeMap, HashMap, IndexMap};
use fs::Fs;
use gpui::{
Action, ActionBuildError, App, InvalidKeystrokeError, KeyBinding, KeyBindingContextPredicate,
@@ -13,12 +13,30 @@ use schemars::{
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
-use std::rc::Rc;
-use std::{fmt::Write, sync::Arc};
+use std::{any::TypeId, fmt::Write, rc::Rc, sync::Arc, sync::LazyLock};
use util::{asset_str, markdown::MarkdownString};
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
+pub trait KeyBindingValidator: Send + Sync {
+ fn action_type_id(&self) -> TypeId;
+ fn validate(&self, binding: &KeyBinding) -> Result<(), MarkdownString>;
+}
+
+pub struct KeyBindingValidatorRegistration(pub fn() -> Box<dyn KeyBindingValidator>);
+
+inventory::collect!(KeyBindingValidatorRegistration);
+
+pub(crate) static KEY_BINDING_VALIDATORS: LazyLock<BTreeMap<TypeId, Box<dyn KeyBindingValidator>>> =
+ LazyLock::new(|| {
+ let mut validators = BTreeMap::new();
+ for validator_registration in inventory::iter::<KeyBindingValidatorRegistration> {
+ let validator = validator_registration.0();
+ validators.insert(validator.action_type_id(), validator);
+ }
+ validators
+ });
+
// Note that the doc comments on these are shown by json-language-server when editing the keymap, so
// they should be considered user-facing documentation. Documentation is not handled well with
// schemars-0.8 - when there are newlines, it is rendered as plaintext (see
@@ -255,9 +273,16 @@ impl KeymapFile {
key_bindings.push(key_binding);
}
Err(err) => {
+ let mut lines = err.lines();
+ let mut indented_err = lines.next().unwrap().to_string();
+ for line in lines {
+ indented_err.push_str(" ");
+ indented_err.push_str(line);
+ indented_err.push_str("\n");
+ }
write!(
section_errors,
- "\n\n - In binding {}, {err}",
+ "\n\n- In binding {}, {indented_err}",
inline_code_string(keystrokes),
)
.unwrap();
@@ -367,13 +392,24 @@ impl KeymapFile {
},
};
- match KeyBinding::load(keystrokes, action, context, key_equivalents) {
- Ok(binding) => Ok(binding),
- Err(InvalidKeystrokeError { keystroke }) => Err(format!(
- "invalid keystroke {}. {}",
- inline_code_string(&keystroke),
- KEYSTROKE_PARSE_EXPECTED_MESSAGE
- )),
+ let key_binding = match KeyBinding::load(keystrokes, action, context, key_equivalents) {
+ Ok(key_binding) => key_binding,
+ Err(InvalidKeystrokeError { keystroke }) => {
+ return Err(format!(
+ "invalid keystroke {}. {}",
+ inline_code_string(&keystroke),
+ KEYSTROKE_PARSE_EXPECTED_MESSAGE
+ ))
+ }
+ };
+
+ if let Some(validator) = KEY_BINDING_VALIDATORS.get(&key_binding.action().type_id()) {
+ match validator.validate(&key_binding) {
+ Ok(()) => Ok(key_binding),
+ Err(error) => Err(error.0),
+ }
+ } else {
+ Ok(key_binding)
}
}
@@ -13,7 +13,9 @@ use util::asset_str;
pub use editable_setting_control::*;
pub use json_schema::*;
pub use key_equivalents::*;
-pub use keymap_file::{KeymapFile, KeymapFileLoadResult};
+pub use keymap_file::{
+ KeyBindingValidator, KeyBindingValidatorRegistration, KeymapFile, KeymapFileLoadResult,
+};
pub use settings_file::*;
pub use settings_store::{
parse_json_with_comments, InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation,
@@ -17,6 +17,7 @@ use std::{
str::{self, FromStr},
sync::{Arc, LazyLock},
};
+use streaming_iterator::StreamingIterator;
use tree_sitter::Query;
use util::RangeExt;
@@ -1262,8 +1263,8 @@ fn replace_value_in_json_text(
let mut last_value_range = 0..0;
let mut first_key_start = None;
let mut existing_value_range = 0..text.len();
- let matches = cursor.matches(&PAIR_QUERY, syntax_tree.root_node(), text.as_bytes());
- for mat in matches {
+ let mut matches = cursor.matches(&PAIR_QUERY, syntax_tree.root_node(), text.as_bytes());
+ while let Some(mat) = matches.next() {
if mat.captures.len() != 2 {
continue;
}
@@ -671,18 +671,16 @@ impl Render for ContextMenu {
.when_some(
*toggle,
|list_item, (position, toggled)| {
- let contents = if toggled {
+ let contents =
div().flex_none().child(
Icon::new(IconName::Check)
.color(Color::Accent)
.size(*icon_size)
)
- } else {
- div().flex_none().child(
- Icon::new(IconName::Check)
- .size(*icon_size)
- ).invisible()
- };
+ .when(!toggled, |contents|
+ contents.invisible()
+ );
+
match position {
IconPosition::Start => {
list_item