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>,
binary: LanguageServerBinary,
static_capabilities: RwLock<ServerCapabilities>,
dynamic_capabilities: RwLock<DynamicCapabilities>,
/// 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
/// 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>>,
}
#[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 {
/// Starts a language server process.
pub fn new(
@ -485,6 +492,7 @@ impl LanguageServer {
.unwrap_or_default(),
binary,
static_capabilities: Default::default(),
dynamic_capabilities: Default::default(),
configuration,
code_action_kinds,
next_id: Default::default(),
@ -1133,6 +1141,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());
}
/// Get the reported capabilities of the running language server and
/// what we know on the client/adapter-side of its capabilities.
pub fn adapter_server_capabilities(&self) -> AdapterServerCapabilities {

View file

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