Re-enable language plugin functionality with some fixes (#7105)

Max Brunsfeld , Antonio , and Marshall Bowers created

Part of https://github.com/zed-industries/zed/issues/7096

* [x] Load all queries for language plugins, not just highlight query
* [x] Auto-reload languages when changing the `plugins` directory
* [x] Bump Tree-sitter for language loading and unloading fixes
* [x] Figure out code signing

Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>

Change summary

Cargo.lock                            | 414 ++++++++++++++++++++++++++++
Cargo.toml                            |   7 
crates/language/src/language.rs       |  46 ++-
crates/zed/resources/zed.entitlements |   6 
crates/zed/src/languages.rs           |  82 ++++-
crates/zed/src/main.rs                |  32 +-
6 files changed, 525 insertions(+), 62 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -243,6 +243,12 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+
 [[package]]
 name = "arrayref"
 version = "0.3.7"
@@ -1852,6 +1858,105 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "cranelift-bforest"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cranelift-entity",
+]
+
+[[package]]
+name = "cranelift-codegen"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "bumpalo",
+ "cranelift-bforest",
+ "cranelift-codegen-meta",
+ "cranelift-codegen-shared",
+ "cranelift-control",
+ "cranelift-entity",
+ "cranelift-isle",
+ "gimli",
+ "hashbrown 0.14.0",
+ "log",
+ "regalloc2",
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-codegen-meta"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cranelift-codegen-shared",
+]
+
+[[package]]
+name = "cranelift-codegen-shared"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+
+[[package]]
+name = "cranelift-control"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "arbitrary",
+]
+
+[[package]]
+name = "cranelift-entity"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "serde",
+ "serde_derive",
+]
+
+[[package]]
+name = "cranelift-frontend"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cranelift-codegen",
+ "log",
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-isle"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+
+[[package]]
+name = "cranelift-native"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cranelift-codegen",
+ "libc",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-wasm"
+version = "0.103.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cranelift-codegen",
+ "cranelift-entity",
+ "cranelift-frontend",
+ "itertools 0.10.5",
+ "log",
+ "smallvec",
+ "wasmparser",
+ "wasmtime-types",
+]
+
 [[package]]
 name = "crc"
 version = "3.0.1"
@@ -2455,6 +2560,12 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
 [[package]]
 name = "fallible-streaming-iterator"
 version = "0.1.9"
@@ -3008,6 +3119,11 @@ name = "gimli"
 version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+dependencies = [
+ "fallible-iterator 0.3.0",
+ "indexmap 2.0.0",
+ "stable_deref_trait",
+]
 
 [[package]]
 name = "git"
@@ -3520,6 +3636,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.0",
+ "serde",
 ]
 
 [[package]]
@@ -3926,6 +4043,12 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
+[[package]]
+name = "leb128"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
+
 [[package]]
 name = "libc"
 version = "0.2.152"
@@ -4154,6 +4277,15 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
 
+[[package]]
+name = "mach"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "mach2"
 version = "0.4.1"
@@ -4232,6 +4364,15 @@ version = "2.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
 
+[[package]]
+name = "memfd"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
+dependencies = [
+ "rustix 0.38.30",
+]
+
 [[package]]
 name = "memmap2"
 version = "0.2.3"
@@ -4889,6 +5030,9 @@ version = "0.32.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
 dependencies = [
+ "crc32fast",
+ "hashbrown 0.14.0",
+ "indexmap 2.0.0",
  "memchr",
 ]
 
@@ -5812,6 +5956,15 @@ version = "2.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
 
+[[package]]
+name = "psm"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "ptr_meta"
 version = "0.1.4"
@@ -6063,6 +6216,19 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "regalloc2"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6"
+dependencies = [
+ "hashbrown 0.13.2",
+ "log",
+ "rustc-hash",
+ "slice-group-by",
+ "smallvec",
+]
+
 [[package]]
 name = "regex"
 version = "1.9.5"
