Display language server name in status bar

Antonio Scandurra created

Change summary

crates/lsp/src/lsp.rs              |  22 ++
crates/project/src/project.rs      | 207 +++++++++++++++++++++----------
crates/rpc/proto/zed.proto         |  16 ++
crates/rpc/src/proto.rs            |   6 
crates/server/src/rpc.rs           |  28 +++
crates/server/src/rpc/store.rs     |  20 +++
crates/workspace/src/lsp_status.rs |  15 +
7 files changed, 228 insertions(+), 86 deletions(-)

Detailed changes

crates/lsp/src/lsp.rs 🔗

@@ -35,6 +35,7 @@ type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>;
 pub struct LanguageServer {
     next_id: AtomicUsize,
     outbound_tx: channel::Sender<Vec<u8>>,
+    name: String,
     capabilities: ServerCapabilities,
     notification_handlers: Arc<RwLock<HashMap<&'static str, NotificationHandler>>>,
     response_handlers: Arc<Mutex<HashMap<usize, ResponseHandler>>>,
@@ -118,9 +119,11 @@ impl LanguageServer {
             .spawn()?;
         let stdin = server.stdin.take().unwrap();
         let stdout = server.stdout.take().unwrap();
-        Ok(Self::new_internal(
-            stdin, stdout, root_path, options, background,
-        ))
+        let mut server = Self::new_internal(stdin, stdout, root_path, options, background);
+        if let Some(name) = binary_path.file_name() {
+            server.name = name.to_string_lossy().to_string();
+        }
+        Ok(server)
     }
 
     fn new_internal<Stdin, Stdout>(
@@ -222,6 +225,7 @@ impl LanguageServer {
         Self {
             notification_handlers,
             response_handlers,
+            name: Default::default(),
             capabilities: Default::default(),
             next_id: Default::default(),
             outbound_tx,
@@ -292,7 +296,13 @@ impl LanguageServer {
         };
 
         let response = this.request::<request::Initialize>(params).await?;
-        Arc::get_mut(&mut this).unwrap().capabilities = response.capabilities;
+        {
+            let this = Arc::get_mut(&mut this).unwrap();
+            if let Some(info) = response.server_info {
+                this.name = info.name;
+            }
+            this.capabilities = response.capabilities;
+        }
         this.notify::<notification::Initialized>(InitializedParams {})?;
         Ok(this)
     }
@@ -355,6 +365,10 @@ impl LanguageServer {
         }
     }
 
+    pub fn name<'a>(self: &'a Arc<Self>) -> &'a str {
+        &self.name
+    }
+
     pub fn capabilities<'a>(self: &'a Arc<Self>) -> &'a ServerCapabilities {
         &self.capabilities
     }

crates/project/src/project.rs 🔗

@@ -7,7 +7,7 @@ pub mod worktree;
 use anyhow::{anyhow, Context, Result};
 use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
 use clock::ReplicaId;
