Skip to main content

Mountain/Environment/LanguageFeatureProvider/
FeatureMethods.rs

1//! All LSP feature method implementations.
2
3use std::sync::Arc;
4
5use CommonLibrary::{
6	Environment::Requires::Requires,
7	Error::CommonError::CommonError,
8	IPC::IPCProvider::IPCProvider,
9	LanguageFeature::DTO::{
10		CompletionContextDTO::CompletionContextDTO,
11		CompletionListDTO::CompletionListDTO,
12		HoverResultDTO::HoverResultDTO,
13		LocationDTO::LocationDTO,
14		PositionDTO::PositionDTO,
15		ProviderType::ProviderType,
16		TextEditDTO::TextEditDTO,
17	},
18};
19use serde_json::{Value, json};
20use url::Url;
21
22use crate::ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO;
23
24// All feature methods delegate to generic invoke pattern
25
26pub(super) async fn provide_code_actions(
27	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
28
29	document_uri:Url,
30
31	range_or_selection_dto:Value,
32
33	context_dto:Value,
34) -> Result<Option<Value>, CommonError> {
35	let provider =
36		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::CodeAction).await?;
37
38	match provider {
39		Some(registration) => {
40			let response = invoke_provider(
41				environment,
42				&registration,
43				vec![
44					json!(registration.Handle),
45					json!({ "external": document_uri.to_string(), "$mid": 1 }),
46					range_or_selection_dto,
47					context_dto,
48				],
49			)
50			.await?;
51
52			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
53		},
54
55		None => Ok(None),
56	}
57}
58
59pub(super) async fn provide_code_lenses(
60	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
61
62	document_uri:Url,
63) -> Result<Option<Value>, CommonError> {
64	let provider =
65		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::CodeLens).await?;
66
67	match provider {
68		Some(registration) => {
69			let response = invoke_provider(
70				environment,
71				&registration,
72				vec![
73					json!(registration.Handle),
74					json!({ "external": document_uri.to_string(), "$mid": 1 }),
75				],
76			)
77			.await?;
78
79			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
80		},
81
82		None => Ok(None),
83	}
84}
85
86pub(super) async fn provide_completions(
87	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
88
89	document_uri:Url,
90
91	position_dto:PositionDTO,
92
93	context_dto:CompletionContextDTO,
94
95	cancellation_token_value:Option<Value>,
96) -> Result<Option<CompletionListDTO>, CommonError> {
97	let provider =
98		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::Completion).await?;
99
100	match provider {
101		Some(registration) => {
102			let response = invoke_provider(
103				environment,
104				&registration,
105				vec![
106					json!(registration.Handle),
107					json!({ "external": document_uri.to_string(), "$mid": 1 }),
108					json!(position_dto),
109					json!(context_dto),
110					cancellation_token_value.unwrap_or_else(|| json!(null)),
111				],
112			)
113			.await?;
114
115			if response.is_null() {
116				Ok(None)
117			} else {
118				serde_json::from_value(response).map_err(|error| {
119					CommonError::SerializationError {
120						Description:format!("Failed to deserialize CompletionListDTO: {}", error),
121					}
122				})
123			}
124		},
125
126		None => Ok(None),
127	}
128}
129
130pub(super) async fn provide_definition(
131	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
132
133	document_uri:Url,
134
135	position_dto:PositionDTO,
136) -> Result<Option<Vec<LocationDTO>>, CommonError> {
137	let provider =
138		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::Definition).await?;
139
140	match provider {
141		Some(registration) => {
142			let response = invoke_provider(
143				environment,
144				&registration,
145				vec![
146					json!(registration.Handle),
147					json!({ "external": document_uri.to_string(), "$mid": 1 }),
148					json!(position_dto),
149				],
150			)
151			.await?;
152
153			if response.is_null() {
154				Ok(None)
155			} else {
156				serde_json::from_value(response).map_err(|error| {
157					CommonError::SerializationError {
158						Description:format!("Failed to deserialize Vec<LocationDTO>: {}", error),
159					}
160				})
161			}
162		},
163
164		None => Ok(None),
165	}
166}
167
168pub(super) async fn provide_document_formatting_edits(
169	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
170
171	document_uri:Url,
172
173	options_dto:Value,
174) -> Result<Option<Vec<TextEditDTO>>, CommonError> {
175	let provider =
176		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::DocumentFormatting)
177			.await?;
178
179	match provider {
180		Some(registration) => {
181			let response = invoke_provider(
182				environment,
183				&registration,
184				vec![
185					json!(registration.Handle),
186					json!({ "external": document_uri.to_string(), "$mid": 1 }),
187					options_dto,
188				],
189			)
190			.await?;
191
192			if response.is_null() {
193				Ok(None)
194			} else {
195				serde_json::from_value(response).map_err(|error| {
196					CommonError::SerializationError {
197						Description:format!("Failed to deserialize Vec<TextEditDTO>: {}", error),
198					}
199				})
200			}
201		},
202
203		None => Ok(None),
204	}
205}
206
207pub(super) async fn provide_document_highlights(
208	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
209
210	document_uri:Url,
211
212	position_dto:PositionDTO,
213) -> Result<Option<Value>, CommonError> {
214	let provider =
215		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::DocumentHighlight)
216			.await?;
217
218	match provider {
219		Some(registration) => {
220			let response = invoke_provider(
221				environment,
222				&registration,
223				vec![
224					json!(registration.Handle),
225					json!({ "external": document_uri.to_string(), "$mid": 1 }),
226					json!(position_dto),
227				],
228			)
229			.await?;
230
231			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
232		},
233
234		None => Ok(None),
235	}
236}
237
238pub(super) async fn provide_document_links(
239	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
240
241	document_uri:Url,
242) -> Result<Option<Value>, CommonError> {
243	let provider =
244		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::DocumentLink).await?;
245
246	match provider {
247		Some(registration) => {
248			let response = invoke_provider(
249				environment,
250				&registration,
251				vec![
252					json!(registration.Handle),
253					json!({ "external": document_uri.to_string(), "$mid": 1 }),
254				],
255			)
256			.await?;
257
258			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
259		},
260
261		None => Ok(None),
262	}
263}
264
265pub(super) async fn provide_document_range_formatting_edits(
266	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
267
268	document_uri:Url,
269
270	range_dto:Value,
271
272	options_dto:Value,
273) -> Result<Option<Vec<TextEditDTO>>, CommonError> {
274	let provider =
275		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::DocumentRangeFormatting)
276			.await?;
277
278	match provider {
279		Some(registration) => {
280			let response = invoke_provider(
281				environment,
282				&registration,
283				vec![
284					json!(registration.Handle),
285					json!({ "external": document_uri.to_string(), "$mid": 1 }),
286					range_dto,
287					options_dto,
288				],
289			)
290			.await?;
291
292			if response.is_null() {
293				Ok(None)
294			} else {
295				serde_json::from_value(response).map_err(|error| {
296					CommonError::SerializationError {
297						Description:format!("Failed to deserialize Vec<TextEditDTO>: {}", error),
298					}
299				})
300			}
301		},
302
303		None => Ok(None),
304	}
305}
306
307pub(super) async fn provide_hover(
308	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
309
310	document_uri:Url,
311
312	position_dto:PositionDTO,
313) -> Result<Option<HoverResultDTO>, CommonError> {
314	let provider =
315		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::Hover).await?;
316
317	match provider {
318		Some(registration) => {
319			let response = invoke_provider(
320				environment,
321				&registration,
322				vec![
323					json!(registration.Handle),
324					json!({ "external": document_uri.to_string(), "$mid": 1 }),
325					json!(position_dto),
326				],
327			)
328			.await?;
329
330			if response.is_null() {
331				Ok(None)
332			} else {
333				serde_json::from_value(response).map_err(|error| {
334					CommonError::SerializationError {
335						Description:format!("Failed to deserialize HoverResultDTO: {}", error),
336					}
337				})
338			}
339		},
340
341		None => Ok(None),
342	}
343}
344
345pub(super) async fn provide_references(
346	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
347
348	document_uri:Url,
349
350	position_dto:PositionDTO,
351
352	context_dto:Value,
353) -> Result<Option<Vec<LocationDTO>>, CommonError> {
354	let provider =
355		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::References).await?;
356
357	match provider {
358		Some(registration) => {
359			let response = invoke_provider(
360				environment,
361				&registration,
362				vec![
363					json!(registration.Handle),
364					json!({ "external": document_uri.to_string(), "$mid": 1 }),
365					json!(position_dto),
366					context_dto,
367				],
368			)
369			.await?;
370
371			if response.is_null() {
372				Ok(None)
373			} else {
374				serde_json::from_value(response).map_err(|error| {
375					CommonError::SerializationError {
376						Description:format!("Failed to deserialize Vec<LocationDTO>: {}", error),
377					}
378				})
379			}
380		},
381
382		None => Ok(None),
383	}
384}
385
386pub(super) async fn prepare_rename(
387	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
388
389	document_uri:Url,
390
391	position_dto:PositionDTO,
392) -> Result<Option<Value>, CommonError> {
393	let provider =
394		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::Rename).await?;
395
396	match provider {
397		Some(registration) => {
398			let response = invoke_provider(
399				environment,
400				&registration,
401				vec![
402					json!(registration.Handle),
403					json!({ "external": document_uri.to_string(), "$mid": 1 }),
404					json!(position_dto),
405				],
406			)
407			.await?;
408
409			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
410		},
411
412		None => Ok(None),
413	}
414}
415
416pub(super) async fn provide_rename_edits(
417	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
418
419	document_uri:Url,
420
421	position_dto:PositionDTO,
422
423	new_name:String,
424) -> Result<Option<Value>, CommonError> {
425	let provider =
426		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::Rename).await?;
427
428	match provider {
429		Some(registration) => {
430			let response = invoke_provider(
431				environment,
432				&registration,
433				vec![
434					json!(registration.Handle),
435					json!({ "external": document_uri.to_string(), "$mid": 1 }),
436					json!(position_dto),
437					json!(new_name),
438				],
439			)
440			.await?;
441
442			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
443		},
444
445		None => Ok(None),
446	}
447}
448
449pub(super) async fn provide_document_symbols(
450	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
451
452	document_uri:Url,
453) -> Result<Option<Value>, CommonError> {
454	let provider =
455		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::DocumentSymbol).await?;
456
457	match provider {
458		Some(registration) => {
459			let response = invoke_provider(
460				environment,
461				&registration,
462				vec![
463					json!(registration.Handle),
464					json!({ "external": document_uri.to_string(), "$mid": 1 }),
465				],
466			)
467			.await?;
468
469			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
470		},
471
472		None => Ok(None),
473	}
474}
475
476pub(super) async fn provide_workspace_symbols(
477	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
478
479	query:String,
480) -> Result<Option<Value>, CommonError> {
481	// Workspace symbols don't have a specific document URI - use a dummy lookup.
482	// The provider is registered globally, so we pick the first WorkspaceSymbol
483	// provider.
484	let MatchingRegistration = {
485		let providers = environment
486			.ApplicationState
487			.Extension
488			.ProviderRegistration
489			.LanguageProviders
490			.lock()
491			.map_err(crate::Environment::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
492
493		providers
494			.values()
495			.find(|p| p.ProviderType == ProviderType::WorkspaceSymbol)
496			.cloned()
497	};
498
499	match MatchingRegistration {
500		Some(registration) => {
501			let response =
502				invoke_provider(environment, &registration, vec![json!(registration.Handle), json!(query)]).await?;
503
504			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
505		},
506
507		None => Ok(None),
508	}
509}
510
511pub(super) async fn provide_signature_help(
512	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
513
514	document_uri:Url,
515
516	position_dto:PositionDTO,
517
518	context_dto:Value,
519) -> Result<Option<Value>, CommonError> {
520	let provider =
521		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::SignatureHelp).await?;
522
523	match provider {
524		Some(registration) => {
525			let response = invoke_provider(
526				environment,
527				&registration,
528				vec![
529					json!(registration.Handle),
530					json!({ "external": document_uri.to_string(), "$mid": 1 }),
531					json!(position_dto),
532					context_dto,
533				],
534			)
535			.await?;
536
537			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
538		},
539
540		None => Ok(None),
541	}
542}
543
544pub(super) async fn provide_folding_ranges(
545	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
546
547	document_uri:Url,
548) -> Result<Option<Value>, CommonError> {
549	let provider =
550		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::FoldingRange).await?;
551
552	match provider {
553		Some(registration) => {
554			let response = invoke_provider(
555				environment,
556				&registration,
557				vec![
558					json!(registration.Handle),
559					json!({ "external": document_uri.to_string(), "$mid": 1 }),
560				],
561			)
562			.await?;
563
564			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
565		},
566
567		None => Ok(None),
568	}
569}
570
571pub(super) async fn provide_selection_ranges(
572	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
573
574	document_uri:Url,
575
576	positions:Vec<PositionDTO>,
577) -> Result<Option<Value>, CommonError> {
578	let provider =
579		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::SelectionRange).await?;
580
581	match provider {
582		Some(registration) => {
583			let response = invoke_provider(
584				environment,
585				&registration,
586				vec![
587					json!(registration.Handle),
588					json!({ "external": document_uri.to_string(), "$mid": 1 }),
589					json!(positions),
590				],
591			)
592			.await?;
593
594			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
595		},
596
597		None => Ok(None),
598	}
599}
600
601pub(super) async fn provide_semantic_tokens_full(
602	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
603
604	document_uri:Url,
605) -> Result<Option<Value>, CommonError> {
606	let provider =
607		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::SemanticTokens).await?;
608
609	match provider {
610		Some(registration) => {
611			let response = invoke_provider(
612				environment,
613				&registration,
614				vec![
615					json!(registration.Handle),
616					json!({ "external": document_uri.to_string(), "$mid": 1 }),
617				],
618			)
619			.await?;
620
621			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
622		},
623
624		None => Ok(None),
625	}
626}
627
628pub(super) async fn provide_inlay_hints(
629	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
630
631	document_uri:Url,
632
633	range_dto:Value,
634) -> Result<Option<Value>, CommonError> {
635	let provider =
636		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::InlayHint).await?;
637
638	match provider {
639		Some(registration) => {
640			let response = invoke_provider(
641				environment,
642				&registration,
643				vec![
644					json!(registration.Handle),
645					json!({ "external": document_uri.to_string(), "$mid": 1 }),
646					range_dto,
647				],
648			)
649			.await?;
650
651			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
652		},
653
654		None => Ok(None),
655	}
656}
657
658pub(super) async fn provide_type_hierarchy_supertypes(
659	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
660
661	item_dto:Value,
662) -> Result<Option<Value>, CommonError> {
663	// Type hierarchy uses the item's URI to find the provider
664	let uri_str = item_dto.get("uri").and_then(|u| u.as_str()).unwrap_or("");
665
666	let document_uri = Url::parse(uri_str).unwrap_or_else(|_| Url::parse("file:///unknown").unwrap());
667
668	let provider =
669		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::TypeHierarchy).await?;
670
671	match provider {
672		Some(registration) => {
673			let response =
674				invoke_provider(environment, &registration, vec![json!(registration.Handle), item_dto]).await?;
675
676			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
677		},
678
679		None => Ok(None),
680	}
681}
682
683pub(super) async fn provide_type_hierarchy_subtypes(
684	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
685
686	item_dto:Value,
687) -> Result<Option<Value>, CommonError> {
688	let uri_str = item_dto.get("uri").and_then(|u| u.as_str()).unwrap_or("");
689
690	let document_uri = Url::parse(uri_str).unwrap_or_else(|_| Url::parse("file:///unknown").unwrap());
691
692	let provider =
693		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::TypeHierarchy).await?;
694
695	match provider {
696		Some(registration) => {
697			let response =
698				invoke_provider(environment, &registration, vec![json!(registration.Handle), item_dto]).await?;
699
700			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
701		},
702
703		None => Ok(None),
704	}
705}
706
707/// Prepare call hierarchy - establish the root `CallHierarchyItem` at the
708/// given document position. Extensions implement `prepareCallHierarchy(doc,
709/// pos, token)`. Without this step the incoming/outgoing calls views are always
710/// empty.
711pub(super) async fn prepare_call_hierarchy(
712	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
713
714	document_uri:Url,
715
716	position_dto:PositionDTO,
717) -> Result<Option<Value>, CommonError> {
718	let provider =
719		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::CallHierarchy).await?;
720
721	match provider {
722		Some(registration) => {
723			let uri_json = json!({ "external": document_uri.to_string(), "$mid": 1 });
724
725			let pos_json = json!({ "Line": position_dto.LineNumber, "Character": position_dto.Column });
726
727			let response = invoke_provider_method(
728				environment,
729				&registration,
730				"$prepareCallHierarchyItems",
731				vec![json!(registration.Handle), uri_json, pos_json],
732			)
733			.await?;
734
735			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
736		},
737
738		None => Ok(None),
739	}
740}
741
742/// Prepare type hierarchy - establish the root `TypeHierarchyItem`.
743pub(super) async fn prepare_type_hierarchy(
744	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
745
746	document_uri:Url,
747
748	position_dto:PositionDTO,
749) -> Result<Option<Value>, CommonError> {
750	let provider =
751		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::TypeHierarchy).await?;
752
753	match provider {
754		Some(registration) => {
755			let uri_json = json!({ "external": document_uri.to_string(), "$mid": 1 });
756
757			let pos_json = json!({ "Line": position_dto.LineNumber, "Character": position_dto.Column });
758
759			let response = invoke_provider_method(
760				environment,
761				&registration,
762				"$prepareTypeHierarchyItems",
763				vec![json!(registration.Handle), uri_json, pos_json],
764			)
765			.await?;
766
767			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
768		},
769
770		None => Ok(None),
771	}
772}
773
774pub(super) async fn provide_call_hierarchy_incoming_calls(
775	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
776
777	item_dto:Value,
778) -> Result<Option<Value>, CommonError> {
779	let uri_str = item_dto.get("uri").and_then(|u| u.as_str()).unwrap_or("");
780
781	let document_uri = Url::parse(uri_str).unwrap_or_else(|_| Url::parse("file:///unknown").unwrap());
782
783	let provider =
784		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::CallHierarchy).await?;
785
786	match provider {
787		Some(registration) => {
788			let response =
789				invoke_provider(environment, &registration, vec![json!(registration.Handle), item_dto]).await?;
790
791			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
792		},
793
794		None => Ok(None),
795	}
796}
797
798pub(super) async fn provide_call_hierarchy_outgoing_calls(
799	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
800
801	item_dto:Value,
802) -> Result<Option<Value>, CommonError> {
803	let uri_str = item_dto.get("uri").and_then(|u| u.as_str()).unwrap_or("");
804
805	let document_uri = Url::parse(uri_str).unwrap_or_else(|_| Url::parse("file:///unknown").unwrap());
806
807	let provider =
808		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::CallHierarchy).await?;
809
810	match provider {
811		Some(registration) => {
812			let response =
813				invoke_provider(environment, &registration, vec![json!(registration.Handle), item_dto]).await?;
814
815			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
816		},
817
818		None => Ok(None),
819	}
820}
821
822pub(super) async fn provide_linked_editing_ranges(
823	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
824
825	document_uri:Url,
826
827	position_dto:PositionDTO,
828) -> Result<Option<Value>, CommonError> {
829	let provider =
830		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::LinkedEditingRange)
831			.await?;
832
833	match provider {
834		Some(registration) => {
835			let response = invoke_provider(
836				environment,
837				&registration,
838				vec![
839					json!(registration.Handle),
840					json!({ "external": document_uri.to_string(), "$mid": 1 }),
841					json!(position_dto),
842				],
843			)
844			.await?;
845
846			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
847		},
848
849		None => Ok(None),
850	}
851}
852
853pub(super) async fn provide_on_type_formatting_edits(
854	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
855
856	document_uri:Url,
857
858	position_dto:PositionDTO,
859
860	character:String,
861
862	options_dto:Value,
863) -> Result<Option<Vec<TextEditDTO>>, CommonError> {
864	let provider =
865		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::OnTypeFormatting)
866			.await?;
867
868	match provider {
869		Some(registration) => {
870			let response = invoke_provider(
871				environment,
872				&registration,
873				vec![
874					json!(registration.Handle),
875					json!({ "external": document_uri.to_string(), "$mid": 1 }),
876					json!(position_dto),
877					json!(character),
878					options_dto,
879				],
880			)
881			.await?;
882
883			if response.is_null() {
884				Ok(None)
885			} else {
886				serde_json::from_value(response).map_err(|error| {
887					CommonError::SerializationError {
888						Description:format!("Failed to deserialize Vec<TextEditDTO>: {}", error),
889					}
890				})
891			}
892		},
893
894		None => Ok(None),
895	}
896}
897
898/// Provide a file decoration (badge, tooltip, colour) for the given URI.
899/// Called by Mountain's `FileDecorationProvider` when the file explorer
900/// or source-control tree requests decoration state for a resource URI.
901/// Cocoon's `$provideFileDecoration` handler (added in session Trench) asks
902/// the registered extension provider and returns the result.
903pub(super) async fn provide_file_decoration(
904	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
905
906	resource_uri:Url,
907) -> Result<Option<Value>, CommonError> {
908	let provider =
909		super::ProviderLookup::get_matching_provider(environment, &resource_uri, ProviderType::FileDecoration).await?;
910
911	match provider {
912		Some(registration) => {
913			let response = invoke_provider_method(
914				environment,
915				&registration,
916				"$provideFileDecoration",
917				vec![
918					json!(registration.Handle),
919					json!({ "external": resource_uri.to_string(), "$mid": 1 }),
920				],
921			)
922			.await?;
923
924			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
925		},
926
927		None => Ok(None),
928	}
929}
930
931/// Provide inline completion items for the given document position.
932/// Called from `ProvideInlineCompletionItems.rs` when Monaco requests
933/// ghost-text completions (GitHub Copilot, Roo Code, Continue, etc.).
934pub(super) async fn provide_inline_completion_items(
935	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
936
937	document_uri:Url,
938
939	position_dto:CommonLibrary::LanguageFeature::DTO::PositionDTO::PositionDTO,
940
941	context_dto:Value,
942) -> Result<Option<Value>, CommonError> {
943	let provider =
944		super::ProviderLookup::get_matching_provider(environment, &document_uri, ProviderType::InlineCompletion)
945			.await?;
946
947	match provider {
948		Some(registration) => {
949			// `$provideInlineCompletionItems` method name follows the
950			// extHostTypes pattern used by Copilot / Roo Code.
951			let response = invoke_provider_method(
952				environment,
953				&registration,
954				"$provideInlineCompletionItems",
955				vec![
956					json!(registration.Handle),
957					json!({ "external": document_uri.to_string(), "$mid": 1 }),
958					json!({ "line": position_dto.LineNumber, "character": position_dto.Column }),
959					context_dto,
960				],
961			)
962			.await?;
963
964			if response.is_null() { Ok(None) } else { Ok(Some(response)) }
965		},
966
967		None => Ok(None),
968	}
969}
970
971async fn invoke_provider(
972	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
973
974	registration:&ProviderRegistrationDTO,
975
976	arguments:Vec<Value>,
977) -> Result<Value, CommonError> {
978	let rpc_method = format!("$provide{}", registration.ProviderType.to_string());
979
980	let ipc_provider:Arc<dyn IPCProvider> = environment.Require();
981
982	ipc_provider
983		.SendRequestToSideCar(registration.SideCarIdentifier.clone(), rpc_method, json!(arguments), 5000)
984		.await
985}
986
987/// Like `invoke_provider` but uses an explicit method name instead of
988/// the `$provide{ProviderType}` convention. Used for prepare steps
989/// (`$prepareCallHierarchyItems`, `$prepareTypeHierarchyItems`) where
990/// the method prefix differs from the provider type string.
991async fn invoke_provider_method(
992	environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
993
994	registration:&ProviderRegistrationDTO,
995
996	method:&str,
997
998	arguments:Vec<Value>,
999) -> Result<Value, CommonError> {
1000	let ipc_provider:Arc<dyn IPCProvider> = environment.Require();
1001
1002	ipc_provider
1003		.SendRequestToSideCar(
1004			registration.SideCarIdentifier.clone(),
1005			method.to_string(),
1006			json!(arguments),
1007			5000,
1008		)
1009		.await
1010}