From a33149736721b882fc842e5185adf742e7ed45fc Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 7 Jan 2025 23:57:59 +0200 Subject: [PATCH] Display language server info in the server logs tab (#22797) Follow-up of https://github.com/zed-industries/zed/pull/19448 When dealing with issues like https://github.com/zed-industries/zed/issues/22749, it's quite tedious to ask for logs and check them out. This PR attempts to establish a single "diagnose my language server" place in the server logs panel, where server capabilities were already displayed after https://github.com/zed-industries/zed/pull/19448 The design is pretty brutal, but seems to be on par with the previous version and it's a technical corner of Zed, so seems to be ok for now: ![image](https://github.com/user-attachments/assets/3471c83a-329e-475a-8cad-af95684da960) Release Notes: - Improved lsp logs view to display more language server data --- crates/language_tools/src/lsp_log.rs | 97 +++++++++++++++------------- crates/lsp/src/lsp.rs | 29 ++++++--- crates/project/src/project.rs | 8 --- 3 files changed, 71 insertions(+), 63 deletions(-) diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index 3b96703609..b488394333 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -10,7 +10,7 @@ use gpui::{ use language::LanguageServerId; use lsp::{ notification::SetTrace, IoKind, LanguageServer, LanguageServerName, MessageType, - ServerCapabilities, SetTraceParams, TraceValue, + SetTraceParams, TraceValue, }; use project::{search::SearchQuery, Project, WorktreeId}; use std::{borrow::Cow, sync::Arc}; @@ -108,7 +108,6 @@ struct LanguageServerState { rpc_state: Option, trace_level: TraceValue, log_level: MessageType, - capabilities: ServerCapabilities, io_logs_subscription: Option, } @@ -178,7 +177,7 @@ pub enum LogKind { Trace, #[default] Logs, - Capabilities, + ServerInfo, } impl LogKind { @@ -187,7 +186,7 @@ impl LogKind { LogKind::Rpc => RPC_MESSAGES, LogKind::Trace => SERVER_TRACE, LogKind::Logs => SERVER_LOGS, - LogKind::Capabilities => SERVER_CAPABILITIES, + LogKind::ServerInfo => SERVER_INFO, } } } @@ -324,7 +323,11 @@ impl LogStore { *id, Some(name.clone()), *worktree_id, - project.read(cx).language_server_for_id(*id, cx), + project + .read(cx) + .lsp_store() + .read(cx) + .language_server_for_id(*id), cx, ); } @@ -378,7 +381,6 @@ impl LogStore { trace_level: TraceValue::Off, log_level: MessageType::LOG, io_logs_subscription: None, - capabilities: ServerCapabilities::default(), } }); @@ -402,10 +404,6 @@ impl LogStore { })); } - if let Some(server) = server { - server_state.capabilities = server.capabilities(); - } - Some(server_state) } @@ -490,10 +488,6 @@ impl LogStore { Some(&self.language_servers.get(&server_id)?.trace_messages) } - fn server_capabilities(&self, server_id: LanguageServerId) -> Option<&ServerCapabilities> { - Some(&self.language_servers.get(&server_id)?.capabilities) - } - fn server_ids_for_project<'a>( &'a self, lookup_project: &'a WeakModel, @@ -619,9 +613,7 @@ impl LspLogView { LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx), LogKind::Trace => this.show_trace_for_server(server_id, cx), LogKind::Logs => this.show_logs_for_server(server_id, cx), - LogKind::Capabilities => { - this.show_capabilities_for_server(server_id, cx) - } + LogKind::ServerInfo => this.show_server_info(server_id, cx), } } else { this.current_server_id = None; @@ -638,7 +630,7 @@ impl LspLogView { LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx), LogKind::Trace => this.show_trace_for_server(server_id, cx), LogKind::Logs => this.show_logs_for_server(server_id, cx), - LogKind::Capabilities => this.show_capabilities_for_server(server_id, cx), + LogKind::ServerInfo => this.show_server_info(server_id, cx), } } @@ -712,14 +704,28 @@ impl LspLogView { (editor, vec![editor_subscription, search_subscription]) } - fn editor_for_capabilities( - capabilities: ServerCapabilities, + fn editor_for_server_info( + server: &LanguageServer, cx: &mut ViewContext, ) -> (View, Vec) { let editor = cx.new_view(|cx| { let mut editor = Editor::multi_line(cx); - editor.set_text(serde_json::to_string_pretty(&capabilities).unwrap(), cx); - editor.move_to_end(&MoveToEnd, cx); + let server_info = format!( + "* Server: {NAME} (id {ID}) + +* Binary: {BINARY:#?} + +* Running in project: {PATH:?} + +* Capabilities: {CAPABILITIES}", + NAME = server.name(), + ID = server.server_id(), + BINARY = server.binary(), + PATH = server.root_path(), + CAPABILITIES = serde_json::to_string_pretty(&server.capabilities()) + .unwrap_or_else(|e| format!("Failed to serialize capabilities: {e}")), + ); + editor.set_text(server_info, cx); editor.set_read_only(true); editor.set_show_inline_completions(Some(false), cx); editor @@ -927,7 +933,13 @@ impl LspLogView { level: TraceValue, cx: &mut ViewContext, ) { - if let Some(server) = self.project.read(cx).language_server_for_id(server_id, cx) { + if let Some(server) = self + .project + .read(cx) + .lsp_store() + .read(cx) + .language_server_for_id(server_id) + { self.log_store.update(cx, |this, _| { if let Some(state) = this.get_language_server_state(server_id) { state.trace_level = level; @@ -940,22 +952,17 @@ impl LspLogView { } } - fn show_capabilities_for_server( - &mut self, - server_id: LanguageServerId, - cx: &mut ViewContext, - ) { - let capabilities = self.log_store.read(cx).server_capabilities(server_id); - - if let Some(capabilities) = capabilities { - self.current_server_id = Some(server_id); - self.active_entry_kind = LogKind::Capabilities; - let (editor, editor_subscriptions) = - Self::editor_for_capabilities(capabilities.clone(), cx); - self.editor = editor; - self.editor_subscriptions = editor_subscriptions; - cx.notify(); - } + fn show_server_info(&mut self, server_id: LanguageServerId, cx: &mut ViewContext) { + let lsp_store = self.project.read(cx).lsp_store(); + let Some(server) = lsp_store.read(cx).language_server_for_id(server_id) else { + return; + }; + self.current_server_id = Some(server_id); + self.active_entry_kind = LogKind::ServerInfo; + let (editor, editor_subscriptions) = Self::editor_for_server_info(&server, cx); + self.editor = editor; + self.editor_subscriptions = editor_subscriptions; + cx.notify(); cx.focus(&self.focus_handle); } } @@ -1026,7 +1033,7 @@ impl Item for LspLogView { LogKind::Rpc => new_view.show_rpc_trace_for_server(server_id, cx), LogKind::Trace => new_view.show_trace_for_server(server_id, cx), LogKind::Logs => new_view.show_logs_for_server(server_id, cx), - LogKind::Capabilities => new_view.show_capabilities_for_server(server_id, cx), + LogKind::ServerInfo => new_view.show_server_info(server_id, cx), } } new_view @@ -1187,9 +1194,7 @@ impl Render for LspLogToolbarItemView { } LogKind::Trace => view.show_trace_for_server(server_id, cx), LogKind::Logs => view.show_logs_for_server(server_id, cx), - LogKind::Capabilities => { - view.show_capabilities_for_server(server_id, cx) - } + LogKind::ServerInfo => view.show_server_info(server_id, cx), } cx.notify(); }), @@ -1272,10 +1277,10 @@ impl Render for LspLogToolbarItemView { ) }) .entry( - SERVER_CAPABILITIES, + SERVER_INFO, None, cx.handler_for(&log_view, move |view, cx| { - view.show_capabilities_for_server(server_id, cx); + view.show_server_info(server_id, cx); }), ) })) @@ -1434,7 +1439,7 @@ impl Render for LspLogToolbarItemView { const RPC_MESSAGES: &str = "RPC Messages"; const SERVER_LOGS: &str = "Server Logs"; const SERVER_TRACE: &str = "Server Trace"; -const SERVER_CAPABILITIES: &str = "Server Capabilities"; +const SERVER_INFO: &str = "Server Info"; impl Default for LspLogToolbarItemView { fn default() -> Self { diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 25b6167c74..63a900e49b 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -82,6 +82,7 @@ pub struct LanguageServer { outbound_tx: channel::Sender, name: LanguageServerName, process_name: Arc, + binary: LanguageServerBinary, capabilities: RwLock, code_action_kinds: Option>, notification_handlers: Arc>>, @@ -347,7 +348,7 @@ impl LanguageServer { let mut server = util::command::new_smol_command(&binary.path) .current_dir(working_dir) .args(&binary.arguments) - .envs(binary.env.unwrap_or_default()) + .envs(binary.env.clone().unwrap_or_default()) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -363,7 +364,7 @@ impl LanguageServer { let stdin = server.stdin.take().unwrap(); let stdout = server.stdout.take().unwrap(); let stderr = server.stderr.take().unwrap(); - let mut server = Self::new_internal( + let server = Self::new_internal( server_id, server_name, stdin, @@ -374,6 +375,7 @@ impl LanguageServer { root_path, working_dir, code_action_kinds, + binary, cx, move |notification| { log::info!( @@ -385,10 +387,6 @@ impl LanguageServer { }, ); - if let Some(name) = binary.path.file_name() { - server.process_name = name.to_string_lossy().into(); - } - Ok(server) } @@ -404,6 +402,7 @@ impl LanguageServer { root_path: &Path, working_dir: &Path, code_action_kinds: Option>, + binary: LanguageServerBinary, cx: AsyncAppContext, on_unhandled_notification: F, ) -> Self @@ -466,7 +465,12 @@ impl LanguageServer { response_handlers, io_handlers, name: server_name, - process_name: Arc::default(), + process_name: binary + .path + .file_name() + .map(|name| Arc::from(name.to_string_lossy())) + .unwrap_or_default(), + binary, capabilities: Default::default(), code_action_kinds, next_id: Default::default(), @@ -1055,6 +1059,11 @@ impl LanguageServer { &self.root_path } + /// Language server's binary information. + pub fn binary(&self) -> &LanguageServerBinary { + &self.binary + } + /// Sends a RPC request to the language server. /// /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage) @@ -1278,12 +1287,13 @@ impl FakeLanguageServer { root, root, None, + binary.clone(), cx.clone(), |_| {}, ); server.process_name = process_name; let fake = FakeLanguageServer { - binary, + binary: binary.clone(), server: Arc::new({ let mut server = LanguageServer::new_internal( server_id, @@ -1296,7 +1306,8 @@ impl FakeLanguageServer { root, root, None, - cx, + binary, + cx.clone(), move |msg| { notifications_tx .try_send(( diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 07d764bd6c..d7ffd6421e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4143,14 +4143,6 @@ impl Project { self.lsp_store.read(cx).supplementary_language_servers() } - pub fn language_server_for_id( - &self, - id: LanguageServerId, - cx: &AppContext, - ) -> Option> { - self.lsp_store.read(cx).language_server_for_id(id) - } - pub fn language_servers_for_local_buffer<'a>( &'a self, buffer: &'a Buffer,