Start work on allowing language servers to support multiple languages

Max Brunsfeld created

Change summary

crates/language/src/language.rs        |  32 ++++-
crates/project/src/lsp_command.rs      |  23 +---
crates/project/src/project.rs          | 141 +++++++++++++++------------
crates/rpc/proto/zed.proto             |   2 
crates/zed/src/languages/c.rs          |   6 
crates/zed/src/languages/json.rs       |   6 
crates/zed/src/languages/rust.rs       |   4 
crates/zed/src/languages/typescript.rs |   6 
8 files changed, 118 insertions(+), 102 deletions(-)

Detailed changes

crates/language/src/language.rs 🔗

@@ -8,7 +8,7 @@ mod tests;
 
 use anyhow::{anyhow, Context, Result};
 use client::http::{self, HttpClient};
-use collections::HashSet;
+use collections::{HashMap, HashSet};
 use futures::{
     future::{BoxFuture, Shared},
     FutureExt, TryFutureExt,
@@ -67,8 +67,11 @@ pub struct GitHubLspBinaryVersion {
     pub url: http::Url,
 }
 
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct LanguageServerName(pub Arc<str>);
+
 pub trait LspAdapter: 'static + Send + Sync {
-    fn name(&self) -> &'static str;
+    fn name(&self) -> LanguageServerName;
     fn fetch_latest_server_version(
         &self,
         http: Arc<dyn HttpClient>,
@@ -159,7 +162,6 @@ pub struct Language {
     pub(crate) config: LanguageConfig,
     pub(crate) grammar: Option<Arc<Grammar>>,
     pub(crate) adapter: Option<Arc<dyn LspAdapter>>,
-    lsp_binary_path: Mutex<Option<Shared<BoxFuture<'static, Result<PathBuf, Arc<anyhow::Error>>>>>>,
 }
 
 pub struct Grammar {
@@ -186,6 +188,12 @@ pub struct LanguageRegistry {
     lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
     lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)>,
     login_shell_env_loaded: Shared<Task<()>>,
+    lsp_binary_paths: Mutex<
+        HashMap<
+            LanguageServerName,
+            Shared<BoxFuture<'static, Result<PathBuf, Arc<anyhow::Error>>>>,
+        >,
+    >,
 }
 
 impl LanguageRegistry {
@@ -197,6 +205,7 @@ impl LanguageRegistry {
             lsp_binary_statuses_tx,
             lsp_binary_statuses_rx,
             login_shell_env_loaded: login_shell_env_loaded.shared(),
+            lsp_binary_paths: Default::default(),
         }
     }
 
@@ -246,7 +255,7 @@ impl LanguageRegistry {
     }
 
     pub fn start_language_server(
-        &self,
+        self: &Arc<Self>,
         language: Arc<Language>,
         root_path: Arc<Path>,
         http_client: Arc<dyn HttpClient>,
@@ -291,16 +300,18 @@ impl LanguageRegistry {
             .ok_or_else(|| anyhow!("language server download directory has not been assigned"))
             .log_err()?;
 
+        let this = self.clone();
         let adapter = language.adapter.clone()?;
         let background = cx.background().clone();
         let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
         let login_shell_env_loaded = self.login_shell_env_loaded.clone();
         Some(cx.background().spawn(async move {
             login_shell_env_loaded.await;
-            let server_binary_path = language
-                .lsp_binary_path
+            let server_binary_path = this
+                .lsp_binary_paths
                 .lock()
-                .get_or_insert_with(|| {
+                .entry(adapter.name())
+                .or_insert_with(|| {
                     get_server_binary_path(
                         adapter.clone(),
                         language.clone(),
@@ -342,7 +353,7 @@ async fn get_server_binary_path(
     download_dir: Arc<Path>,
     statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
 ) -> Result<PathBuf> {
-    let container_dir = download_dir.join(adapter.name());
+    let container_dir = download_dir.join(adapter.name().0.as_ref());
     if !container_dir.exists() {
         smol::fs::create_dir_all(&container_dir)
             .await
@@ -415,10 +426,13 @@ impl Language {
                 })
             }),
             adapter: None,
-            lsp_binary_path: Default::default(),
         }
     }
 
+    pub fn lsp_adapter(&self) -> Option<Arc<dyn LspAdapter>> {
+        self.adapter.clone()
+    }
+
     pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
         let grammar = self
             .grammar

crates/project/src/lsp_command.rs 🔗

@@ -223,22 +223,19 @@ impl LspCommand for PerformRename {
         mut cx: AsyncAppContext,
     ) -> Result<ProjectTransaction> {
         if let Some(edit) = message {
-            let language_server = project
+            let (lsp_adapter, lsp_server) = project
                 .read_with(&cx, |project, cx| {
                     project
                         .language_server_for_buffer(buffer.read(cx), cx)
                         .cloned()
                 })
                 .ok_or_else(|| anyhow!("no language server found for buffer"))?;
-            let language = buffer
-                .read_with(&cx, |buffer, _| buffer.language().cloned())
-                .ok_or_else(|| anyhow!("no language for buffer"))?;
             Project::deserialize_workspace_edit(
                 project,
                 edit,
                 self.push_to_history,
-                language.name(),
-                language_server,
+                lsp_adapter,
+                lsp_server,
                 &mut cx,
             )
             .await
@@ -343,16 +340,13 @@ impl LspCommand for GetDefinition {
         mut cx: AsyncAppContext,
     ) -> Result<Vec<Location>> {
         let mut definitions = Vec::new();
-        let language_server = project
+        let (lsp_adapter, language_server) = project
             .read_with(&cx, |project, cx| {
                 project
                     .language_server_for_buffer(buffer.read(cx), cx)
                     .cloned()
             })
             .ok_or_else(|| anyhow!("no language server found for buffer"))?;
-        let language = buffer
-            .read_with(&cx, |buffer, _| buffer.language().cloned())
-            .ok_or_else(|| anyhow!("no language for buffer"))?;
 
         if let Some(message) = message {
             let mut unresolved_locations = Vec::new();
@@ -377,7 +371,7 @@ impl LspCommand for GetDefinition {
                     .update(&mut cx, |this, cx| {
                         this.open_local_buffer_via_lsp(
                             target_uri,
-                            language.name(),
+                            lsp_adapter.clone(),
                             language_server.clone(),
                             cx,
                         )
@@ -521,16 +515,13 @@ impl LspCommand for GetReferences {
         mut cx: AsyncAppContext,
     ) -> Result<Vec<Location>> {
         let mut references = Vec::new();
-        let language_server = project
+        let (lsp_adapter, language_server) = project
             .read_with(&cx, |project, cx| {
                 project
                     .language_server_for_buffer(buffer.read(cx), cx)
                     .cloned()
             })
             .ok_or_else(|| anyhow!("no language server found for buffer"))?;
-        let language = buffer
-            .read_with(&cx, |buffer, _| buffer.language().cloned())
-            .ok_or_else(|| anyhow!("no language for buffer"))?;
 
         if let Some(locations) = locations {
             for lsp_location in locations {
@@ -538,7 +529,7 @@ impl LspCommand for GetReferences {
                     .update(&mut cx, |this, cx| {
                         this.open_local_buffer_via_lsp(
                             lsp_location.uri,
-                            language.name(),
+                            lsp_adapter.clone(),
                             language_server.clone(),
                             cx,
                         )

crates/project/src/project.rs 🔗

@@ -18,8 +18,8 @@ use language::{
     proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
     range_from_lsp, Anchor, Bias, Buffer, CodeAction, CodeLabel, Completion, Diagnostic,
     DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry,
-    LocalFile, OffsetRangeExt, Operation, Patch, PointUtf16, TextBufferSnapshot, ToLspPosition,
-    ToOffset, ToPointUtf16, Transaction,
+    LanguageServerName, LocalFile, LspAdapter, OffsetRangeExt, Operation, Patch, PointUtf16,
+    TextBufferSnapshot, ToLspPosition, ToOffset, ToPointUtf16, Transaction,
 };
 use lsp::{DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer};
 use lsp_command::*;
@@ -57,8 +57,10 @@ pub struct Project {
     worktrees: Vec<WorktreeHandle>,
     active_entry: Option<ProjectEntryId>,
     languages: Arc<LanguageRegistry>,
-    language_servers: HashMap<(WorktreeId, Arc<str>), Arc<LanguageServer>>,
-    started_language_servers: HashMap<(WorktreeId, Arc<str>), Task<Option<Arc<LanguageServer>>>>,
+    language_servers:
+        HashMap<(WorktreeId, LanguageServerName), (Arc<dyn LspAdapter>, Arc<LanguageServer>)>,
+    started_language_servers:
+        HashMap<(WorktreeId, LanguageServerName), Task<Option<Arc<LanguageServer>>>>,
     language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
     language_server_settings: Arc<Mutex<serde_json::Value>>,
     next_language_server_id: usize,
@@ -185,7 +187,7 @@ pub struct DocumentHighlight {
 pub struct Symbol {
     pub source_worktree_id: WorktreeId,
     pub worktree_id: WorktreeId,
-    pub language_name: String,
+    pub language_server_name: LanguageServerName,
     pub path: PathBuf,
     pub label: CodeLabel,
     pub name: String,
@@ -957,8 +959,8 @@ impl Project {
     fn open_local_buffer_via_lsp(
         &mut self,
         abs_path: lsp::Url,
-        lang_name: Arc<str>,
-        lang_server: Arc<LanguageServer>,
+        lsp_adapter: Arc<dyn LspAdapter>,
+        lsp_server: Arc<LanguageServer>,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ModelHandle<Buffer>>> {
         cx.spawn(|this, mut cx| async move {
@@ -976,8 +978,10 @@ impl Project {
                     })
                     .await?;
                 this.update(&mut cx, |this, cx| {
-                    this.language_servers
-                        .insert((worktree.read(cx).id(), lang_name), lang_server);
+                    this.language_servers.insert(
+                        (worktree.read(cx).id(), lsp_adapter.name()),
+                        (lsp_adapter, lsp_server),
+                    );
                 });
                 (worktree, PathBuf::new())
             };
@@ -1120,7 +1124,7 @@ impl Project {
                     }
                 }
 
-                if let Some(server) = language_server {
+                if let Some((_, server)) = language_server {
                     server
                         .notify::<lsp::notification::DidOpenTextDocument>(
                             lsp::DidOpenTextDocumentParams {
@@ -1153,7 +1157,7 @@ impl Project {
                     if let Some(file) = File::from_dyn(buffer.file()) {
                         if file.is_local() {
                             let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
-                            if let Some(server) = this.language_server_for_buffer(buffer, cx) {
+                            if let Some((_, server)) = this.language_server_for_buffer(buffer, cx) {
                                 server
                                     .notify::<lsp::notification::DidCloseTextDocument>(
                                         lsp::DidCloseTextDocumentParams {
@@ -1189,7 +1193,7 @@ impl Project {
                 cx.background().spawn(request).detach_and_log_err(cx);
             }
             BufferEvent::Edited { .. } => {
-                let language_server = self
+                let (_, language_server) = self
                     .language_server_for_buffer(buffer.read(cx), cx)?
                     .clone();
                 let buffer = buffer.read(cx);
@@ -1262,11 +1266,11 @@ impl Project {
     fn language_servers_for_worktree(
         &self,
         worktree_id: WorktreeId,
-    ) -> impl Iterator<Item = (&str, &Arc<LanguageServer>)> {
+    ) -> impl Iterator<Item = &(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
         self.language_servers.iter().filter_map(
-            move |((language_server_worktree_id, language_name), server)| {
+            move |((language_server_worktree_id, _), server)| {
                 if *language_server_worktree_id == worktree_id {
-                    Some((language_name.as_ref(), server))
+                    Some(server)
                 } else {
                     None
                 }
@@ -1302,7 +1306,12 @@ impl Project {
         language: Arc<Language>,
         cx: &mut ModelContext<Self>,
     ) {
-        let key = (worktree_id, language.name());
+        let adapter = if let Some(adapter) = language.lsp_adapter() {
+            adapter
+        } else {
+            return;
+        };
+        let key = (worktree_id, adapter.name());
         self.started_language_servers
             .entry(key.clone())
             .or_insert_with(|| {
@@ -1416,7 +1425,7 @@ impl Project {
                     let language_server = language_server.initialize().await.log_err()?;
                     this.update(&mut cx, |this, cx| {
                         this.language_servers
-                            .insert(key.clone(), language_server.clone());
+                            .insert(key.clone(), (adapter, language_server.clone()));
                         this.language_server_statuses.insert(
                             server_id,
                             LanguageServerStatus {
@@ -1459,7 +1468,10 @@ impl Project {
                                 } else {
                                     continue;
                                 };
-                                if (file.worktree.read(cx).id(), language.name()) != key {
+                                if file.worktree.read(cx).id() != key.0
+                                    || language.lsp_adapter().map(|a| a.name())
+                                        != Some(key.1.clone())
+                                {
                                     continue;
                                 }
 
@@ -1675,7 +1687,7 @@ impl Project {
     }
 
     pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
-        for server in self.language_servers.values() {
+        for (_, server) in self.language_servers.values() {
             server
                 .notify::<lsp::notification::DidChangeConfiguration>(
                     lsp::DidChangeConfigurationParams {
@@ -1925,7 +1937,7 @@ impl Project {
             let buffer = buffer_handle.read(cx);
             if let Some(file) = File::from_dyn(buffer.file()) {
                 if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) {
-                    if let Some(server) = self.language_server_for_buffer(buffer, cx) {
+                    if let Some((_, server)) = self.language_server_for_buffer(buffer, cx) {
                         local_buffers.push((buffer_handle, buffer_abs_path, server.clone()));
                     }
                 } else {
@@ -2062,25 +2074,24 @@ impl Project {
     pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
         if self.is_local() {
             let mut language_servers = HashMap::default();
-            for ((worktree_id, language_name), language_server) in self.language_servers.iter() {
-                if let Some((worktree, language)) = self
+            for ((worktree_id, _), (lsp_adapter, language_server)) in self.language_servers.iter() {
+                if let Some(worktree) = self
                     .worktree_for_id(*worktree_id, cx)
                     .and_then(|worktree| worktree.read(cx).as_local())
-                    .zip(self.languages.get_language(language_name))
                 {
                     language_servers
                         .entry(Arc::as_ptr(language_server))
                         .or_insert((
+                            lsp_adapter.clone(),
                             language_server.clone(),
                             *worktree_id,
                             worktree.abs_path().clone(),
-                            language.clone(),
                         ));
                 }
             }
 
             let mut requests = Vec::new();
-            for (language_server, _, _, _) in language_servers.values() {
+            for (_, language_server, _, _) in language_servers.values() {
                 requests.push(language_server.request::<lsp::request::WorkspaceSymbol>(
                     lsp::WorkspaceSymbolParams {
                         query: query.to_string(),
@@ -2095,7 +2106,7 @@ impl Project {
                 let mut symbols = Vec::new();
                 if let Some(this) = this.upgrade(&cx) {
                     this.read_with(&cx, |this, cx| {
-                        for ((_, source_worktree_id, worktree_abs_path, language), lsp_symbols) in
+                        for ((adapter, _, source_worktree_id, worktree_abs_path), lsp_symbols) in
                             language_servers.into_values().zip(responses)
                         {
                             symbols.extend(lsp_symbols.into_iter().flatten().filter_map(
@@ -2112,8 +2123,13 @@ impl Project {
                                         path = relativize_path(&worktree_abs_path, &abs_path);
                                     }
 
-                                    let label = language
-                                        .label_for_symbol(&lsp_symbol.name, lsp_symbol.kind)
+                                    let label = this
+                                        .languages
+                                        .select_language(&path)
+                                        .and_then(|language| {
+                                            language
+                                                .label_for_symbol(&lsp_symbol.name, lsp_symbol.kind)
+                                        })
                                         .unwrap_or_else(|| {
                                             CodeLabel::plain(lsp_symbol.name.clone(), None)
                                         });
@@ -2122,7 +2138,7 @@ impl Project {
                                     Some(Symbol {
                                         source_worktree_id,
                                         worktree_id,
-                                        language_name: language.name().to_string(),
+                                        language_server_name: adapter.name(),
                                         name: lsp_symbol.name,
                                         kind: lsp_symbol.kind,
                                         label,
@@ -2169,9 +2185,9 @@ impl Project {
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ModelHandle<Buffer>>> {
         if self.is_local() {
-            let language_server = if let Some(server) = self.language_servers.get(&(
+            let (lsp_adapter, language_server) = if let Some(server) = self.language_servers.get(&(
                 symbol.source_worktree_id,
-                Arc::from(symbol.language_name.as_str()),
+                symbol.language_server_name.clone(),
             )) {
                 server.clone()
             } else {
@@ -2196,12 +2212,7 @@ impl Project {
                 return Task::ready(Err(anyhow!("invalid symbol path")));
             };
 
-            self.open_local_buffer_via_lsp(
-                symbol_uri,
-                Arc::from(symbol.language_name.as_str()),
-                language_server,
-                cx,
-            )
+            self.open_local_buffer_via_lsp(symbol_uri, lsp_adapter, language_server, cx)
         } else if let Some(project_id) = self.remote_id() {
             let request = self.client.request(proto::OpenBufferForSymbol {
                 project_id,
@@ -2242,7 +2253,7 @@ impl Project {
 
         if worktree.read(cx).as_local().is_some() {
             let buffer_abs_path = buffer_abs_path.unwrap();
-            let lang_server =
+            let (_, lang_server) =
                 if let Some(server) = self.language_server_for_buffer(source_buffer, cx) {
                     server.clone()
                 } else {
@@ -2356,7 +2367,8 @@ impl Project {
         let buffer_id = buffer.remote_id();
 
         if self.is_local() {
-            let lang_server = if let Some(server) = self.language_server_for_buffer(buffer, cx) {
+            let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
+            {
                 server.clone()
             } else {
                 return Task::ready(Ok(Default::default()));
@@ -2447,7 +2459,8 @@ impl Project {
 
         if worktree.read(cx).as_local().is_some() {
             let buffer_abs_path = buffer_abs_path.unwrap();
-            let lang_server = if let Some(server) = self.language_server_for_buffer(buffer, cx) {
+            let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
+            {
                 server.clone()
             } else {
                 return Task::ready(Ok(Default::default()));
@@ -2534,16 +2547,12 @@ impl Project {
     ) -> Task<Result<ProjectTransaction>> {
         if self.is_local() {
             let buffer = buffer_handle.read(cx);
-            let lang_name = if let Some(lang) = buffer.language() {
-                lang.name()
-            } else {
-                return Task::ready(Ok(Default::default()));
-            };
-            let lang_server = if let Some(server) = self.language_server_for_buffer(buffer, cx) {
-                server.clone()
-            } else {
-                return Task::ready(Ok(Default::default()));
-            };
+            let (lsp_adapter, lang_server) =
+                if let Some(server) = self.language_server_for_buffer(buffer, cx) {
+                    server.clone()
+                } else {
+                    return Task::ready(Ok(Default::default()));
+                };
             let range = action.range.to_point_utf16(buffer);
 
             cx.spawn(|this, mut cx| async move {
@@ -2580,7 +2589,7 @@ impl Project {
                         this,
                         edit,
                         push_to_history,
-                        lang_name,
+                        lsp_adapter,
                         lang_server,
                         &mut cx,
                     )
@@ -2616,7 +2625,7 @@ impl Project {
         this: ModelHandle<Self>,
         edit: lsp::WorkspaceEdit,
         push_to_history: bool,
-        language_name: Arc<str>,
+        lsp_adapter: Arc<dyn LspAdapter>,
         language_server: Arc<LanguageServer>,
         cx: &mut AsyncAppContext,
     ) -> Result<ProjectTransaction> {
@@ -2693,7 +2702,7 @@ impl Project {
                         .update(cx, |this, cx| {
                             this.open_local_buffer_via_lsp(
                                 op.text_document.uri,
-                                language_name.clone(),
+                                lsp_adapter.clone(),
                                 language_server.clone(),
                                 cx,
                             )
@@ -2988,7 +2997,7 @@ impl Project {
         let buffer = buffer_handle.read(cx);
         if self.is_local() {
             let file = File::from_dyn(buffer.file()).and_then(File::as_local);
-            if let Some((file, language_server)) =
+            if let Some((file, (_, language_server))) =
                 file.zip(self.language_server_for_buffer(buffer, cx).cloned())
             {
                 let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
@@ -4086,9 +4095,8 @@ impl Project {
     }
 
     fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result<Symbol> {
-        let language = self
-            .languages
-            .get_language(&serialized_symbol.language_name);
+        let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
+        let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
         let start = serialized_symbol
             .start
             .ok_or_else(|| anyhow!("invalid start"))?;
@@ -4096,15 +4104,17 @@ impl Project {
             .end
             .ok_or_else(|| anyhow!("invalid end"))?;
         let kind = unsafe { mem::transmute(serialized_symbol.kind) };
+        let path = PathBuf::from(serialized_symbol.path);
+        let language = self.languages.select_language(&path);
         Ok(Symbol {
-            source_worktree_id: WorktreeId::from_proto(serialized_symbol.source_worktree_id),
-            worktree_id: WorktreeId::from_proto(serialized_symbol.worktree_id),
-            language_name: serialized_symbol.language_name.clone(),
+            source_worktree_id,
+            worktree_id,
+            language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()),
             label: language
                 .and_then(|language| language.label_for_symbol(&serialized_symbol.name, kind))
                 .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)),
             name: serialized_symbol.name,
-            path: PathBuf::from(serialized_symbol.path),
+            path,
             range: PointUtf16::new(start.row, start.column)..PointUtf16::new(end.row, end.column),
             kind,
             signature: serialized_symbol
@@ -4349,10 +4359,11 @@ impl Project {
         &self,
         buffer: &Buffer,
         cx: &AppContext,
-    ) -> Option<&Arc<LanguageServer>> {
+    ) -> Option<&(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
         if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
             let worktree_id = file.worktree_id(cx);
-            self.language_servers.get(&(worktree_id, language.name()))
+            self.language_servers
+                .get(&(worktree_id, language.lsp_adapter()?.name()))
         } else {
             None
         }
@@ -4466,7 +4477,7 @@ impl Entity for Project {
         let shutdown_futures = self
             .language_servers
             .drain()
-            .filter_map(|(_, server)| server.shutdown())
+            .filter_map(|(_, (_, server))| server.shutdown())
             .collect::<Vec<_>>();
         Some(
             async move {
@@ -4537,7 +4548,7 @@ fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
     proto::Symbol {
         source_worktree_id: symbol.source_worktree_id.to_proto(),
         worktree_id: symbol.worktree_id.to_proto(),
-        language_name: symbol.language_name.clone(),
+        language_server_name: symbol.language_server_name.0.to_string(),
         name: symbol.name.clone(),
         kind: unsafe { mem::transmute(symbol.kind) },
         path: symbol.path.to_string_lossy().to_string(),

crates/rpc/proto/zed.proto 🔗

@@ -229,7 +229,7 @@ message GetProjectSymbolsResponse {
 message Symbol {
     uint64 source_worktree_id = 1;
     uint64 worktree_id = 2;
-    string language_name = 3;
+    string language_server_name = 3;
     string name = 4;
     int32 kind = 5;
     string path = 6;

crates/zed/src/languages/c.rs 🔗

@@ -3,7 +3,7 @@ use client::http::{self, HttpClient, Method};
 use futures::{future::BoxFuture, FutureExt, StreamExt};
 pub use language::*;
 use smol::fs::{self, File};
-use std::{any::Any, path::PathBuf, str, sync::Arc};
+use std::{any::Any, path::PathBuf, sync::Arc};
 use util::{ResultExt, TryFutureExt};
 
 use super::GithubRelease;
@@ -11,8 +11,8 @@ use super::GithubRelease;
 pub struct CLspAdapter;
 
 impl super::LspAdapter for CLspAdapter {
-    fn name(&self) -> &'static str {
-        "clangd"
+    fn name(&self) -> LanguageServerName {
+        LanguageServerName("clangd".into())
     }
 
     fn fetch_latest_server_version(

crates/zed/src/languages/json.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Context, Result};
 use client::http::HttpClient;
 use futures::{future::BoxFuture, FutureExt, StreamExt};
-use language::LspAdapter;
+use language::{LanguageServerName, LspAdapter};
 use serde::Deserialize;
 use serde_json::json;
 use smol::fs;
@@ -16,8 +16,8 @@ impl JsonLspAdapter {
 }
 
 impl LspAdapter for JsonLspAdapter {
-    fn name(&self) -> &'static str {
-        "vscode-json-languageserver"
+    fn name(&self) -> LanguageServerName {
+        LanguageServerName("vscode-json-languageserver".into())
     }
 
     fn server_args(&self) -> &[&str] {

crates/zed/src/languages/rust.rs 🔗

@@ -14,8 +14,8 @@ use super::GithubRelease;
 pub struct RustLspAdapter;
 
 impl LspAdapter for RustLspAdapter {
-    fn name(&self) -> &'static str {
-        "rust-analyzer"
+    fn name(&self) -> LanguageServerName {
+        LanguageServerName("rust-analyzer".into())
     }
 
     fn fetch_latest_server_version(

crates/zed/src/languages/typescript.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Context, Result};
 use client::http::HttpClient;
 use futures::{future::BoxFuture, FutureExt, StreamExt};
-use language::LspAdapter;
+use language::{LanguageServerName, LspAdapter};
 use serde::Deserialize;
 use serde_json::json;
 use smol::fs;
@@ -20,8 +20,8 @@ struct Versions {
 }
 
 impl LspAdapter for TypeScriptLspAdapter {
-    fn name(&self) -> &'static str {
-        "typescript-language-server"
+    fn name(&self) -> LanguageServerName {
+        LanguageServerName("typescript-language-server".into())
     }
 
     fn server_args(&self) -> &[&str] {