@@ -6399,7 +6565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
 dependencies = [
  "bitflags 2.4.1",
- "fallible-iterator",
+ "fallible-iterator 0.2.0",
  "fallible-streaming-iterator",
  "hashlink",
  "libsqlite3-sys",
@@ -7211,6 +7377,12 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "slice-group-by"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
+
 [[package]]
 name = "slotmap"
 version = "1.0.6"
@@ -7317,6 +7489,12 @@ dependencies = [
  "der",
 ]
 
+[[package]]
+name = "sptr"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
+
 [[package]]
 name = "sqlez"
 version = "0.1.0"
@@ -7573,6 +7751,12 @@ dependencies = [
  "uuid 1.4.1",
 ]
 
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
 [[package]]
 name = "static_assertions"
 version = "1.1.0"
@@ -7846,6 +8030,12 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
 
+[[package]]
+name = "target-lexicon"
+version = "0.12.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae"
+
 [[package]]
 name = "tempfile"
 version = "3.9.0"
@@ -8490,10 +8680,12 @@ dependencies = [
 [[package]]
 name = "tree-sitter"
 version = "0.20.10"
-source = "git+https://github.com/tree-sitter/tree-sitter?rev=31c40449749c4263a91a43593831b82229049a4c#31c40449749c4263a91a43593831b82229049a4c"
+source = "git+https://github.com/tree-sitter/tree-sitter?rev=1d8975319c2d5de1bf710e7e21db25b0eee4bc66#1d8975319c2d5de1bf710e7e21db25b0eee4bc66"
 dependencies = [
  "cc",
  "regex",
+ "wasmtime",
+ "wasmtime-c-api-impl",
 ]
 
 [[package]]
@@ -9328,6 +9520,224 @@ version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
 
+[[package]]
+name = "wasm-encoder"
+version = "0.38.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
+dependencies = [
+ "leb128",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.118.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
+dependencies = [
+ "indexmap 2.0.0",
+ "semver",
+]
+
+[[package]]
+name = "wasmtime"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "anyhow",
+ "bincode",
+ "bumpalo",
+ "cfg-if 1.0.0",
+ "indexmap 2.0.0",
+ "libc",
+ "log",
+ "object",
+ "once_cell",
+ "paste",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "target-lexicon",
+ "wasmparser",
+ "wasmtime-cranelift",
+ "wasmtime-environ",
+ "wasmtime-jit",
+ "wasmtime-runtime",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wasmtime-asm-macros"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "wasmtime-c-api-impl"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "anyhow",
+ "log",
+ "once_cell",
+ "tracing",
+ "wasmtime",
+ "wasmtime-c-api-macros",
+]
+
+[[package]]
+name = "wasmtime-c-api-macros"
+version = "0.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "wasmtime-cranelift"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "anyhow",
+ "cfg-if 1.0.0",
+ "cranelift-codegen",
+ "cranelift-control",
+ "cranelift-entity",
+ "cranelift-frontend",
+ "cranelift-native",
+ "cranelift-wasm",
+ "gimli",
+ "log",
+ "object",
+ "target-lexicon",
+ "thiserror",
+ "wasmparser",
+ "wasmtime-cranelift-shared",
+ "wasmtime-environ",
+ "wasmtime-versioned-export-macros",
+]
+
+[[package]]
+name = "wasmtime-cranelift-shared"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "anyhow",
+ "cranelift-codegen",
+ "cranelift-control",
+ "cranelift-native",
+ "gimli",
+ "object",
+ "target-lexicon",
+ "wasmtime-environ",
+]
+
+[[package]]
+name = "wasmtime-environ"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "anyhow",
+ "cranelift-entity",
+ "gimli",
+ "indexmap 2.0.0",
+ "log",
+ "object",
+ "serde",
+ "serde_derive",
+ "target-lexicon",
+ "thiserror",
+ "wasmparser",
+ "wasmtime-types",
+]
+
+[[package]]
+name = "wasmtime-jit"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "anyhow",
+ "bincode",
+ "cfg-if 1.0.0",
+ "gimli",
+ "log",
+ "object",
+ "rustix 0.38.30",
+ "serde",
+ "serde_derive",
+ "target-lexicon",
+ "wasmtime-environ",
+ "wasmtime-jit-icache-coherence",
+ "wasmtime-runtime",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wasmtime-jit-icache-coherence"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wasmtime-runtime"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "anyhow",
+ "cc",
+ "cfg-if 1.0.0",
+ "indexmap 2.0.0",
+ "libc",
+ "log",
+ "mach",
+ "memfd",
+ "memoffset 0.9.0",
+ "paste",
+ "psm",
+ "rustix 0.38.30",
+ "sptr",
+ "wasm-encoder",
+ "wasmtime-asm-macros",
+ "wasmtime-environ",
+ "wasmtime-versioned-export-macros",
+ "wasmtime-wmemcheck",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wasmtime-types"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "cranelift-entity",
+ "serde",
+ "serde_derive",
+ "thiserror",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmtime-versioned-export-macros"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "wasmtime-wmemcheck"
+version = "16.0.0"
+source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+
 [[package]]
 name = "web-sys"
 version = "0.3.64"

