move effective caps

Smit Barmase created

Change summary

crates/lsp/src/capabilities.rs  | 90 +++++++++++++++++++++++++++++++++++
crates/lsp/src/lsp.rs           | 80 ++----------------------------
crates/project/src/lsp_store.rs | 20 +++----
3 files changed, 106 insertions(+), 84 deletions(-)

Detailed changes

crates/lsp/src/capabilities.rs 🔗

@@ -0,0 +1,90 @@
+use super::DynamicCapabilities;
+use lsp_types::{
+    ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind,
+    TextDocumentSyncSaveOptions,
+};
+
+pub mod cap {
+    pub struct DidChangeTextDocument;
+    pub struct DidSaveTextDocument;
+}
+
+pub trait EffectiveCapability {
+    type Value;
+    fn compute(static_caps: &ServerCapabilities, dynamic_caps: &DynamicCapabilities)
+    -> Self::Value;
+}
+
+impl EffectiveCapability for cap::DidChangeTextDocument {
+    type Value = Option<TextDocumentSyncKind>;
+
+    fn compute(
+        static_caps: &ServerCapabilities,
+        dynamic_caps: &DynamicCapabilities,
+    ) -> Self::Value {
+        dynamic_caps
+            .text_document_sync_did_change
+            .as_ref()
+            .and_then(|id_to_sync_kind_map| {
+                if id_to_sync_kind_map.is_empty() {
+                    None
+                } else {
+                    let mut best: Option<TextDocumentSyncKind> = None;
+                    for kind in id_to_sync_kind_map.values() {
+                        best = Some(match (best, kind) {
+                            (None, kind) => *kind,
+                            (
+                                Some(TextDocumentSyncKind::FULL),
+                                &TextDocumentSyncKind::INCREMENTAL,
+                            ) => TextDocumentSyncKind::INCREMENTAL,
+                            (Some(kind), _) => kind,
+                        });
+                    }
+                    best
+                }
+            })
+            .or_else(|| {
+                static_caps
+                    .text_document_sync
+                    .as_ref()
+                    .and_then(|sync| match sync {
+                        TextDocumentSyncCapability::Kind(kind) => Some(*kind),
+                        TextDocumentSyncCapability::Options(opts) => opts.change,
+                    })
+            })
+    }
+}
+
+impl EffectiveCapability for cap::DidSaveTextDocument {
+    type Value = Option<bool>;
+
+    fn compute(
+        static_caps: &ServerCapabilities,
+        dynamic_caps: &DynamicCapabilities,
+    ) -> Self::Value {
+        dynamic_caps
+            .text_document_sync_did_save
+            .as_ref()
+            .and_then(|id_to_save_options_map| {
+                if id_to_save_options_map.is_empty() {
+                    None
+                } else {
+                    Some(
+                        id_to_save_options_map
+                            .values()
+                            .any(|opts| opts.include_text.unwrap_or(false)),
+                    )
+                }
+            })
+            .or_else(|| match static_caps.text_document_sync.as_ref()? {
+                TextDocumentSyncCapability::Options(opts) => match opts.save.as_ref()? {
+                    TextDocumentSyncSaveOptions::Supported(true) => Some(false),
+                    TextDocumentSyncSaveOptions::Supported(false) => None,
+                    TextDocumentSyncSaveOptions::SaveOptions(save_opts) => {
+                        Some(save_opts.include_text.unwrap_or(false))
+                    }
+                },
+                TextDocumentSyncCapability::Kind(_) => None,
+            })
+    }
+}

crates/lsp/src/lsp.rs 🔗

@@ -1,5 +1,8 @@
+mod capabilities;
 mod input_handler;
 
+pub use capabilities::{EffectiveCapability, cap};
+
 pub use lsp_types::request::*;
 pub use lsp_types::*;
 
@@ -308,15 +311,6 @@ pub struct DynamicCapabilities {
     pub text_document_sync_did_save: Option<HashMap<String, SaveOptions>>,
 }
 
