Skip to repository content1055 lines · 47.1 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:52:09.947Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
dynamic_registration.rs
1//! Handling of `client/registerCapability` and `client/unregisterCapability` requests,
2//! which let language servers toggle their capabilities at runtime.
3//!
4//! Multiple registrations of the same capability may coexist: the most recent one is
5//! "active" and mirrored into the server's [`lsp::ServerCapabilities`], while the rest
6//! (including the statically declared capability, if any) are kept to be restored when
7//! the active registration is unregistered.
8
9use anyhow::{Context as _, Result};
10use collections::{BTreeSet, HashSet};
11use gpui::{Context, SharedString};
12use lsp::{
13 CompletionOptions, DiagnosticServerCapabilities, LanguageServer, LanguageServerId, OneOf,
14 TextDocumentSyncSaveOptions,
15};
16
17use crate::lsp_store::{
18 LanguageServerState, LspStore, RenamePathsWatchedForServer, lsp_workspace_diagnostics_refresh,
19 notify_server_capabilities_updated,
20};
21
22#[derive(Debug)]
23pub(super) enum RegistrationSource {
24 Static,
25 Dynamic(String),
26}
27
28impl RegistrationSource {
29 pub(super) fn registration_id(&self) -> Option<&str> {
30 match self {
31 RegistrationSource::Static => None,
32 RegistrationSource::Dynamic(id) => Some(id),
33 }
34 }
35}
36
37pub(super) type CapabilityRegistrations<T> = Vec<(RegistrationSource, T)>;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40enum CapabilityUnregistration {
41 NotFound,
42 Removed { active_capability_changed: bool },
43}
44
45impl CapabilityUnregistration {
46 fn removed(self) -> bool {
47 self != CapabilityUnregistration::NotFound
48 }
49
50 fn capability_changed(self) -> bool {
51 self == CapabilityUnregistration::Removed {
52 active_capability_changed: true,
53 }
54 }
55}
56
57#[derive(Default, Debug)]
58pub(super) struct DynamicRegistrations {
59 pub(super) did_change_watched_files: HashSet<String>,
60 pub(super) diagnostics: CapabilityRegistrations<DiagnosticServerCapabilities>,
61 workspace_folders: CapabilityRegistrations<lsp::WorkspaceFoldersServerCapabilities>,
62 workspace_symbol: CapabilityRegistrations<OneOf<bool, lsp::WorkspaceSymbolOptions>>,
63 file_operations: CapabilityRegistrations<lsp::WorkspaceFileOperationsServerCapabilities>,
64 execute_command: CapabilityRegistrations<lsp::ExecuteCommandOptions>,
65 range_formatting: CapabilityRegistrations<OneOf<bool, lsp::DocumentRangeFormattingOptions>>,
66 on_type_formatting: CapabilityRegistrations<lsp::DocumentOnTypeFormattingOptions>,
67 formatting: CapabilityRegistrations<OneOf<bool, lsp::DocumentFormattingOptions>>,
68 rename: CapabilityRegistrations<OneOf<bool, lsp::RenameOptions>>,
69 code_action: CapabilityRegistrations<lsp::CodeActionProviderCapability>,
70 definition: CapabilityRegistrations<OneOf<bool, lsp::DefinitionOptions>>,
71 completion: CapabilityRegistrations<lsp::CompletionOptions>,
72 hover: CapabilityRegistrations<lsp::HoverProviderCapability>,
73 signature_help: CapabilityRegistrations<lsp::SignatureHelpOptions>,
74 color: CapabilityRegistrations<lsp::ColorProviderCapability>,
75 folding_range: CapabilityRegistrations<lsp::FoldingRangeProviderCapability>,
76 document_link: CapabilityRegistrations<lsp::DocumentLinkOptions>,
77 semantic_tokens: CapabilityRegistrations<lsp::SemanticTokensServerCapabilities>,
78 inlay_hint: CapabilityRegistrations<OneOf<bool, lsp::InlayHintServerCapabilities>>,
79 code_lens: CapabilityRegistrations<lsp::CodeLensOptions>,
80 document_symbol: CapabilityRegistrations<OneOf<bool, lsp::DocumentSymbolOptions>>,
81}
82
83impl LspStore {
84 /// Returns `true` when the registration changed the server's active capability value:
85 /// duplicate-ID replacements of non-active registrations and (re-)registrations with
86 /// options identical to the active ones do not.
87 fn register_dynamic_capability<T: Clone + PartialEq>(
88 &mut self,
89 server: &LanguageServer,
90 method: &str,
91 registration_id: String,
92 options: T,
93 cx: &mut Context<Self>,
94 registrations_of: impl FnOnce(&mut DynamicRegistrations) -> &mut CapabilityRegistrations<T>,
95 capability_of: impl Fn(&mut lsp::ServerCapabilities) -> &mut Option<T>,
96 ) -> anyhow::Result<bool> {
97 let server_id = server.server_id();
98 let local = self
99 .as_local_mut()
100 .context("Expected LSP Store to be local")?;
101 let registrations = registrations_of(
102 local
103 .language_server_dynamic_registrations
104 .entry(server_id)
105 .or_default(),
106 );
107 if registrations.is_empty() {
108 let mut initial_capabilities = server.capabilities();
109 if let Some(static_options) = capability_of(&mut initial_capabilities).take() {
110 registrations.push((RegistrationSource::Static, static_options));
111 }
112 }
113 let previously_active = registrations.last().map(|(_, options)| options.clone());
114 if let Some(index) = registrations
115 .iter()
116 .position(|(source, _)| source.registration_id() == Some(registration_id.as_str()))
117 {
118 log::warn!(
119 "Received a duplicate {method} registration with ID {registration_id}, replacing the previous one"
120 );
121 registrations[index].1 = options;
122 } else {
123 registrations.push((RegistrationSource::Dynamic(registration_id), options));
124 }
125 let active = registrations.last().map(|(_, options)| options.clone());
126 let active_changed = active != previously_active;
127 if active_changed {
128 server.update_capabilities(|capabilities| *capability_of(capabilities) = active);
129 notify_server_capabilities_updated(server, cx);
130 }
131 Ok(active_changed)
132 }
133
134 fn unregister_dynamic_capability<T: Clone + PartialEq>(
135 &mut self,
136 server: &LanguageServer,
137 unregistration: &lsp::Unregistration,
138 cx: &mut Context<Self>,
139 registrations_of: impl FnOnce(&mut DynamicRegistrations) -> &mut CapabilityRegistrations<T>,
140 capability_of: impl FnOnce(&mut lsp::ServerCapabilities) -> &mut Option<T>,
141 ) -> anyhow::Result<CapabilityUnregistration> {
142 let server_id = server.server_id();
143 let local = self
144 .as_local_mut()
145 .context("Expected LSP Store to be local")?;
146 let registrations = local
147 .language_server_dynamic_registrations
148 .get_mut(&server_id)
149 .map(registrations_of);
150 let index = registrations.as_ref().and_then(|registrations| {
151 registrations.iter().position(|(source, _)| {
152 source.registration_id() == Some(unregistration.id.as_str())
153 })
154 });
155 let (Some(registrations), Some(index)) = (registrations, index) else {
156 log::warn!(
157 "Attempted to unregister non-existent {} registration with ID {}",
158 unregistration.method,
159 unregistration.id
160 );
161 return Ok(CapabilityUnregistration::NotFound);
162 };
163 let removed_active = index + 1 == registrations.len();
164 let (_, removed_options) = registrations.remove(index);
165 let restored = registrations.last().map(|(_, options)| options.clone());
166 if !removed_active || restored.as_ref() == Some(&removed_options) {
167 return Ok(CapabilityUnregistration::Removed {
168 active_capability_changed: false,
169 });
170 }
171 server.update_capabilities(|capabilities| *capability_of(capabilities) = restored);
172 notify_server_capabilities_updated(server, cx);
173 Ok(CapabilityUnregistration::Removed {
174 active_capability_changed: true,
175 })
176 }
177
178 fn update_paths_watched_for_rename(&mut self, server: &LanguageServer) {
179 let Some(local) = self.as_local_mut() else {
180 return;
181 };
182 let watcher = server
183 .capabilities()
184 .workspace
185 .and_then(|workspace| workspace.file_operations)
186 .and_then(|file_operations| {
187 let did_rename_caps = file_operations.did_rename.as_ref();
188 let will_rename_caps = file_operations.will_rename.as_ref();
189 did_rename_caps.or(will_rename_caps)?;
190 Some(
191 RenamePathsWatchedForServer::default()
192 .with_did_rename_patterns(did_rename_caps)
193 .with_will_rename_patterns(will_rename_caps),
194 )
195 });
196 match watcher {
197 Some(watcher) => {
198 local
199 .language_server_paths_watched_for_rename
200 .insert(server.server_id(), watcher);
201 }
202 None => {
203 local
204 .language_server_paths_watched_for_rename
205 .remove(&server.server_id());
206 }
207 }
208 }
209
210 fn apply_completion_triggers(
211 &self,
212 server_id: LanguageServerId,
213 options: Option<CompletionOptions>,
214 cx: &mut Context<Self>,
215 ) {
216 let Some(local) = self.as_local() else {
217 return;
218 };
219 let mut buffers_with_language_server = Vec::new();
220 for handle in self.buffer_store.read(cx).buffers() {
221 let buffer_id = handle.read(cx).remote_id();
222 if local
223 .buffers_opened_in_servers
224 .get(&buffer_id)
225 .filter(|s| s.contains(&server_id))
226 .is_some()
227 {
228 buffers_with_language_server.push(handle);
229 }
230 }
231 let triggers = options
232 .and_then(|options| options.trigger_characters)
233 .unwrap_or_default()
234 .into_iter()
235 .collect::<BTreeSet<_>>();
236 for handle in buffers_with_language_server {
237 let triggers = triggers.clone();
238 handle.update(cx, move |buffer, cx| {
239 buffer.set_completion_triggers(server_id, triggers, cx);
240 });
241 }
242 }
243
244 pub(super) fn register_server_capabilities(
245 &mut self,
246 server_id: LanguageServerId,
247 params: lsp::RegistrationParams,
248 cx: &mut Context<Self>,
249 ) -> anyhow::Result<()> {
250 let server = self
251 .language_server_for_id(server_id)
252 .with_context(|| format!("no server {server_id} found"))?;
253 for reg in params.registrations {
254 match reg.method.as_str() {
255 "workspace/didChangeWatchedFiles" => {
256 if let Some(options) = reg.register_options {
257 let notify = if let Some(local_lsp_store) = self.as_local_mut() {
258 let caps = serde_json::from_value(options)?;
259 local_lsp_store
260 .on_lsp_did_change_watched_files(server_id, ®.id, caps, cx);
261 true
262 } else {
263 false
264 };
265 if notify {
266 notify_server_capabilities_updated(&server, cx);
267 }
268 }
269 }
270 "workspace/didChangeConfiguration" => {
271 // Ignore payload since we notify clients of setting changes unconditionally, relying on them pulling the latest settings.
272 }
273 "workspace/didChangeWorkspaceFolders" => {
274 // In this case register options is an empty object, we can ignore it
275 let caps = lsp::WorkspaceFoldersServerCapabilities {
276 supported: Some(true),
277 change_notifications: Some(OneOf::Right(reg.id.clone())),
278 };
279 self.register_dynamic_capability(
280 &server,
281 ®.method,
282 reg.id,
283 caps,
284 cx,
285 |registrations| &mut registrations.workspace_folders,
286 |capabilities| {
287 &mut capabilities
288 .workspace
289 .get_or_insert_default()
290 .workspace_folders
291 },
292 )?;
293 }
294 "workspace/symbol" => {
295 let options = parse_register_capabilities(reg.register_options)?;
296 self.register_dynamic_capability(
297 &server,
298 ®.method,
299 reg.id,
300 options,
301 cx,
302 |registrations| &mut registrations.workspace_symbol,
303 |capabilities| &mut capabilities.workspace_symbol_provider,
304 )?;
305 }
306 "workspace/fileOperations" => {
307 if let Some(options) = reg.register_options {
308 let caps = serde_json::from_value(options)?;
309 if self.register_dynamic_capability(
310 &server,
311 ®.method,
312 reg.id,
313 caps,
314 cx,
315 |registrations| &mut registrations.file_operations,
316 |capabilities| {
317 &mut capabilities
318 .workspace
319 .get_or_insert_default()
320 .file_operations
321 },
322 )? {
323 self.update_paths_watched_for_rename(&server);
324 }
325 }
326 }
327 "workspace/executeCommand" => {
328 if let Some(options) = reg.register_options {
329 let options = serde_json::from_value(options)?;
330 self.register_dynamic_capability(
331 &server,
332 ®.method,
333 reg.id,
334 options,
335 cx,
336 |registrations| &mut registrations.execute_command,
337 |capabilities| &mut capabilities.execute_command_provider,
338 )?;
339 }
340 }
341 "textDocument/rangeFormatting" => {
342 let options = parse_register_capabilities(reg.register_options)?;
343 self.register_dynamic_capability(
344 &server,
345 ®.method,
346 reg.id,
347 options,
348 cx,
349 |registrations| &mut registrations.range_formatting,
350 |capabilities| &mut capabilities.document_range_formatting_provider,
351 )?;
352 }
353 "textDocument/onTypeFormatting" => {
354 if let Some(options) = reg
355 .register_options
356 .map(serde_json::from_value)
357 .transpose()?
358 {
359 self.register_dynamic_capability(
360 &server,
361 ®.method,
362 reg.id,
363 options,
364 cx,
365 |registrations| &mut registrations.on_type_formatting,
366 |capabilities| &mut capabilities.document_on_type_formatting_provider,
367 )?;
368 }
369 }
370 "textDocument/formatting" => {
371 let options = parse_register_capabilities(reg.register_options)?;
372 self.register_dynamic_capability(
373 &server,
374 ®.method,
375 reg.id,
376 options,
377 cx,
378 |registrations| &mut registrations.formatting,
379 |capabilities| &mut capabilities.document_formatting_provider,
380 )?;
381 }
382 "textDocument/rename" => {
383 let options = parse_register_capabilities(reg.register_options)?;
384 self.register_dynamic_capability(
385 &server,
386 ®.method,
387 reg.id,
388 options,
389 cx,
390 |registrations| &mut registrations.rename,
391 |capabilities| &mut capabilities.rename_provider,
392 )?;
393 }
394 "textDocument/inlayHint" => {
395 let options = parse_register_capabilities(reg.register_options)?;
396 if self.register_dynamic_capability(
397 &server,
398 ®.method,
399 reg.id,
400 options,
401 cx,
402 |registrations| &mut registrations.inlay_hint,
403 |capabilities| &mut capabilities.inlay_hint_provider,
404 )? {
405 self.refresh_inlay_hints(server_id, cx);
406 }
407 }
408 "textDocument/documentSymbol" => {
409 let options = parse_register_capabilities(reg.register_options)?;
410 if self.register_dynamic_capability(
411 &server,
412 ®.method,
413 reg.id,
414 options,
415 cx,
416 |registrations| &mut registrations.document_symbol,
417 |capabilities| &mut capabilities.document_symbol_provider,
418 )? {
419 self.refresh_document_symbols(Some(server_id), cx);
420 }
421 }
422 "textDocument/codeAction" => {
423 let options = parse_register_capabilities(reg.register_options)?;
424 let provider = match options {
425 OneOf::Left(value) => lsp::CodeActionProviderCapability::Simple(value),
426 OneOf::Right(caps) => caps,
427 };
428 self.register_dynamic_capability(
429 &server,
430 ®.method,
431 reg.id,
432 provider,
433 cx,
434 |registrations| &mut registrations.code_action,
435 |capabilities| &mut capabilities.code_action_provider,
436 )?;
437 }
438 "textDocument/definition" => {
439 let options = parse_register_capabilities(reg.register_options)?;
440 self.register_dynamic_capability(
441 &server,
442 ®.method,
443 reg.id,
444 options,
445 cx,
446 |registrations| &mut registrations.definition,
447 |capabilities| &mut capabilities.definition_provider,
448 )?;
449 }
450 "textDocument/completion" => {
451 if let Some(caps) = reg
452 .register_options
453 .map(serde_json::from_value::<CompletionOptions>)
454 .transpose()?
455 {
456 if self.register_dynamic_capability(
457 &server,
458 ®.method,
459 reg.id,
460 caps,
461 cx,
462 |registrations| &mut registrations.completion,
463 |capabilities| &mut capabilities.completion_provider,
464 )? {
465 let active = server.capabilities().completion_provider;
466 self.apply_completion_triggers(server_id, active, cx);
467 }
468 }
469 }
470 "textDocument/hover" => {
471 let options = parse_register_capabilities(reg.register_options)?;
472 let provider = match options {
473 OneOf::Left(value) => lsp::HoverProviderCapability::Simple(value),
474 OneOf::Right(caps) => caps,
475 };
476 self.register_dynamic_capability(
477 &server,
478 ®.method,
479 reg.id,
480 provider,
481 cx,
482 |registrations| &mut registrations.hover,
483 |capabilities| &mut capabilities.hover_provider,
484 )?;
485 }
486 "textDocument/signatureHelp" => {
487 if let Some(caps) = reg
488 .register_options
489 .map(serde_json::from_value)
490 .transpose()?
491 {
492 self.register_dynamic_capability(
493 &server,
494 ®.method,
495 reg.id,
496 caps,
497 cx,
498 |registrations| &mut registrations.signature_help,
499 |capabilities| &mut capabilities.signature_help_provider,
500 )?;
501 }
502 }
503 "textDocument/didChange" => {
504 if let Some(sync_kind) = reg
505 .register_options
506 .and_then(|opts| opts.get("syncKind").cloned())
507 .map(serde_json::from_value::<lsp::TextDocumentSyncKind>)
508 .transpose()?
509 {
510 server.update_capabilities(|capabilities| {
511 let mut sync_options = take_text_document_sync_options(capabilities);
512 sync_options.change = Some(sync_kind);
513 capabilities.text_document_sync =
514 Some(lsp::TextDocumentSyncCapability::Options(sync_options));
515 });
516 notify_server_capabilities_updated(&server, cx);
517 }
518 }
519 "textDocument/didSave" => {
520 if let Some(include_text) = reg
521 .register_options
522 .map(|opts| {
523 let transpose = opts
524 .get("includeText")
525 .cloned()
526 .map(serde_json::from_value::<Option<bool>>)
527 .transpose();
528 match transpose {
529 Ok(value) => Ok(value.flatten()),
530 Err(e) => Err(e),
531 }
532 })
533 .transpose()?
534 {
535 server.update_capabilities(|capabilities| {
536 let mut sync_options = take_text_document_sync_options(capabilities);
537 sync_options.save =
538 Some(TextDocumentSyncSaveOptions::SaveOptions(lsp::SaveOptions {
539 include_text,
540 }));
541 capabilities.text_document_sync =
542 Some(lsp::TextDocumentSyncCapability::Options(sync_options));
543 });
544 notify_server_capabilities_updated(&server, cx);
545 }
546 }
547 "textDocument/codeLens" => {
548 if let Some(options) = reg
549 .register_options
550 .map(serde_json::from_value)
551 .transpose()?
552 {
553 if self.register_dynamic_capability(
554 &server,
555 ®.method,
556 reg.id,
557 options,
558 cx,
559 |registrations| &mut registrations.code_lens,
560 |capabilities| &mut capabilities.code_lens_provider,
561 )? {
562 self.refresh_code_lens(Some(server_id), cx);
563 }
564 }
565 }
566 "textDocument/diagnostic" => {
567 if let Some(caps) = reg
568 .register_options
569 .map(serde_json::from_value::<DiagnosticServerCapabilities>)
570 .transpose()?
571 {
572 let registration_id = reg.id.clone();
573 self.register_dynamic_capability(
574 &server,
575 ®.method,
576 reg.id,
577 caps.clone(),
578 cx,
579 |registrations| &mut registrations.diagnostics,
580 |capabilities| &mut capabilities.diagnostic_provider,
581 )?;
582
583 let supports_workspace_diagnostics =
584 |capabilities: &DiagnosticServerCapabilities| match capabilities {
585 DiagnosticServerCapabilities::Options(diagnostic_options) => {
586 diagnostic_options.workspace_diagnostics
587 }
588 DiagnosticServerCapabilities::RegistrationOptions(
589 diagnostic_registration_options,
590 ) => {
591 diagnostic_registration_options
592 .diagnostic_options
593 .workspace_diagnostics
594 }
595 };
596
597 let local = self
598 .as_local_mut()
599 .context("Expected LSP Store to be local")?;
600 let state = local
601 .language_servers
602 .get_mut(&server_id)
603 .context("Could not obtain Language Servers state")?;
604 if let LanguageServerState::Running {
605 workspace_diagnostics_refresh_tasks,
606 ..
607 } = state
608 {
609 workspace_diagnostics_refresh_tasks
610 .remove(&Some(registration_id.clone()));
611 if supports_workspace_diagnostics(&caps)
612 && let Some(task) = lsp_workspace_diagnostics_refresh(
613 Some(registration_id.clone()),
614 caps,
615 server.clone(),
616 cx,
617 )
618 {
619 workspace_diagnostics_refresh_tasks
620 .insert(Some(registration_id), task);
621 }
622 }
623
624 let _ = self.pull_document_diagnostics_for_server(server_id, None, cx);
625 }
626 }
627 "textDocument/documentColor" => {
628 let options = parse_register_capabilities(reg.register_options)?;
629 let provider = match options {
630 OneOf::Left(value) => lsp::ColorProviderCapability::Simple(value),
631 OneOf::Right(caps) => caps,
632 };
633 if self.register_dynamic_capability(
634 &server,
635 ®.method,
636 reg.id,
637 provider,
638 cx,
639 |registrations| &mut registrations.color,
640 |capabilities| &mut capabilities.color_provider,
641 )? {
642 self.refresh_document_colors(Some(server_id), cx);
643 }
644 }
645 "textDocument/foldingRange" => {
646 let options = parse_register_capabilities(reg.register_options)?;
647 let provider = match options {
648 OneOf::Left(value) => lsp::FoldingRangeProviderCapability::Simple(value),
649 OneOf::Right(caps) => caps,
650 };
651 if self.register_dynamic_capability(
652 &server,
653 ®.method,
654 reg.id,
655 provider,
656 cx,
657 |registrations| &mut registrations.folding_range,
658 |capabilities| &mut capabilities.folding_range_provider,
659 )? {
660 self.refresh_folding_ranges(Some(server_id), cx);
661 }
662 }
663 "textDocument/documentLink" => {
664 if let Some(caps) = reg
665 .register_options
666 .map(serde_json::from_value)
667 .transpose()?
668 {
669 if self.register_dynamic_capability(
670 &server,
671 ®.method,
672 reg.id,
673 caps,
674 cx,
675 |registrations| &mut registrations.document_link,
676 |capabilities| &mut capabilities.document_link_provider,
677 )? {
678 self.refresh_document_links(Some(server_id), cx);
679 }
680 }
681 }
682 "textDocument/semanticTokens" => {
683 if let Some(caps) = reg
684 .register_options
685 .map(serde_json::from_value::<lsp::SemanticTokensRegistrationOptions>)
686 .transpose()?
687 {
688 if self.register_dynamic_capability(
689 &server,
690 ®.method,
691 reg.id,
692 lsp::SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(caps),
693 cx,
694 |registrations| &mut registrations.semantic_tokens,
695 |capabilities| &mut capabilities.semantic_tokens_provider,
696 )? {
697 // Re-query already-open buffers, which would otherwise keep
698 // tree-sitter-only highlighting until edited.
699 self.refresh_semantic_tokens(server_id, cx);
700 }
701 }
702 }
703 _ => log::warn!("unhandled capability registration: {reg:?}"),
704 }
705 }
706
707 Ok(())
708 }
709
710 pub(super) fn unregister_server_capabilities(
711 &mut self,
712 server_id: LanguageServerId,
713 params: lsp::UnregistrationParams,
714 cx: &mut Context<Self>,
715 ) -> anyhow::Result<()> {
716 let server = self
717 .language_server_for_id(server_id)
718 .with_context(|| format!("no server {server_id} found"))?;
719 for unreg in params.unregisterations.iter() {
720 match unreg.method.as_str() {
721 "workspace/didChangeWatchedFiles" => {
722 let notify = if let Some(local_lsp_store) = self.as_local_mut() {
723 local_lsp_store
724 .on_lsp_unregister_did_change_watched_files(server_id, &unreg.id, cx);
725 true
726 } else {
727 false
728 };
729 if notify {
730 notify_server_capabilities_updated(&server, cx);
731 }
732 }
733 "workspace/didChangeConfiguration" => {
734 // Ignore payload since we notify clients of setting changes unconditionally, relying on them pulling the latest settings.
735 }
736 "workspace/didChangeWorkspaceFolders" => {
737 self.unregister_dynamic_capability(
738 &server,
739 unreg,
740 cx,
741 |registrations| &mut registrations.workspace_folders,
742 |capabilities| {
743 &mut capabilities
744 .workspace
745 .get_or_insert_default()
746 .workspace_folders
747 },
748 )?;
749 }
750 "workspace/symbol" => {
751 self.unregister_dynamic_capability(
752 &server,
753 unreg,
754 cx,
755 |registrations| &mut registrations.workspace_symbol,
756 |capabilities| &mut capabilities.workspace_symbol_provider,
757 )?;
758 }
759 "workspace/fileOperations" => {
760 if self
761 .unregister_dynamic_capability(
762 &server,
763 unreg,
764 cx,
765 |registrations| &mut registrations.file_operations,
766 |capabilities| {
767 &mut capabilities
768 .workspace
769 .get_or_insert_default()
770 .file_operations
771 },
772 )?
773 .capability_changed()
774 {
775 self.update_paths_watched_for_rename(&server);
776 }
777 }
778 "workspace/executeCommand" => {
779 self.unregister_dynamic_capability(
780 &server,
781 unreg,
782 cx,
783 |registrations| &mut registrations.execute_command,
784 |capabilities| &mut capabilities.execute_command_provider,
785 )?;
786 }
787 "textDocument/rangeFormatting" => {
788 self.unregister_dynamic_capability(
789 &server,
790 unreg,
791 cx,
792 |registrations| &mut registrations.range_formatting,
793 |capabilities| &mut capabilities.document_range_formatting_provider,
794 )?;
795 }
796 "textDocument/onTypeFormatting" => {
797 self.unregister_dynamic_capability(
798 &server,
799 unreg,
800 cx,
801 |registrations| &mut registrations.on_type_formatting,
802 |capabilities| &mut capabilities.document_on_type_formatting_provider,
803 )?;
804 }
805 "textDocument/formatting" => {
806 self.unregister_dynamic_capability(
807 &server,
808 unreg,
809 cx,
810 |registrations| &mut registrations.formatting,
811 |capabilities| &mut capabilities.document_formatting_provider,
812 )?;
813 }
814 "textDocument/rename" => {
815 self.unregister_dynamic_capability(
816 &server,
817 unreg,
818 cx,
819 |registrations| &mut registrations.rename,
820 |capabilities| &mut capabilities.rename_provider,
821 )?;
822 }
823 "textDocument/codeAction" => {
824 self.unregister_dynamic_capability(
825 &server,
826 unreg,
827 cx,
828 |registrations| &mut registrations.code_action,
829 |capabilities| &mut capabilities.code_action_provider,
830 )?;
831 }
832 "textDocument/definition" => {
833 self.unregister_dynamic_capability(
834 &server,
835 unreg,
836 cx,
837 |registrations| &mut registrations.definition,
838 |capabilities| &mut capabilities.definition_provider,
839 )?;
840 }
841 "textDocument/completion" => {
842 if self
843 .unregister_dynamic_capability(
844 &server,
845 unreg,
846 cx,
847 |registrations| &mut registrations.completion,
848 |capabilities| &mut capabilities.completion_provider,
849 )?
850 .capability_changed()
851 {
852 let restored = server.capabilities().completion_provider;
853 self.apply_completion_triggers(server_id, restored, cx);
854 }
855 }
856 "textDocument/hover" => {
857 self.unregister_dynamic_capability(
858 &server,
859 unreg,
860 cx,
861 |registrations| &mut registrations.hover,
862 |capabilities| &mut capabilities.hover_provider,
863 )?;
864 }
865 "textDocument/signatureHelp" => {
866 self.unregister_dynamic_capability(
867 &server,
868 unreg,
869 cx,
870 |registrations| &mut registrations.signature_help,
871 |capabilities| &mut capabilities.signature_help_provider,
872 )?;
873 }
874 "textDocument/semanticTokens" => {
875 if self
876 .unregister_dynamic_capability(
877 &server,
878 unreg,
879 cx,
880 |registrations| &mut registrations.semantic_tokens,
881 |capabilities| &mut capabilities.semantic_tokens_provider,
882 )?
883 .capability_changed()
884 {
885 self.refresh_semantic_tokens(server_id, cx);
886 }
887 }
888 "textDocument/didChange" => {
889 server.update_capabilities(|capabilities| {
890 let mut sync_options = take_text_document_sync_options(capabilities);
891 sync_options.change = None;
892 capabilities.text_document_sync =
893 Some(lsp::TextDocumentSyncCapability::Options(sync_options));
894 });
895 notify_server_capabilities_updated(&server, cx);
896 }
897 "textDocument/didSave" => {
898 server.update_capabilities(|capabilities| {
899 let mut sync_options = take_text_document_sync_options(capabilities);
900 sync_options.save = None;
901 capabilities.text_document_sync =
902 Some(lsp::TextDocumentSyncCapability::Options(sync_options));
903 });
904 notify_server_capabilities_updated(&server, cx);
905 }
906 "textDocument/inlayHint" => {
907 if self
908 .unregister_dynamic_capability(
909 &server,
910 unreg,
911 cx,
912 |registrations| &mut registrations.inlay_hint,
913 |capabilities| &mut capabilities.inlay_hint_provider,
914 )?
915 .capability_changed()
916 {
917 self.refresh_inlay_hints(server_id, cx);
918 }
919 }
920 "textDocument/documentSymbol" => {
921 if self
922 .unregister_dynamic_capability(
923 &server,
924 unreg,
925 cx,
926 |registrations| &mut registrations.document_symbol,
927 |capabilities| &mut capabilities.document_symbol_provider,
928 )?
929 .capability_changed()
930 {
931 self.refresh_document_symbols(Some(server_id), cx);
932 }
933 }
934 "textDocument/codeLens" => {
935 if self
936 .unregister_dynamic_capability(
937 &server,
938 unreg,
939 cx,
940 |registrations| &mut registrations.code_lens,
941 |capabilities| &mut capabilities.code_lens_provider,
942 )?
943 .capability_changed()
944 {
945 self.refresh_code_lens(Some(server_id), cx);
946 }
947 }
948 "textDocument/diagnostic" => {
949 if self
950 .unregister_dynamic_capability(
951 &server,
952 unreg,
953 cx,
954 |registrations| &mut registrations.diagnostics,
955 |capabilities| &mut capabilities.diagnostic_provider,
956 )?
957 .removed()
958 {
959 let local = self
960 .as_local_mut()
961 .context("Expected LSP Store to be local")?;
962 let state = local
963 .language_servers
964 .get_mut(&server_id)
965 .context("Could not obtain Language Servers state")?;
966 if let LanguageServerState::Running {
967 workspace_diagnostics_refresh_tasks,
968 ..
969 } = state
970 {
971 workspace_diagnostics_refresh_tasks.remove(&Some(unreg.id.clone()));
972 }
973
974 self.clear_unregistered_diagnostics(
975 server_id,
976 SharedString::from(unreg.id.clone()),
977 cx,
978 )?;
979 }
980 }
981 "textDocument/documentColor" => {
982 if self
983 .unregister_dynamic_capability(
984 &server,
985 unreg,
986 cx,
987 |registrations| &mut registrations.color,
988 |capabilities| &mut capabilities.color_provider,
989 )?
990 .capability_changed()
991 {
992 self.refresh_document_colors(Some(server_id), cx);
993 }
994 }
995 "textDocument/foldingRange" => {
996 if self
997 .unregister_dynamic_capability(
998 &server,
999 unreg,
1000 cx,
1001 |registrations| &mut registrations.folding_range,
1002 |capabilities| &mut capabilities.folding_range_provider,
1003 )?
1004 .capability_changed()
1005 {
1006 self.refresh_folding_ranges(Some(server_id), cx);
1007 }
1008 }
1009 "textDocument/documentLink" => {
1010 if self
1011 .unregister_dynamic_capability(
1012 &server,
1013 unreg,
1014 cx,
1015 |registrations| &mut registrations.document_link,
1016 |capabilities| &mut capabilities.document_link_provider,
1017 )?
1018 .capability_changed()
1019 {
1020 self.refresh_document_links(Some(server_id), cx);
1021 }
1022 }
1023 _ => log::warn!("unhandled capability unregistration: {unreg:?}"),
1024 }
1025 }
1026
1027 Ok(())
1028 }
1029}
1030
1031// Registration with registerOptions as null, should fallback to true.
1032// https://github.com/microsoft/vscode-languageserver-node/blob/d90a87f9557a0df9142cfb33e251cfa6fe27d970/client/src/common/client.ts#L2133
1033fn parse_register_capabilities<T: serde::de::DeserializeOwned>(
1034 register_options: Option<serde_json::Value>,
1035) -> Result<OneOf<bool, T>> {
1036 Ok(match register_options {
1037 Some(options) => OneOf::Right(serde_json::from_value::<T>(options)?),
1038 None => OneOf::Left(true),
1039 })
1040}
1041
1042fn take_text_document_sync_options(
1043 capabilities: &mut lsp::ServerCapabilities,
1044) -> lsp::TextDocumentSyncOptions {
1045 match capabilities.text_document_sync.take() {
1046 Some(lsp::TextDocumentSyncCapability::Options(sync_options)) => sync_options,
1047 Some(lsp::TextDocumentSyncCapability::Kind(sync_kind)) => {
1048 let mut sync_options = lsp::TextDocumentSyncOptions::default();
1049 sync_options.change = Some(sync_kind);
1050 sync_options
1051 }
1052 None => lsp::TextDocumentSyncOptions::default(),
1053 }
1054}
1055