Revert "Remote LSP logs (#36709)" (#37051)

Kirill Bulatov created

This reverts commit e2bf8e5d9c9b1b14cad1c9c618bb724c04182a2c.

See
https://github.com/zed-industries/zed/pull/37050#issuecomment-3230017137
for the context: musl builds started to fail and the amount of `cfg!`s
to fix this is too large.

Instead, the lsp_log.rs has to be split and repurposed better for the
remote headless server.

Release Notes:

- N/A

Change summary

Cargo.lock                                                      |   2 
crates/assistant_slash_command/Cargo.toml                       |   2 
crates/assistant_tool/Cargo.toml                                |   2 
crates/breadcrumbs/Cargo.toml                                   |   2 
crates/collab/migrations.sqlite/20221109000000_test_schema.sql  |   1 
crates/collab/migrations/20250827084812_worktree_in_servers.sql |   2 
crates/collab/src/db/queries/projects.rs                        |   4 
crates/collab/src/db/queries/rooms.rs                           |   2 
crates/collab/src/db/tables/language_server.rs                  |   1 
crates/collab/src/rpc.rs                                        |   4 
crates/copilot/Cargo.toml                                       |   2 
crates/editor/Cargo.toml                                        |   2 
crates/language_tools/Cargo.toml                                |   3 
crates/language_tools/src/language_tools.rs                     |   4 
crates/language_tools/src/lsp_log.rs                            | 633 --
crates/language_tools/src/lsp_log_tests.rs                      |   2 
crates/language_tools/src/lsp_tool.rs                           |  87 
crates/project/src/lsp_store.rs                                 |  90 
crates/project/src/project.rs                                   |  21 
crates/project/src/project_tests.rs                             |   1 
crates/proto/proto/lsp.proto                                    |  44 
crates/proto/proto/zed.proto                                    |   3 
crates/proto/src/proto.rs                                       |   7 
crates/remote_server/Cargo.toml                                 |   1 
crates/remote_server/src/headless_project.rs                    |  79 
crates/settings/src/settings.rs                                 |   2 
crates/workspace/Cargo.toml                                     |   3 
crates/workspace/src/pane_group.rs                              |  15 
crates/workspace/src/workspace.rs                               | 237 
29 files changed, 341 insertions(+), 917 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -9213,7 +9213,6 @@ dependencies = [
  "language",
  "lsp",
  "project",
- "proto",
  "release_channel",
  "serde_json",
  "settings",
@@ -13501,7 +13500,6 @@ dependencies = [
  "language",
  "language_extension",
  "language_model",
- "language_tools",
  "languages",
  "libc",
  "log",

crates/assistant_slash_command/Cargo.toml 🔗

@@ -25,7 +25,7 @@ parking_lot.workspace = true
 serde.workspace = true
 serde_json.workspace = true
 ui.workspace = true
-workspace = { path = "../workspace", default-features = false }
+workspace.workspace = true
 workspace-hack.workspace = true
 
 [dev-dependencies]

crates/assistant_tool/Cargo.toml 🔗

@@ -28,7 +28,7 @@ serde.workspace = true
 serde_json.workspace = true
 text.workspace = true
 util.workspace = true
-workspace = { path = "../workspace", default-features = false }
+workspace.workspace = true
 workspace-hack.workspace = true
 
 [dev-dependencies]

crates/breadcrumbs/Cargo.toml 🔗

@@ -19,7 +19,7 @@ itertools.workspace = true
 settings.workspace = true
 theme.workspace = true
 ui.workspace = true
-workspace = { path = "../workspace", default-features = false }
+workspace.workspace = true
 zed_actions.workspace = true
 workspace-hack.workspace = true
 

crates/collab/src/db/queries/projects.rs 🔗

@@ -694,7 +694,6 @@ impl Database {
                 project_id: ActiveValue::set(project_id),
                 id: ActiveValue::set(server.id as i64),
                 name: ActiveValue::set(server.name.clone()),
-                worktree_id: ActiveValue::set(server.worktree_id.map(|id| id as i64)),
                 capabilities: ActiveValue::set(update.capabilities.clone()),
             })
             .on_conflict(
@@ -705,7 +704,6 @@ impl Database {
                 .update_columns([
                     language_server::Column::Name,
                     language_server::Column::Capabilities,
-                    language_server::Column::WorktreeId,
                 ])
                 .to_owned(),
             )
@@ -1067,7 +1065,7 @@ impl Database {
                     server: proto::LanguageServer {
                         id: language_server.id as u64,
                         name: language_server.name,
-                        worktree_id: language_server.worktree_id.map(|id| id as u64),
+                        worktree_id: None,
                     },
                     capabilities: language_server.capabilities,
                 })

crates/collab/src/db/queries/rooms.rs 🔗

@@ -809,7 +809,7 @@ impl Database {
                 server: proto::LanguageServer {
                     id: language_server.id as u64,
                     name: language_server.name,
-                    worktree_id: language_server.worktree_id.map(|id| id as u64),
+                    worktree_id: None,
                 },
                 capabilities: language_server.capabilities,
             })

crates/collab/src/rpc.rs 🔗

@@ -476,9 +476,7 @@ impl Server {
             .add_request_handler(forward_mutating_project_request::<proto::GitChangeBranch>)
             .add_request_handler(forward_mutating_project_request::<proto::CheckForPushedCommits>)
             .add_message_handler(broadcast_project_message_from_host::<proto::AdvertiseContexts>)
-            .add_message_handler(update_context)
-            .add_request_handler(forward_mutating_project_request::<proto::ToggleLspLogs>)
-            .add_message_handler(broadcast_project_message_from_host::<proto::LanguageServerLog>);
+            .add_message_handler(update_context);
 
         Arc::new(server)
     }

crates/copilot/Cargo.toml 🔗

@@ -50,7 +50,7 @@ sum_tree.workspace = true
 task.workspace = true
 ui.workspace = true
 util.workspace = true
-workspace = { path = "../workspace", default-features = false }
+workspace.workspace = true
 workspace-hack.workspace = true
 itertools.workspace = true
 

crates/editor/Cargo.toml 🔗

@@ -89,7 +89,7 @@ ui.workspace = true
 url.workspace = true
 util.workspace = true
 uuid.workspace = true
-workspace = { path = "../workspace", default-features = false }
+workspace.workspace = true
 zed_actions.workspace = true
 workspace-hack.workspace = true
 

crates/language_tools/Cargo.toml 🔗

@@ -24,14 +24,13 @@ itertools.workspace = true
 language.workspace = true
 lsp.workspace = true
 project.workspace = true
-proto.workspace = true
 serde_json.workspace = true
 settings.workspace = true
 theme.workspace = true
 tree-sitter.workspace = true
 ui.workspace = true
 util.workspace = true
-workspace = { path = "../workspace", default-features = false }
+workspace.workspace = true
 zed_actions.workspace = true
 workspace-hack.workspace = true
 

crates/language_tools/src/language_tools.rs 🔗

@@ -1,5 +1,5 @@
 mod key_context_view;
-pub mod lsp_log;
+mod lsp_log;
 pub mod lsp_tool;
 mod syntax_tree_view;
 
@@ -14,7 +14,7 @@ use ui::{Context, Window};
 use workspace::{Item, ItemHandle, SplitDirection, Workspace};
 
 pub fn init(cx: &mut App) {
-    lsp_log::init(true, cx);
+    lsp_log::init(cx);
     syntax_tree_view::init(cx);
     key_context_view::init(cx);
 }

crates/language_tools/src/lsp_log.rs 🔗

@@ -9,16 +9,12 @@ use gpui::{
 use itertools::Itertools;
 use language::{LanguageServerId, language_settings::SoftWrap};
 use lsp::{
-    IoKind, LanguageServer, LanguageServerBinary, LanguageServerName, LanguageServerSelector,
-    MessageType, SetTraceParams, TraceValue, notification::SetTrace,
-};
-use project::{
-    LspStore, Project, ProjectItem, WorktreeId, lsp_store::LanguageServerLogType,
-    search::SearchQuery,
+    IoKind, LanguageServer, LanguageServerName, LanguageServerSelector, MessageType,
+    SetTraceParams, TraceValue, notification::SetTrace,
 };
+use project::{Project, WorktreeId, search::SearchQuery};
 use std::{any::TypeId, borrow::Cow, sync::Arc};
 use ui::{Button, Checkbox, ContextMenu, Label, PopoverMenu, ToggleState, prelude::*};
-use util::ResultExt as _;
 use workspace::{
     SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, WorkspaceId,
     item::{Item, ItemHandle},
@@ -32,7 +28,6 @@ const RECEIVE_LINE: &str = "\n// Receive:";
 const MAX_STORED_LOG_ENTRIES: usize = 2000;
 
 pub struct LogStore {
-    store_logs: bool,
     projects: HashMap<WeakEntity<Project>, ProjectState>,
     language_servers: HashMap<LanguageServerId, LanguageServerState>,
     copilot_log_subscription: Option<lsp::Subscription>,
@@ -51,7 +46,6 @@ trait Message: AsRef<str> {
     }
 }
 
-#[derive(Debug)]
 pub(super) struct LogMessage {
     message: String,
     typ: MessageType,
@@ -79,10 +73,8 @@ impl Message for LogMessage {
     }
 }
 
-#[derive(Debug)]
 pub(super) struct TraceMessage {
     message: String,
-    is_verbose: bool,
 }
 
 impl AsRef<str> for TraceMessage {
@@ -92,18 +84,9 @@ impl AsRef<str> for TraceMessage {
 }
 
 impl Message for TraceMessage {
-    type Level = TraceValue;
-
-    fn should_include(&self, level: Self::Level) -> bool {
-        match level {
-            TraceValue::Off => false,
-            TraceValue::Messages => !self.is_verbose,
-            TraceValue::Verbose => true,
-        }
-    }
+    type Level = ();
 }
 
-#[derive(Debug)]
 struct RpcMessage {
     message: String,
 }
@@ -118,7 +101,7 @@ impl Message for RpcMessage {
     type Level = ();
 }
 
-pub struct LanguageServerState {
+pub(super) struct LanguageServerState {
     name: Option<LanguageServerName>,
     worktree_id: Option<WorktreeId>,
     kind: LanguageServerKind,
@@ -130,35 +113,24 @@ pub struct LanguageServerState {
     io_logs_subscription: Option<lsp::Subscription>,
 }
 
-impl std::fmt::Debug for LanguageServerState {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("LanguageServerState")
-            .field("name", &self.name)
-            .field("worktree_id", &self.worktree_id)
-            .field("kind", &self.kind)
-            .field("log_messages", &self.log_messages)
-            .field("trace_messages", &self.trace_messages)
-            .field("rpc_state", &self.rpc_state)
-            .field("trace_level", &self.trace_level)
-            .field("log_level", &self.log_level)
-            .finish_non_exhaustive()
-    }
-}
-
 #[derive(PartialEq, Clone)]
 pub enum LanguageServerKind {
     Local { project: WeakEntity<Project> },
     Remote { project: WeakEntity<Project> },
-    LocalSsh { lsp_store: WeakEntity<LspStore> },
     Global,
 }
 
+impl LanguageServerKind {
+    fn is_remote(&self) -> bool {
+        matches!(self, LanguageServerKind::Remote { .. })
+    }
+}
+
 impl std::fmt::Debug for LanguageServerKind {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
             LanguageServerKind::Local { .. } => write!(f, "LanguageServerKind::Local"),
             LanguageServerKind::Remote { .. } => write!(f, "LanguageServerKind::Remote"),
-            LanguageServerKind::LocalSsh { .. } => write!(f, "LanguageServerKind::LocalSsh"),
             LanguageServerKind::Global => write!(f, "LanguageServerKind::Global"),
         }
     }
@@ -169,14 +141,12 @@ impl LanguageServerKind {
         match self {
             Self::Local { project } => Some(project),
             Self::Remote { project } => Some(project),
-            Self::LocalSsh { .. } => None,
             Self::Global { .. } => None,
         }
     }
 }
 
-#[derive(Debug)]
-pub struct LanguageServerRpcState {
+struct LanguageServerRpcState {
     rpc_messages: VecDeque<RpcMessage>,
     last_message_kind: Option<MessageKind>,
 }
@@ -197,7 +167,7 @@ pub struct LspLogToolbarItemView {
     _log_view_subscription: Option<Subscription>,
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq)]
 enum MessageKind {
     Send,
     Receive,
@@ -213,13 +183,6 @@ pub enum LogKind {
 }
 
 impl LogKind {
-    fn from_server_log_type(log_type: &LanguageServerLogType) -> Self {
-        match log_type {
-            LanguageServerLogType::Log(_) => Self::Logs,
-            LanguageServerLogType::Trace { .. } => Self::Trace,
-            LanguageServerLogType::Rpc { .. } => Self::Rpc,
-        }
-    }
     fn label(&self) -> &'static str {
         match self {
             LogKind::Rpc => RPC_MESSAGES,
@@ -249,53 +212,59 @@ actions!(
     ]
 );
 
-pub struct GlobalLogStore(pub WeakEntity<LogStore>);
+pub(super) struct GlobalLogStore(pub WeakEntity<LogStore>);
 
 impl Global for GlobalLogStore {}
 
-pub fn init(store_logs: bool, cx: &mut App) {
-    let log_store = cx.new(|cx| LogStore::new(store_logs, cx));
+pub fn init(cx: &mut App) {
+    let log_store = cx.new(LogStore::new);
     cx.set_global(GlobalLogStore(log_store.downgrade()));
 
     cx.observe_new(move |workspace: &mut Workspace, _, cx| {
-        log_store.update(cx, |store, cx| {
-            store.add_project(workspace.project(), cx);
-        });
+        let project = workspace.project();
+        if project.read(cx).is_local() || project.read(cx).is_via_remote_server() {
+            log_store.update(cx, |store, cx| {
+                store.add_project(project, cx);
+            });
+        }
 
         let log_store = log_store.clone();
         workspace.register_action(move |workspace, _: &OpenLanguageServerLogs, window, cx| {
-            let log_store = log_store.clone();
-            let project = workspace.project().clone();
-            get_or_create_tool(
-                workspace,
-                SplitDirection::Right,
-                window,
-                cx,
-                move |window, cx| LspLogView::new(project, log_store, window, cx),
-            );
+            let project = workspace.project().read(cx);
+            if project.is_local() || project.is_via_remote_server() {
+                let project = workspace.project().clone();
+                let log_store = log_store.clone();
+                get_or_create_tool(
+                    workspace,
+                    SplitDirection::Right,
+                    window,
+                    cx,
+                    move |window, cx| LspLogView::new(project, log_store, window, cx),
+                );
+            }
         });
     })
     .detach();
 }
 
 impl LogStore {
-    pub fn new(store_logs: bool, cx: &mut Context<Self>) -> Self {
+    pub fn new(cx: &mut Context<Self>) -> Self {
         let (io_tx, mut io_rx) = mpsc::unbounded();
 
         let copilot_subscription = Copilot::global(cx).map(|copilot| {
             let copilot = &copilot;
-            cx.subscribe(copilot, |log_store, copilot, edit_prediction_event, cx| {
+            cx.subscribe(copilot, |this, copilot, edit_prediction_event, cx| {
                 if let copilot::Event::CopilotLanguageServerStarted = edit_prediction_event
                     && let Some(server) = copilot.read(cx).language_server()
                 {
                     let server_id = server.server_id();
-                    let weak_lsp_store = cx.weak_entity();
-                    log_store.copilot_log_subscription =
+                    let weak_this = cx.weak_entity();
+                    this.copilot_log_subscription =
                         Some(server.on_notification::<copilot::request::LogMessage, _>(
                             move |params, cx| {
-                                weak_lsp_store
-                                    .update(cx, |lsp_store, cx| {
-                                        lsp_store.add_language_server_log(
+                                weak_this
+                                    .update(cx, |this, cx| {
+                                        this.add_language_server_log(
                                             server_id,
                                             MessageType::LOG,
                                             &params.message,
@@ -305,9 +274,8 @@ impl LogStore {
                                     .ok();
                             },
                         ));
-
                     let name = LanguageServerName::new_static("copilot");
-                    log_store.add_language_server(
+                    this.add_language_server(
                         LanguageServerKind::Global,
                         server.server_id(),
                         Some(name),
@@ -319,27 +287,26 @@ impl LogStore {
             })
         });
 
-        let log_store = Self {
+        let this = Self {
             copilot_log_subscription: None,
             _copilot_subscription: copilot_subscription,
             projects: HashMap::default(),
             language_servers: HashMap::default(),
-            store_logs,
             io_tx,
         };
 
-        cx.spawn(async move |log_store, cx| {
+        cx.spawn(async move |this, cx| {
             while let Some((server_id, io_kind, message)) = io_rx.next().await {
-                if let Some(log_store) = log_store.upgrade() {
-                    log_store.update(cx, |log_store, cx| {
-                        log_store.on_io(server_id, io_kind, &message, cx);
+                if let Some(this) = this.upgrade() {
+                    this.update(cx, |this, cx| {
+                        this.on_io(server_id, io_kind, &message, cx);
                     })?;
                 }
             }
             anyhow::Ok(())
         })
         .detach_and_log_err(cx);
-        log_store
+        this
     }
 
     pub fn add_project(&mut self, project: &Entity<Project>, cx: &mut Context<Self>) {
@@ -353,19 +320,20 @@ impl LogStore {
                         this.language_servers
                             .retain(|_, state| state.kind.project() != Some(&weak_project));
                     }),
-                    cx.subscribe(project, move |log_store, project, event, cx| {
-                        let server_kind = if project.read(cx).is_local() {
-                            LanguageServerKind::Local {
+                    cx.subscribe(project, |this, project, event, cx| {
+                        let server_kind = if project.read(cx).is_via_remote_server() {
+                            LanguageServerKind::Remote {
                                 project: project.downgrade(),
                             }
                         } else {
-                            LanguageServerKind::Remote {
+                            LanguageServerKind::Local {
                                 project: project.downgrade(),
                             }
                         };
+
                         match event {
                             project::Event::LanguageServerAdded(id, name, worktree_id) => {
-                                log_store.add_language_server(
+                                this.add_language_server(
                                     server_kind,
                                     *id,
                                     Some(name.clone()),
@@ -378,78 +346,20 @@ impl LogStore {
                                     cx,
                                 );
                             }
-                            project::Event::LanguageServerBufferRegistered {
-                                server_id,
-                                buffer_id,
-                                name,
-                                ..
-                            } if project.read(cx).is_via_collab() => {
-                                let worktree_id = project
-                                    .read(cx)
-                                    .buffer_for_id(*buffer_id, cx)
-                                    .and_then(|buffer| {
-                                        Some(buffer.read(cx).project_path(cx)?.worktree_id)
-                                    });
-                                let name = name.clone().or_else(|| {
-                                    project
-                                        .read(cx)
-                                        .lsp_store()
-                                        .read(cx)
-                                        .language_server_statuses
-                                        .get(server_id)
-                                        .map(|status| status.name.clone())
-                                });
-                                log_store.add_language_server(
-                                    server_kind,
-                                    *server_id,
-                                    name,
-                                    worktree_id,
-                                    None,
-                                    cx,
-                                );
-                            }
                             project::Event::LanguageServerRemoved(id) => {
-                                log_store.remove_language_server(*id, cx);
+                                this.remove_language_server(*id, cx);
                             }
                             project::Event::LanguageServerLog(id, typ, message) => {
-                                log_store.add_language_server(
-                                    server_kind,
-                                    *id,
-                                    None,
-                                    None,
-                                    None,
-                                    cx,
-                                );
+                                this.add_language_server(server_kind, *id, None, None, None, cx);
                                 match typ {
                                     project::LanguageServerLogType::Log(typ) => {
-                                        log_store.add_language_server_log(*id, *typ, message, cx);
+                                        this.add_language_server_log(*id, *typ, message, cx);
                                     }
-                                    project::LanguageServerLogType::Trace { verbose_info } => {
-                                        log_store.add_language_server_trace(
-                                            *id,
-                                            message,
-                                            verbose_info.clone(),
-                                            cx,
-                                        );
-                                    }
-                                    project::LanguageServerLogType::Rpc { received } => {
-                                        let kind = if *received {
-                                            MessageKind::Receive
-                                        } else {
-                                            MessageKind::Send
-                                        };
-                                        log_store.add_language_server_rpc(*id, kind, message, cx);
+                                    project::LanguageServerLogType::Trace(_) => {
+                                        this.add_language_server_trace(*id, message, cx);
                                     }
                                 }
                             }
-                            project::Event::ToggleLspLogs { server_id, enabled } => {
-                                // we do not support any other log toggling yet
-                                if *enabled {
-                                    log_store.enable_rpc_trace_for_language_server(*server_id);
-                                } else {
-                                    log_store.disable_rpc_trace_for_language_server(*server_id);
-                                }
-                            }
                             _ => {}
                         }
                     }),
@@ -465,7 +375,7 @@ impl LogStore {
         self.language_servers.get_mut(&id)
     }
 
-    pub fn add_language_server(
+    fn add_language_server(
         &mut self,
         kind: LanguageServerKind,
         server_id: LanguageServerId,
@@ -516,35 +426,20 @@ impl LogStore {
         message: &str,
         cx: &mut Context<Self>,
     ) -> Option<()> {
-        let store_logs = self.store_logs;
         let language_server_state = self.get_language_server_state(id)?;
 
         let log_lines = &mut language_server_state.log_messages;
-        let message = message.trim_end().to_string();
-        if !store_logs {
-            // Send all messages regardless of the visibility in case of not storing, to notify the receiver anyway
-            self.emit_event(
-                Event::NewServerLogEntry {
-                    id,
-                    kind: LanguageServerLogType::Log(typ),
-                    text: message,
-                },
-                cx,
-            );
-        } else if let Some(new_message) = Self::push_new_message(
+        Self::add_language_server_message(
             log_lines,
-            LogMessage { message, typ },
+            id,
+            LogMessage {
+                message: message.trim_end().to_string(),
+                typ,
+            },
             language_server_state.log_level,
-        ) {
-            self.emit_event(
-                Event::NewServerLogEntry {
-                    id,
-                    kind: LanguageServerLogType::Log(typ),
-                    text: new_message,
-                },
-                cx,
-            );
-        }
+            LogKind::Logs,
+            cx,
+        );
         Some(())
     }
 
@@ -552,127 +447,46 @@ impl LogStore {
         &mut self,
         id: LanguageServerId,
         message: &str,
-        verbose_info: Option<String>,
         cx: &mut Context<Self>,
     ) -> Option<()> {
-        let store_logs = self.store_logs;
         let language_server_state = self.get_language_server_state(id)?;
 
         let log_lines = &mut language_server_state.trace_messages;
-        if !store_logs {
-            // Send all messages regardless of the visibility in case of not storing, to notify the receiver anyway
-            self.emit_event(
-                Event::NewServerLogEntry {
-                    id,
-                    kind: LanguageServerLogType::Trace { verbose_info },
-                    text: message.trim().to_string(),
-                },
-                cx,
-            );
-        } else if let Some(new_message) = Self::push_new_message(
+        Self::add_language_server_message(
             log_lines,
+            id,
             TraceMessage {
                 message: message.trim().to_string(),
-                is_verbose: false,
             },
-            TraceValue::Messages,
-        ) {
-            if let Some(verbose_message) = verbose_info.as_ref() {
-                Self::push_new_message(
-                    log_lines,
-                    TraceMessage {
-                        message: verbose_message.clone(),
-                        is_verbose: true,
-                    },
-                    TraceValue::Verbose,
-                );
-            }
-            self.emit_event(
-                Event::NewServerLogEntry {
-                    id,
-                    kind: LanguageServerLogType::Trace { verbose_info },
-                    text: new_message,
-                },
-                cx,
-            );
-        }
+            (),
+            LogKind::Trace,
+            cx,
+        );
         Some(())
     }
 
-    fn push_new_message<T: Message>(
+    fn add_language_server_message<T: Message>(
         log_lines: &mut VecDeque<T>,
+        id: LanguageServerId,
         message: T,
         current_severity: <T as Message>::Level,
-    ) -> Option<String> {
+        kind: LogKind,
+        cx: &mut Context<Self>,
+    ) {
         while log_lines.len() + 1 >= MAX_STORED_LOG_ENTRIES {
             log_lines.pop_front();
         }
+        let text = message.as_ref().to_string();
         let visible = message.should_include(current_severity);
-
-        let visible_message = visible.then(|| message.as_ref().to_string());
         log_lines.push_back(message);
-        visible_message
-    }
-
-    fn add_language_server_rpc(
-        &mut self,
-        language_server_id: LanguageServerId,
-        kind: MessageKind,
-        message: &str,
-        cx: &mut Context<'_, Self>,
-    ) {
-        let store_logs = self.store_logs;
-        let Some(state) = self
-            .get_language_server_state(language_server_id)
-            .and_then(|state| state.rpc_state.as_mut())
-        else {
-            return;
-        };
-
-        let received = kind == MessageKind::Receive;
-        let rpc_log_lines = &mut state.rpc_messages;
-        if state.last_message_kind != Some(kind) {
-            while rpc_log_lines.len() + 1 >= MAX_STORED_LOG_ENTRIES {
-                rpc_log_lines.pop_front();
-            }
-            let line_before_message = match kind {
-                MessageKind::Send => SEND_LINE,
-                MessageKind::Receive => RECEIVE_LINE,
-            };
-            if store_logs {
-                rpc_log_lines.push_back(RpcMessage {
-                    message: line_before_message.to_string(),
-                });
-            }
-            // Do not send a synthetic message over the wire, it will be derived from the actual RPC message
-            cx.emit(Event::NewServerLogEntry {
-                id: language_server_id,
-                kind: LanguageServerLogType::Rpc { received },
-                text: line_before_message.to_string(),
-            });
-        }
-
-        while rpc_log_lines.len() + 1 >= MAX_STORED_LOG_ENTRIES {
-            rpc_log_lines.pop_front();
-        }
 
-        if store_logs {
-            rpc_log_lines.push_back(RpcMessage {
-                message: message.trim().to_owned(),
-            });
+        if visible {
+            cx.emit(Event::NewServerLogEntry { id, kind, text });
+            cx.notify();
         }
-
-        self.emit_event(
-            Event::NewServerLogEntry {
-                id: language_server_id,
-                kind: LanguageServerLogType::Rpc { received },
-                text: message.to_owned(),
-            },
-            cx,
-        );
     }
 
-    pub fn remove_language_server(&mut self, id: LanguageServerId, cx: &mut Context<Self>) {
+    fn remove_language_server(&mut self, id: LanguageServerId, cx: &mut Context<Self>) {
         self.language_servers.remove(&id);
         cx.notify();
     }
@@ -702,11 +516,11 @@ impl LogStore {
                         None
                     }
                 }
-                LanguageServerKind::Global | LanguageServerKind::LocalSsh { .. } => Some(*id),
+                LanguageServerKind::Global => Some(*id),
             })
     }
 
-    pub fn enable_rpc_trace_for_language_server(
+    fn enable_rpc_trace_for_language_server(
         &mut self,
         server_id: LanguageServerId,
     ) -> Option<&mut LanguageServerRpcState> {
@@ -849,45 +663,50 @@ impl LogStore {
             }
         };
 
+        let state = self
+            .get_language_server_state(language_server_id)?
+            .rpc_state
+            .as_mut()?;
         let kind = if is_received {
             MessageKind::Receive
         } else {
             MessageKind::Send
         };
 
-        self.add_language_server_rpc(language_server_id, kind, message, cx);
-        cx.notify();
-        Some(())
-    }
-
-    fn emit_event(&mut self, e: Event, cx: &mut Context<Self>) {
-        match &e {
-            Event::NewServerLogEntry { id, kind, text } => {
-                if let Some(state) = self.get_language_server_state(*id) {
-                    let downstream_client = match &state.kind {
-                        LanguageServerKind::Remote { project }
-                        | LanguageServerKind::Local { project } => project
-                            .upgrade()
-                            .map(|project| project.read(cx).lsp_store()),
-                        LanguageServerKind::LocalSsh { lsp_store } => lsp_store.upgrade(),
-                        LanguageServerKind::Global => None,
-                    }
-                    .and_then(|lsp_store| lsp_store.read(cx).downstream_client());
-                    if let Some((client, project_id)) = downstream_client {
-                        client
-                            .send(proto::LanguageServerLog {
-                                project_id,
-                                language_server_id: id.to_proto(),
-                                message: text.clone(),
-                                log_type: Some(kind.to_proto()),
-                            })
-                            .ok();
-                    }
-                }
+        let rpc_log_lines = &mut state.rpc_messages;
+        if state.last_message_kind != Some(kind) {
+            while rpc_log_lines.len() + 1 >= MAX_STORED_LOG_ENTRIES {
+                rpc_log_lines.pop_front();
             }
+            let line_before_message = match kind {
+                MessageKind::Send => SEND_LINE,
+                MessageKind::Receive => RECEIVE_LINE,
+            };
+            rpc_log_lines.push_back(RpcMessage {
+                message: line_before_message.to_string(),
+            });
+            cx.emit(Event::NewServerLogEntry {
+                id: language_server_id,
+                kind: LogKind::Rpc,
+                text: line_before_message.to_string(),
+            });
+        }
+
+        while rpc_log_lines.len() + 1 >= MAX_STORED_LOG_ENTRIES {
+            rpc_log_lines.pop_front();
         }
 
-        cx.emit(e);
+        let message = message.trim();
+        rpc_log_lines.push_back(RpcMessage {
+            message: message.to_string(),
+        });
+        cx.emit(Event::NewServerLogEntry {
+            id: language_server_id,
+            kind: LogKind::Rpc,
+            text: message.to_string(),
+        });
+        cx.notify();
+        Some(())
     }
 }
 
@@ -932,14 +751,13 @@ impl LspLogView {
 
                 cx.notify();
             });
-
         let events_subscriptions = cx.subscribe_in(
             &log_store,
             window,
             move |log_view, _, e, window, cx| match e {
                 Event::NewServerLogEntry { id, kind, text } => {
                     if log_view.current_server_id == Some(*id)
-                        && LogKind::from_server_log_type(kind) == log_view.active_entry_kind
+                        && *kind == log_view.active_entry_kind
                     {
                         log_view.editor.update(cx, |editor, cx| {
                             editor.set_read_only(false);
@@ -982,7 +800,7 @@ impl LspLogView {
             window.focus(&log_view.editor.focus_handle(cx));
         });
 
-        let mut lsp_log_view = Self {
+        let mut this = Self {
             focus_handle,
             editor,
             editor_subscriptions,
@@ -997,9 +815,9 @@ impl LspLogView {
             ],
         };
         if let Some(server_id) = server_id {
-            lsp_log_view.show_logs_for_server(server_id, window, cx);
+            this.show_logs_for_server(server_id, window, cx);
         }
-        lsp_log_view
+        this
     }
 
     fn editor_for_logs(
@@ -1020,7 +838,7 @@ impl LspLogView {
     }
 
     fn editor_for_server_info(
-        info: ServerInfo,
+        server: &LanguageServer,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> (Entity<Editor>, Vec<Subscription>) {
@@ -1035,21 +853,22 @@ impl LspLogView {
 * Capabilities: {CAPABILITIES}
 
 * Configuration: {CONFIGURATION}",
-            NAME = info.name,
-            ID = info.id,
-            BINARY = info.binary.as_ref().map_or_else(
-                || "Unknown".to_string(),
-                |bin| bin.path.as_path().to_string_lossy().to_string()
-            ),
-            WORKSPACE_FOLDERS = info.workspace_folders.join(", "),
-            CAPABILITIES = serde_json::to_string_pretty(&info.capabilities)
+            NAME = server.name(),
+            ID = server.server_id(),
+            BINARY = server.binary(),
+            WORKSPACE_FOLDERS = server
+                .workspace_folders()
+                .into_iter()
+                .filter_map(|path| path
+                    .to_file_path()
+                    .ok()
+                    .map(|path| path.to_string_lossy().into_owned()))
+                .collect::<Vec<_>>()
+                .join(", "),
+            CAPABILITIES = serde_json::to_string_pretty(&server.capabilities())
                 .unwrap_or_else(|e| format!("Failed to serialize capabilities: {e}")),
-            CONFIGURATION = info
-                .configuration
-                .map(|configuration| serde_json::to_string_pretty(&configuration))
-                .transpose()
-                .unwrap_or_else(|e| Some(format!("Failed to serialize configuration: {e}")))
-                .unwrap_or_else(|| "Unknown".to_string()),
+            CONFIGURATION = serde_json::to_string_pretty(server.configuration())
+                .unwrap_or_else(|e| format!("Failed to serialize configuration: {e}")),
         );
         let editor = initialize_new_editor(server_info, false, window, cx);
         let editor_subscription = cx.subscribe(
@@ -1072,9 +891,7 @@ impl LspLogView {
             .language_servers
             .iter()
             .map(|(server_id, state)| match &state.kind {
-                LanguageServerKind::Local { .. }
-                | LanguageServerKind::Remote { .. }
-                | LanguageServerKind::LocalSsh { .. } => {
+                LanguageServerKind::Local { .. } | LanguageServerKind::Remote { .. } => {
                     let worktree_root_name = state
                         .worktree_id
                         .and_then(|id| self.project.read(cx).worktree_for_id(id, cx))
@@ -1186,17 +1003,11 @@ impl LspLogView {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        let trace_level = self
-            .log_store
-            .update(cx, |this, _| {
-                Some(this.get_language_server_state(server_id)?.trace_level)
-            })
-            .unwrap_or(TraceValue::Messages);
         let log_contents = self
             .log_store
             .read(cx)
             .server_trace(server_id)
-            .map(|v| log_contents(v, trace_level));
+            .map(|v| log_contents(v, ()));
         if let Some(log_contents) = log_contents {
             self.current_server_id = Some(server_id);
             self.active_entry_kind = LogKind::Trace;
@@ -1214,7 +1025,6 @@ impl LspLogView {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        self.toggle_rpc_trace_for_server(server_id, true, window, cx);
         let rpc_log = self.log_store.update(cx, |log_store, _| {
             log_store
                 .enable_rpc_trace_for_language_server(server_id)
@@ -1259,33 +1069,12 @@ impl LspLogView {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        self.log_store.update(cx, |log_store, cx| {
+        self.log_store.update(cx, |log_store, _| {
             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) {
-                if let LanguageServerKind::Remote { project } = &server_state.kind {
-                    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 {
             self.show_logs_for_server(server_id, window, cx);
@@ -1324,38 +1113,13 @@ impl LspLogView {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        let Some(server_info) = self
-            .project
-            .read(cx)
-            .lsp_store()
-            .update(cx, |lsp_store, _| {
-                lsp_store
-                    .language_server_for_id(server_id)
-                    .as_ref()
-                    .map(|language_server| ServerInfo::new(language_server))
-                    .or_else(move || {
-                        let capabilities =
-                            lsp_store.lsp_server_capabilities.get(&server_id)?.clone();
-                        let name = lsp_store
-                            .language_server_statuses
-                            .get(&server_id)
-                            .map(|status| status.name.clone())?;
-                        Some(ServerInfo {
-                            id: server_id,
-                            capabilities,
-                            binary: None,
-                            name,
-                            workspace_folders: Vec::new(),
-                            configuration: None,
-                        })
-                    })
-            })
-        else {
+        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_info, window, cx);
+        let (editor, editor_subscriptions) = Self::editor_for_server_info(&server, window, cx);
         self.editor = editor;
         self.editor_subscriptions = editor_subscriptions;
         cx.notify();
@@ -1652,6 +1416,7 @@ impl Render for LspLogToolbarItemView {
 
         let view_selector = current_server.map(|server| {
             let server_id = server.server_id;
+            let is_remote = server.server_kind.is_remote();
             let rpc_trace_enabled = server.rpc_trace_enabled;
             let log_view = log_view.clone();
             PopoverMenu::new("LspViewSelector")
@@ -1673,53 +1438,55 @@ impl Render for LspLogToolbarItemView {
                                 view.show_logs_for_server(server_id, window, cx);
                             }),
                         )
-                        .entry(
-                            SERVER_TRACE,
-                            None,
-                            window.handler_for(&log_view, move |view, window, cx| {
-                                view.show_trace_for_server(server_id, window, cx);
-                            }),
-                        )
-                        .custom_entry(
-                            {
-                                let log_toolbar_view = log_toolbar_view.clone();
-                                move |window, _| {
-                                    h_flex()
-                                        .w_full()
-                                        .justify_between()
-                                        .child(Label::new(RPC_MESSAGES))
-                                        .child(
-                                            div().child(
-                                                Checkbox::new(
-                                                    "LspLogEnableRpcTrace",
-                                                    if rpc_trace_enabled {
-                                                        ToggleState::Selected
-                                                    } else {
-                                                        ToggleState::Unselected
-                                                    },
-                                                )
-                                                .on_click(window.listener_for(
-                                                    &log_toolbar_view,
-                                                    move |view, selection, window, cx| {
-                                                        let enabled = matches!(
-                                                            selection,
+                        .when(!is_remote, |this| {
+                            this.entry(
+                                SERVER_TRACE,
+                                None,
+                                window.handler_for(&log_view, move |view, window, cx| {
+                                    view.show_trace_for_server(server_id, window, cx);
+                                }),
+                            )
+                            .custom_entry(
+                                {
+                                    let log_toolbar_view = log_toolbar_view.clone();
+                                    move |window, _| {
+                                        h_flex()
+                                            .w_full()
+                                            .justify_between()
+                                            .child(Label::new(RPC_MESSAGES))
+                                            .child(
+                                                div().child(
+                                                    Checkbox::new(
+                                                        "LspLogEnableRpcTrace",
+                                                        if rpc_trace_enabled {
                                                             ToggleState::Selected
-                                                        );
-                                                        view.toggle_rpc_logging_for_server(
-                                                            server_id, enabled, window, cx,
-                                                        );
-                                                        cx.stop_propagation();
-                                                    },
-                                                )),
-                                            ),
-                                        )
-                                        .into_any_element()
-                                }
-                            },
-                            window.handler_for(&log_view, move |view, window, cx| {
-                                view.show_rpc_trace_for_server(server_id, window, cx);
-                            }),
-                        )
+                                                        } else {
+                                                            ToggleState::Unselected
+                                                        },
+                                                    )
+                                                    .on_click(window.listener_for(
+                                                        &log_toolbar_view,
+                                                        move |view, selection, window, cx| {
+                                                            let enabled = matches!(
+                                                                selection,
+                                                                ToggleState::Selected
+                                                            );
+                                                            view.toggle_rpc_logging_for_server(
+                                                                server_id, enabled, window, cx,
+                                                            );
+                                                            cx.stop_propagation();
+                                                        },
+                                                    )),
+                                                ),
+                                            )
+                                            .into_any_element()
+                                    }
+                                },
+                                window.handler_for(&log_view, move |view, window, cx| {
+                                    view.show_rpc_trace_for_server(server_id, window, cx);
+                                }),
+                            )
+                        })
                         .entry(
                             SERVER_INFO,
                             None,
@@ -1929,6 +1696,12 @@ const SERVER_LOGS: &str = "Server Logs";
 const SERVER_TRACE: &str = "Server Trace";
 const SERVER_INFO: &str = "Server Info";
 
+impl Default for LspLogToolbarItemView {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 impl LspLogToolbarItemView {
     pub fn new() -> Self {
         Self {

crates/language_tools/src/lsp_log_tests.rs 🔗

@@ -51,7 +51,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
         },
     );
 
-    let log_store = cx.new(|cx| LogStore::new(true, cx));
+    let log_store = cx.new(LogStore::new);
     log_store.update(cx, |store, cx| store.add_project(&project, cx));
 
     let _rust_buffer = project

crates/language_tools/src/lsp_tool.rs 🔗

@@ -122,7 +122,8 @@ impl LanguageServerState {
         let lsp_logs = cx
             .try_global::<GlobalLogStore>()
             .and_then(|lsp_logs| lsp_logs.0.upgrade());
-        let Some(lsp_logs) = lsp_logs else {
+        let lsp_store = self.lsp_store.upgrade();
+        let Some((lsp_logs, lsp_store)) = lsp_logs.zip(lsp_store) else {
             return menu;
         };
 
@@ -209,11 +210,10 @@ impl LanguageServerState {
             };
 
             let server_selector = server_info.server_selector();
-            let is_remote = self
-                .lsp_store
-                .update(cx, |lsp_store, _| lsp_store.as_remote().is_some())
-                .unwrap_or(false);
-            let has_logs = is_remote || lsp_logs.read(cx).has_server_logs(&server_selector);
+            // TODO currently, Zed remote does not work well with the LSP logs
+            // https://github.com/zed-industries/zed/issues/28557
+            let has_logs = lsp_store.read(cx).as_local().is_some()
+                && lsp_logs.read(cx).has_server_logs(&server_selector);
 
             let status_color = server_info
                 .binary_status
@@ -241,10 +241,10 @@ impl LanguageServerState {
                 .as_ref()
                 .or_else(|| server_info.binary_status.as_ref()?.message.as_ref())
                 .cloned();
-            let hover_label = if message.is_some() {
-                Some("View Message")
-            } else if has_logs {
+            let hover_label = if has_logs {
                 Some("View Logs")
+            } else if message.is_some() {
+                Some("View Message")
             } else {
                 None
             };
@@ -288,7 +288,16 @@ impl LanguageServerState {
                     let server_name = server_info.name.clone();
                     let workspace = self.workspace.clone();
                     move |window, cx| {
-                        if let Some(message) = &message {
+                        if has_logs {
+                            lsp_logs.update(cx, |lsp_logs, cx| {
+                                lsp_logs.open_server_trace(
+                                    workspace.clone(),
+                                    server_selector.clone(),
+                                    window,
+                                    cx,
+                                );
+                            });
+                        } else if let Some(message) = &message {
                             let Some(create_buffer) = workspace
                                 .update(cx, |workspace, cx| {
                                     workspace
@@ -338,15 +347,6 @@ impl LanguageServerState {
                                 anyhow::Ok(())
                             })
                             .detach();
-                        } else if has_logs {
-                            lsp_logs.update(cx, |lsp_logs, cx| {
-                                lsp_logs.open_server_trace(
-                                    workspace.clone(),
-                                    server_selector.clone(),
-                                    window,
-                                    cx,
-                                );
-                            });
                         } else {
                             cx.propagate();
                         }
@@ -529,48 +529,26 @@ impl LspTool {
             });
 
         let lsp_store = workspace.project().read(cx).lsp_store();
-        let mut language_servers = LanguageServers::default();
-        for (_, status) in lsp_store.read(cx).language_server_statuses() {
-            language_servers.binary_statuses.insert(
-                status.name.clone(),
-                LanguageServerBinaryStatus {
-                    status: BinaryStatus::None,
-                    message: None,
-                },
-            );
-        }
-
         let lsp_store_subscription =
             cx.subscribe_in(&lsp_store, window, |lsp_tool, _, e, window, cx| {
                 lsp_tool.on_lsp_store_event(e, window, cx)
             });
 
-        let server_state = cx.new(|_| LanguageServerState {
+        let state = cx.new(|_| LanguageServerState {
             workspace: workspace.weak_handle(),
             items: Vec::new(),
             lsp_store: lsp_store.downgrade(),
             active_editor: None,
-            language_servers,
+            language_servers: LanguageServers::default(),
         });
 
-        let mut lsp_tool = Self {
-            server_state,
+        Self {
+            server_state: state,
             popover_menu_handle,
             lsp_menu: None,
             lsp_menu_refresh: Task::ready(()),
             _subscriptions: vec![settings_subscription, lsp_store_subscription],
-        };
-        if !lsp_tool
-            .server_state
-            .read(cx)
-            .language_servers
-            .binary_statuses
-            .is_empty()
-        {
-            lsp_tool.refresh_lsp_menu(true, window, cx);
         }
-
-        lsp_tool
     }
 
     fn on_lsp_store_event(
@@ -730,25 +708,6 @@ impl LspTool {
                     }
                 }
             }
-            state
-                .lsp_store
-                .update(cx, |lsp_store, cx| {
-                    for (server_id, status) in lsp_store.language_server_statuses() {
-                        if let Some(worktree) = status.worktree.and_then(|worktree_id| {
-                            lsp_store
-                                .worktree_store()
-                                .read(cx)
-                                .worktree_for_id(worktree_id, cx)
-                        }) {
-                            server_ids_to_worktrees.insert(server_id, worktree.clone());
-                            server_names_to_worktrees
-                                .entry(status.name.clone())
-                                .or_default()
-                                .insert((worktree, server_id));
-                        }
-                    }
-                })
-                .ok();
 
             let mut servers_per_worktree = BTreeMap::<SharedString, Vec<ServerData>>::new();
             let mut servers_without_worktree = Vec::<ServerData>::new();

crates/project/src/lsp_store.rs 🔗

@@ -977,9 +977,7 @@ impl LocalLspStore {
                         this.update(&mut cx, |_, cx| {
                             cx.emit(LspStoreEvent::LanguageServerLog(
                                 server_id,
-                                LanguageServerLogType::Trace {
-                                    verbose_info: params.verbose,
-                                },
+                                LanguageServerLogType::Trace(params.verbose),
                                 params.message,
                             ));
                         })
@@ -3484,13 +3482,13 @@ pub struct LspStore {
     buffer_store: Entity<BufferStore>,
     worktree_store: Entity<WorktreeStore>,
     pub languages: Arc<LanguageRegistry>,
-    pub language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
+    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 lsp_server_capabilities: HashMap<LanguageServerId, lsp::ServerCapabilities>,
+    pub(super) 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<()>>)>,
@@ -3567,7 +3565,6 @@ 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)]
@@ -7486,7 +7483,7 @@ impl LspStore {
                         server: Some(proto::LanguageServer {
                             id: server_id.to_proto(),
                             name: status.name.to_string(),
-                            worktree_id: status.worktree.map(|id| id.to_proto()),
+                            worktree_id: None,
                         }),
                         capabilities: serde_json::to_string(&server.capabilities())
                             .expect("serializing server LSP capabilities"),
@@ -7530,7 +7527,6 @@ impl LspStore {
                         pending_work: Default::default(),
                         has_pending_diagnostic_updates: false,
                         progress_tokens: Default::default(),
-                        worktree: server.worktree_id.map(WorktreeId::from_proto),
                     },
                 )
             })
@@ -8896,7 +8892,6 @@ 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(
@@ -10910,7 +10905,6 @@ impl LspStore {
                 pending_work: Default::default(),
                 has_pending_diagnostic_updates: false,
                 progress_tokens: Default::default(),
-                worktree: Some(key.worktree_id),
             },
         );
 
@@ -12196,14 +12190,6 @@ 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.
@@ -12713,69 +12699,45 @@ impl PartialEq for LanguageServerPromptRequest {
 #[derive(Clone, Debug, PartialEq)]
 pub enum LanguageServerLogType {
     Log(MessageType),
-    Trace { verbose_info: Option<String> },
-    Rpc { received: bool },
+    Trace(Option<String>),
 }
 
 impl LanguageServerLogType {
     pub fn to_proto(&self) -> proto::language_server_log::LogType {
         match self {
             Self::Log(log_type) => {
-                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,
+                let message_type = match *log_type {
+                    MessageType::ERROR => 1,
+                    MessageType::WARNING => 2,
+                    MessageType::INFO => 3,
+                    MessageType::LOG => 4,
                     other => {
-                        log::warn!("Unknown lsp log message type: {other:?}");
-                        LogLevel::Log
+                        log::warn!("Unknown lsp log message type: {:?}", other);
+                        4
                     }
                 };
-                proto::language_server_log::LogType::Log(proto::LogMessage {
-                    level: level as i32,
-                })
+                proto::language_server_log::LogType::LogMessageType(message_type)
             }
-            Self::Trace { verbose_info } => {
-                proto::language_server_log::LogType::Trace(proto::TraceMessage {
-                    verbose_info: verbose_info.to_owned(),
+            Self::Trace(message) => {
+                proto::language_server_log::LogType::LogTrace(proto::LspLogTrace {
+                    message: message.clone(),
                 })
             }
-            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::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,
-                },
-            },
+            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),
         }
     }
 }

crates/project/src/project.rs 🔗

@@ -280,11 +280,6 @@ 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,
@@ -1006,7 +1001,6 @@ 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);
@@ -2977,7 +2971,6 @@ impl Project {
                                 buffer_id,
                                 server_id: *language_server_id,
                                 buffer_abs_path: PathBuf::from(&update.buffer_abs_path),
-                                name: name.clone(),
                             });
                         }
                     }
@@ -4704,20 +4697,6 @@ 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>,

crates/project/src/project_tests.rs 🔗

@@ -1951,7 +1951,6 @@ 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!(

crates/proto/proto/lsp.proto 🔗

@@ -610,36 +610,11 @@ message ServerMetadataUpdated {
 message LanguageServerLog {
     uint64 project_id = 1;
     uint64 language_server_id = 2;
-    string message = 3;
     oneof log_type {
-        LogMessage log = 4;
-        TraceMessage trace = 5;
-        RpcMessage rpc = 6;
-    }
-}
-
-message LogMessage {
-    LogLevel level = 1;
-
-    enum LogLevel {
-        LOG = 0;
-        INFO = 1;
-        WARNING = 2;
-        ERROR = 3;
-    }
-}
-
-message TraceMessage {
-    optional string verbose_info = 1;
-}
-
-message RpcMessage {
-    Kind kind = 1;
-
-    enum Kind {
-        RECEIVED = 0;
-        SENT = 1;
+        uint32 log_message_type = 3;
+        LspLogTrace log_trace = 4;
     }
+    string message = 5;
 }
 
 message LspLogTrace {
@@ -957,16 +932,3 @@ 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;
-    }
-}

crates/proto/proto/zed.proto 🔗

@@ -396,8 +396,7 @@ message Envelope {
         GitCloneResponse git_clone_response = 364;
 
         LspQuery lsp_query = 365;
-        LspQueryResponse lsp_query_response = 366;
-        ToggleLspLogs toggle_lsp_logs = 367; // current max
+        LspQueryResponse lsp_query_response = 366; // current max
     }
 
     reserved 87 to 88;

crates/proto/src/proto.rs 🔗

@@ -312,8 +312,7 @@ messages!(
     (GetDefaultBranch, Background),
     (GetDefaultBranchResponse, Background),
     (GitClone, Background),
-    (GitCloneResponse, Background),
-    (ToggleLspLogs, Background),
+    (GitCloneResponse, Background)
 );
 
 request_messages!(
@@ -482,8 +481,7 @@ request_messages!(
     (GetDocumentDiagnostics, GetDocumentDiagnosticsResponse),
     (PullWorkspaceDiagnostics, Ack),
     (GetDefaultBranch, GetDefaultBranchResponse),
-    (GitClone, GitCloneResponse),
-    (ToggleLspLogs, Ack),
+    (GitClone, GitCloneResponse)
 );
 
 lsp_messages!(
@@ -614,7 +612,6 @@ entity_messages!(
     GitReset,
     GitCheckoutFiles,
     SetIndexText,
-    ToggleLspLogs,
 
     Push,
     Fetch,

crates/remote_server/Cargo.toml 🔗

@@ -43,7 +43,6 @@ gpui_tokio.workspace = true
 http_client.workspace = true
 language.workspace = true
 language_extension.workspace = true
-language_tools.workspace = true
 languages.workspace = true
 log.workspace = true
 lsp.workspace = true

crates/remote_server/src/headless_project.rs 🔗

@@ -1,7 +1,5 @@
 use ::proto::{FromProto, ToProto};
 use anyhow::{Context as _, Result, anyhow};
-use language_tools::lsp_log::{GlobalLogStore, LanguageServerKind};
-use lsp::LanguageServerId;
 
 use extension::ExtensionHostProxy;
 use extension_host::headless_host::HeadlessExtensionStore;
@@ -67,7 +65,6 @@ impl HeadlessProject {
         settings::init(cx);
         language::init(cx);
         project::Project::init_settings(cx);
-        language_tools::lsp_log::init(false, cx);
     }
 
     pub fn new(
@@ -238,7 +235,6 @@ impl HeadlessProject {
         session.add_entity_request_handler(Self::handle_open_new_buffer);
         session.add_entity_request_handler(Self::handle_find_search_candidates);
         session.add_entity_request_handler(Self::handle_open_server_settings);
-        session.add_entity_message_handler(Self::handle_toggle_lsp_logs);
 
         session.add_entity_request_handler(BufferStore::handle_update_buffer);
         session.add_entity_message_handler(BufferStore::handle_close_buffer);
@@ -302,40 +298,11 @@ impl HeadlessProject {
 
     fn on_lsp_store_event(
         &mut self,
-        lsp_store: Entity<LspStore>,
+        _lsp_store: Entity<LspStore>,
         event: &LspStoreEvent,
         cx: &mut Context<Self>,
     ) {
         match event {
-            LspStoreEvent::LanguageServerAdded(id, name, worktree_id) => {
-                let log_store = cx
-                    .try_global::<GlobalLogStore>()
-                    .and_then(|lsp_logs| lsp_logs.0.upgrade());
-                if let Some(log_store) = log_store {
-                    log_store.update(cx, |log_store, cx| {
-                        log_store.add_language_server(
-                            LanguageServerKind::LocalSsh {
-                                lsp_store: self.lsp_store.downgrade(),
-                            },
-                            *id,
-                            Some(name.clone()),
-                            *worktree_id,
-                            lsp_store.read(cx).language_server_for_id(*id),
-                            cx,
-                        );
-                    });
-                }
-            }
-            LspStoreEvent::LanguageServerRemoved(id) => {
-                let log_store = cx
-                    .try_global::<GlobalLogStore>()
-                    .and_then(|lsp_logs| lsp_logs.0.upgrade());
-                if let Some(log_store) = log_store {
-                    log_store.update(cx, |log_store, cx| {
-                        log_store.remove_language_server(*id, cx);
-                    });
-                }
-            }
             LspStoreEvent::LanguageServerUpdate {
                 language_server_id,
                 name,
@@ -359,6 +326,16 @@ impl HeadlessProject {
                     })
                     .log_err();
             }
+            LspStoreEvent::LanguageServerLog(language_server_id, log_type, message) => {
+                self.session
+                    .send(proto::LanguageServerLog {
+                        project_id: REMOTE_SERVER_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: REMOTE_SERVER_PROJECT_ID,
@@ -532,31 +509,7 @@ impl HeadlessProject {
         })
     }
 
-    async fn handle_toggle_lsp_logs(
-        _: Entity<Self>,
-        envelope: TypedEnvelope<proto::ToggleLspLogs>,
-        mut cx: AsyncApp,
-    ) -> Result<()> {
-        let server_id = LanguageServerId::from_proto(envelope.payload.server_id);
-        let lsp_logs = cx
-            .update(|cx| {
-                cx.try_global::<GlobalLogStore>()
-                    .and_then(|lsp_logs| lsp_logs.0.upgrade())
-            })?
-            .context("lsp logs store is missing")?;
-
-        lsp_logs.update(&mut cx, |lsp_logs, _| {
-            // we do not support any other log toggling yet
-            if envelope.payload.enabled {
-                lsp_logs.enable_rpc_trace_for_language_server(server_id);
-            } else {
-                lsp_logs.disable_rpc_trace_for_language_server(server_id);
-            }
-        })?;
-        Ok(())
-    }
-
-    async fn handle_open_server_settings(
+    pub async fn handle_open_server_settings(
         this: Entity<Self>,
         _: TypedEnvelope<proto::OpenServerSettings>,
         mut cx: AsyncApp,
@@ -609,7 +562,7 @@ impl HeadlessProject {
         })
     }
 
-    async fn handle_find_search_candidates(
+    pub async fn handle_find_search_candidates(
         this: Entity<Self>,
         envelope: TypedEnvelope<proto::FindSearchCandidates>,
         mut cx: AsyncApp,
@@ -641,7 +594,7 @@ impl HeadlessProject {
         Ok(response)
     }
 
-    async fn handle_list_remote_directory(
+    pub async fn handle_list_remote_directory(
         this: Entity<Self>,
         envelope: TypedEnvelope<proto::ListRemoteDirectory>,
         cx: AsyncApp,
@@ -673,7 +626,7 @@ impl HeadlessProject {
         })
     }
 
-    async fn handle_get_path_metadata(
+    pub async fn handle_get_path_metadata(
         this: Entity<Self>,
         envelope: TypedEnvelope<proto::GetPathMetadata>,
         cx: AsyncApp,
@@ -691,7 +644,7 @@ impl HeadlessProject {
         })
     }
 
-    async fn handle_shutdown_remote_server(
+    pub async fn handle_shutdown_remote_server(
         _this: Entity<Self>,
         _envelope: TypedEnvelope<proto::ShutdownRemoteServer>,
         cx: AsyncApp,

crates/settings/src/settings.rs 🔗

@@ -30,7 +30,7 @@ pub struct ActiveSettingsProfileName(pub String);
 
 impl Global for ActiveSettingsProfileName {}
 
-#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord, serde::Serialize)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
 pub struct WorktreeId(usize);
 
 impl From<WorktreeId> for usize {

crates/workspace/Cargo.toml 🔗

@@ -13,7 +13,6 @@ path = "src/workspace.rs"
 doctest = false
 
 [features]
-default = ["call"]
 test-support = [
     "call/test-support",
     "client/test-support",
@@ -30,7 +29,7 @@ test-support = [
 any_vec.workspace = true
 anyhow.workspace = true
 async-recursion.workspace = true
-call = { workspace = true, optional = true }
+call.workspace = true
 client.workspace = true
 clock.workspace = true
 collections.workspace = true

crates/workspace/src/pane_group.rs 🔗

@@ -4,14 +4,11 @@ use crate::{
     workspace_settings::{PaneSplitDirectionHorizontal, PaneSplitDirectionVertical},
 };
 use anyhow::Result;
-
-#[cfg(feature = "call")]
 use call::{ActiveCall, ParticipantLocation};
-
 use collections::HashMap;
 use gpui::{
-    Along, AnyView, AnyWeakView, Axis, Bounds, Entity, Hsla, IntoElement, Pixels, Point,
-    StyleRefinement, WeakEntity, Window, point, size,
+    Along, AnyView, AnyWeakView, Axis, Bounds, Entity, Hsla, IntoElement, MouseButton, Pixels,
+    Point, StyleRefinement, WeakEntity, Window, point, size,
 };
 use parking_lot::Mutex;
 use project::Project;
@@ -200,7 +197,6 @@ pub enum Member {
 pub struct PaneRenderContext<'a> {
     pub project: &'a Entity<Project>,
     pub follower_states: &'a HashMap<CollaboratorId, FollowerState>,
-    #[cfg(feature = "call")]
     pub active_call: Option<&'a Entity<ActiveCall>>,
     pub active_pane: &'a Entity<Pane>,
     pub app_state: &'a Arc<AppState>,
@@ -262,11 +258,6 @@ impl PaneLeaderDecorator for PaneRenderContext<'_> {
         let mut leader_color;
         let status_box;
         match leader_id {
-            #[cfg(not(feature = "call"))]
-            CollaboratorId::PeerId(_) => {
-                return LeaderDecoration::default();
-            }
-            #[cfg(feature = "call")]
             CollaboratorId::PeerId(peer_id) => {
                 let Some(leader) = self.active_call.as_ref().and_then(|call| {
                     let room = call.read(cx).room()?.read(cx);
@@ -324,7 +315,7 @@ impl PaneLeaderDecorator for PaneRenderContext<'_> {
                             |this, (leader_project_id, leader_user_id)| {
                                 let app_state = self.app_state.clone();
                                 this.cursor_pointer().on_mouse_down(
-                                    gpui::MouseButton::Left,
+                                    MouseButton::Left,
                                     move |_, _, cx| {
                                         crate::join_in_room_project(
                                             leader_project_id,

crates/workspace/src/workspace.rs 🔗

@@ -9,7 +9,6 @@ pub mod pane_group;
 mod path_list;
 mod persistence;
 pub mod searchable;
-#[cfg(feature = "call")]
 pub mod shared_screen;
 mod status_bar;
 pub mod tasks;
@@ -23,17 +22,11 @@ pub use dock::Panel;
 pub use path_list::PathList;
 pub use toast_layer::{ToastAction, ToastLayer, ToastView};
 
-#[cfg(feature = "call")]
-use call::{ActiveCall, call_settings::CallSettings};
-#[cfg(feature = "call")]
-use client::{Status, proto::ErrorCode};
-#[cfg(feature = "call")]
-use shared_screen::SharedScreen;
-
 use anyhow::{Context as _, Result, anyhow};
+use call::{ActiveCall, call_settings::CallSettings};
 use client::{
-    ChannelId, Client, ErrorExt, TypedEnvelope, UserStore,
-    proto::{self, PanelId, PeerId},
+    ChannelId, Client, ErrorExt, Status, TypedEnvelope, UserStore,
+    proto::{self, ErrorCode, PanelId, PeerId},
 };
 use collections::{HashMap, HashSet, hash_map};
 use dock::{Dock, DockPosition, PanelButtons, PanelHandle, RESIZE_HANDLE_SIZE};
@@ -86,6 +79,7 @@ use schemars::JsonSchema;
 use serde::Deserialize;
 use session::AppSession;
 use settings::{Settings, update_settings_file};
+use shared_screen::SharedScreen;
 use sqlez::{
     bindable::{Bind, Column, StaticColumnCount},
     statement::Statement,
@@ -892,7 +886,6 @@ impl Global for GlobalAppState {}
 
 pub struct WorkspaceStore {
     workspaces: HashSet<WindowHandle<Workspace>>,
-    #[cfg(feature = "call")]
     client: Arc<Client>,
     _subscriptions: Vec<client::Subscription>,
 }
@@ -1124,7 +1117,6 @@ pub struct Workspace {
     window_edited: bool,
     last_window_title: Option<String>,
     dirty_items: HashMap<EntityId, Subscription>,
-    #[cfg(feature = "call")]
     active_call: Option<(Entity<ActiveCall>, Vec<Subscription>)>,
     leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
     database_id: Option<WorkspaceId>,
@@ -1166,7 +1158,6 @@ pub struct FollowerState {
 
 struct FollowerView {
     view: Box<dyn FollowableItemHandle>,
-    #[cfg(feature = "call")]
     location: Option<proto::PanelId>,
 }
 
@@ -1366,15 +1357,10 @@ impl Workspace {
 
         let session_id = app_state.session.read(cx).id().to_owned();
 
-        #[cfg(feature = "call")]
         let mut active_call = None;
-        #[cfg(feature = "call")]
-        {
-            if let Some(call) = ActiveCall::try_global(cx) {
-                let subscriptions =
-                    vec![cx.subscribe_in(&call, window, Self::on_active_call_event)];
-                active_call = Some((call, subscriptions));
-            }
+        if let Some(call) = ActiveCall::try_global(cx) {
+            let subscriptions = vec![cx.subscribe_in(&call, window, Self::on_active_call_event)];
+            active_call = Some((call, subscriptions));
         }
 
         let (serializable_items_tx, serializable_items_rx) =
@@ -1460,7 +1446,6 @@ impl Workspace {
             window_edited: false,
             last_window_title: None,
             dirty_items: Default::default(),
-            #[cfg(feature = "call")]
             active_call,
             database_id: workspace_id,
             app_state,
@@ -2265,7 +2250,6 @@ impl Workspace {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Task<Result<bool>> {
-        #[cfg(feature = "call")]
         let active_call = self.active_call().cloned();
 
         // On Linux and Windows, closing the last window should restore the last workspace.
@@ -2274,58 +2258,51 @@ impl Workspace {
             && cx.windows().len() == 1;
 
         cx.spawn_in(window, async move |this, cx| {
-            #[cfg(feature = "call")]
+            let workspace_count = cx.update(|_window, cx| {
+                cx.windows()
+                    .iter()
+                    .filter(|window| window.downcast::<Workspace>().is_some())
+                    .count()
+            })?;
+
+            if let Some(active_call) = active_call
+                && workspace_count == 1
+                && active_call.read_with(cx, |call, _| call.room().is_some())?
             {
-                let workspace_count = cx.update(|_window, cx| {
-                    cx.windows()
-                        .iter()
-                        .filter(|window| window.downcast::<Workspace>().is_some())
-                        .count()
-                })?;
-                if let Some(active_call) = active_call
-                    && workspace_count == 1
-                    && active_call.read_with(cx, |call, _| call.room().is_some())?
-                {
-                    if close_intent == CloseIntent::CloseWindow {
-                        let answer = cx.update(|window, cx| {
-                            window.prompt(
-                                PromptLevel::Warning,
-                                "Do you want to leave the current call?",
-                                None,
-                                &["Close window and hang up", "Cancel"],
-                                cx,
-                            )
-                        })?;
+                if close_intent == CloseIntent::CloseWindow {
+                    let answer = cx.update(|window, cx| {
+                        window.prompt(
+                            PromptLevel::Warning,
+                            "Do you want to leave the current call?",
+                            None,
+                            &["Close window and hang up", "Cancel"],
+                            cx,
+                        )
+                    })?;
 
-                        if answer.await.log_err() == Some(1) {
-                            return anyhow::Ok(false);
-                        } else {
-                            {
-                                active_call
-                                    .update(cx, |call, cx| call.hang_up(cx))?
-                                    .await
-                                    .log_err();
-                            }
-                        }
+                    if answer.await.log_err() == Some(1) {
+                        return anyhow::Ok(false);
+                    } else {
+                        active_call
+                            .update(cx, |call, cx| call.hang_up(cx))?
+                            .await
+                            .log_err();
                     }
-                    if close_intent == CloseIntent::ReplaceWindow {
-                        #[cfg(feature = "call")]
-                        {
-                            _ = active_call.update(cx, |active_call, cx| {
-                                let workspace = cx
-                                    .windows()
-                                    .iter()
-                                    .filter_map(|window| window.downcast::<Workspace>())
-                                    .next()
-                                    .unwrap();
-                                let project = workspace.read(cx)?.project.clone();
-                                if project.read(cx).is_shared() {
-                                    active_call.unshare_project(project, cx)?;
-                                }
-                                anyhow::Ok(())
-                            })?;
+                }
+                if close_intent == CloseIntent::ReplaceWindow {
+                    _ = active_call.update(cx, |this, cx| {
+                        let workspace = cx
+                            .windows()
+                            .iter()
+                            .filter_map(|window| window.downcast::<Workspace>())
+                            .next()
+                            .unwrap();
+                        let project = workspace.read(cx)?.project.clone();
+                        if project.read(cx).is_shared() {
+                            this.unshare_project(project, cx)?;
                         }
-                    }
+                        Ok::<_, anyhow::Error>(())
+                    })?;
                 }
             }
 
@@ -3509,7 +3486,6 @@ impl Workspace {
         item
     }
 
-    #[cfg(feature = "call")]
     pub fn open_shared_screen(
         &mut self,
         peer_id: PeerId,
@@ -3931,11 +3907,8 @@ impl Workspace {
                 pane.update(cx, |pane, _| {
                     pane.track_alternate_file_items();
                 });
-                #[cfg(feature = "call")]
-                {
-                    if *local {
-                        self.unfollow_in_pane(pane, window, cx);
-                    }
+                if *local {
+                    self.unfollow_in_pane(pane, window, cx);
                 }
                 serialize_workspace = *focus_changed || pane != self.active_pane();
                 if pane == self.active_pane() {
@@ -4000,17 +3973,6 @@ impl Workspace {
         }
     }
 
-    #[cfg(not(feature = "call"))]
-    pub fn unfollow_in_pane(
-        &mut self,
-        _pane: &Entity<Pane>,
-        _window: &mut Window,
-        _cx: &mut Context<Workspace>,
-    ) -> Option<CollaboratorId> {
-        None
-    }
-
-    #[cfg(feature = "call")]
     pub fn unfollow_in_pane(
         &mut self,
         pane: &Entity<Pane>,
@@ -4160,7 +4122,6 @@ impl Workspace {
         cx.notify();
     }
 
-    #[cfg(feature = "call")]
     pub fn start_following(
         &mut self,
         leader_id: impl Into<CollaboratorId>,
@@ -4224,16 +4185,6 @@ impl Workspace {
         }
     }
 
-    #[cfg(not(feature = "call"))]
-    pub fn follow_next_collaborator(
-        &mut self,
-        _: &FollowNextCollaborator,
-        _window: &mut Window,
-        _cx: &mut Context<Self>,
-    ) {
-    }
-
-    #[cfg(feature = "call")]
     pub fn follow_next_collaborator(
         &mut self,
         _: &FollowNextCollaborator,
@@ -4282,16 +4233,6 @@ impl Workspace {
         }
     }
 
-    #[cfg(not(feature = "call"))]
-    pub fn follow(
-        &mut self,
-        _leader_id: impl Into<CollaboratorId>,
-        _window: &mut Window,
-        _cx: &mut Context<Self>,
-    ) {
-    }
-
-    #[cfg(feature = "call")]
     pub fn follow(
         &mut self,
         leader_id: impl Into<CollaboratorId>,
@@ -4344,17 +4285,6 @@ impl Workspace {
         }
     }
 
-    #[cfg(not(feature = "call"))]
-    pub fn unfollow(
-        &mut self,
-        _leader_id: impl Into<CollaboratorId>,
-        _window: &mut Window,
-        _cx: &mut Context<Self>,
-    ) -> Option<()> {
-        None
-    }
-
-    #[cfg(feature = "call")]
     pub fn unfollow(
         &mut self,
         leader_id: impl Into<CollaboratorId>,
@@ -4665,7 +4595,6 @@ impl Workspace {
             anyhow::bail!("no id for view");
         };
         let id = ViewId::from_proto(id)?;
-        #[cfg(feature = "call")]
         let panel_id = view.panel_id.and_then(proto::PanelId::from_i32);
 
         let pane = this.update(cx, |this, _cx| {
@@ -4738,7 +4667,6 @@ impl Workspace {
                 id,
                 FollowerView {
                     view: item,
-                    #[cfg(feature = "call")]
                     location: panel_id,
                 },
             );
@@ -4793,7 +4721,6 @@ impl Workspace {
                     view.map(|view| {
                         entry.insert(FollowerView {
                             view,
-                            #[cfg(feature = "call")]
                             location: None,
                         })
                     })
@@ -4984,17 +4911,6 @@ impl Workspace {
         )
     }
 
-    #[cfg(not(feature = "call"))]
-    fn active_item_for_peer(
-        &self,
-        _peer_id: PeerId,
-        _window: &mut Window,
-        _cx: &mut Context<Self>,
-    ) -> Option<(Option<PanelId>, Box<dyn ItemHandle>)> {
-        None
-    }
-
-    #[cfg(feature = "call")]
     fn active_item_for_peer(
         &self,
         peer_id: PeerId,
@@ -5036,7 +4952,6 @@ impl Workspace {
         item_to_activate
     }
 
-    #[cfg(feature = "call")]
     fn shared_screen_for_peer(
         &self,
         peer_id: PeerId,
@@ -5087,12 +5002,10 @@ impl Workspace {
         }
     }
 
-    #[cfg(feature = "call")]
     pub fn active_call(&self) -> Option<&Entity<ActiveCall>> {
         self.active_call.as_ref().map(|(call, _)| call)
     }
 
-    #[cfg(feature = "call")]
     fn on_active_call_event(
         &mut self,
         _: &Entity<ActiveCall>,
@@ -6005,17 +5918,6 @@ impl Workspace {
     }
 }
 
-#[cfg(not(feature = "call"))]
-fn leader_border_for_pane(
-    _follower_states: &HashMap<CollaboratorId, FollowerState>,
-    _pane: &Entity<Pane>,
-    _: &Window,
-    _cx: &App,
-) -> Option<Div> {
-    None
-}
-
-#[cfg(feature = "call")]
 fn leader_border_for_pane(
     follower_states: &HashMap<CollaboratorId, FollowerState>,
     pane: &Entity<Pane>,
@@ -6482,7 +6384,6 @@ impl Render for Workspace {
                                                                         &PaneRenderContext {
                                                                             follower_states:
                                                                                 &self.follower_states,
-                                                                            #[cfg(feature = "call")]
                                                                             active_call: self.active_call(),
                                                                             active_pane: &self.active_pane,
                                                                             app_state: &self.app_state,
@@ -6547,7 +6448,6 @@ impl Render for Workspace {
                                                                                 &PaneRenderContext {
                                                                                     follower_states:
                                                                                         &self.follower_states,
-                                                                                    #[cfg(feature = "call")]
                                                                                     active_call: self.active_call(),
                                                                                     active_pane: &self.active_pane,
                                                                                     app_state: &self.app_state,
@@ -6610,7 +6510,6 @@ impl Render for Workspace {
                                                                                 &PaneRenderContext {
                                                                                     follower_states:
                                                                                         &self.follower_states,
-                                                                                    #[cfg(feature = "call")]
                                                                                     active_call: self.active_call(),
                                                                                     active_pane: &self.active_pane,
                                                                                     app_state: &self.app_state,
@@ -6659,7 +6558,6 @@ impl Render for Workspace {
                                                                 &PaneRenderContext {
                                                                     follower_states:
                                                                         &self.follower_states,
-                                                                    #[cfg(feature = "call")]
                                                                     active_call: self.active_call(),
                                                                     active_pane: &self.active_pane,
                                                                     app_state: &self.app_state,
@@ -6733,22 +6631,10 @@ impl WorkspaceStore {
                 client.add_request_handler(cx.weak_entity(), Self::handle_follow),
                 client.add_message_handler(cx.weak_entity(), Self::handle_update_followers),
             ],
-            #[cfg(feature = "call")]
             client,
         }
     }
 
-    #[cfg(not(feature = "call"))]
-    pub fn update_followers(
-        &self,
-        _project_id: Option<u64>,
-        _update: proto::update_followers::Variant,
-        _cx: &App,
-    ) -> Option<()> {
-        None
-    }
-
-    #[cfg(feature = "call")]
     pub fn update_followers(
         &self,
         project_id: Option<u64>,
@@ -6914,7 +6800,6 @@ actions!(
     ]
 );
 
-#[cfg(feature = "call")]
 async fn join_channel_internal(
     channel_id: ChannelId,
     app_state: &Arc<AppState>,
@@ -7062,17 +6947,6 @@ async fn join_channel_internal(
     anyhow::Ok(false)
 }
 
-#[cfg(not(feature = "call"))]
-pub fn join_channel(
-    _channel_id: ChannelId,
-    _app_state: Arc<AppState>,
-    _requesting_window: Option<WindowHandle<Workspace>>,
-    _cx: &mut App,
-) -> Task<Result<()>> {
-    Task::ready(Ok(()))
-}
-
-#[cfg(feature = "call")]
 pub fn join_channel(
     channel_id: ChannelId,
     app_state: Arc<AppState>,
@@ -7580,17 +7454,6 @@ fn serialize_ssh_project(
     })
 }
 
-#[cfg(not(feature = "call"))]
-pub fn join_in_room_project(
-    _project_id: u64,
-    _follow_user_id: u64,
-    _app_state: Arc<AppState>,
-    _cx: &mut App,
-) -> Task<Result<()>> {
-    Task::ready(Ok(()))
-}
-
-#[cfg(feature = "call")]
 pub fn join_in_room_project(
     project_id: u64,
     follow_user_id: u64,