From 6ebe599c980508aa8c7e87fad3e59e5a43892368 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 22 Mar 2024 17:29:06 -0700 Subject: [PATCH] Fix issues with extension API that come up when moving Svelte into an extension (#9611) We're doing it. Svelte support is moving into an extension. This PR fixes some issues that came up along the way. Notes * extensions need to be able to retrieve the path the `node` binary installed by Zed * previously we were silently swallowing any errors that occurred while loading a grammar * npm commands ran by extensions weren't run in the right directory * Tree-sitter's WASM stdlib didn't support a C function (`strncmp`) needed by the Svelte parser's external scanner * the way that LSP installation status was reported was unnecessarily complex Release Notes: - Removed built-in support for the Svelte and Gleam languages, because full support for those languages is now available via extensions. These extensions will be suggested for download when you open a `.svelte` or `.gleam` file. --------- Co-authored-by: Marshall --- Cargo.lock | 410 ++++++++---------- Cargo.toml | 19 +- .../src/activity_indicator.rs | 2 +- crates/channel/src/channel_store.rs | 4 +- crates/collab/src/rpc.rs | 24 +- crates/copilot/src/copilot.rs | 5 +- crates/db/src/db.rs | 4 +- crates/extension/src/extension_builder.rs | 5 +- crates/extension/src/extension_lsp_adapter.rs | 2 +- crates/extension/src/extension_store.rs | 65 ++- crates/extension/src/extension_store_test.rs | 16 +- crates/extension/src/wasm_host.rs | 347 ++------------- crates/extension/src/wasm_host/wit.rs | 103 +++++ crates/extension/src/wasm_host/wit/v0_0_1.rs | 210 +++++++++ crates/extension/src/wasm_host/wit/v0_0_4.rs | 284 ++++++++++++ crates/extension_api/Cargo.toml | 4 +- crates/extension_api/src/extension_api.rs | 16 +- .../wit/{ => 0.0.1}/extension.wit | 3 + crates/extension_api/wit/0.0.4/extension.wit | 93 ++++ crates/extensions_ui/src/extension_suggest.rs | 2 + crates/language/src/language.rs | 10 +- crates/language/src/language_registry.rs | 74 ++-- crates/languages/Cargo.toml | 2 - crates/languages/src/astro.rs | 4 +- crates/languages/src/c.rs | 5 +- crates/languages/src/csharp.rs | 5 +- crates/languages/src/css.rs | 4 +- crates/languages/src/deno.rs | 5 +- crates/languages/src/dockerfile.rs | 4 +- crates/languages/src/elixir.rs | 5 +- crates/languages/src/elm.rs | 4 +- crates/languages/src/gleam.rs | 122 ------ crates/languages/src/gleam/config.toml | 11 - crates/languages/src/gleam/highlights.scm | 130 ------ crates/languages/src/gleam/indents.scm | 3 - crates/languages/src/gleam/outline.scm | 31 -- crates/languages/src/go.rs | 4 +- crates/languages/src/html.rs | 4 +- crates/languages/src/json.rs | 4 +- crates/languages/src/lib.rs | 18 +- crates/languages/src/lua.rs | 5 +- crates/languages/src/php.rs | 4 +- crates/languages/src/prisma.rs | 4 +- crates/languages/src/purescript.rs | 4 +- crates/languages/src/rust.rs | 5 +- crates/languages/src/svelte.rs | 162 ------- crates/languages/src/svelte/folds.scm | 9 - crates/languages/src/tailwind.rs | 4 +- crates/languages/src/terraform.rs | 5 +- crates/languages/src/toml.rs | 4 +- crates/languages/src/typescript.rs | 7 +- crates/languages/src/vue.rs | 4 +- crates/languages/src/yaml.rs | 4 +- crates/languages/src/zig.rs | 4 +- crates/project/src/project.rs | 3 +- crates/util/src/util.rs | 19 +- crates/workspace/src/notifications.rs | 2 +- crates/zed/src/main.rs | 4 +- extensions/gleam/Cargo.toml | 6 +- extensions/gleam/src/gleam.rs | 9 - extensions/svelte/.gitignore | 3 + extensions/svelte/Cargo.toml | 16 + extensions/svelte/extension.toml | 15 + .../svelte/languages}/svelte/config.toml | 0 .../svelte/languages}/svelte/highlights.scm | 0 .../svelte/languages}/svelte/indents.scm | 0 .../svelte/languages}/svelte/injections.scm | 0 .../svelte/languages}/svelte/overrides.scm | 0 extensions/svelte/src/svelte.rs | 126 ++++++ extensions/uiua/Cargo.toml | 6 +- 70 files changed, 1278 insertions(+), 1223 deletions(-) create mode 100644 crates/extension/src/wasm_host/wit.rs create mode 100644 crates/extension/src/wasm_host/wit/v0_0_1.rs create mode 100644 crates/extension/src/wasm_host/wit/v0_0_4.rs rename crates/extension_api/wit/{ => 0.0.1}/extension.wit (96%) create mode 100644 crates/extension_api/wit/0.0.4/extension.wit delete mode 100644 crates/languages/src/gleam.rs delete mode 100644 crates/languages/src/gleam/config.toml delete mode 100644 crates/languages/src/gleam/highlights.scm delete mode 100644 crates/languages/src/gleam/indents.scm delete mode 100644 crates/languages/src/gleam/outline.scm delete mode 100644 crates/languages/src/svelte.rs delete mode 100755 crates/languages/src/svelte/folds.scm create mode 100644 extensions/svelte/.gitignore create mode 100644 extensions/svelte/Cargo.toml create mode 100644 extensions/svelte/extension.toml rename {crates/languages/src => extensions/svelte/languages}/svelte/config.toml (100%) rename {crates/languages/src => extensions/svelte/languages}/svelte/highlights.scm (100%) rename {crates/languages/src => extensions/svelte/languages}/svelte/indents.scm (100%) rename {crates/languages/src => extensions/svelte/languages}/svelte/injections.scm (100%) rename {crates/languages/src => extensions/svelte/languages}/svelte/overrides.scm (100%) create mode 100644 extensions/svelte/src/svelte.rs diff --git a/Cargo.lock b/Cargo.lock index bd1666f16b..3f0575d164 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,7 +508,7 @@ dependencies = [ "futures-lite 2.2.0", "parking", "polling 3.3.2", - "rustix 0.38.30", + "rustix 0.38.32", "slab", "tracing", "windows-sys 0.52.0", @@ -610,7 +610,7 @@ dependencies = [ "cfg-if", "event-listener 5.1.0", "futures-lite 2.2.0", - "rustix 0.38.30", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -648,7 +648,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.30", + "rustix 0.38.32", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -1702,7 +1702,7 @@ dependencies = [ "bitflags 2.4.2", "log", "polling 3.3.2", - "rustix 0.38.30", + "rustix 0.38.32", "slab", "thiserror", ] @@ -1714,16 +1714,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" dependencies = [ "calloop", - "rustix 0.38.30", + "rustix 0.38.32", "wayland-backend", "wayland-client", ] [[package]] name = "cap-fs-ext" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88e341d15ac1029aadce600be764a1a1edafe40e03cde23285bc1d261b3a4866" +checksum = "769f8cd02eb04d57f14e2e371ebb533f96817f9b2525d73a5c72b61ca7973747" dependencies = [ "cap-primitives", "cap-std", @@ -1733,21 +1733,21 @@ dependencies = [ [[package]] name = "cap-net-ext" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434168fe6533055f0f4204039abe3ff6d7db338ef46872a5fa39e9d5ad5ab7a9" +checksum = "59ff6d3fb274292a9af283417e383afe6ded1fe66f6472d2c781216d3d80c218" dependencies = [ "cap-primitives", "cap-std", - "rustix 0.38.30", + "rustix 0.38.32", "smallvec", ] [[package]] name = "cap-primitives" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe16767ed8eee6d3f1f00d6a7576b81c226ab917eb54b96e5f77a5216ef67abb" +checksum = "90a0b44fc796b1a84535a63753d50ba3972c4db55c7255c186f79140e63d56d0" dependencies = [ "ambient-authority", "fs-set-times", @@ -1755,16 +1755,16 @@ dependencies = [ "io-lifetimes 2.0.3", "ipnet", "maybe-owned", - "rustix 0.38.30", + "rustix 0.38.32", "windows-sys 0.52.0", "winx", ] [[package]] name = "cap-rand" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e5695565f0cd7106bc3c7170323597540e772bb73e0be2cd2c662a0f8fa4ca" +checksum = "4327f08daac33a99bb03c54ae18c8f32c3ba31c728a33ddf683c6c6a5043de68" dependencies = [ "ambient-authority", "rand 0.8.5", @@ -1772,27 +1772,27 @@ dependencies = [ [[package]] name = "cap-std" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "593db20e4c51f62d3284bae7ee718849c3214f93a3b94ea1899ad85ba119d330" +checksum = "266626ce180cf9709f317d0bf9754e3a5006359d87f4bf792f06c9c5f1b63c0f" dependencies = [ "cap-primitives", "io-extras", "io-lifetimes 2.0.3", - "rustix 0.38.30", + "rustix 0.38.32", ] [[package]] name = "cap-time-ext" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03261630f291f425430a36f38c847828265bc928f517cdd2004c56f4b02f002b" +checksum = "e1353421ba83c19da60726e35db0a89abef984b3be183ff6f58c5b8084fcd0c5" dependencies = [ "ambient-authority", "cap-primitives", "iana-time-zone", "once_cell", - "rustix 0.38.30", + "rustix 0.38.32", "winx", ] @@ -2604,9 +2604,9 @@ dependencies = [ [[package]] name = "cpp_demangle" -version = "0.3.5" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" dependencies = [ "cfg-if", ] @@ -2622,18 +2622,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9515fcc42b6cb5137f76b84c1a6f819782d0cf12473d145d3bc5cd67eedc8bc2" +checksum = "6a535eb1cf5a6003197dc569320c40c1cb2d2f97ef5d5348eebf067f20957381" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad827c6071bfe6d22de1bc331296a29f9ddc506ff926d8415b435ec6a6efce0" +checksum = "11b5066db32cec1492573827183af2142d2d88fe85a83cfc9e73f0f63d3788d4" dependencies = [ "bumpalo", "cranelift-bforest", @@ -2652,33 +2652,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10e6b36237a9ca2ce2fb4cc7741d418a080afa1327402138412ef85d5367bef1" +checksum = "64942e5774308e835fbad4dd25f253105412c90324631910e1ec27963147bddb" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36bf4bfb86898a94ccfa773a1f86e8a5346b1983ff72059bdd2db4600325251" +checksum = "c39c33db9a86dd6d8d04166a10c53deb477aeea3500eaaefca682e4eda9bb986" [[package]] name = "cranelift-control" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbf36560e7a6bd1409ca91e7b43b2cc7ed8429f343d7605eadf9046e8fac0d0" +checksum = "4b7fc4937613aea3156a0538800a17bf56f345a5da2e79ae3df58488c93d867f" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71e11061a75b1184c09bea97c026a88f08b59ade96a7bb1f259d4ea0df2e942" +checksum = "f85575e79a153ce1ddbfb7fe1813519b4bfe1eb200cc9c8353b45ad123ae4d36" dependencies = [ "serde", "serde_derive", @@ -2686,9 +2686,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af5d4da63143ee3485c7bcedde0a818727d737d1083484a0ceedb8950c89e495" +checksum = "bbc31d6c0ab2249fe0c21e988256b42f5f401ab2673b4fc40076c82a698bdfb9" dependencies = [ "cranelift-codegen", "log", @@ -2698,15 +2698,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457a9832b089e26f5eea70dcf49bed8ec6edafed630ce7c83161f24d46ab8085" +checksum = "dc14f37e3314c0e4c53779c2f46753bf242efff76ee9473757a1fff3b495ad37" [[package]] name = "cranelift-native" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b490d579df1ce365e1ea359e24ed86d82289fa785153327c2f6a69a59a731e4" +checksum = "2ea5375f76ab31f9800a23fb2b440810286a6f669a3eb467cdd7ff255ea64268" dependencies = [ "cranelift-codegen", "libc", @@ -2715,14 +2715,14 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.105.2" +version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd747ed7f9a461dda9c388415392f6bb95d1a6ef3b7694d17e0817eb74b7798" +checksum = "79851dba01b1fa83fad95134aa27beca88dc4b027121d92ab19788582389dc5f" dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "itertools 0.10.5", + "itertools 0.12.1", "log", "smallvec", "wasmparser", @@ -3498,7 +3498,7 @@ dependencies = [ "wasmparser", "wasmtime", "wasmtime-wasi", - "wit-component 0.20.3", + "wit-component", ] [[package]] @@ -3589,7 +3589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" dependencies = [ "cfg-if", - "rustix 0.38.30", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -3901,7 +3901,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" dependencies = [ "io-lifetimes 2.0.3", - "rustix 0.38.30", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -5000,6 +5000,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -5284,7 +5293,6 @@ dependencies = [ "tree-sitter-elm", "tree-sitter-embedded-template", "tree-sitter-erlang", - "tree-sitter-gleam", "tree-sitter-glsl", "tree-sitter-go", "tree-sitter-gomod", @@ -5310,7 +5318,6 @@ dependencies = [ "tree-sitter-ruby", "tree-sitter-rust", "tree-sitter-scheme", - "tree-sitter-svelte", "tree-sitter-toml", "tree-sitter-typescript", "tree-sitter-vue", @@ -5697,7 +5704,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.30", + "rustix 0.38.32", ] [[package]] @@ -6884,7 +6891,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.30", + "rustix 0.38.32", "tracing", "windows-sys 0.52.0", ] @@ -7939,9 +7946,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ "bitflags 2.4.2", "errno", @@ -7960,7 +7967,7 @@ checksum = "a25c3aad9fc1424eb82c88087789a7d938e1829724f3e4043163baf0d13cfc12" dependencies = [ "errno", "libc", - "rustix 0.38.30", + "rustix 0.38.32", ] [[package]] @@ -8712,7 +8719,7 @@ dependencies = [ "libc", "log", "memmap2 0.9.4", - "rustix 0.38.30", + "rustix 0.38.32", "thiserror", "wayland-backend", "wayland-client", @@ -9368,16 +9375,16 @@ dependencies = [ [[package]] name = "system-interface" -version = "0.26.1" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0682e006dd35771e392a6623ac180999a9a854b1d4a6c12fb2e804941c2b1f58" +checksum = "9aef1f9d4c1dbdd1cb3a63be9efd2f04d8ddbc919d46112982c76818ffc2f1a7" dependencies = [ "bitflags 2.4.2", "cap-fs-ext", "cap-std", "fd-lock", "io-lifetimes 2.0.3", - "rustix 0.38.30", + "rustix 0.38.32", "windows-sys 0.52.0", "winx", ] @@ -9466,7 +9473,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.0", "redox_syscall 0.4.1", - "rustix 0.38.30", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -10109,11 +10116,10 @@ dependencies = [ [[package]] name = "tree-sitter" version = "0.20.100" -source = "git+https://github.com/tree-sitter/tree-sitter?rev=4294e59279205f503eb14348dd5128bd5910c8fb#4294e59279205f503eb14348dd5128bd5910c8fb" +source = "git+https://github.com/tree-sitter/tree-sitter?rev=05079ae3d1bc44bedc4594eef925b36ba5e317a2#05079ae3d1bc44bedc4594eef925b36ba5e317a2" dependencies = [ "cc", "regex", - "wasmtime", "wasmtime-c-api-impl", ] @@ -10237,15 +10243,6 @@ dependencies = [ "tree-sitter", ] -[[package]] -name = "tree-sitter-gleam" -version = "0.34.0" -source = "git+https://github.com/gleam-lang/tree-sitter-gleam?rev=58b7cac8fc14c92b0677c542610d8738c373fa81#58b7cac8fc14c92b0677c542610d8738c373fa81" -dependencies = [ - "cc", - "tree-sitter", -] - [[package]] name = "tree-sitter-glsl" version = "0.1.4" @@ -10488,15 +10485,6 @@ dependencies = [ "tree-sitter", ] -[[package]] -name = "tree-sitter-svelte" -version = "0.10.2" -source = "git+https://github.com/Himujjal/tree-sitter-svelte?rev=bd60db7d3d06f89b6ec3b287c9a6e9190b5564bd#bd60db7d3d06f89b6ec3b287c9a6e9190b5564bd" -dependencies = [ - "cc", - "tree-sitter", -] - [[package]] name = "tree-sitter-toml" version = "0.5.1" @@ -11031,32 +11019,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasi-common" -version = "18.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "880c1461417b2bf90262591bf8a5f04358fb86dac8a585a49b87024971296763" -dependencies = [ - "anyhow", - "bitflags 2.4.2", - "cap-fs-ext", - "cap-rand", - "cap-std", - "cap-time-ext", - "fs-set-times", - "io-extras", - "io-lifetimes 2.0.3", - "log", - "once_cell", - "rustix 0.38.30", - "system-interface", - "thiserror", - "tracing", - "wasmtime", - "wiggle", - "windows-sys 0.52.0", -] - [[package]] name = "wasm-bindgen" version = "0.2.87" @@ -11125,18 +11087,18 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-encoder" -version = "0.41.2" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.10.20" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18ebaa7bd0f9e7a5e5dd29b9a998acf21c4abed74265524dd7e85934597bfb10" +checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" dependencies = [ "anyhow", "indexmap 2.0.0", @@ -11150,9 +11112,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.121.2" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" dependencies = [ "bitflags 2.4.2", "indexmap 2.0.0", @@ -11161,9 +11123,9 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.2.80" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e73986a6b7fdfedb7c5bf9e7eb71135486507c8fbc4c0c42cffcb6532988b7" +checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265" dependencies = [ "anyhow", "wasmparser", @@ -11171,9 +11133,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c843b8bc4dd4f3a76173ba93405c71111d570af0d90ea5f6299c705d0c2add2" +checksum = "6a08af88fa3d324cc5cf6d388d90ef396a787b3fb4bbd51ba185f8645dc0f02c" dependencies = [ "anyhow", "async-trait", @@ -11188,7 +11150,8 @@ dependencies = [ "object", "once_cell", "paste", - "rustix 0.38.30", + "rustix 0.38.32", + "semver", "serde", "serde_derive", "serde_json", @@ -11201,24 +11164,25 @@ dependencies = [ "wasmtime-fiber", "wasmtime-jit-icache-coherence", "wasmtime-runtime", + "wasmtime-slab", "wasmtime-winch", "windows-sys 0.52.0", ] [[package]] name = "wasmtime-asm-macros" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b9d329c718b3a18412a6a017c912b539baa8fe1210d21b651f6b4dbafed743" +checksum = "16cdbfcf28542bcda0b5fd68d44603e53e5ad126cbe7b9f25c130e1249fd8211" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-c-api-impl" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc93587c24d8e3cb28912eb7abf95f7e350380656faccc46cff04c0821ec58c2" +checksum = "67dea28073e105735210b9e932b5e654198d5e28ed31f1314037cd7664ceda2b" dependencies = [ "anyhow", "log", @@ -11230,9 +11194,9 @@ dependencies = [ [[package]] name = "wasmtime-c-api-macros" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e571a71eba52dfe81ef653a3a336888141f00fc2208a9962722e036fe2a34be" +checksum = "0cfe12050fa28b17ab8434ab757fee281dd0d5c7715fa1bc5e4c0b29d1705415" dependencies = [ "proc-macro2", "quote", @@ -11240,9 +11204,9 @@ dependencies = [ [[package]] name = "wasmtime-component-macro" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d55ddfd02898885c39638eae9631cd430c83a368f5996ed0f7bfb181d02157" +checksum = "0cdcf690257c623506eeec3a502864b282aab0fdfd6981c1ebb63c7e98f4a23a" dependencies = [ "anyhow", "proc-macro2", @@ -11250,20 +11214,20 @@ dependencies = [ "syn 2.0.48", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.13.2", + "wit-parser", ] [[package]] name = "wasmtime-component-util" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d69c430cddc70ec42159506962c66983ce0192ebde4eb125b7aabc49cff88" +checksum = "ab3ae7bf66e2fae1e332ab3634f332d7422e878a6eecc47c8f8f78cc1f24e501" [[package]] name = "wasmtime-cranelift" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ca62f519225492bd555d0ec85a2dacb0c10315db3418c8b9aeb3824bf54a24" +checksum = "67ea025c969a09117818732fa6f96848e858a7953d4659dab8081a6eea3c0523" dependencies = [ "anyhow", "cfg-if", @@ -11286,9 +11250,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift-shared" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5f2071f42e61490bf7cb95b9acdbe6a29dd577a398019304a960585f28b844" +checksum = "dcd6dd2f8d8d4860b384f61f89b597633a5b5f0943c546210e5084c5d321fe20" dependencies = [ "anyhow", "cranelift-codegen", @@ -11302,9 +11266,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82bf1a47f384610da19f58b0fd392ca6a3b720974315c08afb0392c0f3951fed" +checksum = "7f60f3f717658dd77745de03b750d5852126e9be6dad465848c77f90387c44c9" dependencies = [ "anyhow", "bincode", @@ -11328,14 +11292,14 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e31aecada2831e067ebfe93faa3001cc153d506f8af40bbea58aa1d20fe4820" +checksum = "bf8cd22ab1041bf0e54b6283e57824557902e4fed8b1f3a7eef29cbaba89eebf" dependencies = [ "anyhow", "cc", "cfg-if", - "rustix 0.38.30", + "rustix 0.38.32", "wasmtime-asm-macros", "wasmtime-versioned-export-macros", "windows-sys 0.52.0", @@ -11343,9 +11307,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f4121cb29dda08139b2824a734dd095d83ce843f2d613a84eb580b9cfc17ac" +checksum = "2796e4b4989db62899d2117e1e0258b839d088c044591b14e3a0396e7b3ae53a" dependencies = [ "cfg-if", "libc", @@ -11354,9 +11318,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e517f2b996bb3b0e34a82a2bce194f850d9bcfc25c08328ef5fb71b071066b8" +checksum = "4bf2b7745df452a4f41b9aab21d3f7ba1347b12da2fdc5241e59306127884a68" dependencies = [ "anyhow", "cc", @@ -11370,7 +11334,7 @@ dependencies = [ "memoffset 0.9.0", "paste", "psm", - "rustix 0.38.30", + "rustix 0.38.32", "sptr", "wasm-encoder", "wasmtime-asm-macros", @@ -11382,10 +11346,16 @@ dependencies = [ ] [[package]] -name = "wasmtime-types" -version = "18.0.2" +name = "wasmtime-slab" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a327d7a0ef57bd52a507d28b4561a74126c7a8535a2fc6f2025716bc6a52e8" +checksum = "83448ef600ad95977019ebaea84a5516fdbc9561d0a8e26b1e099351f993b527" + +[[package]] +name = "wasmtime-types" +version = "19.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf6fe7ed3fd18ed4b1e4465fe5c8674acc9f03523fca5b1b9f975b2560cd741b" dependencies = [ "cranelift-entity", "serde", @@ -11396,9 +11366,9 @@ dependencies = [ [[package]] name = "wasmtime-versioned-export-macros" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef32eea9fc7035a55159a679d1e89b43ece5ae45d24eed4808e6a92c99a0da4" +checksum = "6d6d967f01032da7d4c6303da32f6a00d5efe1bac124b156e7342d8ace6ffdfc" dependencies = [ "proc-macro2", "quote", @@ -11407,9 +11377,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04d2fb2257245aa05ff799ded40520ae4d8cd31b0d14972afac89061f12fe12" +checksum = "371d828b6849ea06d598ae7dd1c316e8dd9e99b76f77d93d5886cb25c7f8e188" dependencies = [ "anyhow", "async-trait", @@ -11424,15 +11394,13 @@ dependencies = [ "futures 0.3.28", "io-extras", "io-lifetimes 2.0.3", - "log", "once_cell", - "rustix 0.38.30", + "rustix 0.38.32", "system-interface", "thiserror", "tokio", "tracing", "url", - "wasi-common", "wasmtime", "wiggle", "windows-sys 0.52.0", @@ -11440,9 +11408,9 @@ dependencies = [ [[package]] name = "wasmtime-winch" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3378c0e808a744b5d4df2a9a9d2746a53b151811926731f04fc401707f7d54" +checksum = "eb8b3fcbc455105760e4a2aa8ee3f39b8357183a62201383b3f72d4836ca2be8" dependencies = [ "anyhow", "cranelift-codegen", @@ -11457,21 +11425,21 @@ dependencies = [ [[package]] name = "wasmtime-wit-bindgen" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca677c36869e45602617b25a9968ec0d895ad9a0aee3756d9dee1ddd89456f91" +checksum = "96326c9800fb6c099f50d1bd2126d636fc2f96950e1675acf358c0f52516cd38" dependencies = [ "anyhow", "heck 0.4.1", "indexmap 2.0.0", - "wit-parser 0.13.2", + "wit-parser", ] [[package]] name = "wasmtime-wmemcheck" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4cbfb052d66f03603a9b77f18171ea245c7805714caad370a549a6344bf86b" +checksum = "36bd91a4dc55af0bf55e9e2ab0ea13724cfb5c5a1acdf8873039769208f59490" [[package]] name = "wast" @@ -11490,7 +11458,7 @@ checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.30", + "rustix 0.38.32", "scoped-tls", "smallvec", "wayland-sys", @@ -11503,7 +11471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" dependencies = [ "bitflags 2.4.2", - "rustix 0.38.30", + "rustix 0.38.32", "wayland-backend", "wayland-scanner", ] @@ -11525,7 +11493,7 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" dependencies = [ - "rustix 0.38.30", + "rustix 0.38.32", "wayland-client", "xcursor", ] @@ -11636,7 +11604,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.30", + "rustix 0.38.32", ] [[package]] @@ -11648,7 +11616,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.30", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -11660,9 +11628,9 @@ checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" [[package]] name = "wiggle" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69812e493f8a43d8551abfaaf9539e1aff0cf56a58cdd276845fc4af035d0cd" +checksum = "ae1136a209614ace00b0c11f04dc7cf42540773be3b22eff6ad165110aba29c1" dependencies = [ "anyhow", "async-trait", @@ -11675,9 +11643,9 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0446357a5a7af0172848b6eca7b2aa1ab7d90065cd2ab02b633a322e1a52f636" +checksum = "4c2bd99ce26046f4246d720a4198f6a8fc95bc5da82ae4ef62263e24641c3076" dependencies = [ "anyhow", "heck 0.4.1", @@ -11690,9 +11658,9 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "18.0.2" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9498ef53a12cf25dc6de9baef6ccd8b58d159202c412a19f4d72b218393086c5" +checksum = "512d816dbcd0113103b2eb2402ec9018e7f0755202a5b3e67db726f229d8dcae" dependencies = [ "proc-macro2", "quote", @@ -11733,9 +11701,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "0.16.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197ed4a2ebf612f0624ddda10de71f8cd2d3a4ecf8ffac0586a264599708d63" +checksum = "d285c833af9453c037cd220765f86c5c9961e8906a815829107c8801d535b8e4" dependencies = [ "anyhow", "cranelift-codegen", @@ -12094,42 +12062,50 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.18.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5408d742fcdf418b766f23b2393f0f4d9b10b72b7cd96d9525626943593e8cc0" +checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27" dependencies = [ "bitflags 2.4.2", + "wit-bindgen-rt", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.18.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7146725463d08ccf9c6c5357a7a6c1fff96185d95d6e84e7c75c92e5b1273c93" +checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4" dependencies = [ "anyhow", - "wit-parser 0.14.0", + "wit-parser", ] [[package]] -name = "wit-bindgen-rust" -version = "0.18.0" +name = "wit-bindgen-rt" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5fefcf93ff2ea03c8fe9b9db2caee3096103c0e3cd62ed54f6f9493aa6b405" +checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64" + +[[package]] +name = "wit-bindgen-rust" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3" dependencies = [ "anyhow", "heck 0.4.1", + "indexmap 2.0.0", "wasm-metadata", "wit-bindgen-core", - "wit-component 0.21.0", + "wit-component", ] [[package]] name = "wit-bindgen-rust-macro" -version = "0.18.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce4059a1adc671e4457f457cb638ed2f766a1a462bb7daa3b638c6fb1fda156e" +checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b" dependencies = [ "anyhow", "proc-macro2", @@ -12141,9 +12117,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.20.3" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4436190e87b4e539807bcdcf5b817e79d2e29e16bc5ddb6445413fe3d1f5716" +checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" dependencies = [ "anyhow", "bitflags 2.4.2", @@ -12155,50 +12131,14 @@ dependencies = [ "wasm-encoder", "wasm-metadata", "wasmparser", - "wit-parser 0.13.2", -] - -[[package]] -name = "wit-component" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be60cd1b2ff7919305301d0c27528d4867bd793afe890ba3837743da9655d91b" -dependencies = [ - "anyhow", - "bitflags 2.4.2", - "indexmap 2.0.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser 0.14.0", + "wit-parser", ] [[package]] name = "wit-parser" -version = "0.13.2" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "316b36a9f0005f5aa4b03c39bc3728d045df136f8c13a73b7db4510dec725e08" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.0.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", -] - -[[package]] -name = "wit-parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee4ad7310367bf272507c0c8e0c74a80b4ed586b833f7c7ca0b7588f686f11a" +checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" dependencies = [ "anyhow", "id-arena", @@ -12324,7 +12264,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "rustix 0.38.30", + "rustix 0.38.32", "x11rb-protocol", ] @@ -12643,7 +12583,16 @@ dependencies = [ [[package]] name = "zed_extension_api" -version = "0.0.1" +version = "0.0.4" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "zed_extension_api" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5c51cad4152bb5eb35b20dccdcbfb36f48d8952a2ed2d3e25b70361007d953b" dependencies = [ "wit-bindgen", ] @@ -12652,14 +12601,21 @@ dependencies = [ name = "zed_gleam" version = "0.0.1" dependencies = [ - "zed_extension_api", + "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zed_svelte" +version = "0.0.1" +dependencies = [ + "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "zed_uiua" version = "0.0.1" dependencies = [ - "zed_extension_api", + "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9da214d4e5..f24e8c18f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,4 @@ + [workspace] members = [ "crates/activity_indicator", @@ -97,6 +98,7 @@ members = [ "extensions/gleam", "extensions/uiua", + "extensions/svelte", "tooling/xtask", ] @@ -210,7 +212,7 @@ bitflags = "2.4.2" blade-graphics = { git = "https://github.com/kvark/blade", rev = "61cbd6b2c224791d52b150fe535cee665cc91bb2" } blade-macros = { git = "https://github.com/kvark/blade", rev = "61cbd6b2c224791d52b150fe535cee665cc91bb2" } blade-rwh = { package = "raw-window-handle", version = "0.5" } -cap-std = "2.0" +cap-std = "3.0" chrono = { version = "0.4", features = ["serde"] } clap = { version = "4.4", features = ["derive"] } clickhouse = { version = "0.11.6" } @@ -293,7 +295,6 @@ tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir" tree-sitter-elm = { git = "https://github.com/elm-tooling/tree-sitter-elm", rev = "692c50c0b961364c40299e73c1306aecb5d20f40" } tree-sitter-embedded-template = "0.20.0" tree-sitter-erlang = "0.4.0" -tree-sitter-gleam = { git = "https://github.com/gleam-lang/tree-sitter-gleam", rev = "58b7cac8fc14c92b0677c542610d8738c373fa81" } tree-sitter-glsl = { git = "https://github.com/theHamsta/tree-sitter-glsl", rev = "2a56fb7bc8bb03a1892b4741279dd0a8758b7fb3" } tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" } tree-sitter-gomod = { git = "https://github.com/camdencheek/tree-sitter-go-mod" } @@ -320,7 +321,6 @@ tree-sitter-regex = "0.20.0" tree-sitter-ruby = "0.20.0" tree-sitter-rust = "0.20.3" tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9" } -tree-sitter-svelte = { git = "https://github.com/Himujjal/tree-sitter-svelte", rev = "bd60db7d3d06f89b6ec3b287c9a6e9190b5564bd" } tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" } tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" } tree-sitter-vue = { git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42" } @@ -330,18 +330,18 @@ unindent = "0.1.7" unicase = "2.6" url = "2.2" uuid = { version = "1.1.2", features = ["v4"] } -wasmparser = "0.121" -wasm-encoder = "0.41" -wasmtime = { version = "18.0", default-features = false, features = [ +wasmparser = "0.201" +wasm-encoder = "0.201" +wasmtime = { version = "19.0.0", default-features = false, features = [ "async", "demangle", "runtime", "cranelift", "component-model", ] } -wasmtime-wasi = "18.0" +wasmtime-wasi = "19.0.0" which = "6.0.0" -wit-component = "0.20" +wit-component = "0.201" sys-locale = "0.3.1" [workspace.dependencies.windows] @@ -375,7 +375,7 @@ features = [ ] [patch.crates-io] -tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "4294e59279205f503eb14348dd5128bd5910c8fb" } +tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "05079ae3d1bc44bedc4594eef925b36ba5e317a2" } # Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released. pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "30419d07660dc11a21e42ef4a7fa329600cff152" } @@ -389,6 +389,7 @@ cranelift-codegen = { opt-level = 3 } rustybuzz = { opt-level = 3 } ttf-parser = { opt-level = 3 } wasmtime-cranelift = { opt-level = 3 } +wasmtime = { opt-level = 3 } [profile.release] debug = "limited" diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 7ea3258c40..9a81996856 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -205,7 +205,7 @@ impl ActivityIndicator { } LanguageServerBinaryStatus::Downloading => downloading.push(status.name.0.as_ref()), LanguageServerBinaryStatus::Failed { .. } => failed.push(status.name.0.as_ref()), - LanguageServerBinaryStatus::Downloaded | LanguageServerBinaryStatus::Cached => {} + LanguageServerBinaryStatus::None => {} } } diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index b13e682d91..a6a94865b4 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -17,7 +17,7 @@ use rpc::{ }; use settings::Settings; use std::{mem, sync::Arc, time::Duration}; -use util::{async_maybe, maybe, ResultExt}; +use util::{maybe, ResultExt}; pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); @@ -227,7 +227,7 @@ impl ChannelStore { _watch_connection_status: watch_connection_status, disconnect_channel_buffers_task: None, _update_channels: cx.spawn(|this, mut cx| async move { - async_maybe!({ + maybe!(async move { while let Some(update_channels) = update_channels_rx.next().await { if let Some(this) = this.upgrade() { let update_task = this.update(&mut cx, |this, cx| { diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 9545b0c2e4..1cd2614129 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1228,25 +1228,21 @@ async fn create_room( ) -> Result<()> { let live_kit_room = nanoid::nanoid!(30); - let live_kit_connection_info = { - let live_kit_room = live_kit_room.clone(); + let live_kit_connection_info = util::maybe!(async { let live_kit = session.live_kit_client.as_ref(); + let live_kit = live_kit?; let user_id = session.user_id().to_string(); - util::async_maybe!({ - let live_kit = live_kit?; + let token = live_kit + .room_token(&live_kit_room, &user_id.to_string()) + .trace_err()?; - let token = live_kit - .room_token(&live_kit_room, &user_id.to_string()) - .trace_err()?; - - Some(proto::LiveKitConnectionInfo { - server_url: live_kit.url().into(), - token, - can_publish: true, - }) + Some(proto::LiveKitConnectionInfo { + server_url: live_kit.url().into(), + token, + can_publish: true, }) - } + }) .await; let room = session diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 71d9554ac6..83a0ed11c6 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -29,8 +29,7 @@ use std::{ sync::Arc, }; use util::{ - async_maybe, fs::remove_matching, github::latest_github_release, http::HttpClient, paths, - ResultExt, + fs::remove_matching, github::latest_github_release, http::HttpClient, maybe, paths, ResultExt, }; actions!( @@ -1006,7 +1005,7 @@ async fn get_copilot_lsp(http: Arc) -> anyhow::Result { e @ Err(..) => { e.log_err(); // Fetch a cached binary, if it exists - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(paths::COPILOT_DIR.as_path()).await?; while let Some(entry) = entries.next().await { diff --git a/crates/db/src/db.rs b/crates/db/src/db.rs index 2e6b325fee..577a1746d5 100644 --- a/crates/db/src/db.rs +++ b/crates/db/src/db.rs @@ -20,7 +20,7 @@ use sqlez_macros::sql; use std::future::Future; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const CONNECTION_INITIALIZE_QUERY: &str = sql!( PRAGMA foreign_keys=TRUE; @@ -57,7 +57,7 @@ pub async fn open_db( let release_channel_name = release_channel.dev_name(); let main_db_dir = db_dir.join(Path::new(&format!("0-{}", release_channel_name))); - let connection = async_maybe!({ + let connection = maybe!(async { smol::fs::create_dir_all(&main_db_dir) .await .context("Could not create db directory") diff --git a/crates/extension/src/extension_builder.rs b/crates/extension/src/extension_builder.rs index 21a0b7ece7..23969728a3 100644 --- a/crates/extension/src/extension_builder.rs +++ b/crates/extension/src/extension_builder.rs @@ -85,10 +85,7 @@ impl ExtensionBuilder { let cargo_toml_path = extension_dir.join("Cargo.toml"); if extension_manifest.lib.kind == Some(ExtensionLibraryKind::Rust) - || fs::metadata(&cargo_toml_path) - .ok() - .map(|metadata| metadata.is_file()) - .unwrap_or(false) + || fs::metadata(&cargo_toml_path).map_or(false, |stat| stat.is_file()) { log::info!("compiling Rust extension {}", extension_dir.display()); self.compile_rust_extension(extension_dir, options) diff --git a/crates/extension/src/extension_lsp_adapter.rs b/crates/extension/src/extension_lsp_adapter.rs index 9ca48739a8..6c13dda563 100644 --- a/crates/extension/src/extension_lsp_adapter.rs +++ b/crates/extension/src/extension_lsp_adapter.rs @@ -11,7 +11,7 @@ use std::{ pin::Pin, sync::Arc, }; -use wasmtime_wasi::preview2::WasiView as _; +use wasmtime_wasi::WasiView as _; pub struct ExtensionLspAdapter { pub(crate) extension: WasmExtension, diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index 3a1c971c38..237555407d 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -39,6 +39,7 @@ use theme::{ThemeRegistry, ThemeSettings}; use url::Url; use util::{ http::{AsyncBody, HttpClient, HttpClientWithUrl}, + maybe, paths::EXTENSIONS_DIR, ResultExt, }; @@ -108,6 +109,7 @@ pub enum Event { ExtensionsUpdated, StartedReloading, ExtensionInstalled(Arc), + ExtensionFailedToLoad(Arc), } impl EventEmitter for ExtensionStore {} @@ -886,41 +888,38 @@ impl ExtensionStore { continue; }; - let mut path = root_dir.clone(); - path.extend([extension.manifest.id.as_ref(), "extension.wasm"]); - let Some(mut wasm_file) = fs - .open_sync(&path) - .await - .context("failed to open wasm file") - .log_err() - else { - continue; - }; + let wasm_extension = maybe!(async { + let mut path = root_dir.clone(); + path.extend([extension.manifest.clone().id.as_ref(), "extension.wasm"]); + let mut wasm_file = fs + .open_sync(&path) + .await + .context("failed to open wasm file")?; - let mut wasm_bytes = Vec::new(); - if wasm_file - .read_to_end(&mut wasm_bytes) - .context("failed to read wasm") - .log_err() - .is_none() - { - continue; + let mut wasm_bytes = Vec::new(); + wasm_file + .read_to_end(&mut wasm_bytes) + .context("failed to read wasm")?; + + wasm_host + .load_extension( + wasm_bytes, + extension.manifest.clone().clone(), + cx.background_executor().clone(), + ) + .await + .context("failed to load wasm extension") + }) + .await; + + if let Some(wasm_extension) = wasm_extension.log_err() { + wasm_extensions.push((extension.manifest.clone(), wasm_extension)); + } else { + this.update(&mut cx, |_, cx| { + cx.emit(Event::ExtensionFailedToLoad(extension.manifest.id.clone())) + }) + .ok(); } - - let Some(wasm_extension) = wasm_host - .load_extension( - wasm_bytes, - extension.manifest.clone(), - cx.background_executor().clone(), - ) - .await - .context("failed to load wasm extension") - .log_err() - else { - continue; - }; - - wasm_extensions.push((extension.manifest.clone(), wasm_extension)); } this.update(&mut cx, |this, cx| { diff --git a/crates/extension/src/extension_store_test.rs b/crates/extension/src/extension_store_test.rs index c0bd037c5b..b179329168 100644 --- a/crates/extension/src/extension_store_test.rs +++ b/crates/extension/src/extension_store_test.rs @@ -1,6 +1,7 @@ use crate::{ - ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry, ExtensionIndexThemeEntry, - ExtensionManifest, ExtensionStore, GrammarManifestEntry, RELOAD_DEBOUNCE_DURATION, + Event, ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry, + ExtensionIndexThemeEntry, ExtensionManifest, ExtensionStore, GrammarManifestEntry, + RELOAD_DEBOUNCE_DURATION, }; use async_compression::futures::bufread::GzipEncoder; use collections::BTreeMap; @@ -558,6 +559,15 @@ async fn test_extension_store_with_gleam_extension(cx: &mut TestAppContext) { } }); + extension_store.update(cx, |_, cx| { + cx.subscribe(&extension_store, |_, _, event, _| { + if matches!(event, Event::ExtensionFailedToLoad(_)) { + panic!("extension failed to load"); + } + }) + .detach(); + }); + extension_store .update(cx, |store, cx| { store.install_dev_extension(gleam_extension_dir.clone(), cx) @@ -602,7 +612,7 @@ async fn test_extension_store_with_gleam_extension(cx: &mut TestAppContext) { ), ( LanguageServerName("gleam".into()), - LanguageServerBinaryStatus::Downloaded + LanguageServerBinaryStatus::None ) ] ); diff --git a/crates/extension/src/wasm_host.rs b/crates/extension/src/wasm_host.rs index 9d6f2683cb..23801304da 100644 --- a/crates/extension/src/wasm_host.rs +++ b/crates/extension/src/wasm_host.rs @@ -1,8 +1,7 @@ +pub(crate) mod wit; + use crate::ExtensionManifest; use anyhow::{anyhow, bail, Context as _, Result}; -use async_compression::futures::bufread::GzipDecoder; -use async_tar::Archive; -use async_trait::async_trait; use fs::{normalize_path, Fs}; use futures::{ channel::{ @@ -10,42 +9,28 @@ use futures::{ oneshot, }, future::BoxFuture, - io::BufReader, Future, FutureExt, StreamExt as _, }; use gpui::BackgroundExecutor; -use language::{LanguageRegistry, LanguageServerBinaryStatus, LspAdapterDelegate}; +use language::LanguageRegistry; use node_runtime::NodeRuntime; use std::{ - env, path::{Path, PathBuf}, sync::{Arc, OnceLock}, }; use util::{http::HttpClient, SemanticVersion}; use wasmtime::{ - component::{Component, Linker, Resource, ResourceTable}, + component::{Component, ResourceTable}, Engine, Store, }; -use wasmtime_wasi::preview2::{self as wasi, WasiCtx}; - -pub mod wit { - wasmtime::component::bindgen!({ - async: true, - path: "../extension_api/wit", - with: { - "worktree": super::ExtensionWorktree, - }, - }); -} - -pub type ExtensionWorktree = Arc; +use wasmtime_wasi as wasi; +use wit::Extension; pub(crate) struct WasmHost { engine: Engine, - linker: Arc>, http_client: Arc, node_runtime: Arc, - language_registry: Arc, + pub(crate) language_registry: Arc, fs: Arc, pub(crate) work_dir: PathBuf, } @@ -60,17 +45,27 @@ pub struct WasmExtension { pub(crate) struct WasmState { manifest: Arc, - table: ResourceTable, + pub(crate) table: ResourceTable, ctx: wasi::WasiCtx, - host: Arc, + pub(crate) host: Arc, } type ExtensionCall = Box< - dyn Send - + for<'a> FnOnce(&'a mut wit::Extension, &'a mut Store) -> BoxFuture<'a, ()>, + dyn Send + for<'a> FnOnce(&'a mut Extension, &'a mut Store) -> BoxFuture<'a, ()>, >; -static WASM_ENGINE: OnceLock = OnceLock::new(); +fn wasm_engine() -> wasmtime::Engine { + static WASM_ENGINE: OnceLock = OnceLock::new(); + + WASM_ENGINE + .get_or_init(|| { + let mut config = wasmtime::Config::new(); + config.wasm_component_model(true); + config.async_support(true); + wasmtime::Engine::new(&config).unwrap() + }) + .clone() +} impl WasmHost { pub fn new( @@ -80,20 +75,8 @@ impl WasmHost { language_registry: Arc, work_dir: PathBuf, ) -> Arc { - let engine = WASM_ENGINE - .get_or_init(|| { - let mut config = wasmtime::Config::new(); - config.wasm_component_model(true); - config.async_support(true); - wasmtime::Engine::new(&config).unwrap() - }) - .clone(); - let mut linker = Linker::new(&engine); - wasi::command::add_to_linker(&mut linker).unwrap(); - wit::Extension::add_to_linker(&mut linker, wasi_view).unwrap(); Arc::new(Self { - engine, - linker: Arc::new(linker), + engine: wasm_engine(), fs, work_dir, http_client, @@ -144,9 +127,8 @@ impl WasmHost { ); let (mut extension, instance) = - wit::Extension::instantiate_async(&mut store, &component, &this.linker) - .await - .context("failed to instantiate wasm extension")?; + Extension::instantiate_async(&mut store, zed_api_version, &component).await?; + extension .call_init_extension(&mut store) .await @@ -170,7 +152,7 @@ impl WasmHost { } } - async fn build_wasi_ctx(&self, manifest: &Arc) -> Result { + async fn build_wasi_ctx(&self, manifest: &Arc) -> Result { use cap_std::{ambient_authority, fs::Dir}; let extension_work_dir = self.work_dir.join(manifest.id.as_ref()); @@ -232,7 +214,7 @@ impl WasmExtension { T: 'static + Send, Fn: 'static + Send - + for<'a> FnOnce(&'a mut wit::Extension, &'a mut Store) -> BoxFuture<'a, T>, + + for<'a> FnOnce(&'a mut Extension, &'a mut Store) -> BoxFuture<'a, T>, { let (return_tx, return_rx) = oneshot::channel(); self.tx @@ -249,279 +231,10 @@ impl WasmExtension { } } -#[async_trait] -impl wit::HostWorktree for WasmState { - async fn read_text_file( - &mut self, - delegate: Resource>, - path: String, - ) -> wasmtime::Result> { - let delegate = self.table.get(&delegate)?; - Ok(delegate - .read_text_file(path.into()) - .await - .map_err(|error| error.to_string())) +impl WasmState { + fn work_dir(&self) -> PathBuf { + self.host.work_dir.join(self.manifest.id.as_ref()) } - - async fn shell_env( - &mut self, - delegate: Resource>, - ) -> wasmtime::Result { - let delegate = self.table.get(&delegate)?; - Ok(delegate.shell_env().await.into_iter().collect()) - } - - async fn which( - &mut self, - delegate: Resource>, - binary_name: String, - ) -> wasmtime::Result> { - let delegate = self.table.get(&delegate)?; - Ok(delegate - .which(binary_name.as_ref()) - .await - .map(|path| path.to_string_lossy().to_string())) - } - - fn drop(&mut self, _worktree: Resource) -> Result<()> { - // we only ever hand out borrows of worktrees - Ok(()) - } -} - -#[async_trait] -impl wit::ExtensionImports for WasmState { - async fn npm_package_latest_version( - &mut self, - package_name: String, - ) -> wasmtime::Result> { - async fn inner(this: &mut WasmState, package_name: String) -> anyhow::Result { - this.host - .node_runtime - .npm_package_latest_version(&package_name) - .await - } - - Ok(inner(self, package_name) - .await - .map_err(|err| err.to_string())) - } - - async fn npm_package_installed_version( - &mut self, - package_name: String, - ) -> wasmtime::Result, String>> { - async fn inner( - this: &mut WasmState, - package_name: String, - ) -> anyhow::Result> { - this.host - .node_runtime - .npm_package_installed_version(&this.host.work_dir, &package_name) - .await - } - - Ok(inner(self, package_name) - .await - .map_err(|err| err.to_string())) - } - - async fn npm_install_package( - &mut self, - package_name: String, - version: String, - ) -> wasmtime::Result> { - async fn inner( - this: &mut WasmState, - package_name: String, - version: String, - ) -> anyhow::Result<()> { - this.host - .node_runtime - .npm_install_packages(&this.host.work_dir, &[(&package_name, &version)]) - .await - } - - Ok(inner(self, package_name, version) - .await - .map_err(|err| err.to_string())) - } - - async fn latest_github_release( - &mut self, - repo: String, - options: wit::GithubReleaseOptions, - ) -> wasmtime::Result> { - async fn inner( - this: &mut WasmState, - repo: String, - options: wit::GithubReleaseOptions, - ) -> anyhow::Result { - let release = util::github::latest_github_release( - &repo, - options.require_assets, - options.pre_release, - this.host.http_client.clone(), - ) - .await?; - Ok(wit::GithubRelease { - version: release.tag_name, - assets: release - .assets - .into_iter() - .map(|asset| wit::GithubReleaseAsset { - name: asset.name, - download_url: asset.browser_download_url, - }) - .collect(), - }) - } - - Ok(inner(self, repo, options) - .await - .map_err(|err| err.to_string())) - } - - async fn current_platform(&mut self) -> Result<(wit::Os, wit::Architecture)> { - Ok(( - match env::consts::OS { - "macos" => wit::Os::Mac, - "linux" => wit::Os::Linux, - "windows" => wit::Os::Windows, - _ => panic!("unsupported os"), - }, - match env::consts::ARCH { - "aarch64" => wit::Architecture::Aarch64, - "x86" => wit::Architecture::X86, - "x86_64" => wit::Architecture::X8664, - _ => panic!("unsupported architecture"), - }, - )) - } - - async fn set_language_server_installation_status( - &mut self, - server_name: String, - status: wit::LanguageServerInstallationStatus, - ) -> wasmtime::Result<()> { - let status = match status { - wit::LanguageServerInstallationStatus::CheckingForUpdate => { - LanguageServerBinaryStatus::CheckingForUpdate - } - wit::LanguageServerInstallationStatus::Downloading => { - LanguageServerBinaryStatus::Downloading - } - wit::LanguageServerInstallationStatus::Downloaded => { - LanguageServerBinaryStatus::Downloaded - } - wit::LanguageServerInstallationStatus::Cached => LanguageServerBinaryStatus::Cached, - wit::LanguageServerInstallationStatus::Failed(error) => { - LanguageServerBinaryStatus::Failed { error } - } - }; - - self.host - .language_registry - .update_lsp_status(language::LanguageServerName(server_name.into()), status); - Ok(()) - } - - async fn download_file( - &mut self, - url: String, - path: String, - file_type: wit::DownloadedFileType, - ) -> wasmtime::Result> { - let path = PathBuf::from(path); - - async fn inner( - this: &mut WasmState, - url: String, - path: PathBuf, - file_type: wit::DownloadedFileType, - ) -> anyhow::Result<()> { - let extension_work_dir = this.host.work_dir.join(this.manifest.id.as_ref()); - - this.host.fs.create_dir(&extension_work_dir).await?; - - let destination_path = this - .host - .writeable_path_from_extension(&this.manifest.id, &path)?; - - let mut response = this - .host - .http_client - .get(&url, Default::default(), true) - .await - .map_err(|err| anyhow!("error downloading release: {}", err))?; - - if !response.status().is_success() { - Err(anyhow!( - "download failed with status {}", - response.status().to_string() - ))?; - } - let body = BufReader::new(response.body_mut()); - - match file_type { - wit::DownloadedFileType::Uncompressed => { - futures::pin_mut!(body); - this.host - .fs - .create_file_with(&destination_path, body) - .await?; - } - wit::DownloadedFileType::Gzip => { - let body = GzipDecoder::new(body); - futures::pin_mut!(body); - this.host - .fs - .create_file_with(&destination_path, body) - .await?; - } - wit::DownloadedFileType::GzipTar => { - let body = GzipDecoder::new(body); - futures::pin_mut!(body); - this.host - .fs - .extract_tar_file(&destination_path, Archive::new(body)) - .await?; - } - wit::DownloadedFileType::Zip => { - let file_name = destination_path - .file_name() - .ok_or_else(|| anyhow!("invalid download path"))? - .to_string_lossy(); - let zip_filename = format!("{file_name}.zip"); - let mut zip_path = destination_path.clone(); - zip_path.set_file_name(zip_filename); - - futures::pin_mut!(body); - this.host.fs.create_file_with(&zip_path, body).await?; - - let unzip_status = std::process::Command::new("unzip") - .current_dir(&extension_work_dir) - .arg(&zip_path) - .output()? - .status; - if !unzip_status.success() { - Err(anyhow!("failed to unzip {} archive", path.display()))?; - } - } - } - - Ok(()) - } - - Ok(inner(self, url, path, file_type) - .await - .map(|_| ()) - .map_err(|err| err.to_string())) - } -} - -fn wasi_view(state: &mut WasmState) -> &mut WasmState { - state } impl wasi::WasiView for WasmState { diff --git a/crates/extension/src/wasm_host/wit.rs b/crates/extension/src/wasm_host/wit.rs new file mode 100644 index 0000000000..2c752a7a8f --- /dev/null +++ b/crates/extension/src/wasm_host/wit.rs @@ -0,0 +1,103 @@ +mod v0_0_1; +mod v0_0_4; + +use super::{wasm_engine, WasmState}; +use anyhow::{Context, Result}; +use language::LspAdapterDelegate; +use std::sync::Arc; +use util::SemanticVersion; +use wasmtime::{ + component::{Component, Instance, Linker, Resource}, + Store, +}; + +use v0_0_4 as latest; + +pub use latest::{Command, LanguageServerConfig}; + +pub fn new_linker( + f: impl Fn(&mut Linker, fn(&mut WasmState) -> &mut WasmState) -> Result<()>, +) -> Linker { + let mut linker = Linker::new(&wasm_engine()); + wasmtime_wasi::command::add_to_linker(&mut linker).unwrap(); + f(&mut linker, wasi_view).unwrap(); + linker +} + +fn wasi_view(state: &mut WasmState) -> &mut WasmState { + state +} + +pub enum Extension { + V004(v0_0_4::Extension), + V001(v0_0_1::Extension), +} + +impl Extension { + pub async fn instantiate_async( + store: &mut Store, + version: SemanticVersion, + component: &Component, + ) -> Result<(Self, Instance)> { + if version < latest::VERSION { + let (extension, instance) = + v0_0_1::Extension::instantiate_async(store, &component, v0_0_1::linker()) + .await + .context("failed to instantiate wasm extension")?; + Ok((Self::V001(extension), instance)) + } else { + let (extension, instance) = + v0_0_4::Extension::instantiate_async(store, &component, v0_0_4::linker()) + .await + .context("failed to instantiate wasm extension")?; + Ok((Self::V004(extension), instance)) + } + } + + pub async fn call_init_extension(&self, store: &mut Store) -> Result<()> { + match self { + Extension::V004(ext) => ext.call_init_extension(store).await, + Extension::V001(ext) => ext.call_init_extension(store).await, + } + } + + pub async fn call_language_server_command( + &self, + store: &mut Store, + config: &LanguageServerConfig, + resource: Resource>, + ) -> Result> { + match self { + Extension::V004(ext) => { + ext.call_language_server_command(store, config, resource) + .await + } + Extension::V001(ext) => Ok(ext + .call_language_server_command(store, &config.clone().into(), resource) + .await? + .map(|command| command.into())), + } + } + + pub async fn call_language_server_initialization_options( + &self, + store: &mut Store, + config: &LanguageServerConfig, + resource: Resource>, + ) -> Result, String>> { + match self { + Extension::V004(ext) => { + ext.call_language_server_initialization_options(store, config, resource) + .await + } + Extension::V001(ext) => { + ext.call_language_server_initialization_options( + store, + &config.clone().into(), + resource, + ) + .await + } + } + } +} diff --git a/crates/extension/src/wasm_host/wit/v0_0_1.rs b/crates/extension/src/wasm_host/wit/v0_0_1.rs new file mode 100644 index 0000000000..a48569b4e2 --- /dev/null +++ b/crates/extension/src/wasm_host/wit/v0_0_1.rs @@ -0,0 +1,210 @@ +use super::latest; +use crate::wasm_host::WasmState; +use anyhow::Result; +use async_trait::async_trait; +use language::{LanguageServerBinaryStatus, LspAdapterDelegate}; +use std::sync::{Arc, OnceLock}; +use wasmtime::component::{Linker, Resource}; + +wasmtime::component::bindgen!({ + async: true, + path: "../extension_api/wit/0.0.1", + with: { + "worktree": ExtensionWorktree, + }, +}); + +pub type ExtensionWorktree = Arc; + +pub fn linker() -> &'static Linker { + static LINKER: OnceLock> = OnceLock::new(); + LINKER.get_or_init(|| super::new_linker(Extension::add_to_linker)) +} + +impl From for Os { + fn from(value: latest::Os) -> Self { + match value { + latest::Os::Mac => Os::Mac, + latest::Os::Linux => Os::Linux, + latest::Os::Windows => Os::Windows, + } + } +} + +impl From for Architecture { + fn from(value: latest::Architecture) -> Self { + match value { + latest::Architecture::Aarch64 => Self::Aarch64, + latest::Architecture::X86 => Self::X86, + latest::Architecture::X8664 => Self::X8664, + } + } +} + +impl From for GithubRelease { + fn from(value: latest::GithubRelease) -> Self { + Self { + version: value.version, + assets: value.assets.into_iter().map(|asset| asset.into()).collect(), + } + } +} + +impl From for GithubReleaseAsset { + fn from(value: latest::GithubReleaseAsset) -> Self { + Self { + name: value.name, + download_url: value.download_url, + } + } +} + +impl From for latest::GithubReleaseOptions { + fn from(value: GithubReleaseOptions) -> Self { + Self { + require_assets: value.require_assets, + pre_release: value.pre_release, + } + } +} + +impl From for latest::DownloadedFileType { + fn from(value: DownloadedFileType) -> Self { + match value { + DownloadedFileType::Gzip => latest::DownloadedFileType::Gzip, + DownloadedFileType::GzipTar => latest::DownloadedFileType::GzipTar, + DownloadedFileType::Zip => latest::DownloadedFileType::Zip, + DownloadedFileType::Uncompressed => latest::DownloadedFileType::Uncompressed, + } + } +} + +impl From for LanguageServerConfig { + fn from(value: latest::LanguageServerConfig) -> Self { + Self { + name: value.name, + language_name: value.language_name, + } + } +} + +impl From for latest::Command { + fn from(value: Command) -> Self { + Self { + command: value.command, + args: value.args, + env: value.env, + } + } +} + +#[async_trait] +impl HostWorktree for WasmState { + async fn read_text_file( + &mut self, + delegate: Resource>, + path: String, + ) -> wasmtime::Result> { + latest::HostWorktree::read_text_file(self, delegate, path).await + } + + async fn shell_env( + &mut self, + delegate: Resource>, + ) -> wasmtime::Result { + latest::HostWorktree::shell_env(self, delegate).await + } + + async fn which( + &mut self, + delegate: Resource>, + binary_name: String, + ) -> wasmtime::Result> { + latest::HostWorktree::which(self, delegate, binary_name).await + } + + fn drop(&mut self, _worktree: Resource) -> Result<()> { + Ok(()) + } +} + +#[async_trait] +impl ExtensionImports for WasmState { + async fn node_binary_path(&mut self) -> wasmtime::Result> { + latest::ExtensionImports::node_binary_path(self).await + } + + async fn npm_package_latest_version( + &mut self, + package_name: String, + ) -> wasmtime::Result> { + latest::ExtensionImports::npm_package_latest_version(self, package_name).await + } + + async fn npm_package_installed_version( + &mut self, + package_name: String, + ) -> wasmtime::Result, String>> { + latest::ExtensionImports::npm_package_installed_version(self, package_name).await + } + + async fn npm_install_package( + &mut self, + package_name: String, + version: String, + ) -> wasmtime::Result> { + latest::ExtensionImports::npm_install_package(self, package_name, version).await + } + + async fn latest_github_release( + &mut self, + repo: String, + options: GithubReleaseOptions, + ) -> wasmtime::Result> { + Ok( + latest::ExtensionImports::latest_github_release(self, repo, options.into()) + .await? + .map(|github| github.into()), + ) + } + + async fn current_platform(&mut self) -> Result<(Os, Architecture)> { + latest::ExtensionImports::current_platform(self) + .await + .map(|(os, arch)| (os.into(), arch.into())) + } + + async fn set_language_server_installation_status( + &mut self, + server_name: String, + status: LanguageServerInstallationStatus, + ) -> wasmtime::Result<()> { + let status = match status { + LanguageServerInstallationStatus::CheckingForUpdate => { + LanguageServerBinaryStatus::CheckingForUpdate + } + LanguageServerInstallationStatus::Downloading => { + LanguageServerBinaryStatus::Downloading + } + LanguageServerInstallationStatus::Cached + | LanguageServerInstallationStatus::Downloaded => LanguageServerBinaryStatus::None, + LanguageServerInstallationStatus::Failed(error) => { + LanguageServerBinaryStatus::Failed { error } + } + }; + + self.host + .language_registry + .update_lsp_status(language::LanguageServerName(server_name.into()), status); + Ok(()) + } + + async fn download_file( + &mut self, + url: String, + path: String, + file_type: DownloadedFileType, + ) -> wasmtime::Result> { + latest::ExtensionImports::download_file(self, url, path, file_type.into()).await + } +} diff --git a/crates/extension/src/wasm_host/wit/v0_0_4.rs b/crates/extension/src/wasm_host/wit/v0_0_4.rs new file mode 100644 index 0000000000..68bc66b9d3 --- /dev/null +++ b/crates/extension/src/wasm_host/wit/v0_0_4.rs @@ -0,0 +1,284 @@ +use crate::wasm_host::WasmState; +use anyhow::{anyhow, Result}; +use async_compression::futures::bufread::GzipDecoder; +use async_tar::Archive; +use async_trait::async_trait; +use futures::io::BufReader; +use language::{LanguageServerBinaryStatus, LspAdapterDelegate}; +use std::{ + env, + path::PathBuf, + sync::{Arc, OnceLock}, +}; +use util::{maybe, SemanticVersion}; +use wasmtime::component::{Linker, Resource}; + +pub const VERSION: SemanticVersion = SemanticVersion { + major: 0, + minor: 0, + patch: 4, +}; + +wasmtime::component::bindgen!({ + async: true, + path: "../extension_api/wit/0.0.4", + with: { + "worktree": ExtensionWorktree, + }, +}); + +pub type ExtensionWorktree = Arc; + +pub fn linker() -> &'static Linker { + static LINKER: OnceLock> = OnceLock::new(); + LINKER.get_or_init(|| super::new_linker(Extension::add_to_linker)) +} + +#[async_trait] +impl HostWorktree for WasmState { + async fn read_text_file( + &mut self, + delegate: Resource>, + path: String, + ) -> wasmtime::Result> { + let delegate = self.table.get(&delegate)?; + Ok(delegate + .read_text_file(path.into()) + .await + .map_err(|error| error.to_string())) + } + + async fn shell_env( + &mut self, + delegate: Resource>, + ) -> wasmtime::Result { + let delegate = self.table.get(&delegate)?; + Ok(delegate.shell_env().await.into_iter().collect()) + } + + async fn which( + &mut self, + delegate: Resource>, + binary_name: String, + ) -> wasmtime::Result> { + let delegate = self.table.get(&delegate)?; + Ok(delegate + .which(binary_name.as_ref()) + .await + .map(|path| path.to_string_lossy().to_string())) + } + + fn drop(&mut self, _worktree: Resource) -> Result<()> { + // we only ever hand out borrows of worktrees + Ok(()) + } +} + +#[async_trait] +impl ExtensionImports for WasmState { + async fn node_binary_path(&mut self) -> wasmtime::Result> { + convert_result( + self.host + .node_runtime + .binary_path() + .await + .map(|path| path.to_string_lossy().to_string()), + ) + } + + async fn npm_package_latest_version( + &mut self, + package_name: String, + ) -> wasmtime::Result> { + convert_result( + self.host + .node_runtime + .npm_package_latest_version(&package_name) + .await, + ) + } + + async fn npm_package_installed_version( + &mut self, + package_name: String, + ) -> wasmtime::Result, String>> { + convert_result( + self.host + .node_runtime + .npm_package_installed_version(&self.work_dir(), &package_name) + .await, + ) + } + + async fn npm_install_package( + &mut self, + package_name: String, + version: String, + ) -> wasmtime::Result> { + convert_result( + self.host + .node_runtime + .npm_install_packages(&self.work_dir(), &[(&package_name, &version)]) + .await, + ) + } + + async fn latest_github_release( + &mut self, + repo: String, + options: GithubReleaseOptions, + ) -> wasmtime::Result> { + convert_result( + maybe!(async { + let release = util::github::latest_github_release( + &repo, + options.require_assets, + options.pre_release, + self.host.http_client.clone(), + ) + .await?; + Ok(GithubRelease { + version: release.tag_name, + assets: release + .assets + .into_iter() + .map(|asset| GithubReleaseAsset { + name: asset.name, + download_url: asset.browser_download_url, + }) + .collect(), + }) + }) + .await, + ) + } + + async fn current_platform(&mut self) -> Result<(Os, Architecture)> { + Ok(( + match env::consts::OS { + "macos" => Os::Mac, + "linux" => Os::Linux, + "windows" => Os::Windows, + _ => panic!("unsupported os"), + }, + match env::consts::ARCH { + "aarch64" => Architecture::Aarch64, + "x86" => Architecture::X86, + "x86_64" => Architecture::X8664, + _ => panic!("unsupported architecture"), + }, + )) + } + + async fn set_language_server_installation_status( + &mut self, + server_name: String, + status: LanguageServerInstallationStatus, + ) -> wasmtime::Result<()> { + let status = match status { + LanguageServerInstallationStatus::CheckingForUpdate => { + LanguageServerBinaryStatus::CheckingForUpdate + } + LanguageServerInstallationStatus::Downloading => { + LanguageServerBinaryStatus::Downloading + } + LanguageServerInstallationStatus::None => LanguageServerBinaryStatus::None, + LanguageServerInstallationStatus::Failed(error) => { + LanguageServerBinaryStatus::Failed { error } + } + }; + + self.host + .language_registry + .update_lsp_status(language::LanguageServerName(server_name.into()), status); + Ok(()) + } + + async fn download_file( + &mut self, + url: String, + path: String, + file_type: DownloadedFileType, + ) -> wasmtime::Result> { + let result = maybe!(async { + let path = PathBuf::from(path); + let extension_work_dir = self.host.work_dir.join(self.manifest.id.as_ref()); + + self.host.fs.create_dir(&extension_work_dir).await?; + + let destination_path = self + .host + .writeable_path_from_extension(&self.manifest.id, &path)?; + + let mut response = self + .host + .http_client + .get(&url, Default::default(), true) + .await + .map_err(|err| anyhow!("error downloading release: {}", err))?; + + if !response.status().is_success() { + Err(anyhow!( + "download failed with status {}", + response.status().to_string() + ))?; + } + let body = BufReader::new(response.body_mut()); + + match file_type { + DownloadedFileType::Uncompressed => { + futures::pin_mut!(body); + self.host + .fs + .create_file_with(&destination_path, body) + .await?; + } + DownloadedFileType::Gzip => { + let body = GzipDecoder::new(body); + futures::pin_mut!(body); + self.host + .fs + .create_file_with(&destination_path, body) + .await?; + } + DownloadedFileType::GzipTar => { + let body = GzipDecoder::new(body); + futures::pin_mut!(body); + self.host + .fs + .extract_tar_file(&destination_path, Archive::new(body)) + .await?; + } + DownloadedFileType::Zip => { + let file_name = destination_path + .file_name() + .ok_or_else(|| anyhow!("invalid download path"))? + .to_string_lossy(); + let zip_filename = format!("{file_name}.zip"); + let mut zip_path = destination_path.clone(); + zip_path.set_file_name(zip_filename); + + futures::pin_mut!(body); + self.host.fs.create_file_with(&zip_path, body).await?; + + let unzip_status = std::process::Command::new("unzip") + .current_dir(&extension_work_dir) + .arg(&zip_path) + .output()? + .status; + if !unzip_status.success() { + Err(anyhow!("failed to unzip {} archive", path.display()))?; + } + } + } + + Ok(()) + }) + .await; + convert_result(result) + } +} + +fn convert_result(result: Result) -> wasmtime::Result> { + Ok(result.map_err(|error| error.to_string())) +} diff --git a/crates/extension_api/Cargo.toml b/crates/extension_api/Cargo.toml index cd9a790cad..ad00f14283 100644 --- a/crates/extension_api/Cargo.toml +++ b/crates/extension_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zed_extension_api" -version = "0.0.1" +version = "0.0.4" description = "APIs for creating Zed extensions in Rust" repository = "https://github.com/zed-industries/zed" documentation = "https://docs.rs/zed_extension_api" @@ -15,7 +15,7 @@ workspace = true path = "src/extension_api.rs" [dependencies] -wit-bindgen = "0.18" +wit-bindgen = "0.22" [package.metadata.component] target = { path = "wit" } diff --git a/crates/extension_api/src/extension_api.rs b/crates/extension_api/src/extension_api.rs index 14450eb08c..d55eb0a1b7 100644 --- a/crates/extension_api/src/extension_api.rs +++ b/crates/extension_api/src/extension_api.rs @@ -1,6 +1,4 @@ -pub struct Guest; pub use wit::*; - pub type Result = core::result::Result; pub trait Extension: Send + Sync { @@ -10,14 +8,14 @@ pub trait Extension: Send + Sync { fn language_server_command( &mut self, - config: wit::LanguageServerConfig, - worktree: &wit::Worktree, + config: LanguageServerConfig, + worktree: &Worktree, ) -> Result; fn language_server_initialization_options( &mut self, - _config: wit::LanguageServerConfig, - _worktree: &wit::Worktree, + _config: LanguageServerConfig, + _worktree: &Worktree, ) -> Result> { Ok(None) } @@ -54,11 +52,13 @@ pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), " mod wit { wit_bindgen::generate!({ - exports: { world: super::Component }, - skip: ["init-extension"] + skip: ["init-extension"], + path: "./wit/0.0.4", }); } +wit::export!(Component); + struct Component; impl wit::Guest for Component { diff --git a/crates/extension_api/wit/extension.wit b/crates/extension_api/wit/0.0.1/extension.wit similarity index 96% rename from crates/extension_api/wit/extension.wit rename to crates/extension_api/wit/0.0.1/extension.wit index d4e97a86a5..fb268fe271 100644 --- a/crates/extension_api/wit/extension.wit +++ b/crates/extension_api/wit/0.0.1/extension.wit @@ -48,6 +48,9 @@ world extension { /// Gets the current operating system and architecture import current-platform: func() -> tuple; + /// Get the path to the node binary used by Zed. + import node-binary-path: func() -> result; + /// Gets the latest version of the given NPM package. import npm-package-latest-version: func(package-name: string) -> result; diff --git a/crates/extension_api/wit/0.0.4/extension.wit b/crates/extension_api/wit/0.0.4/extension.wit new file mode 100644 index 0000000000..5d0c5fb174 --- /dev/null +++ b/crates/extension_api/wit/0.0.4/extension.wit @@ -0,0 +1,93 @@ +package zed:extension; + +world extension { + export init-extension: func(); + + record github-release { + version: string, + assets: list, + } + + record github-release-asset { + name: string, + download-url: string, + } + + record github-release-options { + require-assets: bool, + pre-release: bool, + } + + enum os { + mac, + linux, + windows, + } + + enum architecture { + aarch64, + x86, + x8664, + } + + enum downloaded-file-type { + gzip, + gzip-tar, + zip, + uncompressed, + } + + variant language-server-installation-status { + none, + downloading, + checking-for-update, + failed(string), + } + + /// Gets the current operating system and architecture + import current-platform: func() -> tuple; + + /// Get the path to the node binary used by Zed. + import node-binary-path: func() -> result; + + /// Gets the latest version of the given NPM package. + import npm-package-latest-version: func(package-name: string) -> result; + + /// Returns the installed version of the given NPM package, if it exists. + import npm-package-installed-version: func(package-name: string) -> result, string>; + + /// Installs the specified NPM package. + import npm-install-package: func(package-name: string, version: string) -> result<_, string>; + + /// Gets the latest release for the given GitHub repository. + import latest-github-release: func(repo: string, options: github-release-options) -> result; + + /// Downloads a file from the given url, and saves it to the given filename within the extension's + /// working directory. Extracts the file according to the given file type. + import download-file: func(url: string, output-filename: string, file-type: downloaded-file-type) -> result<_, string>; + + /// Updates the installation status for the given language server. + import set-language-server-installation-status: func(language-server-name: string, status: language-server-installation-status); + + type env-vars = list>; + + record command { + command: string, + args: list, + env: env-vars, + } + + resource worktree { + read-text-file: func(path: string) -> result; + which: func(binary-name: string) -> option; + shell-env: func() -> env-vars; + } + + record language-server-config { + name: string, + language-name: string, + } + + export language-server-command: func(config: language-server-config, worktree: borrow) -> result; + export language-server-initialization-options: func(config: language-server-config, worktree: borrow) -> result, string>; +} diff --git a/crates/extensions_ui/src/extension_suggest.rs b/crates/extensions_ui/src/extension_suggest.rs index 730738e8bc..b5e7a7ca64 100644 --- a/crates/extensions_ui/src/extension_suggest.rs +++ b/crates/extensions_ui/src/extension_suggest.rs @@ -29,6 +29,7 @@ pub fn suggested_extension(file_extension_or_name: &str) -> Option> { ("git-firefly", "MERGE_MSG"), ("git-firefly", "NOTES_EDITMSG"), ("git-firefly", "TAG_EDITMSG"), + ("gleam", "gleam"), ("graphql", "gql"), ("graphql", "graphql"), ("java", "java"), @@ -39,6 +40,7 @@ pub fn suggested_extension(file_extension_or_name: &str) -> Option> { ("r", "r"), ("r", "R"), ("sql", "sql"), + ("svelte", "svelte"), ("swift", "swift"), ("templ", "templ"), ("wgsl", "wgsl"), diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 7578393478..fee6b2ac2a 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -134,11 +134,15 @@ pub struct CachedLspAdapter { pub language_ids: HashMap, pub adapter: Arc, pub reinstall_attempt_count: AtomicU64, + /// Indicates whether this language server is the primary language server + /// for a given language. Currently, most LSP-backed features only work + /// with one language server, so one server needs to be primary. + pub is_primary: bool, cached_binary: futures::lock::Mutex>, } impl CachedLspAdapter { - pub fn new(adapter: Arc) -> Arc { + pub fn new(adapter: Arc, is_primary: bool) -> Arc { let name = adapter.name(); let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources(); let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token(); @@ -150,6 +154,7 @@ impl CachedLspAdapter { disk_based_diagnostics_progress_token, language_ids, adapter, + is_primary, cached_binary: Default::default(), reinstall_attempt_count: AtomicU64::new(0), }) @@ -293,7 +298,6 @@ pub trait LspAdapter: 'static + Send + Sync { .cached_server_binary(container_dir.to_path_buf(), delegate.as_ref()) .await { - delegate.update_status(self.name(), LanguageServerBinaryStatus::Cached); log::info!( "failed to fetch newest version of language server {:?}. falling back to using {:?}", self.name(), @@ -464,7 +468,7 @@ async fn try_fetch_server_binary .fetch_server_binary(latest_version, container_dir, delegate.as_ref()) .await; - delegate.update_status(name.clone(), LanguageServerBinaryStatus::Downloaded); + delegate.update_status(name.clone(), LanguageServerBinaryStatus::None); binary } diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 183c715538..ae2913c251 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -25,7 +25,7 @@ use sum_tree::Bias; use text::{Point, Rope}; use theme::Theme; use unicase::UniCase; -use util::{paths::PathExt, post_inc, ResultExt}; +use util::{maybe, paths::PathExt, post_inc, ResultExt}; pub struct LanguageRegistry { state: RwLock, @@ -54,10 +54,9 @@ struct LanguageRegistryState { #[derive(Clone, Debug, PartialEq, Eq)] pub enum LanguageServerBinaryStatus { + None, CheckingForUpdate, Downloading, - Downloaded, - Cached, Failed { error: String }, } @@ -91,9 +90,10 @@ enum AvailableGrammar { Loaded(#[allow(unused)] PathBuf, tree_sitter::Language), Loading( #[allow(unused)] PathBuf, - Vec>>, + Vec>>>, ), Unloaded(PathBuf), + LoadFailed(Arc), } #[derive(Debug)] @@ -213,7 +213,20 @@ impl LanguageRegistry { .lsp_adapters .entry(language_name) .or_default() - .push(CachedLspAdapter::new(adapter)); + .push(CachedLspAdapter::new(adapter, true)); + } + + pub fn register_secondary_lsp_adapter( + &self, + language_name: Arc, + adapter: Arc, + ) { + self.state + .write() + .lsp_adapters + .entry(language_name) + .or_default() + .push(CachedLspAdapter::new(adapter, false)); } #[cfg(any(feature = "test-support", test))] @@ -227,7 +240,7 @@ impl LanguageRegistry { .lsp_adapters .entry(language_name.into()) .or_default() - .push(CachedLspAdapter::new(Arc::new(adapter))); + .push(CachedLspAdapter::new(Arc::new(adapter), true)); self.fake_language_servers(language_name) } @@ -578,6 +591,9 @@ impl LanguageRegistry { if let Some(grammar) = state.grammars.get_mut(name.as_ref()) { match grammar { + AvailableGrammar::LoadFailed(error) => { + tx.send(Err(error.clone())).ok(); + } AvailableGrammar::Native(grammar) | AvailableGrammar::Loaded(_, grammar) => { tx.send(Ok(grammar.clone())).ok(); } @@ -586,46 +602,47 @@ impl LanguageRegistry { } AvailableGrammar::Unloaded(wasm_path) => { let this = self.clone(); + let wasm_path = wasm_path.clone(); + *grammar = AvailableGrammar::Loading(wasm_path.clone(), vec![tx]); self.executor - .spawn({ - let wasm_path = wasm_path.clone(); - async move { + .spawn(async move { + let grammar_result = maybe!({ let wasm_bytes = std::fs::read(&wasm_path)?; let grammar_name = wasm_path .file_stem() .and_then(OsStr::to_str) .ok_or_else(|| anyhow!("invalid grammar filename"))?; - let grammar = PARSER.with(|parser| { + anyhow::Ok(PARSER.with(|parser| { let mut parser = parser.borrow_mut(); let mut store = parser.take_wasm_store().unwrap(); let grammar = store.load_language(&grammar_name, &wasm_bytes); parser.set_wasm_store(store).unwrap(); grammar - })?; + })?) + }) + .map_err(Arc::new); - if let Some(AvailableGrammar::Loading(_, txs)) = - this.state.write().grammars.insert( - name, - AvailableGrammar::Loaded(wasm_path, grammar.clone()), - ) - { - for tx in txs { - tx.send(Ok(grammar.clone())).ok(); - } + let value = match &grammar_result { + Ok(grammar) => AvailableGrammar::Loaded(wasm_path, grammar.clone()), + Err(error) => AvailableGrammar::LoadFailed(error.clone()), + }; + + let old_value = this.state.write().grammars.insert(name, value); + if let Some(AvailableGrammar::Loading(_, txs)) = old_value { + for tx in txs { + tx.send(grammar_result.clone()).ok(); } - - anyhow::Ok(()) } }) .detach(); - *grammar = AvailableGrammar::Loading(wasm_path.clone(), vec![tx]); } } } else { - tx.send(Err(anyhow!("no such grammar {}", name))).ok(); + tx.send(Err(Arc::new(anyhow!("no such grammar {}", name)))) + .ok(); } - async move { rx.await? } + async move { rx.await?.map_err(|e| anyhow!(e)) } } pub fn to_vec(&self) -> Vec> { @@ -691,7 +708,7 @@ impl LanguageRegistry { // the login shell to be set on our process. login_shell_env_loaded.await; - let binary = adapter + let binary_result = adapter .clone() .get_language_server_command( language.clone(), @@ -699,8 +716,11 @@ impl LanguageRegistry { delegate.clone(), &mut cx, ) - .await?; + .await; + delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None); + + let binary = binary_result?; let options = adapter .adapter .clone() diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index ab089431d7..60f656d7b2 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -49,7 +49,6 @@ tree-sitter-elixir.workspace = true tree-sitter-elm.workspace = true tree-sitter-embedded-template.workspace = true tree-sitter-erlang.workspace = true -tree-sitter-gleam.workspace = true tree-sitter-glsl.workspace = true tree-sitter-go.workspace = true tree-sitter-gomod.workspace = true @@ -75,7 +74,6 @@ tree-sitter-regex.workspace = true tree-sitter-ruby.workspace = true tree-sitter-rust.workspace = true tree-sitter-scheme.workspace = true -tree-sitter-svelte.workspace = true tree-sitter-toml.workspace = true tree-sitter-typescript.workspace = true tree-sitter-vue.workspace = true diff --git a/crates/languages/src/astro.rs b/crates/languages/src/astro.rs index c978e50638..5706f38739 100644 --- a/crates/languages/src/astro.rs +++ b/crates/languages/src/astro.rs @@ -12,7 +12,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/@astrojs/language-server/bin/nodeServer.js"; @@ -107,7 +107,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/c.rs b/crates/languages/src/c.rs index d135ca5e28..965e6e72fb 100644 --- a/crates/languages/src/c.rs +++ b/crates/languages/src/c.rs @@ -7,10 +7,9 @@ use lsp::LanguageServerBinary; use smol::fs::{self, File}; use std::{any::Any, env::consts, path::PathBuf, sync::Arc}; use util::{ - async_maybe, fs::remove_matching, github::{latest_github_release, GitHubLspBinaryVersion}, - ResultExt, + maybe, ResultExt, }; pub struct CLspAdapter; @@ -264,7 +263,7 @@ impl super::LspAdapter for CLspAdapter { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last_clangd_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/csharp.rs b/crates/languages/src/csharp.rs index 4d9f26328c..82fec8468a 100644 --- a/crates/languages/src/csharp.rs +++ b/crates/languages/src/csharp.rs @@ -9,8 +9,7 @@ use smol::fs; use std::env::consts::ARCH; use std::ffi::OsString; use std::{any::Any, path::PathBuf}; -use util::async_maybe; -use util::github::latest_github_release; +use util::{github::latest_github_release, maybe}; use util::{github::GitHubLspBinaryVersion, ResultExt}; pub struct OmniSharpAdapter; @@ -115,7 +114,7 @@ impl super::LspAdapter for OmniSharpAdapter { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last_binary_path = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/css.rs b/crates/languages/src/css.rs index 6e2e77a0ce..970a223fc1 100644 --- a/crates/languages/src/css.rs +++ b/crates/languages/src/css.rs @@ -12,7 +12,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/vscode-langservers-extracted/bin/vscode-css-language-server"; @@ -105,7 +105,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/deno.rs b/crates/languages/src/deno.rs index 6dab167380..fc7fd49d02 100644 --- a/crates/languages/src/deno.rs +++ b/crates/languages/src/deno.rs @@ -11,10 +11,9 @@ use settings::Settings; use smol::{fs, fs::File}; use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc}; use util::{ - async_maybe, fs::remove_matching, github::{latest_github_release, GitHubLspBinaryVersion}, - ResultExt, + maybe, ResultExt, }; #[derive(Clone, Serialize, Deserialize, JsonSchema)] @@ -207,7 +206,7 @@ impl LspAdapter for DenoLspAdapter { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/dockerfile.rs b/crates/languages/src/dockerfile.rs index 3d768c577d..1fd4e07747 100644 --- a/crates/languages/src/dockerfile.rs +++ b/crates/languages/src/dockerfile.rs @@ -11,7 +11,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/dockerfile-language-server-nodejs/bin/docker-langserver"; @@ -94,7 +94,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/elixir.rs b/crates/languages/src/elixir.rs index 7e122bbb4e..26dfb3c203 100644 --- a/crates/languages/src/elixir.rs +++ b/crates/languages/src/elixir.rs @@ -20,10 +20,9 @@ use std::{ }; use task::static_source::{Definition, TaskDefinitions}; use util::{ - async_maybe, fs::remove_matching, github::{latest_github_release, GitHubLspBinaryVersion}, - ResultExt, + maybe, ResultExt, }; #[derive(Clone, Serialize, Deserialize, JsonSchema)] @@ -413,7 +412,7 @@ impl LspAdapter for NextLspAdapter { } async fn get_cached_server_binary_next(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last_binary_path = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/elm.rs b/crates/languages/src/elm.rs index b82b92941e..e293554c98 100644 --- a/crates/languages/src/elm.rs +++ b/crates/languages/src/elm.rs @@ -15,7 +15,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_NAME: &str = "elm-language-server"; const SERVER_PATH: &str = "node_modules/@elm-tooling/elm-language-server/out/node/index.js"; @@ -120,7 +120,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/gleam.rs b/crates/languages/src/gleam.rs deleted file mode 100644 index e9a5b6a48a..0000000000 --- a/crates/languages/src/gleam.rs +++ /dev/null @@ -1,122 +0,0 @@ -use std::any::Any; -use std::env::consts; -use std::ffi::OsString; -use std::path::PathBuf; - -use anyhow::{anyhow, bail, Result}; -use async_compression::futures::bufread::GzipDecoder; -use async_tar::Archive; -use async_trait::async_trait; -use futures::io::BufReader; -use futures::StreamExt; -use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; -use lsp::LanguageServerBinary; -use smol::fs; -use util::github::{latest_github_release, GitHubLspBinaryVersion}; -use util::{async_maybe, ResultExt}; - -fn server_binary_arguments() -> Vec { - vec!["lsp".into()] -} - -pub struct GleamLspAdapter; - -#[async_trait(?Send)] -impl LspAdapter for GleamLspAdapter { - fn name(&self) -> LanguageServerName { - LanguageServerName("gleam".into()) - } - - async fn fetch_latest_server_version( - &self, - delegate: &dyn LspAdapterDelegate, - ) -> Result> { - let release = - latest_github_release("gleam-lang/gleam", true, false, delegate.http_client()).await?; - let asset_name = format!( - "gleam-{version}-{arch}-{os}.tar.gz", - version = release.tag_name, - arch = std::env::consts::ARCH, - os = match consts::OS { - "macos" => "apple-darwin", - "linux" => "unknown-linux-musl", - "windows" => "pc-windows-msvc", - other => bail!("Running on unsupported os: {other}"), - }, - ); - let asset = release - .assets - .iter() - .find(|asset| asset.name == asset_name) - .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?; - Ok(Box::new(GitHubLspBinaryVersion { - name: release.tag_name, - url: asset.browser_download_url.clone(), - })) - } - - async fn fetch_server_binary( - &self, - version: Box, - container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let version = version.downcast::().unwrap(); - let binary_path = container_dir.join("gleam"); - - if fs::metadata(&binary_path).await.is_err() { - let mut response = delegate - .http_client() - .get(&version.url, Default::default(), true) - .await - .map_err(|err| anyhow!("error downloading release: {}", err))?; - let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut())); - let archive = Archive::new(decompressed_bytes); - archive.unpack(container_dir).await?; - } - - Ok(LanguageServerBinary { - path: binary_path, - env: None, - arguments: server_binary_arguments(), - }) - } - - async fn cached_server_binary( - &self, - container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - get_cached_server_binary(container_dir).await - } - - async fn installation_test_binary( - &self, - container_dir: PathBuf, - ) -> Option { - get_cached_server_binary(container_dir) - .await - .map(|mut binary| { - binary.arguments = vec!["--version".into()]; - binary - }) - } -} - -async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ - let mut last = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - last = Some(entry?.path()); - } - - anyhow::Ok(LanguageServerBinary { - path: last.ok_or_else(|| anyhow!("no cached binary"))?, - env: None, - arguments: server_binary_arguments(), - }) - }) - .await - .log_err() -} diff --git a/crates/languages/src/gleam/config.toml b/crates/languages/src/gleam/config.toml deleted file mode 100644 index 0a472172ad..0000000000 --- a/crates/languages/src/gleam/config.toml +++ /dev/null @@ -1,11 +0,0 @@ -name = "Gleam" -grammar = "gleam" -path_suffixes = ["gleam"] -line_comments = ["// ", "/// "] -autoclose_before = ";:.,=}])>" -brackets = [ - { start = "{", end = "}", close = true, newline = true }, - { start = "[", end = "]", close = true, newline = true }, - { start = "(", end = ")", close = true, newline = true }, - { start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] }, -] diff --git a/crates/languages/src/gleam/highlights.scm b/crates/languages/src/gleam/highlights.scm deleted file mode 100644 index a95f6cb031..0000000000 --- a/crates/languages/src/gleam/highlights.scm +++ /dev/null @@ -1,130 +0,0 @@ -; Comments -(module_comment) @comment -(statement_comment) @comment -(comment) @comment - -; Constants -(constant - name: (identifier) @constant) - -; Modules -(module) @module -(import alias: (identifier) @module) -(remote_type_identifier - module: (identifier) @module) -(remote_constructor_name - module: (identifier) @module) -((field_access - record: (identifier) @module - field: (label) @function) - (#is-not? local)) - -; Functions -(unqualified_import (identifier) @function) -(unqualified_import "type" (type_identifier) @type) -(unqualified_import (type_identifier) @constructor) -(function - name: (identifier) @function) -(external_function - name: (identifier) @function) -(function_parameter - name: (identifier) @variable.parameter) -((function_call - function: (identifier) @function) - (#is-not? local)) -((binary_expression - operator: "|>" - right: (identifier) @function) - (#is-not? local)) - -; "Properties" -; Assumed to be intended to refer to a name for a field; something that comes -; before ":" or after "." -; e.g. record field names, tuple indices, names for named arguments, etc -(label) @property -(tuple_access - index: (integer) @property) - -; Attributes -(attribute - "@" @attribute - name: (identifier) @attribute) - -(attribute_value (identifier) @constant) - -; Type names -(remote_type_identifier) @type -(type_identifier) @type - -; Data constructors -(constructor_name) @constructor - -; Literals -(string) @string -((escape_sequence) @warning - ; Deprecated in v0.33.0-rc2: - (#eq? @warning "\\e")) -(escape_sequence) @string.escape -(bit_string_segment_option) @function.builtin -(integer) @number -(float) @number - -; Reserved identifiers -; TODO: when tree-sitter supports `#any-of?` in the Rust bindings, -; refactor this to use `#any-of?` rather than `#match?` -((identifier) @warning - (#match? @warning "^(auto|delegate|derive|else|implement|macro|test|echo)$")) - -; Variables -(identifier) @variable -(discard) @comment.unused - -; Keywords -[ - (visibility_modifier) ; "pub" - (opacity_modifier) ; "opaque" - "as" - "assert" - "case" - "const" - ; DEPRECATED: 'external' was removed in v0.30. - "external" - "fn" - "if" - "import" - "let" - "panic" - "todo" - "type" - "use" -] @keyword - -; Operators -(binary_expression - operator: _ @operator) -(boolean_negation "!" @operator) -(integer_negation "-" @operator) - -; Punctuation -[ - "(" - ")" - "[" - "]" - "{" - "}" - "<<" - ">>" -] @punctuation.bracket -[ - "." - "," - ;; Controversial -- maybe some are operators? - ":" - "#" - "=" - "->" - ".." - "-" - "<-" -] @punctuation.delimiter diff --git a/crates/languages/src/gleam/indents.scm b/crates/languages/src/gleam/indents.scm deleted file mode 100644 index 112b414aa4..0000000000 --- a/crates/languages/src/gleam/indents.scm +++ /dev/null @@ -1,3 +0,0 @@ -(_ "[" "]" @end) @indent -(_ "{" "}" @end) @indent -(_ "(" ")" @end) @indent diff --git a/crates/languages/src/gleam/outline.scm b/crates/languages/src/gleam/outline.scm deleted file mode 100644 index 5df7a6af80..0000000000 --- a/crates/languages/src/gleam/outline.scm +++ /dev/null @@ -1,31 +0,0 @@ -(external_type - (visibility_modifier)? @context - "type" @context - (type_name) @name) @item - -(type_definition - (visibility_modifier)? @context - (opacity_modifier)? @context - "type" @context - (type_name) @name) @item - -(data_constructor - (constructor_name) @name) @item - -(data_constructor_argument - (label) @name) @item - -(type_alias - (visibility_modifier)? @context - "type" @context - (type_name) @name) @item - -(function - (visibility_modifier)? @context - "fn" @context - name: (_) @name) @item - -(constant - (visibility_modifier)? @context - "const" @context - name: (_) @name) @item diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index af5aba381b..0ffc747c0f 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -19,7 +19,7 @@ use std::{ Arc, }, }; -use util::{async_maybe, fs::remove_matching, github::latest_github_release, ResultExt}; +use util::{fs::remove_matching, github::latest_github_release, maybe, ResultExt}; fn server_binary_arguments() -> Vec { vec!["-mode=stdio".into()] @@ -368,7 +368,7 @@ impl super::LspAdapter for GoLspAdapter { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last_binary_path = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/html.rs b/crates/languages/src/html.rs index 6462280abe..43e92e6f54 100644 --- a/crates/languages/src/html.rs +++ b/crates/languages/src/html.rs @@ -12,7 +12,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/vscode-langservers-extracted/bin/vscode-html-language-server"; @@ -105,7 +105,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index fab6c3470e..25e2224434 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -16,7 +16,7 @@ use std::{ path::{Path, PathBuf}, sync::{Arc, OnceLock}, }; -use util::{async_maybe, paths, ResultExt}; +use util::{maybe, paths, ResultExt}; const SERVER_PATH: &str = "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver"; @@ -167,7 +167,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index 2030f602c8..9c94a107db 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -22,7 +22,6 @@ mod dockerfile; mod elixir; mod elm; mod erlang; -mod gleam; mod go; mod haskell; mod html; @@ -36,7 +35,6 @@ mod purescript; mod python; mod ruby; mod rust; -mod svelte; mod tailwind; mod terraform; mod toml; @@ -83,7 +81,6 @@ pub fn init( tree_sitter_embedded_template::language(), ), ("erlang", tree_sitter_erlang::language()), - ("gleam", tree_sitter_gleam::language()), ("glsl", tree_sitter_glsl::language()), ("go", tree_sitter_go::language()), ("gomod", tree_sitter_gomod::language()), @@ -113,7 +110,6 @@ pub fn init( ("ruby", tree_sitter_ruby::language()), ("rust", tree_sitter_rust::language()), ("scheme", tree_sitter_scheme::language()), - ("svelte", tree_sitter_svelte::language()), ("toml", tree_sitter_toml::language()), ("tsx", tree_sitter_typescript::language_tsx()), ("typescript", tree_sitter_typescript::language_typescript()), @@ -237,8 +233,6 @@ pub fn init( } } language!("erlang", vec![Arc::new(erlang::ErlangLspAdapter)]); - - language!("gleam", vec![Arc::new(gleam::GleamLspAdapter)]); language!("go", vec![Arc::new(go::GoLspAdapter)]); language!("gomod"); language!("gowork"); @@ -346,13 +340,6 @@ pub fn init( "yaml", vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))] ); - language!( - "svelte", - vec![ - Arc::new(svelte::SvelteLspAdapter::new(node_runtime.clone())), - Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ] - ); language!( "php", vec![ @@ -393,6 +380,11 @@ pub fn init( ))] ); language!("dart", vec![Arc::new(dart::DartLanguageServer {})]); + + languages.register_secondary_lsp_adapter( + "Svelte".into(), + Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), + ); } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/languages/src/lua.rs b/crates/languages/src/lua.rs index d0f60378ff..38dacbd378 100644 --- a/crates/languages/src/lua.rs +++ b/crates/languages/src/lua.rs @@ -8,9 +8,8 @@ use lsp::LanguageServerBinary; use smol::fs; use std::{any::Any, env::consts, path::PathBuf}; use util::{ - async_maybe, github::{latest_github_release, GitHubLspBinaryVersion}, - ResultExt, + maybe, ResultExt, }; #[derive(Copy, Clone)] @@ -117,7 +116,7 @@ impl super::LspAdapter for LuaLspAdapter { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last_binary_path = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/php.rs b/crates/languages/src/php.rs index 0151551d33..aaf5ff3d42 100644 --- a/crates/languages/src/php.rs +++ b/crates/languages/src/php.rs @@ -14,7 +14,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; fn intelephense_server_binary_arguments(server_path: &Path) -> Vec { vec![server_path.into(), "--stdio".into()] @@ -106,7 +106,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/prisma.rs b/crates/languages/src/prisma.rs index 124f21aefd..ebde3037f5 100644 --- a/crates/languages/src/prisma.rs +++ b/crates/languages/src/prisma.rs @@ -11,7 +11,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/.bin/prisma-language-server"; @@ -94,7 +94,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/purescript.rs b/crates/languages/src/purescript.rs index b050e41733..0f453c8c7b 100644 --- a/crates/languages/src/purescript.rs +++ b/crates/languages/src/purescript.rs @@ -13,7 +13,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/.bin/purescript-language-server"; @@ -115,7 +115,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 91785459cf..3e9af0a2ef 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -16,10 +16,9 @@ use task::{ TaskVariables, }; use util::{ - async_maybe, fs::remove_matching, github::{latest_github_release, GitHubLspBinaryVersion}, - ResultExt, + maybe, ResultExt, }; pub struct RustLspAdapter; @@ -397,7 +396,7 @@ impl ContextProvider for RustContextProvider { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/svelte.rs b/crates/languages/src/svelte.rs deleted file mode 100644 index 8e890dcf3a..0000000000 --- a/crates/languages/src/svelte.rs +++ /dev/null @@ -1,162 +0,0 @@ -use anyhow::{anyhow, Result}; -use async_trait::async_trait; -use futures::StreamExt; -use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; -use lsp::LanguageServerBinary; -use node_runtime::NodeRuntime; -use serde_json::json; -use smol::fs; -use std::{ - any::Any, - ffi::OsString, - path::{Path, PathBuf}, - sync::Arc, -}; -use util::{async_maybe, ResultExt}; - -const SERVER_PATH: &str = "node_modules/svelte-language-server/bin/server.js"; - -fn server_binary_arguments(server_path: &Path) -> Vec { - vec![server_path.into(), "--stdio".into()] -} - -pub struct SvelteLspAdapter { - node: Arc, -} - -impl SvelteLspAdapter { - pub fn new(node: Arc) -> Self { - SvelteLspAdapter { node } - } -} - -#[async_trait(?Send)] -impl LspAdapter for SvelteLspAdapter { - fn name(&self) -> LanguageServerName { - LanguageServerName("svelte-language-server".into()) - } - - async fn fetch_latest_server_version( - &self, - _: &dyn LspAdapterDelegate, - ) -> Result> { - Ok(Box::new( - self.node - .npm_package_latest_version("svelte-language-server") - .await?, - ) as Box<_>) - } - - async fn fetch_server_binary( - &self, - latest_version: Box, - container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let latest_version = latest_version.downcast::().unwrap(); - let server_path = container_dir.join(SERVER_PATH); - let package_name = "svelte-language-server"; - - let should_install_language_server = self - .node - .should_install_npm_package(package_name, &server_path, &container_dir, &latest_version) - .await; - - if should_install_language_server { - self.node - .npm_install_packages(&container_dir, &[(package_name, latest_version.as_str())]) - .await?; - } - - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: server_binary_arguments(&server_path), - }) - } - - async fn cached_server_binary( - &self, - container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - get_cached_server_binary(container_dir, &*self.node).await - } - - async fn installation_test_binary( - &self, - container_dir: PathBuf, - ) -> Option { - get_cached_server_binary(container_dir, &*self.node).await - } - - async fn initialization_options( - self: Arc, - _: &Arc, - ) -> Result> { - let config = json!({ - "inlayHints": { - "parameterNames": { - "enabled": "all", - "suppressWhenArgumentMatchesName": false - }, - "parameterTypes": { - "enabled": true - }, - "variableTypes": { - "enabled": true, - "suppressWhenTypeMatchesName": false - }, - "propertyDeclarationTypes": { - "enabled": true - }, - "functionLikeReturnType": { - "enabled": true - }, - "enumMemberValues": { - "enabled": true - } - } - }); - - Ok(Some(json!({ - "provideFormatter": true, - "configuration": { - "typescript": config, - "javascript": config - } - }))) - } -} - -async fn get_cached_server_binary( - container_dir: PathBuf, - node: &dyn NodeRuntime, -) -> Option { - async_maybe!({ - let mut last_version_dir = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - let entry = entry?; - if entry.file_type().await?.is_dir() { - last_version_dir = Some(entry.path()); - } - } - let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; - let server_path = last_version_dir.join(SERVER_PATH); - if server_path.exists() { - Ok(LanguageServerBinary { - path: node.binary_path().await?, - env: None, - arguments: server_binary_arguments(&server_path), - }) - } else { - Err(anyhow!( - "missing executable in directory {:?}", - last_version_dir - )) - } - }) - .await - .log_err() -} diff --git a/crates/languages/src/svelte/folds.scm b/crates/languages/src/svelte/folds.scm deleted file mode 100755 index 795c32fc4a..0000000000 --- a/crates/languages/src/svelte/folds.scm +++ /dev/null @@ -1,9 +0,0 @@ -[ - (style_element) - (script_element) - (element) - (if_statement) - (else_statement) - (each_statement) - (await_statement) -] @fold diff --git a/crates/languages/src/tailwind.rs b/crates/languages/src/tailwind.rs index b8f58616dd..bf794f6676 100644 --- a/crates/languages/src/tailwind.rs +++ b/crates/languages/src/tailwind.rs @@ -14,7 +14,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/.bin/tailwindcss-language-server"; @@ -135,7 +135,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/terraform.rs b/crates/languages/src/terraform.rs index d5973cc5da..a7e6b69f95 100644 --- a/crates/languages/src/terraform.rs +++ b/crates/languages/src/terraform.rs @@ -7,10 +7,9 @@ use lsp::{CodeActionKind, LanguageServerBinary}; use smol::fs::{self, File}; use std::{any::Any, ffi::OsString, path::PathBuf}; use util::{ - async_maybe, fs::remove_matching, github::{latest_github_release, GitHubLspBinaryVersion}, - ResultExt, + maybe, ResultExt, }; fn terraform_ls_binary_arguments() -> Vec { @@ -154,7 +153,7 @@ fn build_download_url(version: String) -> Result { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/toml.rs b/crates/languages/src/toml.rs index dee6a19aa7..a488064f4d 100644 --- a/crates/languages/src/toml.rs +++ b/crates/languages/src/toml.rs @@ -6,8 +6,8 @@ use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use smol::fs::{self, File}; use std::{any::Any, path::PathBuf}; -use util::async_maybe; use util::github::latest_github_release; +use util::maybe; use util::{github::GitHubLspBinaryVersion, ResultExt}; pub struct TaploLspAdapter; @@ -108,7 +108,7 @@ impl LspAdapter for TaploLspAdapter { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index 205249d48e..cc7a89f399 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -18,10 +18,9 @@ use std::{ sync::Arc, }; use util::{ - async_maybe, fs::remove_matching, github::{build_tarball_url, GitHubLspBinaryVersion}, - ResultExt, + maybe, ResultExt, }; fn typescript_server_binary_arguments(server_path: &Path) -> Vec { @@ -199,7 +198,7 @@ async fn get_cached_ts_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let old_server_path = container_dir.join(TypeScriptLspAdapter::OLD_SERVER_PATH); let new_server_path = container_dir.join(TypeScriptLspAdapter::NEW_SERVER_PATH); if new_server_path.exists() { @@ -378,7 +377,7 @@ async fn get_cached_eslint_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { // This is unfortunate but we don't know what the version is to build a path directly let mut dir = fs::read_dir(&container_dir).await?; let first = dir.next().await.ok_or(anyhow!("missing first file"))??; diff --git a/crates/languages/src/vue.rs b/crates/languages/src/vue.rs index f220ffdce4..372d6614fc 100644 --- a/crates/languages/src/vue.rs +++ b/crates/languages/src/vue.rs @@ -12,7 +12,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; pub struct VueLspVersion { vue_version: String, @@ -211,7 +211,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: Arc, ) -> Option<(LanguageServerBinary, TypescriptPath)> { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/yaml.rs b/crates/languages/src/yaml.rs index ce8544e012..13f2c829c2 100644 --- a/crates/languages/src/yaml.rs +++ b/crates/languages/src/yaml.rs @@ -15,7 +15,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::{async_maybe, ResultExt}; +use util::{maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/yaml-language-server/bin/yaml-language-server"; @@ -110,7 +110,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - async_maybe!({ + maybe!(async { let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/languages/src/zig.rs b/crates/languages/src/zig.rs index 8d6c836f22..488c56513a 100644 --- a/crates/languages/src/zig.rs +++ b/crates/languages/src/zig.rs @@ -9,8 +9,8 @@ use lsp::LanguageServerBinary; use smol::fs; use std::env::consts::{ARCH, OS}; use std::{any::Any, path::PathBuf}; -use util::async_maybe; use util::github::latest_github_release; +use util::maybe; use util::{github::GitHubLspBinaryVersion, ResultExt}; pub struct ZlsAdapter; @@ -113,7 +113,7 @@ impl LspAdapter for ZlsAdapter { } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { - async_maybe!({ + maybe!(async { let mut last_binary_path = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index d8b9dc115e..01d416fce6 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -9149,7 +9149,8 @@ impl Project { buffer: &Buffer, cx: &AppContext, ) -> Option<(&Arc, &Arc)> { - self.language_servers_for_buffer(buffer, cx).next() + self.language_servers_for_buffer(buffer, cx) + .find(|s| s.0.is_primary) } pub fn language_server_for_buffer( diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 76dc47202d..573ef512ab 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -426,21 +426,20 @@ pub fn unzip_option(option: Option<(T, U)>) -> (Option, Option) { } } -/// Evaluates to an immediately invoked function expression. Good for using the ? operator -/// in functions which do not return an Option or Result +/// Expands to an immediately-invoked function expression. Good for using the ? operator +/// in functions which do not return an Option or Result. +/// +/// Accepts a normal block, an async block, or an async move block. #[macro_export] macro_rules! maybe { ($block:block) => { (|| $block)() }; -} - -/// Evaluates to an immediately invoked function expression. Good for using the ? operator -/// in functions which do not return an Option or Result, but async. -#[macro_export] -macro_rules! async_maybe { - ($block:block) => { - (|| async move { $block })() + (async $block:block) => { + (|| async $block)() + }; + (async move $block:block) => { + (|| async move $block)() }; } diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 8b1cbf6ca5..a4ef2e74d9 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -184,7 +184,7 @@ impl LanguageServerPrompt { } async fn select_option(this: View, ix: usize, mut cx: AsyncWindowContext) { - util::async_maybe!({ + util::maybe!(async move { let potential_future = this.update(&mut cx, |this, _| { this.request.take().map(|request| request.respond(ix)) }); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index a5e959d757..008fe9f66f 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -47,8 +47,8 @@ use std::{ }; use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings}; use util::{ - async_maybe, http::{HttpClient, HttpClientWithUrl}, + maybe, paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR}, ResultExt, TryFutureExt, }; @@ -455,7 +455,7 @@ async fn installation_id() -> Result<(String, bool)> { } async fn restore_or_create_workspace(app_state: Arc, cx: AsyncAppContext) { - async_maybe!({ + maybe!(async { if let Some(location) = workspace::last_opened_workspace_paths().await { cx.update(|cx| { workspace::open_paths( diff --git a/extensions/gleam/Cargo.toml b/extensions/gleam/Cargo.toml index a19c11d449..ec09eed60f 100644 --- a/extensions/gleam/Cargo.toml +++ b/extensions/gleam/Cargo.toml @@ -8,9 +8,9 @@ license = "Apache-2.0" [lints] workspace = true -[dependencies] -zed_extension_api = { path = "../../crates/extension_api" } - [lib] path = "src/gleam.rs" crate-type = ["cdylib"] + +[dependencies] +zed_extension_api = "0.0.4" diff --git a/extensions/gleam/src/gleam.rs b/extensions/gleam/src/gleam.rs index f58d0d2256..4503e5d1d3 100644 --- a/extensions/gleam/src/gleam.rs +++ b/extensions/gleam/src/gleam.rs @@ -9,10 +9,6 @@ impl GleamExtension { fn language_server_binary_path(&mut self, config: zed::LanguageServerConfig) -> Result { if let Some(path) = &self.cached_binary_path { if fs::metadata(path).map_or(false, |stat| stat.is_file()) { - zed::set_language_server_installation_status( - &config.name, - &zed::LanguageServerInstallationStatus::Cached, - ); return Ok(path.clone()); } } @@ -75,11 +71,6 @@ impl GleamExtension { fs::remove_dir_all(&entry.path()).ok(); } } - - zed::set_language_server_installation_status( - &config.name, - &zed::LanguageServerInstallationStatus::Downloaded, - ); } self.cached_binary_path = Some(binary_path.clone()); diff --git a/extensions/svelte/.gitignore b/extensions/svelte/.gitignore new file mode 100644 index 0000000000..6aba30215e --- /dev/null +++ b/extensions/svelte/.gitignore @@ -0,0 +1,3 @@ +target +*.wasm +grammars diff --git a/extensions/svelte/Cargo.toml b/extensions/svelte/Cargo.toml new file mode 100644 index 0000000000..b3be545ac5 --- /dev/null +++ b/extensions/svelte/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "zed_svelte" +version = "0.0.1" +edition = "2021" +publish = false +license = "Apache-2.0" + +[lints] +workspace = true + +[lib] +path = "src/svelte.rs" +crate-type = ["cdylib"] + +[dependencies] +zed_extension_api = "0.0.4" diff --git a/extensions/svelte/extension.toml b/extensions/svelte/extension.toml new file mode 100644 index 0000000000..e64bbbb622 --- /dev/null +++ b/extensions/svelte/extension.toml @@ -0,0 +1,15 @@ +id = "svelte" +name = "Svelte" +description = "Svelte support" +version = "0.0.1" +schema_version = 1 +authors = [] +repository = "https://github.com/zed-extensions/svelte" + +[language_servers.svelte-language-server] +name = "Svelte Language Server" +language = "Svelte" + +[grammars.svelte] +repository = "https://github.com/Himujjal/tree-sitter-svelte" +commit = "ea528fc9985aed8d93c9f438c185644a33d011af" diff --git a/crates/languages/src/svelte/config.toml b/extensions/svelte/languages/svelte/config.toml similarity index 100% rename from crates/languages/src/svelte/config.toml rename to extensions/svelte/languages/svelte/config.toml diff --git a/crates/languages/src/svelte/highlights.scm b/extensions/svelte/languages/svelte/highlights.scm similarity index 100% rename from crates/languages/src/svelte/highlights.scm rename to extensions/svelte/languages/svelte/highlights.scm diff --git a/crates/languages/src/svelte/indents.scm b/extensions/svelte/languages/svelte/indents.scm similarity index 100% rename from crates/languages/src/svelte/indents.scm rename to extensions/svelte/languages/svelte/indents.scm diff --git a/crates/languages/src/svelte/injections.scm b/extensions/svelte/languages/svelte/injections.scm similarity index 100% rename from crates/languages/src/svelte/injections.scm rename to extensions/svelte/languages/svelte/injections.scm diff --git a/crates/languages/src/svelte/overrides.scm b/extensions/svelte/languages/svelte/overrides.scm similarity index 100% rename from crates/languages/src/svelte/overrides.scm rename to extensions/svelte/languages/svelte/overrides.scm diff --git a/extensions/svelte/src/svelte.rs b/extensions/svelte/src/svelte.rs new file mode 100644 index 0000000000..76c6096e83 --- /dev/null +++ b/extensions/svelte/src/svelte.rs @@ -0,0 +1,126 @@ +use std::{env, fs}; +use zed_extension_api::{self as zed, Result}; + +struct SvelteExtension { + did_find_server: bool, +} + +const SERVER_PATH: &str = "node_modules/svelte-language-server/bin/server.js"; +const PACKAGE_NAME: &str = "svelte-language-server"; + +impl SvelteExtension { + fn server_exists(&self) -> bool { + fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file()) + } + + fn server_script_path(&mut self, config: zed::LanguageServerConfig) -> Result { + let server_exists = self.server_exists(); + if self.did_find_server && server_exists { + return Ok(SERVER_PATH.to_string()); + } + + zed::set_language_server_installation_status( + &config.name, + &zed::LanguageServerInstallationStatus::CheckingForUpdate, + ); + let version = zed::npm_package_latest_version(PACKAGE_NAME)?; + + if !server_exists + || zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version) + { + zed::set_language_server_installation_status( + &config.name, + &zed::LanguageServerInstallationStatus::Downloading, + ); + let result = zed::npm_install_package(PACKAGE_NAME, &version); + match result { + Ok(()) => { + if !self.server_exists() { + Err(format!( + "installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'", + ))?; + } + } + Err(error) => { + if !self.server_exists() { + Err(error)?; + } + } + } + } + + self.did_find_server = true; + Ok(SERVER_PATH.to_string()) + } +} + +impl zed::Extension for SvelteExtension { + fn new() -> Self { + Self { + did_find_server: false, + } + } + + fn language_server_command( + &mut self, + config: zed::LanguageServerConfig, + _: &zed::Worktree, + ) -> Result { + let server_path = self.server_script_path(config)?; + Ok(zed::Command { + command: zed::node_binary_path()?, + args: vec![ + env::current_dir() + .unwrap() + .join(&server_path) + .to_string_lossy() + .to_string(), + "--stdio".to_string(), + ], + env: Default::default(), + }) + } + + fn language_server_initialization_options( + &mut self, + _: zed::LanguageServerConfig, + _: &zed::Worktree, + ) -> Result> { + let config = r#"{ + "inlayHints": { + "parameterNames": { + "enabled": "all", + "suppressWhenArgumentMatchesName": false + }, + "parameterTypes": { + "enabled": true + }, + "variableTypes": { + "enabled": true, + "suppressWhenTypeMatchesName": false + }, + "propertyDeclarationTypes": { + "enabled": true + }, + "functionLikeReturnType": { + "enabled": true + }, + "enumMemberValues": { + "enabled": true + } + } + }"#; + + Ok(Some(format!( + r#"{{ + "provideFormatter": true, + "configuration": {{ + "typescript": {config}, + "javascript": {config} + }} + }}"# + ))) + } +} + +zed::register_extension!(SvelteExtension); diff --git a/extensions/uiua/Cargo.toml b/extensions/uiua/Cargo.toml index 107b842d58..ddd0a0d5c1 100644 --- a/extensions/uiua/Cargo.toml +++ b/extensions/uiua/Cargo.toml @@ -8,9 +8,9 @@ license = "Apache-2.0" [lints] workspace = true -[dependencies] -zed_extension_api = { path = "../../crates/extension_api" } - [lib] path = "src/uiua.rs" crate-type = ["cdylib"] + +[dependencies] +zed_extension_api = "0.0.4"