Avoid sending unhandled LSP requests to JSON language server

Max Brunsfeld and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/project/src/lsp_command.rs | 10 +++++++++-
crates/project/src/project.rs     | 23 +++++++++++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)

Detailed changes

crates/project/src/lsp_command.rs 🔗

@@ -8,7 +8,7 @@ use language::{
     proto::{deserialize_anchor, serialize_anchor},
     range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToLspPosition, ToPointUtf16,
 };
-use lsp::DocumentHighlightKind;
+use lsp::{DocumentHighlightKind, ServerCapabilities};
 use std::{cmp::Reverse, ops::Range, path::Path};
 
 #[async_trait(?Send)]
@@ -17,6 +17,10 @@ pub(crate) trait LspCommand: 'static + Sized {
     type LspRequest: 'static + Send + lsp::request::Request;
     type ProtoRequest: 'static + Send + proto::RequestMessage;
 
+    fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
+        true
+    }
+
     fn to_lsp(
         &self,
         path: &Path,
@@ -610,6 +614,10 @@ impl LspCommand for GetDocumentHighlights {
     type LspRequest = lsp::request::DocumentHighlightRequest;
     type ProtoRequest = proto::GetDocumentHighlights;
 
+    fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
+        capabilities.document_highlight_provider.is_some()
+    }
+
     fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
         lsp::DocumentHighlightParams {
             text_document_position_params: lsp::TextDocumentPositionParams {

crates/project/src/project.rs 🔗

@@ -1322,6 +1322,7 @@ impl Project {
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<Vec<DocumentHighlight>>> {
         let position = position.to_point_utf16(buffer.read(cx));
+
         self.request_lsp(buffer.clone(), GetDocumentHighlights { position }, cx)
     }
 
@@ -1724,6 +1725,17 @@ impl Project {
                 return Task::ready(Ok(Default::default()));
             }
 
+            if !lang_server
+                .capabilities()
+                .borrow()
+                .as_ref()
+                .map_or(false, |capabilities| {
+                    capabilities.code_action_provider.is_some()
+                })
+            {
+                return Task::ready(Ok(Default::default()));
+            }
+
             let lsp_range = lsp::Range::new(
                 range.start.to_point_utf16(buffer).to_lsp_position(),
                 range.end.to_point_utf16(buffer).to_lsp_position(),
@@ -2251,6 +2263,17 @@ impl Project {
             if let Some((file, language_server)) = file.zip(buffer.language_server().cloned()) {
                 let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
                 return cx.spawn(|this, cx| async move {
+                    if !language_server
+                        .capabilities()
+                        .borrow()
+                        .as_ref()
+                        .map_or(false, |capabilities| {
+                            request.check_capabilities(capabilities)
+                        })
+                    {
+                        return Ok(Default::default());
+                    }
+
                     let response = language_server
                         .request::<R::LspRequest>(lsp_params)
                         .await