Cargo.toml 🔗

@@ -130,7 +130,7 @@ thiserror = "1.0.29"
 tiktoken-rs = "0.5.7"
 time = { version = "0.3", features = ["serde", "serde-well-known"] }
 toml = "0.5"
-tree-sitter = "0.20"
+tree-sitter = { version = "0.20", features = ["wasm"] }
 tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash", rev = "7331995b19b8f8aba2d5e26deb51d2195c18bc94" }
 tree-sitter-c = "0.20.1"
 tree-sitter-c-sharp = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "dd5e59721a5f8dae34604060833902b882023aaf" }
@@ -168,10 +168,11 @@ tree-sitter-zig = { git = "https://github.com/maxxnino/tree-sitter-zig", rev = "
 unindent = "0.1.7"
 url = "2.2"
 uuid = { version = "1.1.2", features = ["v4"] }
+wasmtime = "16"
 
 [patch.crates-io]
-tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "31c40449749c4263a91a43593831b82229049a4c" }
-# wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" }
+tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d8975319c2d5de1bf710e7e21db25b0eee4bc66" }
+wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" }
 
 [profile.dev]
 split-debuginfo = "unpacked"

crates/language/src/language.rs 🔗

@@ -52,7 +52,7 @@ use std::{
 };
 use syntax_map::SyntaxSnapshot;
 use theme::{SyntaxTheme, Theme};
