diff --git a/Cargo.lock b/Cargo.lock index 7c2f043193f5c432453118a7336c9bc40e9485e8..530042885e2350bdb675aee828d40ecc67501103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3612,6 +3612,26 @@ dependencies = [ "url", ] +[[package]] +name = "lsp_log" +version = "0.1.0" +dependencies = [ + "anyhow", + "collections", + "editor", + "futures 0.3.25", + "gpui", + "language", + "lsp", + "project", + "serde", + "settings", + "theme", + "unindent", + "util", + "workspace", +] + [[package]] name = "mach" version = "0.3.2" @@ -7239,7 +7259,7 @@ dependencies = [ [[package]] name = "tree-sitter-json" version = "0.20.0" -source = "git+https://github.com/tree-sitter/tree-sitter-json?rev=137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8#137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8" +source = "git+https://github.com/tree-sitter/tree-sitter-json?rev=40a81c01a40ac48744e0c8ccabbaba1920441199#40a81c01a40ac48744e0c8ccabbaba1920441199" dependencies = [ "cc", "tree-sitter", @@ -8571,6 +8591,7 @@ dependencies = [ "libc", "log", "lsp", + "lsp_log", "node_runtime", "num_cpus", "outline", diff --git a/Cargo.toml b/Cargo.toml index 127557166848e36cabd50dbe911367083e839fd4..94a0b0d402dcc3cd20320efd28f376c4242d4ddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "crates/live_kit_client", "crates/live_kit_server", "crates/lsp", + "crates/lsp_log", "crates/media", "crates/menu", "crates/node_runtime", @@ -77,6 +78,7 @@ serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } rand = { version = "0.8" } postage = { version = "0.5", features = ["futures-traits"] } smallvec = { version = "1.6", features = ["union"] } +futures = { version = "0.3" } [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14" } diff --git a/crates/activity_indicator/Cargo.toml b/crates/activity_indicator/Cargo.toml index b54271ee2df279f4686375a58e0649b5ddac51e7..a11174357b7612b8b7455217cbc1fd7fb1f4b2f7 100644 --- a/crates/activity_indicator/Cargo.toml +++ b/crates/activity_indicator/Cargo.toml @@ -17,5 +17,5 @@ project = { path = "../project" } settings = { path = "../settings" } util = { path = "../util" } workspace = { path = "../workspace" } -futures = "0.3" +futures = { workspace = true } smallvec = { workspace = true } diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml index 4e738c06510e4a6662969f8d14c32cabef646138..c1d8baa118b0946410d57e58c8ea23ce17c7b5cb 100644 --- a/crates/call/Cargo.toml +++ b/crates/call/Cargo.toml @@ -33,7 +33,7 @@ util = { path = "../util" } anyhow = "1.0.38" async-broadcast = "0.4" -futures = "0.3" +futures = { workspace = true } postage = { workspace = true } [dev-dependencies] diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 560a754bf7ead2d9bd7babf309f084325a0f2916..a3b75e3abadcbce4798e077b825a2166099e17ec 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -22,7 +22,7 @@ sum_tree = { path = "../sum_tree" } anyhow = "1.0.38" async-recursion = "0.3" async-tungstenite = { version = "0.16", features = ["async-tls"] } -futures = "0.3" +futures = { workspace = true } image = "0.23" lazy_static = "1.4.0" log = { version = "0.4.16", features = ["kv_unstable_serde"] } diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 778ae42ec41f3d7a9a59171cd96bd83180d1f09f..4a86a33d71bb7ad664795ce668cdb7fc49d7eac3 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -27,7 +27,7 @@ base64 = "0.13" clap = { version = "3.1", features = ["derive"], optional = true } dashmap = "5.4" envy = "0.4.2" -futures = "0.3" +futures = { workspace = true } hyper = "0.14" lazy_static = "1.4" lipsum = { version = "0.8", optional = true } diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 50f81c335ce670a44d90c11c99a0d7e0f0be13a4..994e61a3867710be9d6f53871bb1c3760e87b7e0 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -40,7 +40,7 @@ theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } anyhow = "1.0" -futures = "0.3" +futures = { workspace = true } log = "0.4" postage = { workspace = true } serde = { workspace = true } diff --git a/crates/copilot/Cargo.toml b/crates/copilot/Cargo.toml index f4d75cc582d9d26957e697b744f5f43054b7d149..a837df97ddd6328033600814482c235a179d4dd2 100644 --- a/crates/copilot/Cargo.toml +++ b/crates/copilot/Cargo.toml @@ -35,7 +35,7 @@ log = "0.4" serde = { workspace = true } serde_derive = { workspace = true } smol = "1.2.5" -futures = "0.3" +futures = { workspace = true } [dev-dependencies] clock = { path = "../clock" } diff --git a/crates/copilot_button/Cargo.toml b/crates/copilot_button/Cargo.toml index f44493b32394c8c64d533d451f1e201290f75716..67d44f8bd42308556d154aa3b09e9b64102a8a8a 100644 --- a/crates/copilot_button/Cargo.toml +++ b/crates/copilot_button/Cargo.toml @@ -19,4 +19,4 @@ util = { path = "../util" } workspace = { path = "../workspace" } anyhow = "1.0" smol = "1.2.5" -futures = "0.3" +futures = { workspace = true } diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 4cd81df1dc6c7558579ceb7f5f28779033b0ab1f..47c5aaa715adcf5ef8464201e0812f551675b51c 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -47,7 +47,7 @@ workspace = { path = "../workspace" } aho-corasick = "0.7" anyhow = "1.0" -futures = "0.3" +futures = { workspace = true } indoc = "1.0.4" itertools = "0.10" lazy_static = "1.4" diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 54ba2c1a9cef0c9d4308c9e1d5b1ce741b2b7172..563c0aa132079d2d1f90510a728998abae73f9ba 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -511,6 +511,7 @@ pub struct Editor { workspace_id: Option, keymap_context_layers: BTreeMap, input_enabled: bool, + read_only: bool, leader_replica_id: Option, remote_id: Option, hover_state: HoverState, @@ -1283,6 +1284,7 @@ impl Editor { workspace_id: None, keymap_context_layers: Default::default(), input_enabled: true, + read_only: false, leader_replica_id: None, remote_id: None, hover_state: Default::default(), @@ -1425,6 +1427,10 @@ impl Editor { self.input_enabled = input_enabled; } + pub fn set_read_only(&mut self, read_only: bool) { + self.read_only = read_only; + } + fn selections_did_change( &mut self, local: bool, @@ -1533,6 +1539,10 @@ impl Editor { S: ToOffset, T: Into>, { + if self.read_only { + return; + } + self.buffer .update(cx, |buffer, cx| buffer.edit(edits, None, cx)); } @@ -1543,6 +1553,10 @@ impl Editor { S: ToOffset, T: Into>, { + if self.read_only { + return; + } + self.buffer.update(cx, |buffer, cx| { buffer.edit(edits, Some(AutoindentMode::EachLine), cx) }); @@ -1897,6 +1911,9 @@ impl Editor { pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext) { let text: Arc = text.into(); + if self.read_only { + return; + } if !self.input_enabled { cx.emit(Event::InputIgnored { text }); return; @@ -2282,6 +2299,10 @@ impl Editor { autoindent_mode: Option, cx: &mut ViewContext, ) { + if self.read_only { + return; + } + let text: Arc = text.into(); self.transact(cx, |this, cx| { let old_selections = this.selections.all_adjusted(cx); diff --git a/crates/feedback/Cargo.toml b/crates/feedback/Cargo.toml index 57b91876e388f9e2c60101c514daa7a661651b42..1acc14892c2ed5b4d0a438bb5e9385dbedf0fa3a 100644 --- a/crates/feedback/Cargo.toml +++ b/crates/feedback/Cargo.toml @@ -16,7 +16,7 @@ client = { path = "../client" } editor = { path = "../editor" } language = { path = "../language" } log = "0.4" -futures = "0.3" +futures = { workspace = true } gpui = { path = "../gpui" } human_bytes = "0.4.1" isahc = "1.7" diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index f4981ac13a9744e82b08eff456e947e59db6ddc8..5eab919b7cda7805f687a9a8cd63350cfcb47955 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -15,7 +15,7 @@ rope = { path = "../rope" } util = { path = "../util" } anyhow = "1.0.57" async-trait = "0.1" -futures = "0.3" +futures = { workspace = true } tempfile = "3" fsevent = { path = "../fsevent" } lazy_static = "1.4.0" diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 3e88d723139447e393abb4b4fe61278309afed5f..b1fa0eda785c9361f1d0e7b5be20454e6dd83255 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -19,7 +19,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] } smol = "1.2" parking_lot = "0.11.1" async-trait = "0.1" -futures = "0.3" +futures = { workspace = true } git2 = { version = "0.15", default-features = false } [dev-dependencies] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 88c40665c20ed23aafffe5e92e297db0403f9f64..858f67cf5fd9a055a091de6c782e045ee56d6d37 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -25,7 +25,7 @@ ctor = "0.1" dhat = { version = "0.3", optional = true } env_logger = { version = "0.9", optional = true } etagere = "0.2" -futures = "0.3" +futures = { workspace = true } image = "0.23" itertools = "0.10" lazy_static = "1.4.0" diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 4e15ccf679db6d6de21a72f9094ee7c69508d447..d09b346348ff63aa7911938ded324a6aa88f7b8c 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -39,7 +39,7 @@ util = { path = "../util" } anyhow = "1.0.38" async-broadcast = "0.4" async-trait = "0.1" -futures = "0.3" +futures = { workspace = true } lazy_static = "1.4" log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index 70032d83aaa3bb930c5b93336b9fcf8d6d3fec2c..95057b3d0ef54b88bce881e2812f798abcffed7e 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -32,7 +32,7 @@ anyhow = "1.0.38" async-broadcast = "0.4" core-foundation = "0.9.3" core-graphics = "0.22.3" -futures = "0.3" +futures = { workspace = true } log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" postage = { workspace = true } @@ -56,7 +56,7 @@ cocoa = "0.24" core-foundation = "0.9.3" core-graphics = "0.22.3" foreign-types = "0.3" -futures = "0.3" +futures = { workspace = true } hmac = "0.12" jwt = "0.16" lazy_static = "1.4" diff --git a/crates/live_kit_server/Cargo.toml b/crates/live_kit_server/Cargo.toml index 8cced6d08951bdecabb22c8921c30262acf97ad4..cfb3a9c885dd6154774a7cd4ec26fa0f7b21b39d 100644 --- a/crates/live_kit_server/Cargo.toml +++ b/crates/live_kit_server/Cargo.toml @@ -12,7 +12,7 @@ doctest = false [dependencies] anyhow = "1.0.38" async-trait = "0.1" -futures = "0.3" +futures = { workspace = true } hmac = "0.12" log = "0.4" jwt = "0.16" diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index 4370aaab06870ed8f232271a51eced39562164ad..86b4ed9e409abec79d00cdfce92003cd7c7c3887 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -17,7 +17,7 @@ gpui = { path = "../gpui" } util = { path = "../util" } anyhow = "1.0" async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553", optional = true } -futures = "0.3" +futures = { workspace = true } log = { version = "0.4.16", features = ["kv_unstable_serde"] } lsp-types = "0.91" parking_lot = "0.11" diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index b6a4d8513eea2655b0803cd7bd911926a1248f20..f4f538b061c892bb07cedc10304233936ee9c079 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -20,10 +20,10 @@ use std::{ future::Future, io::Write, path::PathBuf, - str::FromStr, + str::{self, FromStr as _}, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, - Arc, + Arc, Weak, }, }; use std::{path::Path, process::Stdio}; @@ -34,16 +34,18 @@ const CONTENT_LEN_HEADER: &str = "Content-Length: "; type NotificationHandler = Box, &str, AsyncAppContext)>; type ResponseHandler = Box)>; +type IoHandler = Box; pub struct LanguageServer { server_id: LanguageServerId, next_id: AtomicUsize, - outbound_tx: channel::Sender>, + outbound_tx: channel::Sender, name: String, capabilities: ServerCapabilities, code_action_kinds: Option>, notification_handlers: Arc>>, response_handlers: Arc>>>, + io_handlers: Arc>>, executor: Arc, #[allow(clippy::type_complexity)] io_tasks: Mutex>, Task>)>>, @@ -56,9 +58,15 @@ pub struct LanguageServer { #[repr(transparent)] pub struct LanguageServerId(pub usize); -pub struct Subscription { - method: &'static str, - notification_handlers: Arc>>, +pub enum Subscription { + Notification { + method: &'static str, + notification_handlers: Option>>>, + }, + Io { + id: usize, + io_handlers: Option>>>, + }, } #[derive(Serialize, Deserialize)] @@ -177,33 +185,40 @@ impl LanguageServer { Stdout: AsyncRead + Unpin + Send + 'static, F: FnMut(AnyNotification) + 'static + Send, { - let (outbound_tx, outbound_rx) = channel::unbounded::>(); + let (outbound_tx, outbound_rx) = channel::unbounded::(); + let (output_done_tx, output_done_rx) = barrier::channel(); let notification_handlers = Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default())); let response_handlers = Arc::new(Mutex::new(Some(HashMap::<_, ResponseHandler>::default()))); + let io_handlers = Arc::new(Mutex::new(HashMap::default())); let input_task = cx.spawn(|cx| { - let notification_handlers = notification_handlers.clone(); - let response_handlers = response_handlers.clone(); Self::handle_input( stdout, on_unhandled_notification, - notification_handlers, - response_handlers, + notification_handlers.clone(), + response_handlers.clone(), + io_handlers.clone(), cx, ) .log_err() }); - let (output_done_tx, output_done_rx) = barrier::channel(); let output_task = cx.background().spawn({ - let response_handlers = response_handlers.clone(); - Self::handle_output(stdin, outbound_rx, output_done_tx, response_handlers).log_err() + Self::handle_output( + stdin, + outbound_rx, + output_done_tx, + response_handlers.clone(), + io_handlers.clone(), + ) + .log_err() }); Self { server_id, notification_handlers, response_handlers, + io_handlers, name: Default::default(), capabilities: Default::default(), code_action_kinds, @@ -226,6 +241,7 @@ impl LanguageServer { mut on_unhandled_notification: F, notification_handlers: Arc>>, response_handlers: Arc>>>, + io_handlers: Arc>>, cx: AsyncAppContext, ) -> anyhow::Result<()> where @@ -252,7 +268,13 @@ impl LanguageServer { buffer.resize(message_len, 0); stdout.read_exact(&mut buffer).await?; - log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer)); + + if let Ok(message) = str::from_utf8(&buffer) { + log::trace!("incoming message:{}", message); + for handler in io_handlers.lock().values_mut() { + handler(true, message); + } + } if let Ok(msg) = serde_json::from_slice::(&buffer) { if let Some(handler) = notification_handlers.lock().get_mut(msg.method) { @@ -291,9 +313,10 @@ impl LanguageServer { async fn handle_output( stdin: Stdin, - outbound_rx: channel::Receiver>, + outbound_rx: channel::Receiver, output_done_tx: barrier::Sender, response_handlers: Arc>>>, + io_handlers: Arc>>, ) -> anyhow::Result<()> where Stdin: AsyncWrite + Unpin + Send + 'static, @@ -307,13 +330,17 @@ impl LanguageServer { }); let mut content_len_buffer = Vec::new(); while let Ok(message) = outbound_rx.recv().await { - log::trace!("outgoing message:{}", String::from_utf8_lossy(&message)); + log::trace!("outgoing message:{}", message); + for handler in io_handlers.lock().values_mut() { + handler(false, &message); + } + content_len_buffer.clear(); write!(content_len_buffer, "{}", message.len()).unwrap(); stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?; stdin.write_all(&content_len_buffer).await?; stdin.write_all("\r\n\r\n".as_bytes()).await?; - stdin.write_all(&message).await?; + stdin.write_all(message.as_bytes()).await?; stdin.flush().await?; } drop(output_done_tx); @@ -464,6 +491,19 @@ impl LanguageServer { self.on_custom_request(T::METHOD, f) } + #[must_use] + pub fn on_io(&self, f: F) -> Subscription + where + F: 'static + Send + FnMut(bool, &str), + { + let id = self.next_id.fetch_add(1, SeqCst); + self.io_handlers.lock().insert(id, Box::new(f)); + Subscription::Io { + id, + io_handlers: Some(Arc::downgrade(&self.io_handlers)), + } + } + pub fn remove_request_handler(&self) { self.notification_handlers.lock().remove(T::METHOD); } @@ -490,9 +530,9 @@ impl LanguageServer { prev_handler.is_none(), "registered multiple handlers for the same LSP method" ); - Subscription { + Subscription::Notification { method, - notification_handlers: self.notification_handlers.clone(), + notification_handlers: Some(self.notification_handlers.clone()), } } @@ -537,7 +577,7 @@ impl LanguageServer { }, }; if let Some(response) = - serde_json::to_vec(&response).log_err() + serde_json::to_string(&response).log_err() { outbound_tx.try_send(response).ok(); } @@ -560,7 +600,7 @@ impl LanguageServer { message: error.to_string(), }), }; - if let Some(response) = serde_json::to_vec(&response).log_err() { + if let Some(response) = serde_json::to_string(&response).log_err() { outbound_tx.try_send(response).ok(); } } @@ -572,9 +612,9 @@ impl LanguageServer { prev_handler.is_none(), "registered multiple handlers for the same LSP method" ); - Subscription { + Subscription::Notification { method, - notification_handlers: self.notification_handlers.clone(), + notification_handlers: Some(self.notification_handlers.clone()), } } @@ -612,14 +652,14 @@ impl LanguageServer { fn request_internal( next_id: &AtomicUsize, response_handlers: &Mutex>>, - outbound_tx: &channel::Sender>, + outbound_tx: &channel::Sender, params: T::Params, ) -> impl 'static + Future> where T::Result: 'static + Send, { let id = next_id.fetch_add(1, SeqCst); - let message = serde_json::to_vec(&Request { + let message = serde_json::to_string(&Request { jsonrpc: JSON_RPC_VERSION, id, method: T::METHOD, @@ -662,10 +702,10 @@ impl LanguageServer { } fn notify_internal( - outbound_tx: &channel::Sender>, + outbound_tx: &channel::Sender, params: T::Params, ) -> Result<()> { - let message = serde_json::to_vec(&Notification { + let message = serde_json::to_string(&Notification { jsonrpc: JSON_RPC_VERSION, method: T::METHOD, params, @@ -685,8 +725,14 @@ impl Drop for LanguageServer { } impl Subscription { - pub fn detach(mut self) { - self.method = ""; + pub fn detach(&mut self) { + match self { + Subscription::Notification { + notification_handlers, + .. + } => *notification_handlers = None, + Subscription::Io { io_handlers, .. } => *io_handlers = None, + } } } @@ -698,7 +744,21 @@ impl fmt::Display for LanguageServerId { impl Drop for Subscription { fn drop(&mut self) { - self.notification_handlers.lock().remove(self.method); + match self { + Subscription::Notification { + method, + notification_handlers, + } => { + if let Some(handlers) = notification_handlers { + handlers.lock().remove(method); + } + } + Subscription::Io { id, io_handlers } => { + if let Some(io_handlers) = io_handlers.as_ref().and_then(|h| h.upgrade()) { + io_handlers.lock().remove(id); + } + } + } } } diff --git a/crates/lsp_log/Cargo.toml b/crates/lsp_log/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..4eeebef9a46001f24e96237a1e11e275c16e87dd --- /dev/null +++ b/crates/lsp_log/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "lsp_log" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/lsp_log.rs" +doctest = false + +[dependencies] +collections = { path = "../collections" } +editor = { path = "../editor" } +settings = { path = "../settings" } +theme = { path = "../theme" } +language = { path = "../language" } +project = { path = "../project" } +workspace = { path = "../workspace" } +gpui = { path = "../gpui" } +util = { path = "../util" } +lsp = { path = "../lsp" } +futures = { workspace = true } +serde = { workspace = true } +anyhow = "1.0" + +[dev-dependencies] +gpui = { path = "../gpui", features = ["test-support"] } +util = { path = "../util", features = ["test-support"] } +unindent = "0.1.7" diff --git a/crates/lsp_log/src/lsp_log.rs b/crates/lsp_log/src/lsp_log.rs new file mode 100644 index 0000000000000000000000000000000000000000..0efcd3afc83d773c15bdb484c6d0f50b7876f106 --- /dev/null +++ b/crates/lsp_log/src/lsp_log.rs @@ -0,0 +1,523 @@ +use collections::{hash_map, HashMap}; +use editor::Editor; +use futures::{channel::mpsc, StreamExt}; +use gpui::{ + actions, + elements::{ + AnchorCorner, ChildView, Empty, Flex, Label, MouseEventHandler, Overlay, OverlayFitMode, + ParentElement, Stack, + }, + platform::{CursorStyle, MouseButton}, + AnyElement, AppContext, Element, Entity, ModelContext, ModelHandle, View, ViewContext, + ViewHandle, WeakModelHandle, +}; +use language::{Buffer, LanguageServerId, LanguageServerName}; +use project::{Project, WorktreeId}; +use settings::Settings; +use std::{borrow::Cow, sync::Arc}; +use theme::{ui, Theme}; +use workspace::{ + item::{Item, ItemHandle}, + ToolbarItemLocation, ToolbarItemView, Workspace, +}; + +const SEND_LINE: &str = "// Send:\n"; +const RECEIVE_LINE: &str = "// Receive:\n"; + +struct LogStore { + projects: HashMap, LogStoreProject>, + io_tx: mpsc::UnboundedSender<(WeakModelHandle, LanguageServerId, bool, String)>, +} + +struct LogStoreProject { + servers: HashMap, + _subscription: gpui::Subscription, +} + +struct LogStoreLanguageServer { + buffer: ModelHandle, + last_message_kind: Option, + _subscription: lsp::Subscription, +} + +pub struct LspLogView { + log_store: ModelHandle, + current_server_id: Option, + editor: Option>, + project: ModelHandle, +} + +pub struct LspLogToolbarItemView { + log_view: Option>, + menu_open: bool, + project: ModelHandle, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +enum MessageKind { + Send, + Receive, +} + +actions!(log, [OpenLanguageServerLogs]); + +pub fn init(cx: &mut AppContext) { + let log_set = cx.add_model(|cx| LogStore::new(cx)); + + cx.add_action( + move |workspace: &mut Workspace, _: &OpenLanguageServerLogs, cx: _| { + let project = workspace.project().read(cx); + if project.is_local() { + workspace.add_item( + Box::new(cx.add_view(|cx| { + LspLogView::new(workspace.project().clone(), log_set.clone(), cx) + })), + cx, + ); + } + }, + ); +} + +impl LogStore { + fn new(cx: &mut ModelContext) -> Self { + let (io_tx, mut io_rx) = mpsc::unbounded(); + let this = Self { + projects: HashMap::default(), + io_tx, + }; + cx.spawn_weak(|this, mut cx| async move { + while let Some((project, server_id, is_output, mut message)) = io_rx.next().await { + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + message.push('\n'); + this.on_io(project, server_id, is_output, &message, cx); + }); + } + } + anyhow::Ok(()) + }) + .detach(); + this + } + + pub fn has_enabled_logs_for_language_server( + &self, + project: &ModelHandle, + server_id: LanguageServerId, + ) -> bool { + self.projects + .get(&project.downgrade()) + .map_or(false, |store| store.servers.contains_key(&server_id)) + } + + pub fn enable_logs_for_language_server( + &mut self, + project: &ModelHandle, + server_id: LanguageServerId, + cx: &mut ModelContext, + ) -> Option> { + let server = project.read(cx).language_server_for_id(server_id)?; + let weak_project = project.downgrade(); + let project_logs = match self.projects.entry(weak_project) { + hash_map::Entry::Occupied(entry) => entry.into_mut(), + hash_map::Entry::Vacant(entry) => entry.insert(LogStoreProject { + servers: HashMap::default(), + _subscription: cx.observe_release(&project, move |this, _, _| { + this.projects.remove(&weak_project); + }), + }), + }; + let server_log_state = project_logs.servers.entry(server_id).or_insert_with(|| { + let io_tx = self.io_tx.clone(); + let language = project.read(cx).languages().language_for_name("JSON"); + let buffer = cx.add_model(|cx| Buffer::new(0, "", cx)); + cx.spawn_weak({ + let buffer = buffer.clone(); + |_, mut cx| async move { + let language = language.await.ok(); + buffer.update(&mut cx, |buffer, cx| { + buffer.set_language(language, cx); + }); + } + }) + .detach(); + + let project = project.downgrade(); + LogStoreLanguageServer { + buffer, + last_message_kind: None, + _subscription: server.on_io(move |is_received, json| { + io_tx + .unbounded_send((project, server_id, is_received, json.to_string())) + .ok(); + }), + } + }); + Some(server_log_state.buffer.clone()) + } + + pub fn disable_logs_for_language_server( + &mut self, + project: &ModelHandle, + server_id: LanguageServerId, + _: &mut ModelContext, + ) { + let project = project.downgrade(); + if let Some(store) = self.projects.get_mut(&project) { + store.servers.remove(&server_id); + if store.servers.is_empty() { + self.projects.remove(&project); + } + } + } + + fn on_io( + &mut self, + project: WeakModelHandle, + language_server_id: LanguageServerId, + is_received: bool, + message: &str, + cx: &mut AppContext, + ) -> Option<()> { + let state = self + .projects + .get_mut(&project)? + .servers + .get_mut(&language_server_id)?; + state.buffer.update(cx, |buffer, cx| { + let kind = if is_received { + MessageKind::Receive + } else { + MessageKind::Send + }; + if state.last_message_kind != Some(kind) { + let len = buffer.len(); + let line = match kind { + MessageKind::Send => SEND_LINE, + MessageKind::Receive => RECEIVE_LINE, + }; + buffer.edit([(len..len, line)], None, cx); + state.last_message_kind = Some(kind); + } + let len = buffer.len(); + buffer.edit([(len..len, message)], None, cx); + }); + Some(()) + } +} + +impl LspLogView { + fn new( + project: ModelHandle, + log_set: ModelHandle, + _: &mut ViewContext, + ) -> Self { + Self { + project, + log_store: log_set, + editor: None, + current_server_id: None, + } + } + + fn show_logs_for_server(&mut self, server_id: LanguageServerId, cx: &mut ViewContext) { + let buffer = self.log_store.update(cx, |log_set, cx| { + log_set.enable_logs_for_language_server(&self.project, server_id, cx) + }); + if let Some(buffer) = buffer { + self.current_server_id = Some(server_id); + self.editor = Some(cx.add_view(|cx| { + let mut editor = Editor::for_buffer(buffer, Some(self.project.clone()), cx); + editor.set_read_only(true); + editor.move_to_end(&Default::default(), cx); + editor + })); + cx.notify(); + } + } + + fn toggle_logging_for_server( + &mut self, + server_id: LanguageServerId, + enabled: bool, + cx: &mut ViewContext, + ) { + self.log_store.update(cx, |log_store, cx| { + if enabled { + log_store.enable_logs_for_language_server(&self.project, server_id, cx); + } else { + log_store.disable_logs_for_language_server(&self.project, server_id, cx); + } + }); + } +} + +impl View for LspLogView { + fn ui_name() -> &'static str { + "LspLogView" + } + + fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + if let Some(editor) = &self.editor { + ChildView::new(&editor, cx).into_any() + } else { + Empty::new().into_any() + } + } +} + +impl Item for LspLogView { + fn tab_content( + &self, + _: Option, + style: &theme::Tab, + _: &AppContext, + ) -> AnyElement { + Label::new("LSP Logs", style.label.clone()).into_any() + } +} + +impl ToolbarItemView for LspLogToolbarItemView { + fn set_active_pane_item( + &mut self, + active_pane_item: Option<&dyn ItemHandle>, + _: &mut ViewContext, + ) -> workspace::ToolbarItemLocation { + self.menu_open = false; + if let Some(item) = active_pane_item { + if let Some(log_view) = item.downcast::() { + self.log_view = Some(log_view.clone()); + return ToolbarItemLocation::PrimaryLeft { + flex: Some((1., false)), + }; + } + } + self.log_view = None; + ToolbarItemLocation::Hidden + } +} + +impl View for LspLogToolbarItemView { + fn ui_name() -> &'static str { + "LspLogView" + } + + fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + let theme = cx.global::().theme.clone(); + let Some(log_view) = self.log_view.as_ref() else { return Empty::new().into_any() }; + let project = self.project.read(cx); + let log_view = log_view.read(cx); + let log_store = log_view.log_store.read(cx); + + let mut language_servers = project + .language_servers() + .map(|(id, name, worktree)| { + ( + id, + name, + worktree, + log_store.has_enabled_logs_for_language_server(&self.project, id), + ) + }) + .collect::>(); + language_servers.sort_by_key(|a| (a.0, a.2)); + language_servers.dedup_by_key(|a| a.0); + + let current_server_id = log_view.current_server_id; + let current_server = current_server_id.and_then(|current_server_id| { + if let Ok(ix) = language_servers.binary_search_by_key(¤t_server_id, |e| e.0) { + Some(language_servers[ix].clone()) + } else { + None + } + }); + + enum Menu {} + + Stack::new() + .with_child(Self::render_language_server_menu_header( + current_server, + &self.project, + &theme, + cx, + )) + .with_children(if self.menu_open { + Some( + Overlay::new( + MouseEventHandler::::new(0, cx, move |_, cx| { + Flex::column() + .with_children(language_servers.into_iter().filter_map( + |(id, name, worktree_id, logging_enabled)| { + Self::render_language_server_menu_item( + id, + name, + worktree_id, + logging_enabled, + Some(id) == current_server_id, + &self.project, + &theme, + cx, + ) + }, + )) + .contained() + .with_style(theme.context_menu.container) + .constrained() + .with_width(400.) + .with_height(400.) + }) + .on_down_out(MouseButton::Left, |_, this, cx| { + this.menu_open = false; + cx.notify() + }), + ) + .with_fit_mode(OverlayFitMode::SwitchAnchor) + .with_anchor_corner(AnchorCorner::TopLeft) + .with_z_index(999) + .aligned() + .bottom() + .left(), + ) + } else { + None + }) + .aligned() + .left() + .clipped() + .into_any() + } +} + +impl LspLogToolbarItemView { + pub fn new(project: ModelHandle) -> Self { + Self { + menu_open: false, + log_view: None, + project, + } + } + + fn toggle_menu(&mut self, cx: &mut ViewContext) { + self.menu_open = !self.menu_open; + cx.notify(); + } + + fn toggle_logging_for_server( + &mut self, + id: LanguageServerId, + enabled: bool, + cx: &mut ViewContext, + ) { + if let Some(log_view) = &self.log_view { + log_view.update(cx, |log_view, cx| { + log_view.toggle_logging_for_server(id, enabled, cx); + if !enabled && Some(id) == log_view.current_server_id { + log_view.current_server_id = None; + log_view.editor = None; + cx.notify(); + } + }); + } + cx.notify(); + } + + fn show_logs_for_server(&mut self, id: LanguageServerId, cx: &mut ViewContext) { + if let Some(log_view) = &self.log_view { + log_view.update(cx, |log_view, cx| { + log_view.show_logs_for_server(id, cx); + }); + self.menu_open = false; + } + cx.notify(); + } + + fn render_language_server_menu_header( + current_server: Option<(LanguageServerId, LanguageServerName, WorktreeId, bool)>, + project: &ModelHandle, + theme: &Arc, + cx: &mut ViewContext, + ) -> impl Element { + enum ToggleMenu {} + MouseEventHandler::::new(0, cx, move |state, cx| { + let project = project.read(cx); + let label: Cow = current_server + .and_then(|(_, server_name, worktree_id, _)| { + let worktree = project.worktree_for_id(worktree_id, cx)?; + let worktree = &worktree.read(cx); + Some(format!("{} - ({})", server_name.0, worktree.root_name()).into()) + }) + .unwrap_or_else(|| "No server selected".into()); + Label::new( + label, + theme + .context_menu + .item + .style_for(state, false) + .label + .clone(), + ) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, view, cx| { + view.toggle_menu(cx); + }) + } + + fn render_language_server_menu_item( + id: LanguageServerId, + name: LanguageServerName, + worktree_id: WorktreeId, + logging_enabled: bool, + is_selected: bool, + project: &ModelHandle, + theme: &Arc, + cx: &mut ViewContext, + ) -> Option> { + enum ActivateLog {} + let project = project.read(cx); + let worktree = project.worktree_for_id(worktree_id, cx)?; + let worktree = &worktree.read(cx); + if !worktree.is_visible() { + return None; + } + let label = format!("{} - ({})", name.0, worktree.root_name()); + + Some( + MouseEventHandler::::new(id.0, cx, move |state, cx| { + let item_style = theme.context_menu.item.style_for(state, is_selected); + Flex::row() + .with_child(ui::checkbox_with_label::( + Empty::new(), + &theme.welcome.checkbox, + logging_enabled, + id.0, + cx, + move |this, enabled, cx| { + this.toggle_logging_for_server(id, enabled, cx); + }, + )) + .with_child(Label::new(label, item_style.label.clone()).aligned().left()) + .align_children_center() + .contained() + .with_style(item_style.container) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, view, cx| { + view.show_logs_for_server(id, cx); + }), + ) + } +} + +impl Entity for LogStore { + type Event = (); +} + +impl Entity for LspLogView { + type Event = (); +} + +impl Entity for LspLogToolbarItemView { + type Event = (); +} diff --git a/crates/node_runtime/Cargo.toml b/crates/node_runtime/Cargo.toml index 32ab6abbb3c1b022495d286859b4826881de1f19..eb43be61cef588f211ecf0cccff786c2002e8497 100644 --- a/crates/node_runtime/Cargo.toml +++ b/crates/node_runtime/Cargo.toml @@ -13,7 +13,7 @@ gpui = { path = "../gpui" } util = { path = "../util" } async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] } async-tar = "0.4.2" -futures = "0.3" +futures = { workspace = true } anyhow = "1.0.38" parking_lot = "0.11.1" serde = { workspace = true } diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index e30ab56e45d1b088aa5ce811dfe246094c00c413..4ce5f24e9d3bd7bee19ff4a3123d3644368a6f38 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -41,7 +41,7 @@ aho-corasick = "0.7" anyhow = "1.0.57" async-trait = "0.1" backtrace = "0.3" -futures = "0.3" +futures = { workspace = true } ignore = "0.4" lazy_static = "1.4.0" log = { version = "0.4.16", features = ["kv_unstable_serde"] } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index afd0b3bbae9d68a8b35952a8826230abf99739ae..aad9a5085609a23882d8d73693aa2219ba549532 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -185,6 +185,8 @@ pub struct Collaborator { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { + LanguageServerAdded(LanguageServerId), + LanguageServerRemoved(LanguageServerId), ActiveEntryChanged(Option), WorktreeAdded, WorktreeRemoved(WorktreeId), @@ -1869,7 +1871,7 @@ impl Project { let next_snapshot = buffer.text_snapshot(); let language_servers: Vec<_> = self - .language_servers_iter_for_buffer(buffer, cx) + .language_servers_for_buffer(buffer, cx) .map(|i| i.1.clone()) .collect(); @@ -6279,7 +6281,25 @@ impl Project { } } - pub fn language_servers_iter_for_buffer( + pub fn language_servers( + &self, + ) -> impl '_ + Iterator { + self.language_server_ids + .iter() + .map(|((worktree_id, server_name), server_id)| { + (*server_id, server_name.clone(), *worktree_id) + }) + } + + pub fn language_server_for_id(&self, id: LanguageServerId) -> Option> { + if let LanguageServerState::Running { server, .. } = self.language_servers.get(&id)? { + Some(server.clone()) + } else { + None + } + } + + pub fn language_servers_for_buffer( &self, buffer: &Buffer, cx: &AppContext, @@ -6299,20 +6319,12 @@ impl Project { }) } - fn language_servers_for_buffer( - &self, - buffer: &Buffer, - cx: &AppContext, - ) -> Vec<(&Arc, &Arc)> { - self.language_servers_iter_for_buffer(buffer, cx).collect() - } - fn primary_language_servers_for_buffer( &self, buffer: &Buffer, cx: &AppContext, ) -> Option<(&Arc, &Arc)> { - self.language_servers_iter_for_buffer(buffer, cx).next() + self.language_servers_for_buffer(buffer, cx).next() } fn language_server_for_buffer( @@ -6321,7 +6333,7 @@ impl Project { server_id: LanguageServerId, cx: &AppContext, ) -> Option<(&Arc, &Arc)> { - self.language_servers_iter_for_buffer(buffer, cx) + self.language_servers_for_buffer(buffer, cx) .find(|(_, s)| s.server_id() == server_id) } diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index 2b72959e25e76bfe35ea8c8ddbd921db123fc06e..195bcee10a615651428db9daa090bc0b1637897e 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -20,7 +20,7 @@ theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } postage = { workspace = true } -futures = "0.3" +futures = { workspace = true } unicase = "2.6" [dev-dependencies] diff --git a/crates/project_symbols/Cargo.toml b/crates/project_symbols/Cargo.toml index 9e79b09d72ec7c398dd7cd29965a6eec797520a1..b03e62cc55d347f5a4c9be1634a6478ae37ea7d9 100644 --- a/crates/project_symbols/Cargo.toml +++ b/crates/project_symbols/Cargo.toml @@ -24,7 +24,7 @@ postage = { workspace = true } smol = "1.2" [dev-dependencies] -futures = "0.3" +futures = { workspace = true } settings = { path = "../settings", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 2773dd2f3b60ba27e59fa85d69b61dc684c60fc8..78f37fd5aee2615f0562e6181a477bd1af7567f8 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -21,7 +21,7 @@ anyhow = "1.0" async-lock = "2.4" async-tungstenite = "0.16" base64 = "0.13" -futures = "0.3" +futures = { workspace = true } parking_lot = "0.11.1" prost = "0.8" rand = "0.8" diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index fb4e79a7033ca65f7e58c5ee15d1b7ccef766629..fc8737e73fa153471d656842ec5c91057a53feee 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -20,7 +20,7 @@ theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } anyhow = "1.0" -futures = "0.3" +futures = { workspace = true } log = { version = "0.4.16", features = ["kv_unstable_serde"] } postage = { workspace = true } serde = { workspace = true } diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index fbb3ad63f34761a0f7004a818582569c87dbd7ad..ab5e428b7bce52d50fb07d48850ff4f34a0fbf65 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -18,7 +18,7 @@ gpui = { path = "../gpui" } sqlez = { path = "../sqlez" } fs = { path = "../fs" } anyhow = "1.0.38" -futures = "0.3" +futures = { workspace = true } theme = { path = "../theme" } staff_mode = { path = "../staff_mode" } util = { path = "../util" } diff --git a/crates/sqlez/Cargo.toml b/crates/sqlez/Cargo.toml index 716ec766443bc997c57fb77f78d94f0290aad579..7e82a50247e36367f536476a28b9dd7c79d66ed9 100644 --- a/crates/sqlez/Cargo.toml +++ b/crates/sqlez/Cargo.toml @@ -14,5 +14,5 @@ smol = "1.2" thread_local = "1.1.4" lazy_static = "1.4" parking_lot = "0.11.1" -futures = "0.3" -uuid = { version = "1.1.2", features = ["v4"] } \ No newline at end of file +futures = { workspace = true } +uuid = { version = "1.1.2", features = ["v4"] } diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 107d325677f99231f8100344226e89d6bebd4ee6..f46bdad5d5d2e2417885fdb86ea4b6e367a29edd 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -20,7 +20,7 @@ procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f smallvec = { workspace = true } smol = "1.2.5" mio-extras = "2.0.6" -futures = "0.3" +futures = { workspace = true } ordered-float = "2.1.1" itertools = "0.10" dirs = "4.0.0" diff --git a/crates/terminal_view/Cargo.toml b/crates/terminal_view/Cargo.toml index f271cd118d8de443af35999c916b21ed0c19a459..fe7dd833e967c068e4f319b7df8c5cf00d5596c1 100644 --- a/crates/terminal_view/Cargo.toml +++ b/crates/terminal_view/Cargo.toml @@ -24,7 +24,7 @@ terminal = { path = "../terminal" } smallvec = { workspace = true } smol = "1.2.5" mio-extras = "2.0.6" -futures = "0.3" +futures = { workspace = true } ordered-float = "2.1.1" itertools = "0.10" dirs = "4.0.0" diff --git a/crates/theme/src/ui.rs b/crates/theme/src/ui.rs index 4e705b12b8a8b94c15ea10eb371477d16f3627c4..0f58ee08ad5a78fe477f4b432ae6c65ed119a016 100644 --- a/crates/theme/src/ui.rs +++ b/crates/theme/src/ui.rs @@ -27,28 +27,40 @@ pub struct CheckboxStyle { pub hovered_and_checked: ContainerStyle, } -pub fn checkbox( +pub fn checkbox( label: &'static str, style: &CheckboxStyle, checked: bool, + id: usize, cx: &mut ViewContext, - change: fn(checked: bool, cx: &mut EventContext) -> (), -) -> MouseEventHandler { + change: F, +) -> MouseEventHandler +where + Tag: 'static, + V: View, + F: 'static + Fn(&mut V, bool, &mut EventContext), +{ let label = Label::new(label, style.label.text.clone()) .contained() .with_style(style.label.container); - - checkbox_with_label(label, style, checked, cx, change) + checkbox_with_label(label, style, checked, id, cx, change) } -pub fn checkbox_with_label, V: View>( +pub fn checkbox_with_label( label: D, style: &CheckboxStyle, checked: bool, + id: usize, cx: &mut ViewContext, - change: fn(checked: bool, cx: &mut EventContext) -> (), -) -> MouseEventHandler { - MouseEventHandler::new(0, cx, |state, _| { + change: F, +) -> MouseEventHandler +where + Tag: 'static, + D: Element, + V: View, + F: 'static + Fn(&mut V, bool, &mut EventContext), +{ + MouseEventHandler::new(id, cx, |state, _| { let indicator = if checked { svg(&style.icon) } else { @@ -75,8 +87,8 @@ pub fn checkbox_with_label, V: View>( .with_child(label) .align_children_center() }) - .on_click(platform::MouseButton::Left, move |_, _, cx| { - change(!checked, cx) + .on_click(platform::MouseButton::Left, move |_, view, cx| { + change(view, !checked, cx) }) .with_cursor_style(platform::CursorStyle::PointingHand) } diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 38124dcc1130efe4c542eea24eb4029450cf4298..faf88c5c9ddc23f94000c3df0f2e02d17c2cbc79 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -16,7 +16,7 @@ anyhow = "1.0.38" backtrace = "0.3" log = { version = "0.4.16", features = ["kv_unstable_serde"] } lazy_static = "1.4.0" -futures = "0.3" +futures = { workspace = true } isahc = "1.7" smol = "1.2.5" url = "2.2" diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index d75bbea56ee594e892c07fac9b645bfc7dd868ed..629e6f3989f28b8d36ad05a157ebcce1c9e866c5 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -126,7 +126,7 @@ impl View for WelcomePage { .with_child( Flex::column() .with_child( - theme::ui::checkbox_with_label::( + theme::ui::checkbox_with_label::( Flex::column() .with_child( Label::new( @@ -146,8 +146,9 @@ impl View for WelcomePage { ), &theme.welcome.checkbox, metrics, + 0, cx, - |checked, cx| { + |_, checked, cx| { SettingsFile::update(cx, move |file| { file.telemetry.set_metrics(checked) }) @@ -157,12 +158,13 @@ impl View for WelcomePage { .with_style(theme.welcome.checkbox_container), ) .with_child( - theme::ui::checkbox::( + theme::ui::checkbox::( "Send crash reports", &theme.welcome.checkbox, diagnostics, + 0, cx, - |checked, cx| { + |_, checked, cx| { SettingsFile::update(cx, move |file| { file.telemetry.set_diagnostics(checked) }) diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 84db04e9a67cb8d453e629cf7bd7019f8ce6d6d4..69ee515a91f9d991789260c4e13eb44cb7b90c79 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -38,7 +38,7 @@ util = { path = "../util" } async-recursion = "1.0.0" bincode = "1.2.1" anyhow = "1.0.38" -futures = "0.3" +futures = { workspace = true } lazy_static = "1.4" env_logger = "0.9.1" log = { version = "0.4.16", features = ["kv_unstable_serde"] } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 7eb35f3b3e13a6c1006b4bafb1b871310db3bf57..51a41db2b8646c32f15fddeda247f30a8f6fc1dd 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -46,6 +46,7 @@ journal = { path = "../journal" } language = { path = "../language" } language_selector = { path = "../language_selector" } lsp = { path = "../lsp" } +lsp_log = { path = "../lsp_log" } node_runtime = { path = "../node_runtime" } outline = { path = "../outline" } plugin_runtime = { path = "../plugin_runtime" } @@ -76,7 +77,7 @@ chrono = "0.4" ctor = "0.1.20" easy-parallel = "3.1.0" env_logger = "0.9" -futures = "0.3" +futures = { workspace = true } ignore = "0.4" image = "0.23" indexmap = "1.6.2" @@ -109,7 +110,7 @@ tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "05e3631c6a0701c1fa518b0fee7be95a2ceef5e2" } tree-sitter-embedded-template = "0.20.0" tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" } -tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8" } +tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" } tree-sitter-rust = "0.20.3" tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" } tree-sitter-python = "0.20.2" diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c6cfd8c109957f480479fed38446ccd1551e4b5e..c035ac07dd8b1068ed539ae13e65128a87d5c04c 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -262,6 +262,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { ); activity_indicator::init(cx); copilot_button::init(cx); + lsp_log::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); settings::KeymapFileContent::load_defaults(cx); } @@ -273,7 +274,7 @@ pub fn initialize_workspace( ) { let workspace_handle = cx.handle(); cx.subscribe(&workspace_handle, { - move |_, _, event, cx| { + move |workspace, _, event, cx| { if let workspace::Event::PaneAdded(pane) = event { pane.update(cx, |pane, cx| { pane.toolbar().update(cx, |toolbar, cx| { @@ -287,6 +288,10 @@ pub fn initialize_workspace( toolbar.add_item(submit_feedback_button, cx); let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new()); toolbar.add_item(feedback_info_text, cx); + let lsp_log_item = cx.add_view(|_| { + lsp_log::LspLogToolbarItemView::new(workspace.project().clone()) + }); + toolbar.add_item(lsp_log_item, cx); }) }); }