Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/RPC/CocoonService/Provider/
ProvideInlineCompletionItems.rs

1//! `ProvideInlineCompletionItems` gRPC RPC handler.
2//!
3//! Called by Mountain's `IInlineCompletionsProvider` (wired into Monaco via
4//! the language feature registry) when the cursor pauses or the user presses
5//! a completion trigger key. Forwards the request to Cocoon which dispatches
6//! `$provideInlineCompletionItems` to the registered extension provider
7//! (GitHub Copilot, Roo Code, Continue, etc.).
8//!
9//! The response is a list of `InlineCompletionItem` messages that Mountain
10//! maps back into Monaco's `InlineCompletionList`.
11
12use tonic::{Response, Status};
13use url::Url;
14use CommonLibrary::LanguageFeature::{
15	DTO::PositionDTO::PositionDTO,
16	LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
17};
18use serde_json::json;
19
20use crate::{
21	RPC::CocoonService::CocoonServiceImpl,
22	Vine::Generated::{InlineCompletionItem, ProvideInlineCompletionRequest, ProvideInlineCompletionResponse},
23	dev_log,
24};
25
26pub async fn Fn(
27	Service:&CocoonServiceImpl,
28
29	Request:ProvideInlineCompletionRequest,
30) -> Result<Response<ProvideInlineCompletionResponse>, Status> {
31	let URI = Request.uri.as_ref().map(|U| U.value.as_str()).unwrap_or("");
32
33	let Position_ = Request.position.as_ref();
34
35	let Line = Position_.map(|P| P.line).unwrap_or(0);
36
37	let Character = Position_.map(|P| P.character).unwrap_or(0);
38
39	dev_log!(
40		"provider",
41		"ProvideInlineCompletionItems handle={} uri={} line={} char={}",
42		Request.provider_handle,
43		URI,
44		Line,
45		Character
46	);
47
48	let DocumentURI = Url::parse(URI).map_err(|E| Status::invalid_argument(format!("Invalid URI: {}", E)))?;
49
50	let PositionDTO_ = PositionDTO { LineNumber:Line, Column:Character };
51
52	let Context = json!({
53		"triggerKind": Request.context.as_ref().map(|C| C.trigger_kind).unwrap_or(0),
54		"selectedCompletionInfo": Request.context.as_ref()
55			.map(|C| C.selected_completion_info.as_str())
56			.unwrap_or(""),
57	});
58
59	match Service
60		.environment
61		.ProvideInlineCompletionItems(DocumentURI, PositionDTO_, Context)
62		.await
63	{
64		Ok(Some(Raw)) => {
65			// Raw is a JSON Value returned by Cocoon's provider.
66			// Shape: { items: [{ insertText, range?, isSnippet?, command? }] }
67			// or an array directly.
68			let ItemsArr = Raw
69				.get("items")
70				.and_then(|V| V.as_array())
71				.cloned()
72				.or_else(|| Raw.as_array().cloned())
73				.unwrap_or_default();
74
75			let Items:Vec<InlineCompletionItem> = ItemsArr
76				.iter()
77				.filter_map(|Item| {
78					let InsertText = Item
79						.get("insertText")
80						.and_then(|V| V.as_str())
81						.or_else(|| Item.get("text").and_then(|V| V.as_str()))
82						.unwrap_or("");
83
84					if InsertText.is_empty() {
85						return None;
86					}
87
88					let IsSnippet = Item.get("isSnippet").and_then(|V| V.as_bool()).unwrap_or(false);
89
90					let Command = Item
91						.get("command")
92						.and_then(|V| V.get("command"))
93						.and_then(|V| V.as_str())
94						.or_else(|| Item.get("command").and_then(|V| V.as_str()))
95						.unwrap_or("")
96						.to_string();
97
98					// prost generates snake_case field names from proto PascalCase.
99					Some(InlineCompletionItem {
100						insert_text:InsertText.to_string(),
101						range:None, // Range hydration deferred to Mountain's provider impl
102						command:Command,
103						is_snippet:IsSnippet,
104					})
105				})
106				.collect();
107
108			Ok(Response::new(ProvideInlineCompletionResponse { items:Items }))
109		},
110
111		Ok(None) => Ok(Response::new(ProvideInlineCompletionResponse::default())),
112
113		Err(Error) => {
114			dev_log!("provider", "warn: [ProvideInlineCompletionItems] provider error: {}", Error);
115
116			Ok(Response::new(ProvideInlineCompletionResponse::default()))
117		},
118	}
119}