text document sync dynamic caps

This commit is contained in:
Smit Barmase 2025-08-25 12:47:36 +05:30
parent 7684bc265b
commit 0617098175
No known key found for this signature in database
2 changed files with 75 additions and 33 deletions

View file

@ -87,6 +87,7 @@ pub struct LanguageServer {
process_name: Arc<str>, process_name: Arc<str>,
binary: LanguageServerBinary, binary: LanguageServerBinary,
static_capabilities: RwLock<ServerCapabilities>, static_capabilities: RwLock<ServerCapabilities>,
dynamic_capabilities: RwLock<DynamicCapabilities>,
/// Configuration sent to the server, stored for display in the language server logs /// Configuration sent to the server, stored for display in the language server logs
/// buffer. This is represented as the message sent to the LSP in order to avoid cloning it (can /// buffer. This is represented as the message sent to the LSP in order to avoid cloning it (can
/// be large in cases like sending schemas to the json server). /// be large in cases like sending schemas to the json server).
@ -301,6 +302,12 @@ pub struct AdapterServerCapabilities {
pub code_action_kinds: Option<Vec<CodeActionKind>>, pub code_action_kinds: Option<Vec<CodeActionKind>>,
} }
#[derive(Debug, Default, Clone)]
pub struct DynamicCapabilities {
pub text_document_sync_did_change: Option<HashMap<String, TextDocumentSyncKind>>,
pub text_document_sync_did_save: Option<HashMap<String, SaveOptions>>,
}
impl LanguageServer { impl LanguageServer {
/// Starts a language server process. /// Starts a language server process.
pub fn new( pub fn new(
@ -485,6 +492,7 @@ impl LanguageServer {
.unwrap_or_default(), .unwrap_or_default(),
binary, binary,
static_capabilities: Default::default(), static_capabilities: Default::default(),
dynamic_capabilities: Default::default(),
configuration, configuration,
code_action_kinds, code_action_kinds,
next_id: Default::default(), next_id: Default::default(),
@ -1133,6 +1141,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)) {
update(self.dynamic_capabilities.write().deref_mut());
}
/// Get the reported capabilities of the running language server and /// Get the reported capabilities of the running language server and
/// what we know on the client/adapter-side of its capabilities. /// what we know on the client/adapter-side of its capabilities.
pub fn adapter_server_capabilities(&self) -> AdapterServerCapabilities { pub fn adapter_server_capabilities(&self) -> AdapterServerCapabilities {

View file

@ -7208,14 +7208,40 @@ impl LspStore {
.collect() .collect()
}; };
let document_sync_kind = language_server let document_sync_kind = {
.capabilities() let dyn_caps = language_server.dynamic_capabilities();
.text_document_sync let dynamic = dyn_caps
.as_ref() .text_document_sync_did_change
.and_then(|sync| match sync { .as_ref()
lsp::TextDocumentSyncCapability::Kind(kind) => Some(*kind), .and_then(|m| {
lsp::TextDocumentSyncCapability::Options(options) => options.change, if m.is_empty() {
}); None
} else {
let mut best: Option<lsp::TextDocumentSyncKind> = None;
for kind in m.values() {
best = Some(match (best, kind) {
(None, k) => *k,
(
Some(lsp::TextDocumentSyncKind::FULL),
lsp::TextDocumentSyncKind::INCREMENTAL,
) => lsp::TextDocumentSyncKind::INCREMENTAL,
(Some(curr), _) => curr,
});
}
best
}
});
dynamic.or_else(|| {
language_server
.capabilities()
.text_document_sync
.as_ref()
.and_then(|sync| match sync {
lsp::TextDocumentSyncCapability::Kind(kind) => Some(*kind),
lsp::TextDocumentSyncCapability::Options(options) => options.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) => {
@ -11843,12 +11869,11 @@ impl LspStore {
.map(serde_json::from_value::<lsp::TextDocumentSyncKind>) .map(serde_json::from_value::<lsp::TextDocumentSyncKind>)
.transpose()? .transpose()?
{ {
server.update_capabilities(|capabilities| { server.update_dynamic_capabilities(|dyn_caps| {
let mut sync_options = let map = dyn_caps
Self::take_text_document_sync_options(capabilities); .text_document_sync_did_change
sync_options.change = Some(sync_kind); .get_or_insert_with(HashMap::default);
capabilities.text_document_sync = map.insert(reg.id.clone(), sync_kind);
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
@ -11869,15 +11894,11 @@ impl LspStore {
}) })
.transpose()? .transpose()?
{ {
server.update_capabilities(|capabilities| { server.update_dynamic_capabilities(|dyn_caps| {
let mut sync_options = let map = dyn_caps
Self::take_text_document_sync_options(capabilities); .text_document_sync_did_save
sync_options.save = .get_or_insert_with(HashMap::default);
Some(TextDocumentSyncSaveOptions::SaveOptions(lsp::SaveOptions { map.insert(reg.id.clone(), lsp::SaveOptions { include_text });
include_text,
}));
capabilities.text_document_sync =
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
@ -12028,20 +12049,18 @@ impl LspStore {
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
"textDocument/didChange" => { "textDocument/didChange" => {
server.update_capabilities(|capabilities| { server.update_dynamic_capabilities(|dyn_caps| {
let mut sync_options = Self::take_text_document_sync_options(capabilities); if let Some(map) = dyn_caps.text_document_sync_did_change.as_mut() {
sync_options.change = None; map.remove(&unreg.id);
capabilities.text_document_sync = }
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
"textDocument/didSave" => { "textDocument/didSave" => {
server.update_capabilities(|capabilities| { server.update_dynamic_capabilities(|dyn_caps| {
let mut sync_options = Self::take_text_document_sync_options(capabilities); if let Some(map) = dyn_caps.text_document_sync_did_save.as_mut() {
sync_options.save = None; map.remove(&unreg.id);
capabilities.text_document_sync = }
Some(lsp::TextDocumentSyncCapability::Options(sync_options));
}); });
notify_server_capabilities_updated(&server, cx); notify_server_capabilities_updated(&server, cx);
} }
@ -13266,6 +13285,13 @@ async fn populate_labels_for_symbols(
} }
fn include_text(server: &lsp::LanguageServer) -> Option<bool> { fn include_text(server: &lsp::LanguageServer) -> Option<bool> {
let dyn_caps = server.dynamic_capabilities();
if let Some(map) = dyn_caps.text_document_sync_did_save.as_ref() {
if !map.is_empty() {
let any_true = map.values().any(|opts| opts.include_text.unwrap_or(false));
return Some(any_true);
}
}
match server.capabilities().text_document_sync.as_ref()? { match server.capabilities().text_document_sync.as_ref()? {
lsp::TextDocumentSyncCapability::Options(opts) => match opts.save.as_ref()? { lsp::TextDocumentSyncCapability::Options(opts) => match opts.save.as_ref()? {
// Server wants didSave but didn't specify includeText. // Server wants didSave but didn't specify includeText.