-use tree_sitter::{self, Query};
+use tree_sitter::{self, wasmtime, Query, WasmStore};
 use unicase::UniCase;
 use util::{http::HttpClient, paths::PathExt};
 use util::{post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
@@ -96,7 +96,9 @@ impl LspBinaryStatusSender {
 
 thread_local! {
     static PARSER: RefCell<Parser> = {
-        RefCell::new(Parser::new())
+        let mut parser = Parser::new();
+        parser.set_wasm_store(WasmStore::new(WASM_ENGINE.clone()).unwrap()).unwrap();
+        RefCell::new(parser)
     };
 }
 
@@ -104,6 +106,7 @@ lazy_static! {
     pub(crate) static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
     /// A shared grammar for plain text, exposed for reuse by downstream crates.
     #[doc(hidden)]
+    pub static ref WASM_ENGINE: wasmtime::Engine = wasmtime::Engine::default();
     pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
         LanguageConfig {
             name: "Plain Text".into(),
@@ -706,8 +709,8 @@ enum AvailableGrammar {
         get_queries: fn(&str) -> LanguageQueries,
     },
     Wasm {
-        _grammar_name: Arc<str>,
-        _path: Arc<Path>,
+        path: Arc<Path>,
+        get_queries: fn(&Path) -> LanguageQueries,
     },
 }
 
@@ -773,9 +776,6 @@ impl LanguageRegistry {
     }
 
     /// Clear out all of the loaded languages and reload them from scratch.
-    ///
-    /// This is useful in development, when queries have changed.
-    #[cfg(debug_assertions)]
     pub fn reload(&self) {
         self.state.write().reload();
     }
@@ -802,15 +802,17 @@ impl LanguageRegistry {
         });
     }
 
-    pub fn register_wasm(&self, path: Arc<Path>, grammar_name: Arc<str>, config: LanguageConfig) {
+    pub fn register_wasm(
+        &self,
+        path: Arc<Path>,
+        config: LanguageConfig,
+        get_queries: fn(&Path) -> LanguageQueries,
+    ) {
         let state = &mut *self.state.write();
         state.available_languages.push(AvailableLanguage {
             id: post_inc(&mut state.next_available_language_id),
             config,
-            grammar: AvailableGrammar::Wasm {
-                _grammar_name: grammar_name,
-                _path: path,
-            },
+            grammar: AvailableGrammar::Wasm { path, get_queries },
             lsp_adapters: Vec::new(),
             loaded: false,
         });
@@ -944,8 +946,23 @@ impl LanguageRegistry {
                                             asset_dir,
                                             get_queries,
                                         } => (grammar, (get_queries)(asset_dir)),
-                                        AvailableGrammar::Wasm { .. } => {
-                                            Err(anyhow!("not supported"))?
+                                        AvailableGrammar::Wasm { path, get_queries } => {
+                                            let grammar_name =
+                                                &language.config.grammar_name.as_ref().ok_or_else(
+                                                    || anyhow!("missing grammar name"),
+                                                )?;
+                                            let mut wasm_path = path.join(grammar_name.as_ref());
+                                            wasm_path.set_extension("wasm");
+                                            let wasm_bytes = std::fs::read(&wasm_path)?;
+                                            let grammar = 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
+                                            })?;
+                                            (grammar, get_queries(path.as_ref()))
                                         }
                                     };
                                     Language::new(language.config, Some(grammar))
@@ -1172,7 +1189,6 @@ impl LanguageRegistryState {
         *self.subscription.0.borrow_mut() = ();
     }
 
-    #[cfg(debug_assertions)]
     fn reload(&mut self) {
         self.languages.clear();
         self.version += 1;

crates/zed/resources/zed.entitlements 🔗

@@ -6,6 +6,10 @@
 	<true/>
 	<key>com.apple.security.cs.allow-jit</key>
 	<true/>
+	<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
+	<true/>
+	<!-- <key>com.apple.security.cs.disable-library-validation</key>
+	<true/> -->
 	<key>com.apple.security.device.audio-input</key>
 	<true/>
 	<key>com.apple.security.device.camera</key>
@@ -18,7 +22,5 @@
 	<true/>
 	<key>com.apple.security.personal-information.photos-library</key>
 	<true/>
-	<!-- <key>com.apple.security.cs.disable-library-validation</key>
-	<true/> -->
 </dict>
 </plist>

crates/zed/src/languages.rs 🔗

@@ -4,8 +4,8 @@ pub use language::*;
 use node_runtime::NodeRuntime;
 use rust_embed::RustEmbed;
 use settings::Settings;
-use std::{borrow::Cow, str, sync::Arc};
-use util::{asset_str, paths::PLUGINS_DIR};
+use std::{borrow::Cow, fs, path::Path, str, sync::Arc};
+use util::{asset_str, paths::PLUGINS_DIR, ResultExt};
 
 use self::{deno::DenoSettings, elixir::ElixirSettings};
 
@@ -303,10 +303,11 @@ pub fn init(
                 let path = child.path();
                 let config_path = path.join("config.toml");
                 if let Ok(config) = std::fs::read(&config_path) {
-                    let config: LanguageConfig = ::toml::from_slice(&config).unwrap();
-                    if let Some(grammar_name) = config.grammar_name.clone() {
-                        languages.register_wasm(path.into(), grammar_name, config);
-                    }
+                    languages.register_wasm(
+                        path.into(),
+                        ::toml::from_slice(&config).unwrap(),
+                        load_plugin_queries,
+                    );
                 }
             }
         }
@@ -338,27 +339,62 @@ fn load_config(name: &str) -> LanguageConfig {
     .unwrap()
 }
 
+const QUERY_FILENAME_PREFIXES: &[(
+    &str,
+    fn(&mut LanguageQueries) -> &mut Option<Cow<'static, str>>,
+)] = &[
+    ("highlights", |q| &mut q.highlights),
+    ("brackets", |q| &mut q.brackets),
+    ("outline", |q| &mut q.outline),
+    ("indents", |q| &mut q.indents),
+    ("embedding", |q| &mut q.embedding),
+    ("injections", |q| &mut q.injections),
+    ("overrides", |q| &mut q.overrides),
+];
+
 fn load_queries(name: &str) -> LanguageQueries {
-    LanguageQueries {
-        highlights: load_query(name, "/highlights"),
-        brackets: load_query(name, "/brackets"),
-        indents: load_query(name, "/indents"),
-        outline: load_query(name, "/outline"),
-        embedding: load_query(name, "/embedding"),
-        injections: load_query(name, "/injections"),
-        overrides: load_query(name, "/overrides"),
+    let mut result = LanguageQueries::default();
+    for path in LanguageDir::iter() {
+        if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
+            if !remainder.ends_with(".scm") {
+                continue;
+            }
+            for (name, query) in QUERY_FILENAME_PREFIXES {
+                if remainder.starts_with(name) {
+                    let contents = asset_str::<LanguageDir>(path.as_ref());
+                    match query(&mut result) {
+                        None => *query(&mut result) = Some(contents),
+                        Some(r) => r.to_mut().push_str(contents.as_ref()),
+                    }
+                }
+            }
+        }
     }
+    result
 }
 
-fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
-    let mut result = None;
-    for path in LanguageDir::iter() {
-        if let Some(remainder) = path.strip_prefix(name) {
-            if remainder.starts_with(filename_prefix) {
-                let contents = asset_str::<LanguageDir>(path.as_ref());
-                match &mut result {
-                    None => result = Some(contents),
-                    Some(r) => r.to_mut().push_str(contents.as_ref()),
+fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
+    let mut result = LanguageQueries::default();
+    if let Some(entries) = fs::read_dir(root_path).log_err() {
+        for entry in entries {
+            let Some(entry) = entry.log_err() else {
+                continue;
+            };
+            let path = entry.path();
+            if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
+                if !remainder.ends_with(".scm") {
+                    continue;
+                }
+                for (name, query) in QUERY_FILENAME_PREFIXES {
+                    if remainder.starts_with(name) {
+                        if let Some(contents) = fs::read_to_string(&path).log_err() {
+                            match query(&mut result) {
+                                None => *query(&mut result) = Some(contents.into()),
+                                Some(r) => r.to_mut().push_str(contents.as_ref()),
+                            }
+                        }
+                        break;
+                    }
                 }
             }
         }

crates/zed/src/main.rs 🔗

@@ -39,12 +39,13 @@ use std::{
         Arc,
     },
     thread,
+    time::Duration,
 };
 use theme::{ActiveTheme, ThemeRegistry, ThemeSettings};
 use util::{
     async_maybe,
     http::{self, HttpClient, ZedHttpClient},
-    paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR},
+    paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR, PLUGINS_DIR},
     ResultExt,
 };
 use uuid::Uuid;
@@ -894,26 +895,28 @@ fn load_embedded_fonts(cx: &AppContext) {
         .unwrap();
 }
 
-#[cfg(debug_assertions)]
-async fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>) -> Option<()> {
-    use std::time::Duration;
+async fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>) {
+    let reload_debounce = Duration::from_millis(250);
+
+    let mut events = fs.watch(PLUGINS_DIR.as_ref(), reload_debounce).await;
 
-    let mut events = fs
-        .watch(
-            "crates/zed/src/languages".as_ref(),
-            Duration::from_millis(100),
+    #[cfg(debug_assertions)]
+    {
+        events = futures::stream::select(
+            events,
+            fs.watch("crates/zed/src/languages".as_ref(), reload_debounce)
+                .await,
         )
-        .await;
+        .boxed();
+    }
+
     while (events.next().await).is_some() {
         languages.reload();
     }
-    Some(())
 }
 
 #[cfg(debug_assertions)]
 fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
-    use std::time::Duration;
-
     cx.spawn(|cx| async move {
         let mut events = fs
             .watch(
@@ -933,10 +936,5 @@ fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
     .detach()
 }
 
-#[cfg(not(debug_assertions))]
-async fn watch_languages(_: Arc<dyn fs::Fs>, _: Arc<LanguageRegistry>) -> Option<()> {
-    None
-}
-
 #[cfg(not(debug_assertions))]
 fn watch_file_types(_fs: Arc<dyn fs::Fs>, _cx: &mut AppContext) {}