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:  Release Notes: - Improved lsp logs view to display more language server data
This commit is contained in:
parent
a653e8adda
commit
a331497367
3 changed files with 71 additions and 63 deletions
|
@ -10,7 +10,7 @@ use gpui::{
|
||||||
use language::LanguageServerId;
|
use language::LanguageServerId;
|
||||||
use lsp::{
|
use lsp::{
|
||||||
notification::SetTrace, IoKind, LanguageServer, LanguageServerName, MessageType,
|
notification::SetTrace, IoKind, LanguageServer, LanguageServerName, MessageType,
|
||||||
ServerCapabilities, SetTraceParams, TraceValue,
|
SetTraceParams, TraceValue,
|
||||||
};
|
};
|
||||||
use project::{search::SearchQuery, Project, WorktreeId};
|
use project::{search::SearchQuery, Project, WorktreeId};
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
@ -108,7 +108,6 @@ struct LanguageServerState {
|
||||||
rpc_state: Option<LanguageServerRpcState>,
|
rpc_state: Option<LanguageServerRpcState>,
|
||||||
trace_level: TraceValue,
|
trace_level: TraceValue,
|
||||||
log_level: MessageType,
|
log_level: MessageType,
|
||||||
capabilities: ServerCapabilities,
|
|
||||||
io_logs_subscription: Option<lsp::Subscription>,
|
io_logs_subscription: Option<lsp::Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +177,7 @@ pub enum LogKind {
|
||||||
Trace,
|
Trace,
|
||||||
#[default]
|
#[default]
|
||||||
Logs,
|
Logs,
|
||||||
Capabilities,
|
ServerInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogKind {
|
impl LogKind {
|
||||||
|
@ -187,7 +186,7 @@ impl LogKind {
|
||||||
LogKind::Rpc => RPC_MESSAGES,
|
LogKind::Rpc => RPC_MESSAGES,
|
||||||
LogKind::Trace => SERVER_TRACE,
|
LogKind::Trace => SERVER_TRACE,
|
||||||
LogKind::Logs => SERVER_LOGS,
|
LogKind::Logs => SERVER_LOGS,
|
||||||
LogKind::Capabilities => SERVER_CAPABILITIES,
|
LogKind::ServerInfo => SERVER_INFO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,7 +323,11 @@ impl LogStore {
|
||||||
*id,
|
*id,
|
||||||
Some(name.clone()),
|
Some(name.clone()),
|
||||||
*worktree_id,
|
*worktree_id,
|
||||||
project.read(cx).language_server_for_id(*id, cx),
|
project
|
||||||
|
.read(cx)
|
||||||
|
.lsp_store()
|
||||||
|
.read(cx)
|
||||||
|
.language_server_for_id(*id),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -378,7 +381,6 @@ impl LogStore {
|
||||||
trace_level: TraceValue::Off,
|
trace_level: TraceValue::Off,
|
||||||
log_level: MessageType::LOG,
|
log_level: MessageType::LOG,
|
||||||
io_logs_subscription: None,
|
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)
|
Some(server_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,10 +488,6 @@ impl LogStore {
|
||||||
Some(&self.language_servers.get(&server_id)?.trace_messages)
|
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>(
|
fn server_ids_for_project<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
lookup_project: &'a WeakModel<Project>,
|
lookup_project: &'a WeakModel<Project>,
|
||||||
|
@ -619,9 +613,7 @@ impl LspLogView {
|
||||||
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
||||||
LogKind::Trace => this.show_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::Logs => this.show_logs_for_server(server_id, cx),
|
||||||
LogKind::Capabilities => {
|
LogKind::ServerInfo => this.show_server_info(server_id, cx),
|
||||||
this.show_capabilities_for_server(server_id, cx)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.current_server_id = None;
|
this.current_server_id = None;
|
||||||
|
@ -638,7 +630,7 @@ impl LspLogView {
|
||||||
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
||||||
LogKind::Trace => this.show_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::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])
|
(editor, vec![editor_subscription, search_subscription])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor_for_capabilities(
|
fn editor_for_server_info(
|
||||||
capabilities: ServerCapabilities,
|
server: &LanguageServer,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> (View<Editor>, Vec<Subscription>) {
|
) -> (View<Editor>, Vec<Subscription>) {
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
let mut editor = Editor::multi_line(cx);
|
let mut editor = Editor::multi_line(cx);
|
||||||
editor.set_text(serde_json::to_string_pretty(&capabilities).unwrap(), cx);
|
let server_info = format!(
|
||||||
editor.move_to_end(&MoveToEnd, cx);
|
"* 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_read_only(true);
|
||||||
editor.set_show_inline_completions(Some(false), cx);
|
editor.set_show_inline_completions(Some(false), cx);
|
||||||
editor
|
editor
|
||||||
|
@ -927,7 +933,13 @@ impl LspLogView {
|
||||||
level: TraceValue,
|
level: TraceValue,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
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, _| {
|
self.log_store.update(cx, |this, _| {
|
||||||
if let Some(state) = this.get_language_server_state(server_id) {
|
if let Some(state) = this.get_language_server_state(server_id) {
|
||||||
state.trace_level = level;
|
state.trace_level = level;
|
||||||
|
@ -940,22 +952,17 @@ impl LspLogView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_capabilities_for_server(
|
fn show_server_info(&mut self, server_id: LanguageServerId, cx: &mut ViewContext<Self>) {
|
||||||
&mut self,
|
let lsp_store = self.project.read(cx).lsp_store();
|
||||||
server_id: LanguageServerId,
|
let Some(server) = lsp_store.read(cx).language_server_for_id(server_id) else {
|
||||||
cx: &mut ViewContext<Self>,
|
return;
|
||||||
) {
|
};
|
||||||
let capabilities = self.log_store.read(cx).server_capabilities(server_id);
|
|
||||||
|
|
||||||
if let Some(capabilities) = capabilities {
|
|
||||||
self.current_server_id = Some(server_id);
|
self.current_server_id = Some(server_id);
|
||||||
self.active_entry_kind = LogKind::Capabilities;
|
self.active_entry_kind = LogKind::ServerInfo;
|
||||||
let (editor, editor_subscriptions) =
|
let (editor, editor_subscriptions) = Self::editor_for_server_info(&server, cx);
|
||||||
Self::editor_for_capabilities(capabilities.clone(), cx);
|
|
||||||
self.editor = editor;
|
self.editor = editor;
|
||||||
self.editor_subscriptions = editor_subscriptions;
|
self.editor_subscriptions = editor_subscriptions;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
|
||||||
cx.focus(&self.focus_handle);
|
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::Rpc => new_view.show_rpc_trace_for_server(server_id, cx),
|
||||||
LogKind::Trace => new_view.show_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::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
|
new_view
|
||||||
|
@ -1187,9 +1194,7 @@ impl Render for LspLogToolbarItemView {
|
||||||
}
|
}
|
||||||
LogKind::Trace => view.show_trace_for_server(server_id, cx),
|
LogKind::Trace => view.show_trace_for_server(server_id, cx),
|
||||||
LogKind::Logs => view.show_logs_for_server(server_id, cx),
|
LogKind::Logs => view.show_logs_for_server(server_id, cx),
|
||||||
LogKind::Capabilities => {
|
LogKind::ServerInfo => view.show_server_info(server_id, cx),
|
||||||
view.show_capabilities_for_server(server_id, cx)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}),
|
}),
|
||||||
|
@ -1272,10 +1277,10 @@ impl Render for LspLogToolbarItemView {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.entry(
|
.entry(
|
||||||
SERVER_CAPABILITIES,
|
SERVER_INFO,
|
||||||
None,
|
None,
|
||||||
cx.handler_for(&log_view, move |view, cx| {
|
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 RPC_MESSAGES: &str = "RPC Messages";
|
||||||
const SERVER_LOGS: &str = "Server Logs";
|
const SERVER_LOGS: &str = "Server Logs";
|
||||||
const SERVER_TRACE: &str = "Server Trace";
|
const SERVER_TRACE: &str = "Server Trace";
|
||||||
const SERVER_CAPABILITIES: &str = "Server Capabilities";
|
const SERVER_INFO: &str = "Server Info";
|
||||||
|
|
||||||
impl Default for LspLogToolbarItemView {
|
impl Default for LspLogToolbarItemView {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
|
@ -82,6 +82,7 @@ pub struct LanguageServer {
|
||||||
outbound_tx: channel::Sender<String>,
|
outbound_tx: channel::Sender<String>,
|
||||||
name: LanguageServerName,
|
name: LanguageServerName,
|
||||||
process_name: Arc<str>,
|
process_name: Arc<str>,
|
||||||
|
binary: LanguageServerBinary,
|
||||||
capabilities: RwLock<ServerCapabilities>,
|
capabilities: RwLock<ServerCapabilities>,
|
||||||
code_action_kinds: Option<Vec<CodeActionKind>>,
|
code_action_kinds: Option<Vec<CodeActionKind>>,
|
||||||
notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
|
notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
|
||||||
|
@ -347,7 +348,7 @@ impl LanguageServer {
|
||||||
let mut server = util::command::new_smol_command(&binary.path)
|
let mut server = util::command::new_smol_command(&binary.path)
|
||||||
.current_dir(working_dir)
|
.current_dir(working_dir)
|
||||||
.args(&binary.arguments)
|
.args(&binary.arguments)
|
||||||
.envs(binary.env.unwrap_or_default())
|
.envs(binary.env.clone().unwrap_or_default())
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
|
@ -363,7 +364,7 @@ impl LanguageServer {
|
||||||
let stdin = server.stdin.take().unwrap();
|
let stdin = server.stdin.take().unwrap();
|
||||||
let stdout = server.stdout.take().unwrap();
|
let stdout = server.stdout.take().unwrap();
|
||||||
let stderr = server.stderr.take().unwrap();
|
let stderr = server.stderr.take().unwrap();
|
||||||
let mut server = Self::new_internal(
|
let server = Self::new_internal(
|
||||||
server_id,
|
server_id,
|
||||||
server_name,
|
server_name,
|
||||||
stdin,
|
stdin,
|
||||||
|
@ -374,6 +375,7 @@ impl LanguageServer {
|
||||||
root_path,
|
root_path,
|
||||||
working_dir,
|
working_dir,
|
||||||
code_action_kinds,
|
code_action_kinds,
|
||||||
|
binary,
|
||||||
cx,
|
cx,
|
||||||
move |notification| {
|
move |notification| {
|
||||||
log::info!(
|
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)
|
Ok(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,6 +402,7 @@ impl LanguageServer {
|
||||||
root_path: &Path,
|
root_path: &Path,
|
||||||
working_dir: &Path,
|
working_dir: &Path,
|
||||||
code_action_kinds: Option<Vec<CodeActionKind>>,
|
code_action_kinds: Option<Vec<CodeActionKind>>,
|
||||||
|
binary: LanguageServerBinary,
|
||||||
cx: AsyncAppContext,
|
cx: AsyncAppContext,
|
||||||
on_unhandled_notification: F,
|
on_unhandled_notification: F,
|
||||||
) -> Self
|
) -> Self
|
||||||
|
@ -466,7 +465,12 @@ impl LanguageServer {
|
||||||
response_handlers,
|
response_handlers,
|
||||||
io_handlers,
|
io_handlers,
|
||||||
name: server_name,
|
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(),
|
capabilities: Default::default(),
|
||||||
code_action_kinds,
|
code_action_kinds,
|
||||||
next_id: Default::default(),
|
next_id: Default::default(),
|
||||||
|
@ -1055,6 +1059,11 @@ impl LanguageServer {
|
||||||
&self.root_path
|
&self.root_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Language server's binary information.
|
||||||
|
pub fn binary(&self) -> &LanguageServerBinary {
|
||||||
|
&self.binary
|
||||||
|
}
|
||||||
|
|
||||||
/// Sends a RPC request to the language server.
|
/// Sends a RPC request to the language server.
|
||||||
///
|
///
|
||||||
/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
|
/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
|
||||||
|
@ -1278,12 +1287,13 @@ impl FakeLanguageServer {
|
||||||
root,
|
root,
|
||||||
root,
|
root,
|
||||||
None,
|
None,
|
||||||
|
binary.clone(),
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
|_| {},
|
|_| {},
|
||||||
);
|
);
|
||||||
server.process_name = process_name;
|
server.process_name = process_name;
|
||||||
let fake = FakeLanguageServer {
|
let fake = FakeLanguageServer {
|
||||||
binary,
|
binary: binary.clone(),
|
||||||
server: Arc::new({
|
server: Arc::new({
|
||||||
let mut server = LanguageServer::new_internal(
|
let mut server = LanguageServer::new_internal(
|
||||||
server_id,
|
server_id,
|
||||||
|
@ -1296,7 +1306,8 @@ impl FakeLanguageServer {
|
||||||
root,
|
root,
|
||||||
root,
|
root,
|
||||||
None,
|
None,
|
||||||
cx,
|
binary,
|
||||||
|
cx.clone(),
|
||||||
move |msg| {
|
move |msg| {
|
||||||
notifications_tx
|
notifications_tx
|
||||||
.try_send((
|
.try_send((
|
||||||
|
|
|
@ -4143,14 +4143,6 @@ impl Project {
|
||||||
self.lsp_store.read(cx).supplementary_language_servers()
|
self.lsp_store.read(cx).supplementary_language_servers()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn language_server_for_id(
|
|
||||||
&self,
|
|
||||||
id: LanguageServerId,
|
|
||||||
cx: &AppContext,
|
|
||||||
) -> Option<Arc<LanguageServer>> {
|
|
||||||
self.lsp_store.read(cx).language_server_for_id(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn language_servers_for_local_buffer<'a>(
|
pub fn language_servers_for_local_buffer<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
buffer: &'a Buffer,
|
buffer: &'a Buffer,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue