Remote LSP logs (#37083)

Take 2: https://github.com/zed-industries/zed/pull/36709 but without the
very bad `cfg`-based approach for storing the RPC logs.

--------------

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: Ben Kunkle <ben@zed.dev>
Co-authored-by: Lukas Wirth <lukas@zed.dev>
This commit is contained in:
Kirill Bulatov
2025-08-28 15:32:44 +03:00
committed by GitHub
co-authored by Ben Kunkle Lukas Wirth
parent 39d86eeb7f
commit f127ba82d1
22 changed files with 1357 additions and 875 deletions
+48 -16
View File
@@ -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);
@@ -1475,7 +1481,7 @@ impl Project {
})?;
let lsp_store = cx.new(|cx| {
let mut lsp_store = LspStore::new_remote(
LspStore::new_remote(
buffer_store.clone(),
worktree_store.clone(),
languages.clone(),
@@ -1483,12 +1489,7 @@ impl Project {
remote_id,
fs.clone(),
cx,
);
lsp_store.set_language_server_statuses_from_proto(
response.payload.language_servers,
response.payload.language_server_capabilities,
);
lsp_store
)
})?;
let task_store = cx.new(|cx| {
@@ -1522,7 +1523,7 @@ impl Project {
)
})?;
let this = cx.new(|cx| {
let project = cx.new(|cx| {
let replica_id = response.payload.replica_id as ReplicaId;
let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
@@ -1553,7 +1554,7 @@ impl Project {
cx.subscribe(&dap_store, Self::on_dap_store_event).detach();
let mut this = Self {
let mut project = Self {
buffer_ordered_messages_tx: tx,
buffer_store: buffer_store.clone(),
image_store,
@@ -1596,13 +1597,25 @@ impl Project {
toolchain_store: None,
agent_location: None,
};
this.set_role(role, cx);
project.set_role(role, cx);
for worktree in worktrees {
this.add_worktree(&worktree, cx);
project.add_worktree(&worktree, cx);
}
this
project
})?;
let weak_project = project.downgrade();
lsp_store
.update(&mut cx, |lsp_store, cx| {
lsp_store.set_language_server_statuses_from_proto(
weak_project,
response.payload.language_servers,
response.payload.language_server_capabilities,
cx,
);
})
.ok();
let subscriptions = subscriptions
.into_iter()
.map(|s| match s {
@@ -1618,7 +1631,7 @@ impl Project {
EntitySubscription::SettingsObserver(subscription) => {
subscription.set_entity(&settings_observer, &cx)
}
EntitySubscription::Project(subscription) => subscription.set_entity(&this, &cx),
EntitySubscription::Project(subscription) => subscription.set_entity(&project, &cx),
EntitySubscription::LspStore(subscription) => {
subscription.set_entity(&lsp_store, &cx)
}
@@ -1638,13 +1651,13 @@ impl Project {
.update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))?
.await?;
this.update(&mut cx, |this, cx| {
project.update(&mut cx, |this, cx| {
this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
this.client_subscriptions.extend(subscriptions);
anyhow::Ok(())
})??;
Ok(this)
Ok(project)
}
fn new_search_history() -> SearchHistory {
@@ -2315,10 +2328,14 @@ impl Project {
self.join_project_response_message_id = message_id;
self.set_worktrees_from_proto(message.worktrees, cx)?;
self.set_collaborators_from_proto(message.collaborators, cx)?;
self.lsp_store.update(cx, |lsp_store, _| {
let project = cx.weak_entity();
self.lsp_store.update(cx, |lsp_store, cx| {
lsp_store.set_language_server_statuses_from_proto(
project,
message.language_servers,
message.language_server_capabilities,
cx,
)
});
self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
@@ -2971,6 +2988,7 @@ impl Project {
buffer_id,
server_id: *language_server_id,
buffer_abs_path: PathBuf::from(&update.buffer_abs_path),
name: name.clone(),
});
}
}
@@ -4697,6 +4715,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>,