diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 73f327166a..7b5a8106d7 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -476,7 +476,8 @@ impl Server { .add_request_handler(forward_mutating_project_request::) .add_request_handler(forward_mutating_project_request::) .add_message_handler(broadcast_project_message_from_host::) - .add_message_handler(update_context); + .add_message_handler(update_context) + .add_request_handler(forward_mutating_project_request::); Arc::new(server) } diff --git a/crates/language_tools/src/language_tools.rs b/crates/language_tools/src/language_tools.rs index 34c3d4b9eb..f5af3d01f6 100644 --- a/crates/language_tools/src/language_tools.rs +++ b/crates/language_tools/src/language_tools.rs @@ -6,6 +6,7 @@ mod syntax_tree_view; #[cfg(test)] mod lsp_log_tests; +use client::AnyProtoClient; use gpui::{App, AppContext, Entity}; pub use lsp_log::{LogStore, LspLogToolbarItemView, LspLogView}; @@ -13,8 +14,8 @@ pub use syntax_tree_view::{SyntaxTreeToolbarItemView, SyntaxTreeView}; use ui::{Context, Window}; use workspace::{Item, ItemHandle, SplitDirection, Workspace}; -pub fn init(cx: &mut App) { - lsp_log::init(cx); +pub fn init(client: AnyProtoClient, cx: &mut App) { + lsp_log::init(client, cx); syntax_tree_view::init(cx); key_context_view::init(cx); } diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index 2b1c82cd1f..7b4d21af6c 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -1,9 +1,11 @@ +use anyhow::Result; +use client::AnyProtoClient; use collections::{HashMap, VecDeque}; use copilot::Copilot; use editor::{Editor, EditorEvent, actions::MoveToEnd, scroll::Autoscroll}; use futures::{StreamExt, channel::mpsc}; use gpui::{ - AnyView, App, Context, Corner, Entity, EventEmitter, FocusHandle, Focusable, Global, + AnyView, App, AsyncApp, Context, Corner, Entity, EventEmitter, FocusHandle, Focusable, Global, IntoElement, ParentElement, Render, Styled, Subscription, WeakEntity, Window, actions, div, }; use itertools::Itertools; @@ -13,6 +15,7 @@ use lsp::{ SetTraceParams, TraceValue, notification::SetTrace, }; use project::{Project, WorktreeId, lsp_store::LanguageServerLogType, search::SearchQuery}; +use proto::TypedEnvelope; use std::{any::TypeId, borrow::Cow, sync::Arc}; use ui::{Button, Checkbox, ContextMenu, Label, PopoverMenu, ToggleState, prelude::*}; use util::ResultExt as _; @@ -103,7 +106,7 @@ impl Message for RpcMessage { } pub(super) struct LanguageServerState { - project: WeakEntity, + project: Option>, name: Option, worktree_id: Option, kind: LanguageServerKind, @@ -226,7 +229,9 @@ pub struct GlobalLogStore(pub WeakEntity); impl Global for GlobalLogStore {} // todo! do separate headless and local cases here: headless cares only about the downstream_client() part, NO log storage is needed -pub fn init(cx: &mut App) { +pub fn init(client: AnyProtoClient, cx: &mut App) { + client.add_entity_message_handler(handle_toggle_lsp_logs); + let log_store = cx.new(LogStore::new); cx.set_global(GlobalLogStore(log_store.downgrade())); @@ -291,6 +296,7 @@ impl LogStore { Some(name), None, Some(server.clone()), + None, cx, ); } @@ -331,7 +337,8 @@ impl LogStore { this.language_servers .retain(|_, state| state.kind.project() != Some(&weak_project)); }), - cx.subscribe(project, |this, project, event, cx| { + cx.subscribe(project, move |log_store, project, event, cx| { + let subscription_weak_project = project.downgrade(); let server_kind = if project.read(cx).is_via_ssh() { LanguageServerKind::Remote { project: project.downgrade(), @@ -344,7 +351,7 @@ impl LogStore { match event { project::Event::LanguageServerAdded(id, name, worktree_id) => { - this.add_language_server( + log_store.add_language_server( server_kind, *id, Some(name.clone()), @@ -354,21 +361,30 @@ impl LogStore { .lsp_store() .read(cx) .language_server_for_id(*id), + Some(subscription_weak_project), cx, ); } project::Event::LanguageServerRemoved(id) => { - this.remove_language_server(*id, cx); + log_store.remove_language_server(*id, cx); } project::Event::LanguageServerLog(id, typ, message) => { - this.add_language_server(server_kind, *id, None, None, None, cx); + log_store.add_language_server( + server_kind, + *id, + None, + None, + None, + Some(subscription_weak_project), + cx, + ); match typ { project::LanguageServerLogType::Log(typ) => { - this.add_language_server_log(*id, *typ, message, cx); + log_store.add_language_server_log(*id, *typ, message, cx); } project::LanguageServerLogType::Trace(_) => { // todo! do something with trace level - this.add_language_server_trace(*id, message, cx); + log_store.add_language_server_trace(*id, message, cx); } project::LanguageServerLogType::Rpc { received } => { let kind = if *received { @@ -376,7 +392,7 @@ impl LogStore { } else { MessageKind::Send }; - this.add_language_server_rpc(*id, kind, message, cx); + log_store.add_language_server_rpc(*id, kind, message, cx); } } } @@ -422,7 +438,7 @@ impl LogStore { name: Option, worktree_id: Option, server: Option>, - project: WeakEntity, + project: Option>, cx: &mut Context, ) -> Option<&mut LanguageServerState> { let server_state = self.language_servers.entry(server_id).or_insert_with(|| { @@ -609,7 +625,7 @@ impl LogStore { }) } - pub fn enable_rpc_trace_for_language_server( + fn enable_rpc_trace_for_language_server( &mut self, server_id: LanguageServerId, ) -> Option<&mut LanguageServerRpcState> { @@ -771,6 +787,23 @@ impl LogStore { } } +async fn handle_toggle_lsp_logs( + lsp_log: Entity, + envelope: TypedEnvelope, + mut cx: AsyncApp, +) -> Result<()> { + let server_id = LanguageServerId::from_proto(envelope.payload.server_id); + lsp_log.update(&mut cx, |lsp_log, _| { + // we do not support any other log toggling yet + if envelope.payload.enabled { + lsp_log.enable_rpc_trace_for_language_server(server_id); + } else { + lsp_log.disable_rpc_trace_for_language_server(server_id); + } + })?; + Ok(()) +} + impl LspLogView { pub fn new( project: Entity, @@ -1130,26 +1163,32 @@ impl LspLogView { window: &mut Window, cx: &mut Context, ) { - self.log_store.update(cx, |log_store, _| { + self.log_store.update(cx, |log_store, cx| { if enabled { log_store.enable_rpc_trace_for_language_server(server_id); } else { log_store.disable_rpc_trace_for_language_server(server_id); } - if let Some(server_state) = log_store.language_servers.get(server_id) { - server_state - .project - .update(cx, |project, cx| { - if let Some((client, project)) = - project.lsp_store().read(cx).upstream_client() - { - // todo! client.send a new proto message to propagate the enabled - // !!!! we have to have a handler on both headless and normal projects - // that handler has to touch the Global and amend the sending bit - } - }) - .ok(); + if let Some(server_state) = log_store.language_servers.get(&server_id) { + if let Some(project) = &server_state.project { + project + .update(cx, |project, cx| { + if let Some((client, project_id)) = + project.lsp_store().read(cx).upstream_client() + { + client + .send(proto::ToggleLspLogs { + project_id, + log_type: proto::toggle_lsp_logs::LogType::Rpc as i32, + server_id: server_id.to_proto(), + enabled, + }) + .log_err(); + } + }) + .ok(); + } }; }); if !enabled && Some(server_id) == self.current_server_id { diff --git a/crates/proto/proto/lsp.proto b/crates/proto/proto/lsp.proto index f935f2263b..942a64a696 100644 --- a/crates/proto/proto/lsp.proto +++ b/crates/proto/proto/lsp.proto @@ -963,3 +963,16 @@ message MultiLspQuery { message MultiLspQueryResponse { repeated LspResponse responses = 1; } + +message ToggleLspLogs { + uint64 project_id = 1; + LogType log_type = 2; + uint64 server_id = 3; + bool enabled = 4; + + enum LogType { + LOG = 0; + TRACE = 1; + RPC = 2; + } +} diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 70689bcd63..2222bdec08 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -396,7 +396,8 @@ message Envelope { GitCloneResponse git_clone_response = 364; LspQuery lsp_query = 365; - LspQueryResponse lsp_query_response = 366; // current max + LspQueryResponse lsp_query_response = 366; + ToggleLspLogs toggle_lsp_logs = 367; // current max } reserved 87 to 88; diff --git a/crates/proto/src/proto.rs b/crates/proto/src/proto.rs index d38e54685f..5ae3e7a28b 100644 --- a/crates/proto/src/proto.rs +++ b/crates/proto/src/proto.rs @@ -312,7 +312,8 @@ messages!( (GetDefaultBranch, Background), (GetDefaultBranchResponse, Background), (GitClone, Background), - (GitCloneResponse, Background) + (GitCloneResponse, Background), + (ToggleLspLogs, Background), ); request_messages!( @@ -481,7 +482,8 @@ request_messages!( (GetDocumentDiagnostics, GetDocumentDiagnosticsResponse), (PullWorkspaceDiagnostics, Ack), (GetDefaultBranch, GetDefaultBranchResponse), - (GitClone, GitCloneResponse) + (GitClone, GitCloneResponse), + (ToggleLspLogs, Ack), ); lsp_messages!( @@ -612,6 +614,7 @@ entity_messages!( GitReset, GitCheckoutFiles, SetIndexText, + ToggleLspLogs, Push, Fetch, diff --git a/crates/remote_server/src/headless_project.rs b/crates/remote_server/src/headless_project.rs index 14e2fbce21..ee400add7a 100644 --- a/crates/remote_server/src/headless_project.rs +++ b/crates/remote_server/src/headless_project.rs @@ -65,13 +65,6 @@ impl HeadlessProject { settings::init(cx); language::init(cx); project::Project::init_settings(cx); - // todo! what to do with the RPC log spam? - // if we have not enabled RPC logging on the remote client, we do not need these - // - // Maybe, add another RPC message, proto::ToggleRpcLogging(bool) - // and send it into the upstream client from the remotes, so that the local/headless counterpart - // can access this Global and toggle the spam send - language_tools::lsp_log::init(cx); } pub fn new( @@ -87,6 +80,8 @@ impl HeadlessProject { ) -> Self { debug_adapter_extension::init(proxy.clone(), cx); languages::init(languages.clone(), node_runtime.clone(), cx); + // todo! avoid "memory leaks" here as we do not need to gather any logs locally, just proxy the to the client + language_tools::lsp_log::init(session.clone(), cx); let worktree_store = cx.new(|cx| { let mut store = WorktreeStore::local(true, fs.clone()); @@ -333,16 +328,6 @@ impl HeadlessProject { }) .log_err(); } - LspStoreEvent::LanguageServerLog(language_server_id, log_type, message) => { - self.session - .send(proto::LanguageServerLog { - project_id: SSH_PROJECT_ID, - language_server_id: language_server_id.to_proto(), - message: message.clone(), - log_type: Some(log_type.to_proto()), - }) - .log_err(); - } LspStoreEvent::LanguageServerPrompt(prompt) => { let request = self.session.request(proto::LanguageServerPromptRequest { project_id: SSH_PROJECT_ID, diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index e99c8b564b..6cacf0fcbd 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -5,7 +5,7 @@ use agent_ui::AgentPanel; use anyhow::{Context as _, Result}; use clap::{Parser, command}; use cli::FORCE_CLI_MODE_ENV_VAR_NAME; -use client::{Client, ProxySettings, UserStore, parse_zed_link}; +use client::{AnyProtoClient, Client, ProxySettings, UserStore, parse_zed_link}; use collab_ui::channel_view::ChannelView; use collections::HashMap; use crashes::InitCrashHandler; @@ -621,7 +621,7 @@ pub fn main() { toolchain_selector::init(cx); theme_selector::init(cx); settings_profile_selector::init(cx); - language_tools::init(cx); + language_tools::init(AnyProtoClient::new(app_state.client.clone()), cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); notifications::init(app_state.client.clone(), app_state.user_store.clone(), cx); collab_ui::init(&app_state, cx);