Detailed changes
@@ -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,
+ })
+ }
+}
@@ -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
@@ -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.