-use collections::{hash_map, HashMap, HashSet};
+use collections::{hash_map, BTreeMap, HashMap, HashSet};
 use futures::{future::Shared, Future, FutureExt, StreamExt, TryFutureExt};
 use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
 use gpui::{
@@ -51,7 +51,8 @@ pub struct Project {
     languages: Arc<LanguageRegistry>,
     language_servers: HashMap<(WorktreeId, Arc<str>), Arc<LanguageServer>>,
     started_language_servers: HashMap<(WorktreeId, Arc<str>), Task<Option<Arc<LanguageServer>>>>,
-    pending_language_server_work: HashMap<(usize, String), LspWorkProgress>,
+    pending_language_server_work: BTreeMap<(usize, String), LanguageServerProgress>,
+    language_server_names: HashMap<usize, String>,
     next_language_server_id: usize,
     client: Arc<client::Client>,
     user_store: ModelHandle<UserStore>,
@@ -116,13 +117,13 @@ pub enum Event {
     DiagnosticsUpdated(ProjectPath),
 }
 
-enum LspEvent {
+enum LanguageServerEvent {
     WorkStart {
         token: String,
     },
     WorkProgress {
         token: String,
-        progress: LspWorkProgress,
+        progress: LanguageServerProgress,
     },
     WorkEnd {
         token: String,
@@ -131,7 +132,7 @@ enum LspEvent {
 }
 
 #[derive(Clone, Default)]
-pub struct LspWorkProgress {
+pub struct LanguageServerProgress {
     pub message: Option<String>,
     pub percentage: Option<usize>,
 }
@@ -224,7 +225,8 @@ impl Project {
         client.add_entity_message_handler(Self::handle_add_collaborator);
         client.add_entity_message_handler(Self::handle_buffer_reloaded);
         client.add_entity_message_handler(Self::handle_buffer_saved);
-        client.add_entity_message_handler(Self::handle_lsp_event);
+        client.add_entity_message_handler(Self::handle_start_language_server);
+        client.add_entity_message_handler(Self::handle_update_language_server);
         client.add_entity_message_handler(Self::handle_remove_collaborator);
         client.add_entity_message_handler(Self::handle_register_worktree);
         client.add_entity_message_handler(Self::handle_unregister_worktree);
@@ -325,6 +327,7 @@ impl Project {
                 language_servers: Default::default(),
                 started_language_servers: Default::default(),
                 pending_language_server_work: Default::default(),
+                language_server_names: Default::default(),
                 next_language_server_id: 0,
                 nonce: StdRng::from_entropy().gen(),
             }
@@ -396,6 +399,11 @@ impl Project {
                 language_servers: Default::default(),
                 started_language_servers: Default::default(),
                 pending_language_server_work: Default::default(),
+                language_server_names: response
+                    .language_servers
+                    .into_iter()
+                    .map(|s| (s.id as usize, s.name))
+                    .collect(),
                 next_language_server_id: 0,
                 opened_buffers: Default::default(),
                 buffer_snapshots: Default::default(),
@@ -1193,14 +1201,15 @@ impl Project {
                 cx.spawn_weak(|this, mut cx| async move {
                     let mut language_server = language_server?.await.log_err()?;
                     let this = this.upgrade(&cx)?;
-                    let (lsp_events_tx, lsp_events_rx) = smol::channel::unbounded();
+                    let (language_server_events_tx, language_server_events_rx) =
+                        smol::channel::unbounded();
 
                     language_server
                         .on_notification::<lsp::notification::PublishDiagnostics, _>({
-                            let lsp_events_tx = lsp_events_tx.clone();
+                            let language_server_events_tx = language_server_events_tx.clone();
                             move |params| {
-                                lsp_events_tx
-                                    .try_send(LspEvent::DiagnosticsUpdate(params))
+                                language_server_events_tx
+                                    .try_send(LanguageServerEvent::DiagnosticsUpdate(params))
                                     .ok();
                             }
                         })
@@ -1219,13 +1228,15 @@ impl Project {
                             match params.value {
                                 lsp::ProgressParamsValue::WorkDone(progress) => match progress {
                                     lsp::WorkDoneProgress::Begin(_) => {
-                                        lsp_events_tx.try_send(LspEvent::WorkStart { token }).ok();
+                                        language_server_events_tx
+                                            .try_send(LanguageServerEvent::WorkStart { token })
+                                            .ok();
                                     }
                                     lsp::WorkDoneProgress::Report(report) => {
-                                        lsp_events_tx
-                                            .try_send(LspEvent::WorkProgress {
+                                        language_server_events_tx
+                                            .try_send(LanguageServerEvent::WorkProgress {
                                                 token,
-                                                progress: LspWorkProgress {
+                                                progress: LanguageServerProgress {
                                                     message: report.message,
                                                     percentage: report
                                                         .percentage
@@ -1235,7 +1246,9 @@ impl Project {
                                             .ok();
                                     }
                                     lsp::WorkDoneProgress::End(_) => {
-                                        lsp_events_tx.try_send(LspEvent::WorkEnd { token }).ok();
+                                        language_server_events_tx
+                                            .try_send(LanguageServerEvent::WorkEnd { token })
+                                            .ok();
                                     }
                                 },
                             }
@@ -1246,10 +1259,10 @@ impl Project {
                     cx.spawn(|mut cx| {
                         let this = this.downgrade();
                         async move {
-                            while let Ok(event) = lsp_events_rx.recv().await {
+                            while let Ok(event) = language_server_events_rx.recv().await {
                                 let this = this.upgrade(&cx)?;
                                 this.update(&mut cx, |this, cx| {
-                                    this.on_local_lsp_event(server_id, event, &language, cx)
+                                    this.on_lsp_event(server_id, event, &language, cx)
                                 });
                             }
                             Some(())
@@ -1261,6 +1274,20 @@ impl Project {
                     this.update(&mut cx, |this, cx| {
                         this.language_servers
                             .insert(key.clone(), language_server.clone());
+                        this.language_server_names
+                            .insert(server_id, language_server.name().to_string());
+
+                        if let Some(project_id) = this.remote_id() {
+                            this.client
+                                .send(proto::StartLanguageServer {
+                                    project_id,
+                                    server: Some(proto::LanguageServer {
+                                        id: server_id as u64,
+                                        name: language_server.name().to_string(),
+                                    }),
+                                })
+                                .log_err();
+                        }
 
                         // Tell the language server about every open buffer in the worktree that matches the language.
                         for buffer in this.opened_buffers.values() {
@@ -1315,6 +1342,7 @@ impl Project {
                             }
                         }
 
+                        cx.notify();
                         Some(())
                     });
 
@@ -1323,33 +1351,35 @@ impl Project {
             });
     }
 
-    fn on_local_lsp_event(
+    fn on_lsp_event(
         &mut self,
         language_server_id: usize,
-        event: LspEvent,
+        event: LanguageServerEvent,
         language: &Arc<Language>,
         cx: &mut ModelContext<Self>,
     ) {
         let disk_diagnostics_token = language.disk_based_diagnostics_progress_token();
         match event {
-            LspEvent::WorkStart { token } => {
+            LanguageServerEvent::WorkStart { token } => {
                 if Some(&token) == disk_diagnostics_token {
                     self.disk_based_diagnostics_started(cx);
-                    self.broadcast_lsp_event(
+                    self.broadcast_language_server_update(
                         language_server_id,
-                        proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(
+                        proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
                             proto::LspDiskBasedDiagnosticsUpdating {},
                         ),
                     );
                 } else {
                     self.on_lsp_work_start(language_server_id, token.clone(), cx);
-                    self.broadcast_lsp_event(
+                    self.broadcast_language_server_update(
                         language_server_id,
-                        proto::lsp_event::Variant::WorkStart(proto::LspWorkStart { token }),
+                        proto::update_language_server::Variant::WorkStart(proto::LspWorkStart {
+                            token,
+                        }),
                     );
                 }
             }
-            LspEvent::WorkProgress { token, progress } => {
+            LanguageServerEvent::WorkProgress { token, progress } => {
                 if Some(&token) != disk_diagnostics_token {
                     self.on_lsp_work_progress(
                         language_server_id,
@@ -1357,41 +1387,45 @@ impl Project {
                         progress.clone(),
                         cx,
                     );
-                    self.broadcast_lsp_event(
+                    self.broadcast_language_server_update(
                         language_server_id,
-                        proto::lsp_event::Variant::WorkProgress(proto::LspWorkProgress {
-                            token,
-                            message: progress.message,
-                            percentage: progress.percentage.map(|p| p as u32),
-                        }),
+                        proto::update_language_server::Variant::WorkProgress(
+                            proto::LspWorkProgress {
+                                token,
+                                message: progress.message,
+                                percentage: progress.percentage.map(|p| p as u32),
+                            },
+                        ),
                     );
                 }
             }
-            LspEvent::WorkEnd { token } => {
+            LanguageServerEvent::WorkEnd { token } => {
                 if Some(&token) == disk_diagnostics_token {
                     self.disk_based_diagnostics_finished(cx);
-                    self.broadcast_lsp_event(
+                    self.broadcast_language_server_update(
                         language_server_id,
-                        proto::lsp_event::Variant::DiskBasedDiagnosticsUpdated(
+                        proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
                             proto::LspDiskBasedDiagnosticsUpdated {},
                         ),
                     );
                 } else {
                     self.on_lsp_work_end(language_server_id, token.clone(), cx);
-                    self.broadcast_lsp_event(
+                    self.broadcast_language_server_update(
                         language_server_id,
-                        proto::lsp_event::Variant::WorkEnd(proto::LspWorkEnd { token }),
+                        proto::update_language_server::Variant::WorkEnd(proto::LspWorkEnd {
+                            token,
+                        }),
                     );
                 }
             }
-            LspEvent::DiagnosticsUpdate(mut params) => {
+            LanguageServerEvent::DiagnosticsUpdate(mut params) => {
                 language.process_diagnostics(&mut params);
 
                 if disk_diagnostics_token.is_none() {
                     self.disk_based_diagnostics_started(cx);
-                    self.broadcast_lsp_event(
+                    self.broadcast_language_server_update(
                         language_server_id,
-                        proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(
+                        proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
                             proto::LspDiskBasedDiagnosticsUpdating {},
                         ),
                     );
@@ -1406,9 +1440,9 @@ impl Project {
                 .log_err();
                 if disk_diagnostics_token.is_none() {
                     self.disk_based_diagnostics_finished(cx);
-                    self.broadcast_lsp_event(
+                    self.broadcast_language_server_update(
                         language_server_id,
-                        proto::lsp_event::Variant::DiskBasedDiagnosticsUpdated(
+                        proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
                             proto::LspDiskBasedDiagnosticsUpdated {},
                         ),
                     );
@@ -1425,7 +1459,7 @@ impl Project {
     ) {
         self.pending_language_server_work.insert(
             (language_server_id, token),
-            LspWorkProgress {
+            LanguageServerProgress {
                 message: None,
                 percentage: None,
             },
@@ -1437,7 +1471,7 @@ impl Project {
         &mut self,
         language_server_id: usize,
         token: String,
-        progress: LspWorkProgress,
+        progress: LanguageServerProgress,
         cx: &mut ModelContext<Self>,
     ) {
         self.pending_language_server_work
@@ -1456,10 +1490,14 @@ impl Project {
         cx.notify();
     }
 
-    fn broadcast_lsp_event(&self, language_server_id: usize, event: proto::lsp_event::Variant) {
+    fn broadcast_language_server_update(
+        &self,
+        language_server_id: usize,
+        event: proto::update_language_server::Variant,
+    ) {
         if let Some(project_id) = self.remote_id() {
             self.client
-                .send(proto::LspEvent {
+                .send(proto::UpdateLanguageServer {
                     project_id,
                     language_server_id: language_server_id as u64,
                     variant: Some(event),
@@ -1468,10 +1506,15 @@ impl Project {
         }
     }
 
-    pub fn pending_language_server_work(&self) -> impl Iterator<Item = (&str, &LspWorkProgress)> {
-        self.pending_language_server_work
-            .iter()
-            .map(|((_, token), progress)| (token.as_str(), progress))
+    pub fn pending_language_server_work(
+        &self,
+    ) -> impl Iterator<Item = (&str, &str, &LanguageServerProgress)> {
+        self.pending_language_server_work.iter().filter_map(
+            |((language_server_id, token), progress)| {
+                let name = self.language_server_names.get(language_server_id)?;
+                Some((name.as_str(), token.as_str(), progress))
+            },
+        )
     }
 
     pub fn update_diagnostics(
@@ -3212,9 +3255,27 @@ impl Project {
         })
     }
 
-    async fn handle_lsp_event(
+    async fn handle_start_language_server(
+        this: ModelHandle<Self>,
+        envelope: TypedEnvelope<proto::StartLanguageServer>,
+        _: Arc<Client>,
+        mut cx: AsyncAppContext,
+    ) -> Result<()> {
+        let server = envelope
+            .payload
+            .server
+            .ok_or_else(|| anyhow!("invalid server"))?;
+        this.update(&mut cx, |this, cx| {
+            this.language_server_names
+                .insert(server.id as usize, server.name);
+            cx.notify();
+        });
+        Ok(())
+    }
+
+    async fn handle_update_language_server(
         this: ModelHandle<Self>,
-        envelope: TypedEnvelope<proto::LspEvent>,
+        envelope: TypedEnvelope<proto::UpdateLanguageServer>,
         _: Arc<Client>,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
@@ -3224,29 +3285,35 @@ impl Project {
             .variant
             .ok_or_else(|| anyhow!("invalid variant"))?
         {
-            proto::lsp_event::Variant::WorkStart(payload) => this.update(&mut cx, |this, cx| {
-                this.on_lsp_work_start(language_server_id, payload.token, cx);
-            }),
-            proto::lsp_event::Variant::WorkProgress(payload) => this.update(&mut cx, |this, cx| {
-                this.on_lsp_work_progress(
-                    language_server_id,
-                    payload.token,
-                    LspWorkProgress {
-                        message: payload.message,
-                        percentage: payload.percentage.map(|p| p as usize),
-                    },
-                    cx,
-                );
-            }),
-            proto::lsp_event::Variant::WorkEnd(payload) => this.update(&mut cx, |this, cx| {
-                this.on_lsp_work_end(language_server_id, payload.token, cx);
-            }),
-            proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(_) => {
+            proto::update_language_server::Variant::WorkStart(payload) => {
+                this.update(&mut cx, |this, cx| {
+                    this.on_lsp_work_start(language_server_id, payload.token, cx);
+                })
+            }
+            proto::update_language_server::Variant::WorkProgress(payload) => {
+                this.update(&mut cx, |this, cx| {
+                    this.on_lsp_work_progress(
+                        language_server_id,
+                        payload.token,
+                        LanguageServerProgress {
+                            message: payload.message,
+                            percentage: payload.percentage.map(|p| p as usize),
+                        },
+                        cx,
+                    );
+                })
+            }
+            proto::update_language_server::Variant::WorkEnd(payload) => {
+                this.update(&mut cx, |this, cx| {
+                    this.on_lsp_work_end(language_server_id, payload.token, cx);
+                })
+            }
+            proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(_) => {
                 this.update(&mut cx, |this, cx| {
                     this.disk_based_diagnostics_started(cx);
                 })
             }
-            proto::lsp_event::Variant::DiskBasedDiagnosticsUpdated(_) => {
+            proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(_) => {
                 this.update(&mut cx, |this, cx| this.disk_based_diagnostics_finished(cx));
             }
         }

crates/rpc/proto/zed.proto 🔗

@@ -37,7 +37,8 @@ message Envelope {
         UnregisterWorktree unregister_worktree = 29;
         UpdateWorktree update_worktree = 31;
         UpdateDiagnosticSummary update_diagnostic_summary = 32;
-        LspEvent lsp_event = 33;
+        StartLanguageServer start_language_server = 33;
+        UpdateLanguageServer update_language_server = 34;
 
         OpenBuffer open_buffer = 35;
         OpenBufferResponse open_buffer_response = 36;
@@ -121,6 +122,7 @@ message JoinProjectResponse {
     uint32 replica_id = 1;
     repeated Worktree worktrees = 2;
     repeated Collaborator collaborators = 3;
+    repeated LanguageServer language_servers = 4;
 }
 
 message LeaveProject {
@@ -409,6 +411,16 @@ message LocalTimestamp {
     uint32 value = 2;
 }
 
+message LanguageServer {
+    uint64 id = 1;
+    string name = 2;
+}
+
+message StartLanguageServer {
+    uint64 project_id = 1;
+    LanguageServer server = 2;
+}
+
 message UpdateDiagnosticSummary {
     uint64 project_id = 1;
     uint64 worktree_id = 2;
@@ -423,7 +435,7 @@ message DiagnosticSummary {
     uint32 hint_count = 5;
 }
 
-message LspEvent {
+message UpdateLanguageServer {
     uint64 project_id = 1;
     uint64 language_server_id = 2;
     oneof variant {

crates/rpc/src/proto.rs 🔗

@@ -171,9 +171,10 @@ messages!(
     (JoinChannelResponse, Foreground),
     (JoinProject, Foreground),
     (JoinProjectResponse, Foreground),
+    (StartLanguageServer, Foreground),
+    (UpdateLanguageServer, Foreground),
     (LeaveChannel, Foreground),
     (LeaveProject, Foreground),
-    (LspEvent, Background),
     (OpenBuffer, Background),
     (OpenBufferForSymbol, Background),
     (OpenBufferForSymbolResponse, Background),
@@ -254,7 +255,6 @@ entity_messages!(
     GetProjectSymbols,
     JoinProject,
     LeaveProject,
-    LspEvent,
     OpenBuffer,
     OpenBufferForSymbol,
     PerformRename,
@@ -262,11 +262,13 @@ entity_messages!(
     RemoveProjectCollaborator,
     SaveBuffer,
     SearchProject,
+    StartLanguageServer,
     UnregisterWorktree,
     UnshareProject,
     UpdateBuffer,
     UpdateBufferFile,
     UpdateDiagnosticSummary,
+    UpdateLanguageServer,
     RegisterWorktree,
     UpdateWorktree,
 );

crates/server/src/rpc.rs 🔗

@@ -83,8 +83,9 @@ impl Server {
             .add_request_handler(Server::register_worktree)
             .add_message_handler(Server::unregister_worktree)
             .add_request_handler(Server::update_worktree)
+            .add_message_handler(Server::start_language_server)
+            .add_message_handler(Server::update_language_server)
             .add_message_handler(Server::update_diagnostic_summary)
-            .add_message_handler(Server::lsp_event)
             .add_request_handler(Server::forward_project_request::<proto::GetDefinition>)
             .add_request_handler(Server::forward_project_request::<proto::GetReferences>)
             .add_request_handler(Server::forward_project_request::<proto::SearchProject>)
@@ -385,6 +386,7 @@ impl Server {
                     worktrees,
                     replica_id: joined.replica_id as u32,
                     collaborators,
+                    language_servers: joined.project.language_servers.clone(),
                 };
                 let connection_ids = joined.project.connection_ids();
                 let contact_user_ids = joined.project.authorized_user_ids();
@@ -534,9 +536,29 @@ impl Server {
         Ok(())
     }
 
-    async fn lsp_event(
+    async fn start_language_server(
+        mut self: Arc<Server>,
+        request: TypedEnvelope<proto::StartLanguageServer>,
+    ) -> tide::Result<()> {
+        let receiver_ids = self.state_mut().start_language_server(
+            request.payload.project_id,
+            request.sender_id,
+            request
+                .payload
+                .server
+                .clone()
+                .ok_or_else(|| anyhow!("invalid language server"))?,
+        )?;
+        broadcast(request.sender_id, receiver_ids, |connection_id| {
+            self.peer
+                .forward_send(request.sender_id, connection_id, request.payload.clone())
+        })?;
+        Ok(())
+    }
+
+    async fn update_language_server(
         self: Arc<Server>,
-        request: TypedEnvelope<proto::LspEvent>,
+        request: TypedEnvelope<proto::UpdateLanguageServer>,
     ) -> tide::Result<()> {
         let receiver_ids = self
             .state()

crates/server/src/rpc/store.rs 🔗

@@ -25,6 +25,7 @@ pub struct Project {
     pub host_user_id: UserId,
     pub share: Option<ProjectShare>,
     pub worktrees: HashMap<u64, Worktree>,
+    pub language_servers: Vec<proto::LanguageServer>,
 }
 
 pub struct Worktree {
@@ -240,6 +241,7 @@ impl Store {
                 host_user_id,
                 share: None,
                 worktrees: Default::default(),
+                language_servers: Default::default(),
             },
         );
         self.next_project_id += 1;
@@ -438,6 +440,24 @@ impl Store {
         Err(anyhow!("no such worktree"))?
     }
 
+    pub fn start_language_server(
+        &mut self,
+        project_id: u64,
+        connection_id: ConnectionId,
+        language_server: proto::LanguageServer,
+    ) -> tide::Result<Vec<ConnectionId>> {
+        let project = self
+            .projects
+            .get_mut(&project_id)
+            .ok_or_else(|| anyhow!("no such project"))?;
+        if project.host_connection_id == connection_id {
+            project.language_servers.push(language_server);
+            return Ok(project.connection_ids());
+        }
+
+        Err(anyhow!("no such project"))?
+    }
+
     pub fn join_project(
         &mut self,
         connection_id: ConnectionId,

crates/workspace/src/lsp_status.rs 🔗

@@ -96,11 +96,16 @@ impl View for LspStatus {
         let theme = &self.settings_rx.borrow().theme;
 
         let mut pending_work = self.project.read(cx).pending_language_server_work();
-        if let Some((progress_token, progress)) = pending_work.next() {
-            let mut message = progress
-                .message
-                .clone()
-                .unwrap_or_else(|| progress_token.to_string());
+        if let Some((lang_server_name, progress_token, progress)) = pending_work.next() {
+            let mut message = lang_server_name.to_string();
+
+            message.push_str(": ");
+            if let Some(progress_message) = progress.message.as_ref() {
+                message.push_str(progress_message);
+            } else {
+                message.push_str(progress_token);
+            }
+
             if let Some(percentage) = progress.percentage {
                 write!(&mut message, " ({}%)", percentage).unwrap();
             }