move effective caps

This commit is contained in:
Smit Barmase 2025-08-25 17:02:23 +05:30
parent 3410652c71
commit 6f69765172
No known key found for this signature in database
3 changed files with 106 additions and 84 deletions

View file

@ -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,
})
}
}

View file

@ -1,5 +1,8 @@
mod capabilities;
mod input_handler; mod input_handler;
pub use capabilities::{EffectiveCapability, cap};
pub use lsp_types::request::*; pub use lsp_types::request::*;
pub use lsp_types::*; pub use lsp_types::*;
@ -308,15 +311,6 @@ pub struct DynamicCapabilities {
pub text_document_sync_did_save: Option<HashMap<String, SaveOptions>>, 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 { impl LanguageServer {
/// Starts a language server process. /// Starts a language server process.
pub fn new( pub fn new(
@ -1150,74 +1144,14 @@ impl LanguageServer {
self.static_capabilities.read().clone() 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)) { pub fn update_dynamic_capabilities(&self, update: impl FnOnce(&mut DynamicCapabilities)) {
update(self.dynamic_capabilities.write().deref_mut()); update(self.dynamic_capabilities.write().deref_mut());
} }
pub fn effective_text_document_sync(&self) -> EffectiveTextDocumentSync { pub fn effective_capability<Cap: EffectiveCapability>(&self) -> Cap::Value {
let static_caps = self.capabilities(); let static_capabilities = self.capabilities();
let dyn_caps = self.dynamic_capabilities(); let dynamic_capabilities = self.dynamic_capabilities.read().clone();
Cap::compute(&static_capabilities, &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,
}
} }
/// Get the reported capabilities of the running language server and /// Get the reported capabilities of the running language server and

View file

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