-/// Effective text document synchronization behavior, merging static and dynamic capabilities.
-#[derive(Debug, Default, Clone)]
-pub struct EffectiveTextDocumentSync {
-    /// Effective change sync kind (FULL or INCREMENTAL), if any.
-    pub change: Option<TextDocumentSyncKind>,
-    /// Whether to include text on didSave, or None if didSave is not supported.
-    pub save_include_text: Option<bool>,
-}
-
 impl LanguageServer {
     /// Starts a language server process.
     pub fn new(
@@ -1150,74 +1144,14 @@ impl LanguageServer {
         self.static_capabilities.read().clone()
     }
 
-    pub fn dynamic_capabilities(&self) -> DynamicCapabilities {
-        self.dynamic_capabilities.read().clone()
-    }
-
     pub fn update_dynamic_capabilities(&self, update: impl FnOnce(&mut DynamicCapabilities)) {
         update(self.dynamic_capabilities.write().deref_mut());
     }
 
-    pub fn effective_text_document_sync(&self) -> EffectiveTextDocumentSync {
-        let static_caps = self.capabilities();
-        let dyn_caps = self.dynamic_capabilities();
-
-        let change = dyn_caps
-            .text_document_sync_did_change
-            .as_ref()
-            .and_then(|m| {
-                if m.is_empty() {
-                    None
-                } else {
-                    let mut best: Option<TextDocumentSyncKind> = None;
-                    for kind in m.values() {
-                        best = Some(match (best, kind) {
-                            (None, k) => *k,
-                            (
-                                Some(TextDocumentSyncKind::FULL),
-                                &TextDocumentSyncKind::INCREMENTAL,
-                            ) => TextDocumentSyncKind::INCREMENTAL,
-                            (Some(curr), _) => curr,
-                        });
-                    }
-                    best
-                }
-            })
-            .or_else(|| {
-                static_caps
-                    .text_document_sync
-                    .as_ref()
-                    .and_then(|sync| match sync {
-                        TextDocumentSyncCapability::Kind(kind) => Some(*kind),
-                        TextDocumentSyncCapability::Options(options) => options.change,
-                    })
-            });
-
-        let save_include_text = dyn_caps
-            .text_document_sync_did_save
-            .as_ref()
-            .and_then(|m| {
-                if m.is_empty() {
-                    None
-                } else {
-                    Some(m.values().any(|opts| opts.include_text.unwrap_or(false)))
-                }
-            })
-            .or_else(|| match static_caps.text_document_sync.as_ref()? {
-                TextDocumentSyncCapability::Options(opts) => match opts.save.as_ref()? {
-                    TextDocumentSyncSaveOptions::Supported(true) => Some(false),
-                    TextDocumentSyncSaveOptions::Supported(false) => None,
-                    TextDocumentSyncSaveOptions::SaveOptions(save_opts) => {
-                        Some(save_opts.include_text.unwrap_or(false))
-                    }
-                },
-                TextDocumentSyncCapability::Kind(_) => None,
-            });
-
-        EffectiveTextDocumentSync {
-            change,
-            save_include_text,
-        }
+    pub fn effective_capability<Cap: EffectiveCapability>(&self) -> Cap::Value {
+        let static_capabilities = self.capabilities();
+        let dynamic_capabilities = self.dynamic_capabilities.read().clone();
+        Cap::compute(&static_capabilities, &dynamic_capabilities)
     }
 
     /// Get the reported capabilities of the running language server and

crates/project/src/lsp_store.rs 🔗

@@ -74,9 +74,8 @@ use lsp::{
     FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher,
     LSP_REQUEST_TIMEOUT, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions,
     LanguageServerId, LanguageServerName, LanguageServerSelector, LspRequestFuture,
-    MessageActionItem, MessageType, OneOf, RenameFilesParams, SymbolKind,
-    TextEdit, WillRenameFiles, WorkDoneProgressCancelParams,
-    WorkspaceFolder, notification::DidRenameFiles,
+    MessageActionItem, MessageType, OneOf, RenameFilesParams, SymbolKind, TextEdit,
+    WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder, notification::DidRenameFiles,
 };
 use node_runtime::read_package_installed_version;
 use parking_lot::Mutex;
@@ -7208,9 +7207,8 @@ impl LspStore {
                     .collect()
             };
 
-            let document_sync_kind = language_server
-                .effective_text_document_sync()
-                .change;
+            let document_sync_kind =
+                language_server.effective_capability::<lsp::cap::DidChangeTextDocument>();
 
             let content_changes: Vec<_> = match document_sync_kind {
                 Some(lsp::TextDocumentSyncKind::FULL) => {
@@ -7271,7 +7269,9 @@ impl LspStore {
         let local = self.as_local()?;
 
         for server in local.language_servers_for_worktree(worktree_id) {
-            if let Some(include_text) = server.effective_text_document_sync().save_include_text {
+            if let Some(include_text) =
+                server.effective_capability::<lsp::cap::DidSaveTextDocument>()
+            {
                 let text = if include_text {
                     Some(buffer.read(cx).text())
                 } else {
@@ -11842,7 +11842,7 @@ impl LspStore {
                             let map = dyn_caps
                                 .text_document_sync_did_change
                                 .get_or_insert_with(HashMap::default);
-                            map.insert(reg.id.clone(), sync_kind);
+                            map.insert(reg.id, sync_kind);
                         });
                         notify_server_capabilities_updated(&server, cx);
                     }
@@ -11867,7 +11867,7 @@ impl LspStore {
                             let map = dyn_caps
                                 .text_document_sync_did_save
                                 .get_or_insert_with(HashMap::default);
-                            map.insert(reg.id.clone(), lsp::SaveOptions { include_text });
+                            map.insert(reg.id, lsp::SaveOptions { include_text });
                         });
                         notify_server_capabilities_updated(&server, cx);
                     }
@@ -13253,8 +13253,6 @@ async fn populate_labels_for_symbols(
     }
 }
 
-// include_text logic moved into lsp::LanguageServer::effective_text_document_sync()
-
 /// Completion items are displayed in a `UniformList`.
 /// Usually, those items are single-line strings, but in LSP responses,
 /// completion items `label`, `detail` and `label_details.description` may contain newlines or long spaces.