Retrieve project symbols over RPC

Antonio Scandurra created

Change summary

crates/project/src/project.rs                 | 90 +++++++++++++++++++-
crates/project_symbols/src/project_symbols.rs |  4 
crates/rpc/src/proto.rs                       |  1 
crates/server/src/rpc.rs                      | 87 ++++++++++---------
4 files changed, 132 insertions(+), 50 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -119,7 +119,7 @@ pub struct Definition {
 }
 
 #[derive(Debug)]
-pub struct ProjectSymbol {
+pub struct Symbol {
     pub label: CodeLabel,
     pub lsp_symbol: lsp::SymbolInformation,
 }
@@ -192,6 +192,7 @@ impl Project {
         client.add_entity_request_handler(Self::handle_lsp_command::<GetDefinition>);
         client.add_entity_request_handler(Self::handle_lsp_command::<PrepareRename>);
         client.add_entity_request_handler(Self::handle_lsp_command::<PerformRename>);
+        client.add_entity_request_handler(Self::handle_get_project_symbols);
         client.add_entity_request_handler(Self::handle_open_buffer);
         client.add_entity_request_handler(Self::handle_save_buffer);
     }
@@ -446,7 +447,7 @@ impl Project {
             .find(|worktree| worktree.read(cx).id() == id)
     }
 
-    pub fn share(&self, cx: &mut ModelContext<Self>) -> Task<anyhow::Result<()>> {
+    pub fn share(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
         let rpc = self.client.clone();
         cx.spawn(|this, mut cx| async move {
             let project_id = this.update(&mut cx, |this, _| {
@@ -483,7 +484,7 @@ impl Project {
         })
     }
 
-    pub fn unshare(&self, cx: &mut ModelContext<Self>) -> Task<anyhow::Result<()>> {
+    pub fn unshare(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
         let rpc = self.client.clone();
         cx.spawn(|this, mut cx| async move {
             let project_id = this.update(&mut cx, |this, _| {
@@ -1226,7 +1227,7 @@ impl Project {
         &self,
         query: &str,
         cx: &mut ModelContext<Self>,
-    ) -> Task<Result<HashMap<String, Vec<ProjectSymbol>>>> {
+    ) -> Task<Result<HashMap<String, Vec<Symbol>>>> {
         if self.is_local() {
             let mut language_servers = HashMap::default();
             for ((_, language_name), language_server) in self.language_servers.iter() {
@@ -1257,13 +1258,56 @@ impl Project {
                         let label = language
                             .label_for_symbol(&lsp_symbol)
                             .unwrap_or_else(|| CodeLabel::plain(lsp_symbol.name.clone(), None));
-                        language_symbols.push(ProjectSymbol { label, lsp_symbol });
+                        language_symbols.push(Symbol { label, lsp_symbol });
                     }
                 }
                 Ok(symbols)
             })
         } else if let Some(project_id) = self.remote_id() {
-            todo!()
+            let request = self.client.request(proto::GetProjectSymbols {
+                project_id,
+                query: query.to_string(),
+            });
+            cx.spawn_weak(|this, cx| async move {
+                let response = request.await?;
+                let mut symbols = HashMap::default();
+                if let Some(this) = this.upgrade(&cx) {
+                    this.read_with(&cx, |this, _| {
+                        let mut serialized_symbols = response.symbols.into_iter();
+                        for (language_name, symbol_count) in response
+                            .languages
+                            .into_iter()
+                            .zip(response.symbol_counts_per_language)
+                        {
+                            let language = this.languages.get_language(&language_name);
+                            let language_symbols =
+                                symbols.entry(language_name).or_insert(Vec::new());
+                            language_symbols.extend(
+                                serialized_symbols
+                                    .by_ref()
+                                    .take(symbol_count as usize)
+                                    .filter_map(|serialized_symbol| {
+                                        let lsp_symbol =
+                                            serde_json::from_slice(&serialized_symbol.lsp_symbol)
+                                                .log_err()?;
+                                        Some(Symbol {
+                                            label: language
+                                                .and_then(|language| {
+                                                    language.label_for_symbol(&lsp_symbol)
+                                                })
+                                                .unwrap_or(CodeLabel::plain(
+                                                    lsp_symbol.name.clone(),
+                                                    None,
+                                                )),
+                                            lsp_symbol,
+                                        })
+                                    }),
+                            );
+                        }
+                    })
+                }
+                Ok(symbols)
+            })
         } else {
             Task::ready(Ok(Default::default()))
         }
@@ -2578,12 +2622,42 @@ impl Project {
         })
     }
 
+    async fn handle_get_project_symbols(
+        this: ModelHandle<Self>,
+        envelope: TypedEnvelope<proto::GetProjectSymbols>,
+        _: Arc<Client>,
+        mut cx: AsyncAppContext,
+    ) -> Result<proto::GetProjectSymbolsResponse> {
+        let symbols = this
+            .update(&mut cx, |this, cx| {
+                this.symbols(&envelope.payload.query, cx)
+            })
+            .await?;
+
+        let mut languages = Vec::new();
+        let mut symbol_counts_per_language = Vec::new();
+        let mut serialized_symbols = Vec::new();
+        for (language_name, language_symbols) in symbols {
+            languages.push(language_name);
+            symbol_counts_per_language.push(language_symbols.len() as u64);
+            serialized_symbols.extend(language_symbols.into_iter().map(|symbol| proto::Symbol {
+                lsp_symbol: serde_json::to_vec(&symbol.lsp_symbol).unwrap(),
+            }));
+        }
+
+        Ok(proto::GetProjectSymbolsResponse {
+            languages,
+            symbol_counts_per_language,
+            symbols: serialized_symbols,
+        })
+    }
+
     async fn handle_open_buffer(
         this: ModelHandle<Self>,
         envelope: TypedEnvelope<proto::OpenBuffer>,
         _: Arc<Client>,
         mut cx: AsyncAppContext,
-    ) -> anyhow::Result<proto::OpenBufferResponse> {
+    ) -> Result<proto::OpenBufferResponse> {
         let peer_id = envelope.original_sender_id()?;
         let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
         let open_buffer = this.update(&mut cx, |this, cx| {
@@ -2744,7 +2818,7 @@ impl Project {
         envelope: TypedEnvelope<proto::CloseBuffer>,
         _: Arc<Client>,
         mut cx: AsyncAppContext,
-    ) -> anyhow::Result<()> {
+    ) -> Result<()> {
         this.update(&mut cx, |this, cx| {
             if let Some(shared_buffers) =
                 this.shared_buffers.get_mut(&envelope.original_sender_id()?)

crates/project_symbols/src/project_symbols.rs 🔗

@@ -11,7 +11,7 @@ use gpui::{
 };
 use ordered_float::OrderedFloat;
 use postage::watch;
-use project::{Project, ProjectSymbol};
+use project::{Project, Symbol};
 use std::{
     cmp::{self, Reverse},
     sync::Arc,
@@ -43,7 +43,7 @@ pub struct ProjectSymbolsView {
     settings: watch::Receiver<Settings>,
     selected_match_index: usize,
     list_state: UniformListState,
-    symbols: Vec<ProjectSymbol>,
+    symbols: Vec<Symbol>,
     match_candidates: Vec<StringMatchCandidate>,
     matches: Vec<StringMatch>,
     pending_symbols_task: Task<Option<()>>,

crates/rpc/src/proto.rs 🔗

@@ -239,6 +239,7 @@ entity_messages!(
     GetCodeActions,
     GetCompletions,
     GetDefinition,
+    GetProjectSymbols,
     JoinProject,
     LeaveProject,
     OpenBuffer,

crates/server/src/rpc.rs 🔗

@@ -79,6 +79,7 @@ impl Server {
             .add_message_handler(Server::disk_based_diagnostics_updating)
             .add_message_handler(Server::disk_based_diagnostics_updated)
             .add_request_handler(Server::get_definition)
+            .add_request_handler(Server::get_project_symbols)
             .add_request_handler(Server::open_buffer)
             .add_message_handler(Server::close_buffer)
             .add_request_handler(Server::update_buffer)
@@ -587,6 +588,20 @@ impl Server {
             .await?)
     }
 
+    async fn get_project_symbols(
+        self: Arc<Server>,
+        request: TypedEnvelope<proto::GetProjectSymbols>,
+    ) -> tide::Result<proto::GetProjectSymbolsResponse> {
+        let host_connection_id = self
+            .state()
+            .read_project(request.payload.project_id, request.sender_id)?
+            .host_connection_id;
+        Ok(self
+            .peer
+            .forward_request(request.sender_id, host_connection_id, request.payload)
+            .await?)
+    }
+
     async fn open_buffer(
         self: Arc<Server>,
         request: TypedEnvelope<proto::OpenBuffer>,
@@ -2001,8 +2016,9 @@ mod tests {
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -2010,9 +2026,7 @@ mod tests {
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),
-            )),
-            
-        );
+            )));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2233,8 +2247,9 @@ mod tests {
             }),
             ..Default::default()
         });
-        Arc::get_mut(&mut lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -2242,9 +2257,7 @@ mod tests {
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),
-            )),
-            
-        );
+            )));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2436,8 +2449,9 @@ mod tests {
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -2445,9 +2459,7 @@ mod tests {
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),
-            )),
-            
-        );
+            )));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2554,8 +2566,9 @@ mod tests {
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -2563,9 +2576,7 @@ mod tests {
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),
-            )),
-            
-        );
+            )));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2703,8 +2714,9 @@ mod tests {
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
 
-        Arc::get_mut(&mut lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -2712,9 +2724,7 @@ mod tests {
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),
-            )),
-            
-        );
+            )));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -2805,8 +2815,9 @@ mod tests {
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -2814,9 +2825,7 @@ mod tests {
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),
-            )),
-            
-        );
+            )));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -3045,8 +3054,9 @@ mod tests {
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
-        Arc::get_mut(&mut lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -3054,9 +3064,7 @@ mod tests {
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),
-            )),
-            
-        );
+            )));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -3852,8 +3860,9 @@ mod tests {
             });
         });
 
-        Arc::get_mut(&mut host_lang_registry).unwrap().add(
-            Arc::new(Language::new(
+        Arc::get_mut(&mut host_lang_registry)
+            .unwrap()
+            .add(Arc::new(Language::new(
                 LanguageConfig {
                     name: "Rust".to_string(),
                     path_suffixes: vec!["rs".to_string()],
@@ -3861,9 +3870,7 @@ mod tests {
                     ..Default::default()
                 },
                 None,
-            )),
-            
-        );
+            )));
 
         let fs = FakeFs::new(cx.background());
         fs.insert_tree(