Remote LSP logs (#36709)
Enables LSP log tracing in both remote collab and remote ssh environments. Server logs and server RPC traces can now be viewed remotely, and the LSP button is now shown in such projects too. Closes https://github.com/zed-industries/zed/issues/28557 Co-Authored-By: Kirill <kirill@zed.dev> Co-Authored-By: Lukas <lukas@zed.dev> Release Notes: - Enabled LSP log tracing in both remote collab and remote ssh environments --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> Co-authored-by: Lukas Wirth <lukas@zed.dev>
This commit is contained in:
co-authored by
Kirill Bulatov
Lukas Wirth
parent
c158eb2442
commit
e2bf8e5d9c
@@ -977,7 +977,9 @@ impl LocalLspStore {
|
||||
this.update(&mut cx, |_, cx| {
|
||||
cx.emit(LspStoreEvent::LanguageServerLog(
|
||||
server_id,
|
||||
LanguageServerLogType::Trace(params.verbose),
|
||||
LanguageServerLogType::Trace {
|
||||
verbose_info: params.verbose,
|
||||
},
|
||||
params.message,
|
||||
));
|
||||
})
|
||||
@@ -3482,13 +3484,13 @@ pub struct LspStore {
|
||||
buffer_store: Entity<BufferStore>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
pub languages: Arc<LanguageRegistry>,
|
||||
language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
|
||||
pub language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
|
||||
active_entry: Option<ProjectEntryId>,
|
||||
_maintain_workspace_config: (Task<Result<()>>, watch::Sender<()>),
|
||||
_maintain_buffer_languages: Task<()>,
|
||||
diagnostic_summaries:
|
||||
HashMap<WorktreeId, HashMap<Arc<Path>, HashMap<LanguageServerId, DiagnosticSummary>>>,
|
||||
pub(super) lsp_server_capabilities: HashMap<LanguageServerId, lsp::ServerCapabilities>,
|
||||
pub lsp_server_capabilities: HashMap<LanguageServerId, lsp::ServerCapabilities>,
|
||||
lsp_document_colors: HashMap<BufferId, DocumentColorData>,
|
||||
lsp_code_lens: HashMap<BufferId, CodeLensData>,
|
||||
running_lsp_requests: HashMap<TypeId, (Global, HashMap<LspRequestId, Task<()>>)>,
|
||||
@@ -3565,6 +3567,7 @@ pub struct LanguageServerStatus {
|
||||
pub pending_work: BTreeMap<String, LanguageServerProgress>,
|
||||
pub has_pending_diagnostic_updates: bool,
|
||||
progress_tokens: HashSet<String>,
|
||||
pub worktree: Option<WorktreeId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -7483,7 +7486,7 @@ impl LspStore {
|
||||
server: Some(proto::LanguageServer {
|
||||
id: server_id.to_proto(),
|
||||
name: status.name.to_string(),
|
||||
worktree_id: None,
|
||||
worktree_id: status.worktree.map(|id| id.to_proto()),
|
||||
}),
|
||||
capabilities: serde_json::to_string(&server.capabilities())
|
||||
.expect("serializing server LSP capabilities"),
|
||||
@@ -7527,6 +7530,7 @@ impl LspStore {
|
||||
pending_work: Default::default(),
|
||||
has_pending_diagnostic_updates: false,
|
||||
progress_tokens: Default::default(),
|
||||
worktree: server.worktree_id.map(WorktreeId::from_proto),
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -8892,6 +8896,7 @@ impl LspStore {
|
||||
pending_work: Default::default(),
|
||||
has_pending_diagnostic_updates: false,
|
||||
progress_tokens: Default::default(),
|
||||
worktree: server.worktree_id.map(WorktreeId::from_proto),
|
||||
},
|
||||
);
|
||||
cx.emit(LspStoreEvent::LanguageServerAdded(
|
||||
@@ -10905,6 +10910,7 @@ impl LspStore {
|
||||
pending_work: Default::default(),
|
||||
has_pending_diagnostic_updates: false,
|
||||
progress_tokens: Default::default(),
|
||||
worktree: Some(key.worktree_id),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -12190,6 +12196,14 @@ impl LspStore {
|
||||
let data = self.lsp_code_lens.get_mut(&buffer_id)?;
|
||||
Some(data.update.take()?.1)
|
||||
}
|
||||
|
||||
pub fn downstream_client(&self) -> Option<(AnyProtoClient, u64)> {
|
||||
self.downstream_client.clone()
|
||||
}
|
||||
|
||||
pub fn worktree_store(&self) -> Entity<WorktreeStore> {
|
||||
self.worktree_store.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// Registration with registerOptions as null, should fallback to true.
|
||||
@@ -12699,45 +12713,69 @@ impl PartialEq for LanguageServerPromptRequest {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum LanguageServerLogType {
|
||||
Log(MessageType),
|
||||
Trace(Option<String>),
|
||||
Trace { verbose_info: Option<String> },
|
||||
Rpc { received: bool },
|
||||
}
|
||||
|
||||
impl LanguageServerLogType {
|
||||
pub fn to_proto(&self) -> proto::language_server_log::LogType {
|
||||
match self {
|
||||
Self::Log(log_type) => {
|
||||
let message_type = match *log_type {
|
||||
MessageType::ERROR => 1,
|
||||
MessageType::WARNING => 2,
|
||||
MessageType::INFO => 3,
|
||||
MessageType::LOG => 4,
|
||||
use proto::log_message::LogLevel;
|
||||
let level = match *log_type {
|
||||
MessageType::ERROR => LogLevel::Error,
|
||||
MessageType::WARNING => LogLevel::Warning,
|
||||
MessageType::INFO => LogLevel::Info,
|
||||
MessageType::LOG => LogLevel::Log,
|
||||
other => {
|
||||
log::warn!("Unknown lsp log message type: {:?}", other);
|
||||
4
|
||||
log::warn!("Unknown lsp log message type: {other:?}");
|
||||
LogLevel::Log
|
||||
}
|
||||
};
|
||||
proto::language_server_log::LogType::LogMessageType(message_type)
|
||||
}
|
||||
Self::Trace(message) => {
|
||||
proto::language_server_log::LogType::LogTrace(proto::LspLogTrace {
|
||||
message: message.clone(),
|
||||
proto::language_server_log::LogType::Log(proto::LogMessage {
|
||||
level: level as i32,
|
||||
})
|
||||
}
|
||||
Self::Trace { verbose_info } => {
|
||||
proto::language_server_log::LogType::Trace(proto::TraceMessage {
|
||||
verbose_info: verbose_info.to_owned(),
|
||||
})
|
||||
}
|
||||
Self::Rpc { received } => {
|
||||
let kind = if *received {
|
||||
proto::rpc_message::Kind::Received
|
||||
} else {
|
||||
proto::rpc_message::Kind::Sent
|
||||
};
|
||||
let kind = kind as i32;
|
||||
proto::language_server_log::LogType::Rpc(proto::RpcMessage { kind })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_proto(log_type: proto::language_server_log::LogType) -> Self {
|
||||
use proto::log_message::LogLevel;
|
||||
use proto::rpc_message;
|
||||
match log_type {
|
||||
proto::language_server_log::LogType::LogMessageType(message_type) => {
|
||||
Self::Log(match message_type {
|
||||
1 => MessageType::ERROR,
|
||||
2 => MessageType::WARNING,
|
||||
3 => MessageType::INFO,
|
||||
4 => MessageType::LOG,
|
||||
_ => MessageType::LOG,
|
||||
})
|
||||
}
|
||||
proto::language_server_log::LogType::LogTrace(trace) => Self::Trace(trace.message),
|
||||
proto::language_server_log::LogType::Log(message_type) => Self::Log(
|
||||
match LogLevel::from_i32(message_type.level).unwrap_or(LogLevel::Log) {
|
||||
LogLevel::Error => MessageType::ERROR,
|
||||
LogLevel::Warning => MessageType::WARNING,
|
||||
LogLevel::Info => MessageType::INFO,
|
||||
LogLevel::Log => MessageType::LOG,
|
||||
},
|
||||
),
|
||||
proto::language_server_log::LogType::Trace(trace_message) => Self::Trace {
|
||||
verbose_info: trace_message.verbose_info,
|
||||
},
|
||||
proto::language_server_log::LogType::Rpc(message) => Self::Rpc {
|
||||
received: match rpc_message::Kind::from_i32(message.kind)
|
||||
.unwrap_or(rpc_message::Kind::Received)
|
||||
{
|
||||
rpc_message::Kind::Received => true,
|
||||
rpc_message::Kind::Sent => false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +280,11 @@ pub enum Event {
|
||||
server_id: LanguageServerId,
|
||||
buffer_id: BufferId,
|
||||
buffer_abs_path: PathBuf,
|
||||
name: Option<LanguageServerName>,
|
||||
},
|
||||
ToggleLspLogs {
|
||||
server_id: LanguageServerId,
|
||||
enabled: bool,
|
||||
},
|
||||
Toast {
|
||||
notification_id: SharedString,
|
||||
@@ -1001,6 +1006,7 @@ impl Project {
|
||||
client.add_entity_request_handler(Self::handle_open_buffer_by_path);
|
||||
client.add_entity_request_handler(Self::handle_open_new_buffer);
|
||||
client.add_entity_message_handler(Self::handle_create_buffer_for_peer);
|
||||
client.add_entity_message_handler(Self::handle_toggle_lsp_logs);
|
||||
|
||||
WorktreeStore::init(&client);
|
||||
BufferStore::init(&client);
|
||||
@@ -2971,6 +2977,7 @@ impl Project {
|
||||
buffer_id,
|
||||
server_id: *language_server_id,
|
||||
buffer_abs_path: PathBuf::from(&update.buffer_abs_path),
|
||||
name: name.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4697,6 +4704,20 @@ impl Project {
|
||||
})?
|
||||
}
|
||||
|
||||
async fn handle_toggle_lsp_logs(
|
||||
project: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::ToggleLspLogs>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<()> {
|
||||
project.update(&mut cx, |_, cx| {
|
||||
cx.emit(Event::ToggleLspLogs {
|
||||
server_id: LanguageServerId::from_proto(envelope.payload.server_id),
|
||||
enabled: envelope.payload.enabled,
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_synchronize_buffers(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::SynchronizeBuffers>,
|
||||
|
||||
@@ -1951,6 +1951,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
||||
server_id: LanguageServerId(1),
|
||||
buffer_id,
|
||||
buffer_abs_path: PathBuf::from(path!("/dir/a.rs")),
|
||||
name: Some(fake_server.server.name())
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user