diff --git a/Cargo.lock b/Cargo.lock index 6a8cfe5c98..b2a540338f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8113,7 +8113,6 @@ dependencies = [ "tree-sitter-rust", "tree-sitter-typescript", "tree-sitter-yaml", - "util", ] [[package]] @@ -9751,8 +9750,9 @@ dependencies = [ "anyhow", "collections", "gpui_shared_string", + "gpui_util", "log", - "lsp", + "lsp-types", "parking_lot", "regex", "schemars 1.0.4", @@ -10549,10 +10549,12 @@ name = "lsp" version = "0.1.0" dependencies = [ "anyhow", + "async-channel 2.5.0", "async-pipe", "collections", "ctor", "futures 0.3.32", + "futures-lite 1.13.0", "gpui", "gpui_util", "log", @@ -10564,7 +10566,6 @@ dependencies = [ "semver", "serde", "serde_json", - "smol", "util", "zlog", ] diff --git a/Cargo.toml b/Cargo.toml index 26324298e4..29e6e44daa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -814,7 +814,7 @@ toml_edit = { version = "0.22", default-features = false, features = [ "serde", ] } tower-http = "0.4.4" -tree-sitter = { version = "0.26.9", features = ["wasm"] } +tree-sitter = "0.26.9" tree-sitter-bash = "0.25.1" tree-sitter-c = "0.24.1" tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "5cb9b693cfd7bfacab1d9ff4acac1a4150700609" } diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index 195d0d8df9..8a2364abb6 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -69,7 +69,7 @@ text.workspace = true theme.workspace = true theme_settings.workspace = true tree-sitter-json.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } ui.workspace = true ui_input.workspace = true unindent = { workspace = true, optional = true } diff --git a/crates/edit_prediction_context/Cargo.toml b/crates/edit_prediction_context/Cargo.toml index 244b5f736e..f1bcefc406 100644 --- a/crates/edit_prediction_context/Cargo.toml +++ b/crates/edit_prediction_context/Cargo.toml @@ -26,7 +26,7 @@ serde.workspace = true smallvec.workspace = true telemetry.workspace = true text.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } util.workspace = true zeta_prompt.workspace = true @@ -43,4 +43,3 @@ serde_json.workspace = true settings = {workspace= true, features = ["test-support"]} text = { workspace = true, features = ["test-support"] } util = { workspace = true, features = ["test-support"] } - diff --git a/crates/extension_cli/Cargo.toml b/crates/extension_cli/Cargo.toml index 623187faaa..0927c24668 100644 --- a/crates/extension_cli/Cargo.toml +++ b/crates/extension_cli/Cargo.toml @@ -34,5 +34,5 @@ theme_settings.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["full"] } toml.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } wasmtime.workspace = true diff --git a/crates/grammars/Cargo.toml b/crates/grammars/Cargo.toml index 13b3bf5c94..9d37f6cf26 100644 --- a/crates/grammars/Cargo.toml +++ b/crates/grammars/Cargo.toml @@ -15,7 +15,6 @@ language_core.workspace = true rust-embed.workspace = true anyhow.workspace = true toml.workspace = true -util.workspace = true tree-sitter = { workspace = true, optional = true } tree-sitter-bash = { workspace = true, optional = true } @@ -57,4 +56,4 @@ load-grammars = [ "tree-sitter-typescript", "tree-sitter-yaml", ] -test-support = ["load-grammars"] +test-support = ["load-grammars", "tree-sitter/wasm"] diff --git a/crates/grammars/src/grammars.rs b/crates/grammars/src/grammars.rs index 00d6e6281c..e3c70c9dc3 100644 --- a/crates/grammars/src/grammars.rs +++ b/crates/grammars/src/grammars.rs @@ -1,7 +1,8 @@ +use std::borrow::Cow; + use anyhow::Context as _; use language_core::{LanguageConfig, LanguageQueries, QUERY_FILENAME_PREFIXES}; use rust_embed::RustEmbed; -use util::asset_str; #[derive(RustEmbed)] #[folder = "src/"] @@ -95,7 +96,10 @@ pub fn load_queries(name: &str) -> LanguageQueries { } for (prefix, query) in QUERY_FILENAME_PREFIXES { if remainder.starts_with(prefix) { - let contents = asset_str::(path.as_ref()); + let contents = match GrammarDir::get(path.as_ref()).unwrap().data { + Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8(bytes).unwrap()), + Cow::Owned(bytes) => Cow::Owned(String::from_utf8(bytes).unwrap()), + }; match query(&mut result) { None => *query(&mut result) = Some(contents), Some(existing) => existing.to_mut().push_str(contents.as_ref()), diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 58a7517d2c..df493c393e 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -68,7 +68,7 @@ tree-sitter-md = { workspace = true, optional = true } tree-sitter-python = { workspace = true, optional = true } tree-sitter-rust = { workspace = true, optional = true } tree-sitter-typescript = { workspace = true, optional = true } -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } unicase = "2.6" util.workspace = true watch.workspace = true diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index f15b241b29..4c57001cd6 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1239,9 +1239,9 @@ impl LanguageScope { pub fn language_allowed(&self, name: &LanguageServerName) -> bool { let config = &self.language.config; let opt_in_servers = &config.scope_opt_in_language_servers; - if opt_in_servers.contains(name) { + if opt_in_servers.contains(&name.0) { if let Some(over) = self.config_override() { - over.opt_into_language_servers.contains(name) + over.opt_into_language_servers.contains(&name.0) } else { false } diff --git a/crates/language_core/Cargo.toml b/crates/language_core/Cargo.toml index cd1143f61d..91dca562a8 100644 --- a/crates/language_core/Cargo.toml +++ b/crates/language_core/Cargo.toml @@ -12,7 +12,6 @@ anyhow.workspace = true collections.workspace = true gpui_shared_string.workspace = true log.workspace = true -lsp.workspace = true parking_lot.workspace = true regex.workspace = true schemars.workspace = true @@ -21,7 +20,8 @@ serde_json.workspace = true toml.workspace = true tree-sitter.workspace = true util.workspace = true - +gpui_util.workspace = true +lsp-types.workspace = true [features] test-support = [] diff --git a/crates/language_core/src/code_label.rs b/crates/language_core/src/code_label.rs index 0a98743d02..74e1d43e0c 100644 --- a/crates/language_core/src/code_label.rs +++ b/crates/language_core/src/code_label.rs @@ -4,7 +4,7 @@ use std::ops::Range; #[derive(Debug, Clone)] pub struct Symbol { pub name: String, - pub kind: lsp::SymbolKind, + pub kind: lsp_types::SymbolKind, pub container_name: Option, } diff --git a/crates/language_core/src/diagnostic.rs b/crates/language_core/src/diagnostic.rs index 00abcb61d1..b8c4fe8535 100644 --- a/crates/language_core/src/diagnostic.rs +++ b/crates/language_core/src/diagnostic.rs @@ -1,5 +1,5 @@ use gpui_shared_string::SharedString; -use lsp::{DiagnosticSeverity, NumberOrString}; +use lsp_types::{DiagnosticSeverity, NumberOrString}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -12,7 +12,7 @@ pub struct Diagnostic { pub registration_id: Option, /// A machine-readable code that identifies this diagnostic. pub code: Option, - pub code_description: Option, + pub code_description: Option, /// Whether this diagnostic is a hint, warning, or error. pub severity: DiagnosticSeverity, /// The human-readable message associated with this diagnostic. diff --git a/crates/language_core/src/grammar.rs b/crates/language_core/src/grammar.rs index d81ced4420..7ab210b56c 100644 --- a/crates/language_core/src/grammar.rs +++ b/crates/language_core/src/grammar.rs @@ -5,7 +5,6 @@ use crate::{ use anyhow::{Context as _, Result}; use collections::HashMap; use gpui_shared_string::SharedString; -use lsp::LanguageServerName; use parking_lot::Mutex; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use tree_sitter::Query; @@ -673,7 +672,7 @@ impl Grammar { language_name: &LanguageName, overrides: &HashMap, brackets: &mut BracketPairConfig, - scope_opt_in_language_servers: &[LanguageServerName], + scope_opt_in_language_servers: &[SharedString], ) -> Result { let query = Query::new(&self.ts_language, source)?; @@ -691,7 +690,7 @@ impl Grammar { let value = overrides.get(name).cloned().unwrap_or_default(); for server_name in &value.opt_into_language_servers { if !scope_opt_in_language_servers.contains(server_name) { - util::debug_panic!( + gpui_util::debug_panic!( "Server {server_name:?} has been opted-in by scope {name:?} but has not been marked as an opt-in server" ); } diff --git a/crates/language_core/src/language_config.rs b/crates/language_core/src/language_config.rs index 93f9423ada..51903aef76 100644 --- a/crates/language_core/src/language_config.rs +++ b/crates/language_core/src/language_config.rs @@ -1,7 +1,6 @@ use crate::LanguageName; use collections::{HashMap, HashSet, IndexSet}; use gpui_shared_string::SharedString; -use lsp::LanguageServerName; use regex::Regex; use schemars::{JsonSchema, SchemaGenerator, json_schema}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; @@ -105,7 +104,7 @@ pub struct LanguageConfig { pub rewrap_prefixes: Vec, /// A list of language servers that are allowed to run on subranges of a given language. #[serde(default)] - pub scope_opt_in_language_servers: Vec, + pub scope_opt_in_language_servers: Vec, #[serde(default)] pub overrides: HashMap, /// A list of characters that Zed should treat as word characters for the @@ -377,7 +376,7 @@ pub struct LanguageConfigOverride { #[serde(default)] pub linked_edit_characters: Override>, #[serde(default)] - pub opt_into_language_servers: Vec, + pub opt_into_language_servers: Vec, #[serde(default)] pub prefer_label_for_snippet: Option, } diff --git a/crates/language_core/src/lsp_adapter.rs b/crates/language_core/src/lsp_adapter.rs index 8f449637b3..230e1b4aa1 100644 --- a/crates/language_core/src/lsp_adapter.rs +++ b/crates/language_core/src/lsp_adapter.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; /// Converts a value into an LSP position. pub trait ToLspPosition { /// Converts the value into an LSP position. - fn to_lsp_position(self) -> lsp::Position; + fn to_lsp_position(self) -> lsp_types::Position; } /// Context provided to LSP adapters when a user responds to a ShowMessageRequest prompt. @@ -15,7 +15,7 @@ pub struct PromptResponseContext { /// The original message shown to the user pub message: String, /// The action (button) the user selected - pub selected_action: lsp::MessageActionItem, + pub selected_action: lsp_types::MessageActionItem, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/language_tools/Cargo.toml b/crates/language_tools/Cargo.toml index 26e230c1d9..4e18b4f3ef 100644 --- a/crates/language_tools/Cargo.toml +++ b/crates/language_tools/Cargo.toml @@ -31,7 +31,7 @@ serde_json.workspace = true settings.workspace = true telemetry.workspace = true theme.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } sysinfo.workspace = true ui.workspace = true util.workspace = true @@ -45,4 +45,4 @@ gpui = { workspace = true, features = ["test-support"] } semver.workspace = true util = { workspace = true, features = ["test-support"] } zlog.workspace = true -theme_settings.workspace = true \ No newline at end of file +theme_settings.workspace = true diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index 358a9ad267..cf35e2e1e1 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -59,7 +59,7 @@ snippet.workspace = true task.workspace = true terminal.workspace = true theme.workspace = true -tree-sitter = { workspace = true, optional = true } +tree-sitter = { workspace = true, optional = true , features = ["wasm"] } tree-sitter-gitcommit = { workspace = true, optional = true } url.workspace = true util.workspace = true @@ -78,5 +78,5 @@ tree-sitter-go.workspace = true tree-sitter-python.workspace = true tree-sitter-rust.workspace = true tree-sitter-typescript.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } unindent.workspace = true diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index 2c48575a64..7bffc73f2f 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -13,25 +13,26 @@ path = "src/lsp.rs" doctest = false [features] -test-support = ["async-pipe", "gpui_util"] +test-support = ["async-pipe"] [dependencies] anyhow.workspace = true +async-channel.workspace = true async-pipe = { workspace = true, optional = true } collections.workspace = true -gpui_util = { workspace = true, optional = true } +futures-lite.workspace = true futures.workspace = true gpui.workspace = true +gpui_util.workspace = true log.workspace = true lsp-types.workspace = true parking_lot.workspace = true postage.workspace = true +release_channel.workspace = true +schemars.workspace = true serde.workspace = true serde_json.workspace = true -schemars.workspace = true -smol.workspace = true util.workspace = true -release_channel.workspace = true [dev-dependencies] async-pipe.workspace = true diff --git a/crates/lsp/src/input_handler.rs b/crates/lsp/src/input_handler.rs index 679ae5c1f6..9173eeae85 100644 --- a/crates/lsp/src/input_handler.rs +++ b/crates/lsp/src/input_handler.rs @@ -6,11 +6,11 @@ use collections::HashMap; use futures::{ AsyncBufReadExt, AsyncRead, AsyncReadExt as _, SinkExt as _, channel::mpsc::{Receiver, Sender, channel}, + io::BufReader, }; use gpui::{BackgroundExecutor, Task}; use log::warn; use parking_lot::Mutex; -use smol::io::BufReader; use crate::{ AnyResponse, CONTENT_LEN_HEADER, IoHandler, IoKind, NotificationOrRequest, RequestId, @@ -190,12 +190,12 @@ mod tests { #[gpui::test] async fn test_read_headers() { let mut buf = Vec::new(); - let mut reader = smol::io::BufReader::new(b"Content-Length: 123\r\n\r\n" as &[u8]); + let mut reader = BufReader::new(b"Content-Length: 123\r\n\r\n" as &[u8]); read_headers(&mut reader, &mut buf).await.unwrap(); assert_eq!(buf, b"Content-Length: 123\r\n\r\n"); let mut buf = Vec::new(); - let mut reader = smol::io::BufReader::new(b"Content-Type: application/vscode-jsonrpc\r\nContent-Length: 1235\r\n\r\n{\"somecontent\":123}" as &[u8]); + let mut reader = BufReader::new(b"Content-Type: application/vscode-jsonrpc\r\nContent-Length: 1235\r\n\r\n{\"somecontent\":123}" as &[u8]); read_headers(&mut reader, &mut buf).await.unwrap(); assert_eq!( buf, @@ -203,7 +203,7 @@ mod tests { ); let mut buf = Vec::new(); - let mut reader = smol::io::BufReader::new(b"Content-Length: 1235\r\nContent-Type: application/vscode-jsonrpc\r\n\r\n{\"somecontent\":true}" as &[u8]); + let mut reader = BufReader::new(b"Content-Length: 1235\r\nContent-Type: application/vscode-jsonrpc\r\n\r\n{\"somecontent\":true}" as &[u8]); read_headers(&mut reader, &mut buf).await.unwrap(); assert_eq!( buf, diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 550b27e131..361c5665d2 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -6,10 +6,10 @@ pub use lsp_types::*; use anyhow::{Context as _, Result, anyhow}; use collections::{BTreeMap, HashMap}; use futures::{ - AsyncRead, AsyncWrite, Future, FutureExt, + AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, Future, FutureExt, StreamExt, channel::oneshot::{self, Canceled}, future::{self, Either}, - io::BufWriter, + io::{BufReader, BufWriter}, select, }; use gpui::{App, AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Task}; @@ -19,12 +19,9 @@ use postage::{barrier, prelude::Stream}; use schemars::JsonSchema; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::{Value, json, value::RawValue}; -use smol::{ - channel, - io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, -}; use util::command::{Child, Stdio}; +use gpui_util::{ResultExt, TryFutureExt}; use std::path::Path; use std::{ any::TypeId, @@ -42,7 +39,7 @@ use std::{ task::Poll, time::{Duration, Instant}, }; -use util::{ConnectionResult, ResultExt, TryFutureExt, redact}; +use util::{ConnectionResult, redact}; const JSON_RPC_VERSION: &str = "2.0"; const CONTENT_LEN_HEADER: &str = "Content-Length: "; @@ -99,8 +96,8 @@ struct NotificationSerializer(Box String + Send + Sync>); pub struct LanguageServer { server_id: LanguageServerId, next_id: AtomicI32, - outbound_tx: channel::Sender, - notification_tx: channel::Sender, + outbound_tx: async_channel::Sender, + notification_tx: async_channel::Sender, name: LanguageServerName, version: Option, process_name: Arc, @@ -483,7 +480,7 @@ impl LanguageServer { Stderr: AsyncRead + Unpin + Send + 'static, F: Fn(&NotificationOrRequest) -> bool + 'static + Send + Sync + Clone, { - let (outbound_tx, outbound_rx) = channel::unbounded::(); + let (outbound_tx, outbound_rx) = async_channel::unbounded::(); let (output_done_tx, output_done_rx) = barrier::channel(); let notification_handlers = Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default())); @@ -563,7 +560,8 @@ impl LanguageServer { } .into(); - let (notification_tx, notification_rx) = channel::unbounded::(); + let (notification_tx, notification_rx) = + async_channel::unbounded::(); cx.background_spawn({ let outbound_tx = outbound_tx.clone(); async move { @@ -623,9 +621,8 @@ impl LanguageServer { where Stdout: AsyncRead + Unpin + Send + 'static, { - use smol::stream::StreamExt; let stdout = BufReader::new(stdout); - let _clear_response_handlers = util::defer({ + let _clear_response_handlers = gpui_util::defer({ let response_handlers = response_handlers.clone(); move || { response_handlers.lock().take(); @@ -667,7 +664,7 @@ impl LanguageServer { } // Don't starve the main thread when receiving lots of notifications at once. - smol::future::yield_now().await; + futures_lite::future::yield_now().await; } input_handler.loop_handle.await } @@ -703,13 +700,13 @@ impl LanguageServer { } // Don't starve the main thread when receiving lots of messages at once. - smol::future::yield_now().await; + futures_lite::future::yield_now().await; } } async fn handle_outgoing_messages( stdin: Stdin, - outbound_rx: channel::Receiver, + outbound_rx: async_channel::Receiver, output_done_tx: barrier::Sender, response_handlers: Arc>>>, io_handlers: Arc>>, @@ -1426,8 +1423,8 @@ impl LanguageServer { fn request_internal_with_timer( next_id: &AtomicI32, response_handlers: &Arc>>>, - outbound_tx: &channel::Sender, - notification_serializers: &channel::Sender, + outbound_tx: &async_channel::Sender, + notification_serializers: &async_channel::Sender, executor: &BackgroundExecutor, timer: U, params: T::Params, @@ -1489,7 +1486,7 @@ impl LanguageServer { return ConnectionResult::Result(Err(e)); } - let cancel_on_drop = util::defer(move || { + let cancel_on_drop = gpui_util::defer(move || { if let Some(notification_serializers) = notification_serializers.upgrade() { Self::notify_internal::( ¬ification_serializers, @@ -1536,8 +1533,8 @@ impl LanguageServer { fn request_internal( next_id: &AtomicI32, response_handlers: &Arc>>>, - outbound_tx: &channel::Sender, - notification_serializers: &channel::Sender, + outbound_tx: &async_channel::Sender, + notification_serializers: &async_channel::Sender, executor: &BackgroundExecutor, request_timeout: Duration, params: T::Params, @@ -1588,7 +1585,7 @@ impl LanguageServer { } fn notify_internal( - outbound_tx: &channel::Sender, + outbound_tx: &async_channel::Sender, params: T::Params, ) -> Result<()> { let serializer = NotificationSerializer(Box::new(move || { @@ -1822,7 +1819,7 @@ impl Drop for Subscription { pub struct FakeLanguageServer { pub binary: LanguageServerBinary, pub server: Arc, - notifications_rx: channel::Receiver<(String, String)>, + notifications_rx: async_channel::Receiver<(String, String)>, } #[cfg(any(test, feature = "test-support"))] @@ -1837,7 +1834,7 @@ impl FakeLanguageServer { ) -> (LanguageServer, FakeLanguageServer) { let (stdin_writer, stdin_reader) = async_pipe::pipe(); let (stdout_writer, stdout_reader) = async_pipe::pipe(); - let (notifications_tx, notifications_rx) = channel::unbounded(); + let (notifications_tx, notifications_rx) = async_channel::unbounded(); let server_name = LanguageServerName(name.clone().into()); let process_name = Arc::from(name.as_str()); @@ -2105,8 +2102,8 @@ mod tests { &mut cx.to_async(), ); - let (message_tx, message_rx) = channel::unbounded(); - let (diagnostics_tx, diagnostics_rx) = channel::unbounded(); + let (message_tx, message_rx) = async_channel::unbounded(); + let (diagnostics_tx, diagnostics_rx) = async_channel::unbounded(); server .on_notification::(move |params, _| { message_tx.try_send(params).unwrap() diff --git a/crates/migrator/Cargo.toml b/crates/migrator/Cargo.toml index cb97f7fb6c..162473060a 100644 --- a/crates/migrator/Cargo.toml +++ b/crates/migrator/Cargo.toml @@ -19,7 +19,7 @@ convert_case.workspace = true log.workspace = true streaming-iterator.workspace = true tree-sitter-json.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } serde_json_lenient.workspace = true serde_json.workspace = true settings_content.workspace = true diff --git a/crates/multi_buffer/Cargo.toml b/crates/multi_buffer/Cargo.toml index 5dccddaba6..fa680c4fc5 100644 --- a/crates/multi_buffer/Cargo.toml +++ b/crates/multi_buffer/Cargo.toml @@ -41,7 +41,7 @@ smallvec.workspace = true sum_tree.workspace = true text.workspace = true theme.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } ztracing.workspace = true tracing.workspace = true util.workspace = true diff --git a/crates/settings_json/Cargo.toml b/crates/settings_json/Cargo.toml index aeaf5ec3c1..f5aac0c53c 100644 --- a/crates/settings_json/Cargo.toml +++ b/crates/settings_json/Cargo.toml @@ -16,7 +16,7 @@ default = [] [dependencies] anyhow.workspace = true -tree-sitter.workspace = true +tree-sitter = { workspace = true, features = ["wasm"] } tree-sitter-json.workspace = true util.workspace = true serde.workspace = true