1use crate::disk_cache::DiskDiagnosticsCacheSessionV0;
2use crate::workspace_runtime_registry::WorkspaceRuntimeRegistry;
3use omena_incremental::IncrementalCancellationRegistryV0;
4#[cfg(feature = "salsa-style-diagnostics")]
5use omena_query::ReverseDependencyIndexV0;
6use omena_query::{
7 AnalyzedGraphV0, OmenaQueryExternalSifInputV0, OmenaQuerySourceSelectorOccurrenceIndexV0,
8 OmenaQuerySourceSelectorReferenceFactV0 as SourceSelectorReferenceFact,
9 OmenaQuerySourceSyntaxIndexV0 as SourceSyntaxIndex, OmenaQueryStyleCascadeNarrowingSubstrateV0,
10 OmenaQueryStylePackageManifestV0, OmenaQueryStyleResolutionInputsV0,
11 OmenaQueryStyleSelectorDefinitionV0, OmenaQueryStyleSourceInputV0,
12 OmenaWorkspaceOccurrenceFamilyV0, OmenaWorkspaceOccurrenceIndexV0,
13 OmenaWorkspaceOccurrenceKindV0, OmenaWorkspaceOccurrenceRoleV0, ParserPositionV0,
14 ParserRangeV0,
15};
16#[cfg(feature = "parallel-style-diagnostics")]
17use omena_query::{
18 OmenaResolverStyleModuleConfirmationIdentityIndexV0,
19 OmenaResolverStyleModuleDiskCandidateIdentityV0,
20};
21use omena_tsgo_client::{TsgoTypeFactResultEntryV0, TsgoWorkspaceProcessPoolV0};
22use serde::Serialize;
23use std::cell::RefCell;
24use std::collections::{BTreeMap, BTreeSet};
25use std::sync::{
26 Arc, Mutex, MutexGuard,
27 atomic::{AtomicU8, AtomicU64, Ordering},
28};
29
30#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
31#[serde(rename_all = "camelCase")]
32pub enum LspDocumentOrigin {
33 Local,
34 Foreign,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
38#[serde(rename_all = "camelCase")]
39pub struct LspTextDocumentState {
40 pub uri: String,
41 #[serde(skip)]
42 pub origin: LspDocumentOrigin,
43 pub workspace_folder_uri: Option<String>,
44 pub language_id: String,
45 pub version: i64,
46 pub text: String,
47 #[serde(skip)]
48 pub(crate) text_hash: String,
49 pub style_summary: Option<LspStyleDocumentSummary>,
50 pub diagnostics_schedule_count: usize,
51 pub optimizing_tier_feedback: Option<LspOptimizingTierFeedback>,
52 #[serde(skip)]
53 pub style_candidates: Vec<LspStyleHoverCandidate>,
54 #[serde(skip)]
55 pub(crate) source_syntax_index: SourceSyntaxIndex,
56 #[serde(skip)]
57 pub(crate) has_unresolved_style_import: bool,
58 #[serde(skip)]
59 pub source_selector_candidates: Vec<LspStyleHoverCandidate>,
60 #[serde(skip)]
61 pub(crate) source_type_fact_selector_references: Vec<SourceSelectorReferenceFact>,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
65#[serde(rename_all = "camelCase")]
66pub struct LspOptimizingTierFeedback {
67 pub schema_version: &'static str,
68 pub product: &'static str,
69 pub document_version: i64,
70 pub policy: &'static str,
71 pub consumer: &'static str,
72 pub analyzed_graph: AnalyzedGraphV0,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
76#[serde(rename_all = "camelCase")]
77pub struct LspStyleDocumentSummary {
78 pub language: &'static str,
79 pub selector_names: Vec<String>,
80 pub custom_property_decl_names: Vec<String>,
81 pub custom_property_ref_names: Vec<String>,
82 pub sass_module_use_sources: Vec<String>,
83 pub sass_module_forward_sources: Vec<String>,
84 pub diagnostic_count: usize,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
88#[serde(rename_all = "camelCase")]
89pub struct LspStyleHoverCandidatesResult {
90 pub schema_version: &'static str,
91 pub product: &'static str,
92 pub document_uri: String,
93 pub workspace_folder_uri: Option<String>,
94 pub language: Option<&'static str>,
95 pub query_position: Option<ParserPositionV0>,
96 pub candidate_count: usize,
97 pub candidates: Vec<LspStyleHoverCandidate>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
101#[serde(rename_all = "camelCase")]
102pub struct LspStyleHoverCandidate {
103 pub kind: &'static str,
104 pub name: String,
105 pub range: ParserRangeV0,
106 pub source: &'static str,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub target_style_uri: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub namespace: Option<String>,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
114#[serde(rename_all = "camelCase")]
115pub struct LspWorkspaceFolderState {
116 pub uri: String,
117 pub name: String,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
121#[serde(rename_all = "camelCase")]
122pub struct LspWatchedFileChangeState {
123 pub uri: String,
124 pub change_type: u64,
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
128pub(crate) struct LspFileId(u32);
129
130impl LspFileId {
131 #[cfg(test)]
132 pub(crate) fn incremental_key(self) -> u32 {
133 self.0
134 }
135}
136
137#[derive(Debug, Clone, Default)]
138pub(crate) struct LspFileIdentityInterner {
139 next_id: u32,
140 ids_by_storage_uri: BTreeMap<String, LspFileId>,
141 storage_uris_by_id: BTreeMap<LspFileId, String>,
142}
143
144impl LspFileIdentityInterner {
145 fn intern_uri(&mut self, uri: &str) -> (LspFileId, String) {
146 let storage_uri = Self::storage_uri(uri);
147 if let Some(file_id) = self.ids_by_storage_uri.get(storage_uri.as_str()) {
148 return (*file_id, storage_uri);
149 }
150 let file_id = LspFileId(self.next_id);
151 self.next_id = self.next_id.saturating_add(1);
152 self.ids_by_storage_uri.insert(storage_uri.clone(), file_id);
153 self.storage_uris_by_id.insert(file_id, storage_uri.clone());
154 (file_id, storage_uri)
155 }
156
157 fn file_id_for_uri(&self, uri: &str) -> Option<LspFileId> {
158 let storage_uri = Self::storage_uri(uri);
159 self.ids_by_storage_uri.get(storage_uri.as_str()).copied()
160 }
161
162 pub(crate) fn storage_uri_for_file_id(&self, file_id: LspFileId) -> Option<&str> {
163 self.storage_uris_by_id.get(&file_id).map(String::as_str)
164 }
165
166 fn storage_uri(uri: &str) -> String {
167 crate::protocol::canonical_file_uri(uri).unwrap_or_else(|| uri.to_string())
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
172#[serde(rename_all = "camelCase")]
173pub struct LspShellStateSnapshot {
174 pub shutdown_requested: bool,
175 pub should_exit: bool,
176 pub features: LspFeatureSettings,
177 pub diagnostics: LspDiagnosticSettings,
178 pub resolution: LspResolutionSettings,
179 pub cancelled_request_count: usize,
180 pub suppressed_dispatched_result_count: u64,
181 pub workspace_style_index_exhausted_count: usize,
182 pub workspace_index_pending_file_count: usize,
183 pub external_sif_lock_read_count: usize,
184 pub external_sif_bridge_generation_count: usize,
185 pub document_count: usize,
186 pub workspace_folder_count: usize,
187 pub configuration_change_count: usize,
188 pub watched_file_event_count: usize,
189 pub cached_workspace_resolution_input_count: usize,
190 pub tide_epoch: u64,
194 pub tide_sif_lane_generation: u64,
195 pub tide_sif_lane_in_flight: bool,
196 pub tide_sif_lane_has_demand: bool,
197 pub tide_republish_lane_generation: u64,
198 pub tide_republish_lane_in_flight: bool,
199 pub tide_republish_lane_has_demand: bool,
200 pub tide_starvation_alarm_count: u64,
201 pub tide_sif_lane_oldest_deposit_age_ticks: Option<u64>,
205 pub tide_republish_lane_oldest_deposit_age_ticks: Option<u64>,
206 pub documents: Vec<LspTextDocumentState>,
207 pub workspace_folders: Vec<LspWorkspaceFolderState>,
208 pub watched_file_changes: Vec<LspWatchedFileChangeState>,
209}
210
211const DISPATCHED_REQUEST_PENDING: u8 = 0;
212const DISPATCHED_REQUEST_CANCELLED: u8 = 1;
213const DISPATCHED_REQUEST_COMPLETED: u8 = 2;
214
215#[derive(Debug, Default)]
216struct LspInFlightRequestRegistryInner {
217 next_generation: u64,
218 requests: BTreeMap<String, LspInFlightRequestEntry>,
219}
220
221#[derive(Debug, Clone)]
222struct LspInFlightRequestEntry {
223 generation: u64,
224 status: Arc<AtomicU8>,
225}
226
227#[derive(Debug, Clone, Default)]
232pub(crate) struct LspInFlightRequestRegistry {
233 inner: Arc<Mutex<LspInFlightRequestRegistryInner>>,
234 suppressed_result_count: Arc<AtomicU64>,
235}
236
237#[derive(Debug, Clone)]
238pub(crate) struct LspDispatchedRequestToken {
239 request_key: String,
240 generation: u64,
241 status: Arc<AtomicU8>,
242 registry: LspInFlightRequestRegistry,
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
246pub(crate) enum LspDispatchedRequestCompletion {
247 Result,
248 Cancelled,
249 AlreadyCompleted,
250}
251
252impl LspInFlightRequestRegistry {
253 pub(crate) fn register(&self, request_key: String) -> LspDispatchedRequestToken {
254 let mut inner = self.inner.lock().unwrap_or_else(|error| error.into_inner());
255 inner.next_generation = inner.next_generation.saturating_add(1).max(1);
256 let generation = inner.next_generation;
257 let status = Arc::new(AtomicU8::new(DISPATCHED_REQUEST_PENDING));
258 inner.requests.insert(
259 request_key.clone(),
260 LspInFlightRequestEntry {
261 generation,
262 status: Arc::clone(&status),
263 },
264 );
265 LspDispatchedRequestToken {
266 request_key,
267 generation,
268 status,
269 registry: self.clone(),
270 }
271 }
272
273 pub(crate) fn cancel(&self, request_key: &str) -> bool {
274 let inner = self.inner.lock().unwrap_or_else(|error| error.into_inner());
275 let Some(entry) = inner.requests.get(request_key) else {
276 return false;
277 };
278 let _ = entry.status.compare_exchange(
279 DISPATCHED_REQUEST_PENDING,
280 DISPATCHED_REQUEST_CANCELLED,
281 Ordering::AcqRel,
282 Ordering::Acquire,
283 );
284 true
285 }
286
287 pub(crate) fn suppressed_result_count(&self) -> u64 {
288 self.suppressed_result_count.load(Ordering::Acquire)
289 }
290
291 fn remove_if_current(&self, request_key: &str, generation: u64, status: &Arc<AtomicU8>) {
292 let mut inner = self.inner.lock().unwrap_or_else(|error| error.into_inner());
293 let is_current = inner.requests.get(request_key).is_some_and(|entry| {
294 entry.generation == generation && Arc::ptr_eq(&entry.status, status)
295 });
296 if is_current {
297 inner.requests.remove(request_key);
298 }
299 }
300}
301
302impl LspDispatchedRequestToken {
303 pub(crate) fn complete(&self) -> LspDispatchedRequestCompletion {
304 let completion = match self.status.compare_exchange(
305 DISPATCHED_REQUEST_PENDING,
306 DISPATCHED_REQUEST_COMPLETED,
307 Ordering::AcqRel,
308 Ordering::Acquire,
309 ) {
310 Ok(_) => LspDispatchedRequestCompletion::Result,
311 Err(DISPATCHED_REQUEST_CANCELLED) => {
312 if self
313 .status
314 .compare_exchange(
315 DISPATCHED_REQUEST_CANCELLED,
316 DISPATCHED_REQUEST_COMPLETED,
317 Ordering::AcqRel,
318 Ordering::Acquire,
319 )
320 .is_ok()
321 {
322 self.registry
323 .suppressed_result_count
324 .fetch_add(1, Ordering::AcqRel);
325 LspDispatchedRequestCompletion::Cancelled
326 } else {
327 LspDispatchedRequestCompletion::AlreadyCompleted
328 }
329 }
330 Err(_) => LspDispatchedRequestCompletion::AlreadyCompleted,
331 };
332 self.registry
333 .remove_if_current(self.request_key.as_str(), self.generation, &self.status);
334 completion
335 }
336}
337
338#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
339#[serde(rename_all = "camelCase")]
340pub struct LspFeatureSettings {
341 pub definition: bool,
342 pub hover: bool,
343 pub completion: bool,
344 pub references: bool,
345 pub rename: bool,
346}
347
348impl Default for LspFeatureSettings {
349 fn default() -> Self {
350 Self {
351 definition: true,
352 hover: true,
353 completion: true,
354 references: true,
355 rename: true,
356 }
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
361#[serde(rename_all = "camelCase")]
362pub struct LspDiagnosticSettings {
363 pub severity: u8,
364 pub deep_analysis: bool,
365}
366
367impl Default for LspDiagnosticSettings {
368 fn default() -> Self {
369 Self {
370 severity: 2,
371 deep_analysis: false,
372 }
373 }
374}
375
376#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
377#[serde(rename_all = "camelCase")]
378pub struct LspResolutionSettings {
379 pub package_manifest_paths: Vec<String>,
380 #[serde(skip)]
381 pub package_manifests: Vec<OmenaQueryStylePackageManifestV0>,
382 #[serde(skip)]
383 pub workspace_style_resolution_inputs: BTreeMap<String, OmenaQueryStyleResolutionInputsV0>,
384 #[serde(skip)]
388 pub external_sifs: Vec<OmenaQueryExternalSifInputV0>,
389 #[serde(skip)]
390 pub(crate) bridge_external_sif_urls: BTreeSet<String>,
391}
392
393#[derive(Debug)]
399pub(crate) struct LspCascadeNarrowingSubstrateMemo {
400 pub(crate) style_sources: Vec<OmenaQueryStyleSourceInputV0>,
401 pub(crate) package_manifests: Vec<OmenaQueryStylePackageManifestV0>,
402 pub(crate) external_sifs: Vec<OmenaQueryExternalSifInputV0>,
403 pub(crate) resolution_inputs: OmenaQueryStyleResolutionInputsV0,
404 pub(crate) substrate: Arc<OmenaQueryStyleCascadeNarrowingSubstrateV0>,
405}
406
407#[cfg(feature = "parallel-style-diagnostics")]
408#[derive(Debug, Clone)]
409pub(crate) struct LspResolverIdentityIndexMemo {
410 pub(crate) available_style_paths: Vec<String>,
411 pub(crate) disk_style_path_identities: Vec<OmenaResolverStyleModuleDiskCandidateIdentityV0>,
412 pub(crate) index: Arc<OmenaResolverStyleModuleConfirmationIdentityIndexV0>,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
416#[serde(rename_all = "camelCase")]
417pub(crate) struct LspSourceSelectorOccurrenceDocumentKey {
418 pub(crate) uri: String,
419 pub(crate) workspace_folder_uri: Option<String>,
420 pub(crate) language_id: String,
421 pub(crate) version: i64,
422 pub(crate) text_hash: String,
423}
424
425#[derive(Debug, Clone)]
426pub(crate) struct LspWorkspaceOccurrenceIndexMemo {
427 pub(crate) workspace_folder_uri: Option<String>,
428 pub(crate) environment_digest: Option<String>,
435 pub(crate) source_document_keys: Vec<LspSourceSelectorOccurrenceDocumentKey>,
436 pub(crate) style_document_keys: Vec<LspSourceSelectorOccurrenceDocumentKey>,
437 pub(crate) definitions: Vec<OmenaQueryStyleSelectorDefinitionV0>,
438 pub(crate) source_selector_index: Arc<OmenaQuerySourceSelectorOccurrenceIndexV0>,
439 pub(crate) workspace_index: Arc<OmenaWorkspaceOccurrenceIndexV0>,
440}
441
442pub(crate) type LspDocumentColorCacheV0 = BTreeMap<String, ((i64, u64, u64), serde_json::Value)>;
444
445#[cfg(feature = "salsa-style-diagnostics")]
446#[derive(Debug, Clone, PartialEq, Eq)]
447pub(crate) struct LspReverseDependencyIndexMemo {
448 pub(crate) revision: u64,
449 pub(crate) summary_hash: String,
450 pub(crate) ledger_epoch: u64,
454 pub(crate) index: ReverseDependencyIndexV0,
455}
456
457#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
458#[serde(rename_all = "camelCase")]
459pub(crate) struct LspStyleSymbolOccurrenceV0 {
460 pub(crate) moniker: String,
461 pub(crate) uri: String,
462 pub(crate) kind: OmenaWorkspaceOccurrenceKindV0,
463 pub(crate) family: OmenaWorkspaceOccurrenceFamilyV0,
464 pub(crate) name: String,
465 pub(crate) range: ParserRangeV0,
466 pub(crate) role: OmenaWorkspaceOccurrenceRoleV0,
467 #[serde(skip_serializing_if = "Option::is_none")]
468 pub(crate) namespace: Option<String>,
469}
470
471#[allow(private_interfaces)]
483pub trait LspQueryReadView {
484 #[doc(hidden)]
485 fn query_features(&self) -> &LspFeatureSettings;
486
487 #[doc(hidden)]
488 fn query_diagnostics(&self) -> &LspDiagnosticSettings;
489
490 #[doc(hidden)]
491 fn query_resolution(&self) -> &LspResolutionSettings;
492
493 #[doc(hidden)]
494 fn query_file_identity(&self) -> &LspFileIdentityInterner;
495
496 #[doc(hidden)]
497 fn query_documents(&self) -> &BTreeMap<LspFileId, Arc<LspTextDocumentState>>;
498
499 #[doc(hidden)]
500 fn query_open_document_uris(&self) -> &BTreeSet<LspFileId>;
501
502 #[doc(hidden)]
503 fn query_workspace_runtime_registry(&self) -> &WorkspaceRuntimeRegistry;
504
505 #[doc(hidden)]
506 fn query_tide_ledger(&self) -> &crate::tide::TideEpochLedgerV0;
507
508 #[cfg(feature = "salsa-style-diagnostics")]
509 #[doc(hidden)]
510 fn query_style_workspace_snapshot_revision_hint(&self) -> u64;
511
512 #[doc(hidden)]
513 fn query_document_color_cache(&self) -> &Arc<Mutex<LspDocumentColorCacheV0>>;
514
515 #[doc(hidden)]
516 fn query_cascade_narrowing_substrate_memo(
517 &self,
518 ) -> &Arc<Mutex<Option<LspCascadeNarrowingSubstrateMemo>>>;
519
520 #[doc(hidden)]
521 fn query_workspace_occurrence_index_memo(
522 &self,
523 ) -> &Arc<Mutex<Option<LspWorkspaceOccurrenceIndexMemo>>>;
524
525 #[cfg(feature = "parallel-style-diagnostics")]
526 #[doc(hidden)]
527 fn query_resolver_identity_index_memo(
528 &self,
529 ) -> &Arc<Mutex<Option<LspResolverIdentityIndexMemo>>>;
530
531 fn document(&self, uri: &str) -> Option<&LspTextDocumentState> {
532 let file_id = self.query_file_identity().file_id_for_uri(uri)?;
533 self.query_documents().get(&file_id).map(Arc::as_ref)
534 }
535
536 fn document_for_file_id(&self, file_id: LspFileId) -> Option<&LspTextDocumentState> {
537 self.query_documents().get(&file_id).map(Arc::as_ref)
538 }
539
540 fn workspace_folder(&self, uri: &str) -> Option<&LspWorkspaceFolderState> {
541 self.query_workspace_runtime_registry().get(uri)
542 }
543
544 fn cascade_narrowing_substrate_memo_lock(
545 &self,
546 ) -> MutexGuard<'_, Option<LspCascadeNarrowingSubstrateMemo>> {
547 self.query_cascade_narrowing_substrate_memo()
548 .lock()
549 .unwrap_or_else(|error| error.into_inner())
550 }
551
552 fn workspace_occurrence_index_memo_lock(
553 &self,
554 ) -> MutexGuard<'_, Option<LspWorkspaceOccurrenceIndexMemo>> {
555 self.query_workspace_occurrence_index_memo()
556 .lock()
557 .unwrap_or_else(|error| error.into_inner())
558 }
559
560 #[cfg(feature = "parallel-style-diagnostics")]
561 fn resolver_identity_index_memo_lock(
562 &self,
563 ) -> MutexGuard<'_, Option<LspResolverIdentityIndexMemo>> {
564 self.query_resolver_identity_index_memo()
565 .lock()
566 .unwrap_or_else(|error| error.into_inner())
567 }
568
569 #[cfg(feature = "salsa-style-diagnostics")]
570 fn style_workspace_snapshot_revision_hint(&self) -> omena_query::IncrementalRevisionV0 {
571 omena_query::IncrementalRevisionV0 {
572 value: self.query_style_workspace_snapshot_revision_hint().max(1),
573 }
574 }
575}
576
577#[derive(Debug, Default)]
578pub struct LspShellState {
579 pub shutdown_requested: bool,
580 pub should_exit: bool,
581 pub(crate) features: LspFeatureSettings,
582 pub(crate) diagnostics: LspDiagnosticSettings,
583 pub(crate) resolution: LspResolutionSettings,
584 pub(crate) cancelled_request_ids: IncrementalCancellationRegistryV0,
585 pub(crate) in_flight_requests: LspInFlightRequestRegistry,
586 pub(crate) workspace_style_index_exhausted_count: usize,
587 pub(crate) workspace_index_pending_file_count: usize,
588 pub(crate) external_sif_lock_read_count: usize,
589 pub(crate) external_sif_bridge_generation_count: usize,
590 pub(crate) external_sif_refresh_deferred: bool,
591 pub(crate) tide_ledger: crate::tide::TideEpochLedgerV0,
596 pub(crate) tide_sif_lane: crate::tide::TideLaneV0<crate::tide::TideSifDemandV0>,
597 pub(crate) tide_republish_lane: crate::tide::TideLaneV0<crate::tide::TideRepublishDemandV0>,
598 pub(crate) tide_republish_gen_watch: std::sync::Arc<std::sync::atomic::AtomicU64>,
602 pub(crate) tide_tick: u64,
605 pub(crate) workspace_index_revision: u64,
606 #[cfg(feature = "salsa-style-diagnostics")]
607 pub(crate) style_workspace_snapshot_revision_hint: u64,
608 pub(crate) configuration_change_count: usize,
609 pub(crate) file_identity: LspFileIdentityInterner,
615 pub(crate) documents: BTreeMap<LspFileId, Arc<LspTextDocumentState>>,
616 pub(crate) open_document_uris: BTreeSet<LspFileId>,
617 pub(crate) workspace_runtime_registry: WorkspaceRuntimeRegistry,
618 pub(crate) tsgo_workspace_process_pool: TsgoWorkspaceProcessPoolV0,
619 pub(crate) watched_file_changes: Vec<LspWatchedFileChangeState>,
620 pub(crate) client_supports_work_done_progress: bool,
621 pub(crate) next_server_progress_request_id: u64,
622 pub(crate) pending_server_progress_request_tokens: BTreeMap<String, String>,
623 pub(crate) cascade_narrowing_substrate_memo:
629 Arc<Mutex<Option<LspCascadeNarrowingSubstrateMemo>>>,
630 #[cfg(feature = "parallel-style-diagnostics")]
631 pub(crate) resolver_identity_index_memo: Arc<Mutex<Option<LspResolverIdentityIndexMemo>>>,
632 pub(crate) workspace_occurrence_index_memo: Arc<Mutex<Option<LspWorkspaceOccurrenceIndexMemo>>>,
638 pub(crate) document_color_cache: Arc<Mutex<LspDocumentColorCacheV0>>,
642 #[cfg(feature = "salsa-style-diagnostics")]
643 pub(crate) reverse_dependency_index_memo: RefCell<Option<LspReverseDependencyIndexMemo>>,
644 pub(crate) style_module_interface_memo:
650 RefCell<BTreeMap<String, omena_query::OmenaQueryModuleInterfaceProjectionV0>>,
651 pub(crate) source_type_fact_cache: BTreeMap<String, Vec<TsgoTypeFactResultEntryV0>>,
652 pub(crate) disk_diagnostics_cache_session: RefCell<DiskDiagnosticsCacheSessionV0>,
656 #[cfg(feature = "salsa-style-diagnostics")]
660 pub(crate) style_memo_host: RefCell<Option<omena_query::OmenaQueryStyleMemoHostV0>>,
661}
662
663impl LspShellState {
664 pub fn document_count(&self) -> usize {
665 self.documents.len()
666 }
667
668 pub fn workspace_folder_count(&self) -> usize {
669 self.workspace_runtime_registry.len()
670 }
671
672 pub(crate) fn allocate_work_done_progress_request(&mut self) -> (String, String) {
673 self.next_server_progress_request_id += 1;
674 let id = format!(
675 "omena-work-done-progress-create-{}",
676 self.next_server_progress_request_id
677 );
678 let token = format!(
679 "omena-workspace-index-{}",
680 self.next_server_progress_request_id
681 );
682 self.pending_server_progress_request_tokens
683 .insert(id.clone(), token.clone());
684 (id, token)
685 }
686
687 pub(crate) fn take_server_progress_response(&mut self, id: &str) -> bool {
688 self.pending_server_progress_request_tokens
689 .remove(id)
690 .is_some()
691 }
692
693 pub fn document(&self, uri: &str) -> Option<&LspTextDocumentState> {
694 let file_id = self.file_identity.file_id_for_uri(uri)?;
695 self.documents.get(&file_id).map(Arc::as_ref)
696 }
697
698 #[cfg(feature = "test-support")]
699 pub fn evict_document_for_test(&mut self, uri: &str) -> Option<LspTextDocumentState> {
700 self.remove_document_uri(uri)
701 }
702
703 #[cfg(feature = "test-support")]
704 pub fn clear_workspace_occurrence_index_memo_for_test(&self) {
705 *self.workspace_occurrence_index_memo_lock() = None;
706 }
707
708 pub(crate) fn workspace_occurrence_index_memo_lock(
709 &self,
710 ) -> MutexGuard<'_, Option<LspWorkspaceOccurrenceIndexMemo>> {
711 self.workspace_occurrence_index_memo
712 .lock()
713 .unwrap_or_else(|error| error.into_inner())
714 }
715
716 pub(crate) fn document_mut(&mut self, uri: &str) -> Option<&mut LspTextDocumentState> {
717 let file_id = self.file_identity.file_id_for_uri(uri)?;
718 let document = self.documents.get_mut(&file_id)?;
719 if document.origin == LspDocumentOrigin::Foreign {
720 return None;
721 }
722 Some(Arc::make_mut(document))
723 }
724
725 #[cfg(test)]
726 pub(crate) fn document_storage_uri(uri: &str) -> String {
727 LspFileIdentityInterner::storage_uri(uri)
728 }
729
730 #[cfg(test)]
731 pub(crate) fn document_file_id(&self, uri: &str) -> Option<LspFileId> {
732 self.file_identity.file_id_for_uri(uri)
733 }
734
735 pub(crate) fn document_storage_uri_for_file_id(&self, file_id: LspFileId) -> Option<&str> {
736 self.file_identity.storage_uri_for_file_id(file_id)
737 }
738
739 pub(crate) fn document_for_file_id(&self, file_id: LspFileId) -> Option<&LspTextDocumentState> {
740 self.documents.get(&file_id).map(Arc::as_ref)
741 }
742
743 pub(crate) fn insert_open_document_uri(&mut self, uri: &str) -> String {
744 let (file_id, storage_uri) = self.file_identity.intern_uri(uri);
745 self.open_document_uris.insert(file_id);
746 storage_uri
747 }
748
749 pub(crate) fn remove_open_document_uri(&mut self, uri: &str) {
750 if let Some(file_id) = self.file_identity.file_id_for_uri(uri) {
751 self.open_document_uris.remove(&file_id);
752 }
753 }
754
755 pub(crate) fn has_open_document_uri(&self, uri: &str) -> bool {
756 self.file_identity
757 .file_id_for_uri(uri)
758 .is_some_and(|file_id| self.open_document_uris.contains(&file_id))
759 }
760
761 pub(crate) fn insert_document(&mut self, uri: &str, document: LspTextDocumentState) {
762 let (file_id, _) = self.file_identity.intern_uri(uri);
763 self.documents.insert(file_id, Arc::new(document));
764 }
765
766 pub(crate) fn remove_document_uri(&mut self, uri: &str) -> Option<LspTextDocumentState> {
767 let file_id = self.file_identity.file_id_for_uri(uri)?;
768 self.documents.remove(&file_id).map(Arc::unwrap_or_clone)
769 }
770
771 pub(crate) fn contains_document_uri(&self, uri: &str) -> bool {
772 self.document(uri).is_some()
773 }
774
775 pub fn workspace_folder(&self, uri: &str) -> Option<&LspWorkspaceFolderState> {
776 self.workspace_runtime_registry.get(uri)
777 }
778
779 pub fn snapshot(&self) -> LspShellStateSnapshot {
780 LspShellStateSnapshot {
781 shutdown_requested: self.shutdown_requested,
782 should_exit: self.should_exit,
783 features: self.features.clone(),
784 diagnostics: self.diagnostics.clone(),
785 resolution: self.resolution.clone(),
786 cancelled_request_count: self.cancelled_request_ids.len(),
787 suppressed_dispatched_result_count: self.in_flight_requests.suppressed_result_count(),
788 workspace_style_index_exhausted_count: self.workspace_style_index_exhausted_count,
789 workspace_index_pending_file_count: self.workspace_index_pending_file_count,
790 external_sif_lock_read_count: self.external_sif_lock_read_count,
791 external_sif_bridge_generation_count: self.external_sif_bridge_generation_count,
792 document_count: self.document_count(),
793 workspace_folder_count: self.workspace_folder_count(),
794 configuration_change_count: self.configuration_change_count,
795 watched_file_event_count: self.watched_file_changes.len(),
796 cached_workspace_resolution_input_count: self
797 .resolution
798 .workspace_style_resolution_inputs
799 .len(),
800 tide_epoch: self.tide_ledger.epoch(),
801 tide_sif_lane_generation: self.tide_sif_lane.generation(),
802 tide_sif_lane_in_flight: self.tide_sif_lane.in_flight(),
803 tide_sif_lane_has_demand: self.tide_sif_lane.has_demand(),
804 tide_republish_lane_generation: self.tide_republish_lane.generation(),
805 tide_republish_lane_in_flight: self.tide_republish_lane.in_flight(),
806 tide_republish_lane_has_demand: self.tide_republish_lane.has_demand(),
807 tide_starvation_alarm_count: self.tide_sif_lane.starvation_alarm_count()
808 + self.tide_republish_lane.starvation_alarm_count(),
809 tide_sif_lane_oldest_deposit_age_ticks: self
810 .tide_sif_lane
811 .oldest_deposit_age_ticks(self.tide_tick),
812 tide_republish_lane_oldest_deposit_age_ticks: self
813 .tide_republish_lane
814 .oldest_deposit_age_ticks(self.tide_tick),
815 documents: {
816 let mut documents = self
817 .documents
818 .values()
819 .map(|document| (**document).clone())
820 .collect::<Vec<_>>();
821 documents.sort_by(|left, right| left.uri.cmp(&right.uri));
822 documents
823 },
824 workspace_folders: self.workspace_runtime_registry.folder_snapshots(),
825 watched_file_changes: self.watched_file_changes.clone(),
826 }
827 }
828
829 #[cfg(feature = "parallel-style-diagnostics")]
830 pub(crate) fn resolver_identity_index_memo_lock(
831 &self,
832 ) -> MutexGuard<'_, Option<LspResolverIdentityIndexMemo>> {
833 self.resolver_identity_index_memo
834 .lock()
835 .unwrap_or_else(|error| error.into_inner())
836 }
837
838 pub fn tide_republish_lane_generation(&self) -> u64 {
841 self.tide_republish_lane.generation()
842 }
843
844 pub fn tide_republish_lane_in_flight(&self) -> bool {
848 self.tide_republish_lane.in_flight()
849 }
850
851 pub fn advance_tide_tick(&mut self) {
854 self.tide_tick = self.tide_tick.saturating_add(1);
855 }
856
857 pub(crate) fn tide_reopen_republish_window(&mut self) {
860 let generation = self.tide_republish_lane.reopen_window();
861 self.tide_republish_gen_watch
862 .store(generation, std::sync::atomic::Ordering::Relaxed);
863 }
864
865 #[cfg(feature = "salsa-style-diagnostics")]
866 pub(crate) fn mark_style_workspace_snapshot_changed(
867 &mut self,
868 ) -> omena_query::IncrementalRevisionV0 {
869 let committed = self
870 .style_memo_host
871 .borrow()
872 .as_ref()
873 .map(|host| host.committed_revision().value)
874 .unwrap_or_default();
875 let next = self
876 .style_workspace_snapshot_revision_hint
877 .max(committed)
878 .saturating_add(1)
879 .max(1);
880 self.style_workspace_snapshot_revision_hint = next;
881 omena_query::IncrementalRevisionV0 { value: next }
882 }
883
884 pub fn query_snapshot(&self) -> LspQuerySnapshotV0 {
891 LspQuerySnapshotV0 {
892 state: LspShellState {
893 features: self.features.clone(),
894 diagnostics: self.diagnostics.clone(),
895 resolution: self.resolution.clone(),
896 file_identity: self.file_identity.clone(),
897 documents: self.documents.clone(),
898 open_document_uris: self.open_document_uris.clone(),
899 workspace_runtime_registry: self.workspace_runtime_registry.clone(),
900 tide_ledger: self.tide_ledger.clone(),
901 #[cfg(feature = "salsa-style-diagnostics")]
902 style_workspace_snapshot_revision_hint: self.style_workspace_snapshot_revision_hint,
903 document_color_cache: Arc::clone(&self.document_color_cache),
904 cascade_narrowing_substrate_memo: Arc::clone(
905 &self.cascade_narrowing_substrate_memo,
906 ),
907 workspace_occurrence_index_memo: Arc::clone(&self.workspace_occurrence_index_memo),
908 #[cfg(feature = "parallel-style-diagnostics")]
909 resolver_identity_index_memo: Arc::clone(&self.resolver_identity_index_memo),
910 ..LspShellState::default()
911 },
912 }
913 }
914}
915
916#[allow(private_interfaces)]
917impl LspQueryReadView for LspShellState {
918 fn query_features(&self) -> &LspFeatureSettings {
919 &self.features
920 }
921
922 fn query_diagnostics(&self) -> &LspDiagnosticSettings {
923 &self.diagnostics
924 }
925
926 fn query_resolution(&self) -> &LspResolutionSettings {
927 &self.resolution
928 }
929
930 fn query_file_identity(&self) -> &LspFileIdentityInterner {
931 &self.file_identity
932 }
933
934 fn query_documents(&self) -> &BTreeMap<LspFileId, Arc<LspTextDocumentState>> {
935 &self.documents
936 }
937
938 fn query_open_document_uris(&self) -> &BTreeSet<LspFileId> {
939 &self.open_document_uris
940 }
941
942 fn query_workspace_runtime_registry(&self) -> &WorkspaceRuntimeRegistry {
943 &self.workspace_runtime_registry
944 }
945
946 fn query_tide_ledger(&self) -> &crate::tide::TideEpochLedgerV0 {
947 &self.tide_ledger
948 }
949
950 #[cfg(feature = "salsa-style-diagnostics")]
951 fn query_style_workspace_snapshot_revision_hint(&self) -> u64 {
952 self.style_workspace_snapshot_revision_hint
953 }
954
955 fn query_document_color_cache(&self) -> &Arc<Mutex<LspDocumentColorCacheV0>> {
956 &self.document_color_cache
957 }
958
959 fn query_cascade_narrowing_substrate_memo(
960 &self,
961 ) -> &Arc<Mutex<Option<LspCascadeNarrowingSubstrateMemo>>> {
962 &self.cascade_narrowing_substrate_memo
963 }
964
965 fn query_workspace_occurrence_index_memo(
966 &self,
967 ) -> &Arc<Mutex<Option<LspWorkspaceOccurrenceIndexMemo>>> {
968 &self.workspace_occurrence_index_memo
969 }
970
971 #[cfg(feature = "parallel-style-diagnostics")]
972 fn query_resolver_identity_index_memo(
973 &self,
974 ) -> &Arc<Mutex<Option<LspResolverIdentityIndexMemo>>> {
975 &self.resolver_identity_index_memo
976 }
977}
978
979#[derive(Debug)]
986pub struct LspQuerySnapshotV0 {
987 pub(crate) state: LspShellState,
988}
989
990impl LspQuerySnapshotV0 {
991 #[cfg(test)]
992 pub(crate) fn shell_state_for_test(&self) -> &LspShellState {
993 &self.state
994 }
995}
996
997#[allow(private_interfaces)]
998impl LspQueryReadView for LspQuerySnapshotV0 {
999 fn query_features(&self) -> &LspFeatureSettings {
1000 &self.state.features
1001 }
1002
1003 fn query_diagnostics(&self) -> &LspDiagnosticSettings {
1004 &self.state.diagnostics
1005 }
1006
1007 fn query_resolution(&self) -> &LspResolutionSettings {
1008 &self.state.resolution
1009 }
1010
1011 fn query_file_identity(&self) -> &LspFileIdentityInterner {
1012 &self.state.file_identity
1013 }
1014
1015 fn query_documents(&self) -> &BTreeMap<LspFileId, Arc<LspTextDocumentState>> {
1016 &self.state.documents
1017 }
1018
1019 fn query_open_document_uris(&self) -> &BTreeSet<LspFileId> {
1020 &self.state.open_document_uris
1021 }
1022
1023 fn query_workspace_runtime_registry(&self) -> &WorkspaceRuntimeRegistry {
1024 &self.state.workspace_runtime_registry
1025 }
1026
1027 fn query_tide_ledger(&self) -> &crate::tide::TideEpochLedgerV0 {
1028 &self.state.tide_ledger
1029 }
1030
1031 #[cfg(feature = "salsa-style-diagnostics")]
1032 fn query_style_workspace_snapshot_revision_hint(&self) -> u64 {
1033 self.state.style_workspace_snapshot_revision_hint
1034 }
1035
1036 fn query_document_color_cache(&self) -> &Arc<Mutex<LspDocumentColorCacheV0>> {
1037 &self.state.document_color_cache
1038 }
1039
1040 fn query_cascade_narrowing_substrate_memo(
1041 &self,
1042 ) -> &Arc<Mutex<Option<LspCascadeNarrowingSubstrateMemo>>> {
1043 &self.state.cascade_narrowing_substrate_memo
1044 }
1045
1046 fn query_workspace_occurrence_index_memo(
1047 &self,
1048 ) -> &Arc<Mutex<Option<LspWorkspaceOccurrenceIndexMemo>>> {
1049 &self.state.workspace_occurrence_index_memo
1050 }
1051
1052 #[cfg(feature = "parallel-style-diagnostics")]
1053 fn query_resolver_identity_index_memo(
1054 &self,
1055 ) -> &Arc<Mutex<Option<LspResolverIdentityIndexMemo>>> {
1056 &self.state.resolver_identity_index_memo
1057 }
1058}
1059
1060const _: () = {
1063 const fn assert_send<T: Send>() {}
1064 assert_send::<LspQuerySnapshotV0>();
1065};