Skip to main content

Mountain/Vine/Server/
VineHostImpl.rs

1//! Mountain's implementation of [`::Vine::Host::VineHost`] for
2//! [`MountainVinegRPCService`]. Lets the canonical handler tree in the
3//! Vine crate operate against `&dyn VineHost` while reusing Mountain's
4//! `AppHandle`-based `emit` plumbing and IPC bus.
5
6use std::sync::Arc;
7
8use serde_json::Value;
9use tauri::{AppHandle, Emitter};
10use ::Vine::Host::{ApplicationStateAccess, IPCProvider, RendererEmitter, VineHost};
11
12use crate::{Vine::Server::MountainVinegRPCService::MountainVinegRPCService, dev_log};
13
14/// Minimal `ApplicationStateAccess` carrier for the Mountain embedder.
15/// Vine handlers only need the embedder label today; richer state lives
16/// behind Mountain-local sub-traits added as port families need them.
17struct MountainApplicationStateAccess;
18
19impl ApplicationStateAccess for MountainApplicationStateAccess {
20	fn EmbedderName(&self) -> &'static str { "Mountain" }
21}
22
23static MOUNTAIN_APP_STATE:MountainApplicationStateAccess = MountainApplicationStateAccess;
24
25/// Cheap-to-clone renderer event sink. Internally holds a
26/// [`tauri::AppHandle`], which is itself a thin `Arc` wrapper - cloning
27/// is a ref-count bump. Used by Vine handlers with long-lived flushers
28/// (`ProgressReport`, `DecorationTypeLifecycle`, `OutputChannelCoalesce`,
29/// `SetTextEditorDecorations`, `RegisterCommand`) that emit from a
30/// background task.
31pub struct TauriRendererEmitter {
32	Handle:AppHandle,
33}
34
35impl TauriRendererEmitter {
36	pub fn New(Handle:AppHandle) -> Self { Self { Handle } }
37}
38
39impl RendererEmitter for TauriRendererEmitter {
40	fn Emit(&self, Channel:&str, Payload:Value) {
41		if let Err(Error) = self.Handle.emit(Channel, Payload) {
42			dev_log!("sky-emit", "[SkyEmit] fail channel={} error={}", Channel, Error);
43		}
44	}
45}
46
47/// IPC bridge that routes `SendNotification` calls to the Vine gRPC client
48/// so breakpoint fan-backs and similar cross-extension notifications reach
49/// Cocoon. `SendRequest` is left as a no-op until a handler needs it.
50struct MountainIPCProvider;
51
52impl IPCProvider for MountainIPCProvider {
53	fn SendRequest(
54		&self,
55
56		Channel:&str,
57
58		_Payload:Value,
59	) -> futures::future::BoxFuture<'_, ::Vine::Error::Result<Value>> {
60		let Channel = Channel.to_string();
61
62		Box::pin(async move {
63			dev_log!(
64				"grpc",
65				"warn: [VineHost] IPCProvider::SendRequest channel={} - not wired",
66				Channel
67			);
68
69			Ok(Value::Null)
70		})
71	}
72
73	fn SendNotification(&self, Channel:&str, Method:&str, Payload:Value) {
74		let Ch = Channel.to_string();
75
76		let M = Method.to_string();
77
78		tauri::async_runtime::spawn(async move {
79			let _ = ::Vine::Client::SendNotification::Fn(Ch, M, Payload).await;
80		});
81	}
82}
83
84impl VineHost for MountainVinegRPCService {
85	fn ApplicationState(&self) -> &dyn ApplicationStateAccess { &MOUNTAIN_APP_STATE }
86
87	fn EmitToRenderer(&self, Channel:&str, Payload:Value) {
88		if let Err(Error) = self.ApplicationHandle().emit(Channel, Payload) {
89			dev_log!("sky-emit", "[SkyEmit] fail channel={} error={}", Channel, Error);
90		}
91	}
92
93	fn RendererEmitter(&self) -> Arc<dyn RendererEmitter> {
94		Arc::new(TauriRendererEmitter::New(self.ApplicationHandle().clone()))
95	}
96
97	fn IPCProvider(&self) -> Arc<dyn IPCProvider> { Arc::new(MountainIPCProvider) }
98
99	fn UnregisterProvider(&self, Handle:u32) {
100		self.RunTime()
101			.Environment
102			.ApplicationState
103			.Extension
104			.ProviderRegistration
105			.UnregisterProvider(Handle);
106	}
107
108	fn RegisterCommandInRegistry(&self, CommandId:&str, SideCarIdentifier:&str) {
109		use crate::Environment::CommandProvider::CommandHandler;
110
111		if let Ok(mut Registry) = self
112			.RunTime()
113			.Environment
114			.ApplicationState
115			.Extension
116			.Registry
117			.CommandRegistry
118			.lock()
119		{
120			Registry.insert(
121				CommandId.to_string(),
122				CommandHandler::Proxied {
123					SideCarIdentifier:SideCarIdentifier.to_string(),
124					CommandIdentifier:CommandId.to_string(),
125				},
126			);
127		}
128	}
129
130	fn UnregisterCommandInRegistry(&self, CommandId:&str) {
131		if let Ok(mut Registry) = self
132			.RunTime()
133			.Environment
134			.ApplicationState
135			.Extension
136			.Registry
137			.CommandRegistry
138			.lock()
139		{
140			Registry.remove(CommandId);
141		}
142	}
143
144	fn SpawnSendTextToTerminal(&self, TerminalId:u64, Text:String) {
145		use CommonLibrary::{Environment::Requires::Requires, Terminal::TerminalProvider::TerminalProvider};
146
147		let Provider:Arc<dyn TerminalProvider> = self.RunTime().Environment.Require();
148
149		tauri::async_runtime::spawn(async move {
150			let _ = Provider.SendTextToTerminal(TerminalId, Text).await;
151		});
152	}
153
154	fn SpawnDisposeTerminal(&self, TerminalId:u64) {
155		use CommonLibrary::{Environment::Requires::Requires, Terminal::TerminalProvider::TerminalProvider};
156
157		let Provider:Arc<dyn TerminalProvider> = self.RunTime().Environment.Require();
158
159		tauri::async_runtime::spawn(async move {
160			let _ = Provider.DisposeTerminal(TerminalId).await;
161		});
162	}
163
164	fn CreateTerminal<'a>(&'a self, Options:&'a Value) -> futures::future::BoxFuture<'a, Option<Value>> {
165		use CommonLibrary::{Environment::Requires::Requires, Terminal::TerminalProvider::TerminalProvider};
166
167		let Provider:Arc<dyn TerminalProvider> = self.RunTime().Environment.Require();
168
169		let Opts = Options.clone();
170
171		Box::pin(async move { Provider.CreateTerminal(Opts).await.ok() })
172	}
173
174	fn RegisterScmInRegistry(&self, Handle:u32, ScmId:&str, Label:&str, ExtId:&str) {
175		use CommonLibrary::LanguageFeature::DTO::ProviderType::ProviderType;
176		use serde_json::json;
177
178		use crate::ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO;
179
180		let Dto = ProviderRegistrationDTO {
181			Handle,
182
183			ProviderType:ProviderType::SourceControl,
184
185			Selector:json!([{ "scmId": ScmId }]),
186
187			SideCarIdentifier:"cocoon-main".to_string(),
188
189			ExtensionIdentifier:json!(ExtId),
190
191			Options:Some(json!({ "scmId": ScmId, "label": Label })),
192		};
193
194		self.RunTime()
195			.Environment
196			.ApplicationState
197			.Extension
198			.ProviderRegistration
199			.RegisterProvider(Handle, Dto);
200	}
201
202	fn CreateSourceControl<'a>(&'a self, Payload:Value) -> futures::future::BoxFuture<'a, ()> {
203		use CommonLibrary::SourceControlManagement::SourceControlManagementProvider::SourceControlManagementProvider;
204
205		let RunTime = self.RunTime().clone();
206
207		Box::pin(async move {
208			if let Err(E) = RunTime.Environment.CreateSourceControl(Payload).await {
209				dev_log!("grpc", "warn: [VineHost] CreateSourceControl failed: {}", E);
210			}
211		})
212	}
213
214	fn UpdateSourceControlGroup<'a>(&'a self, ScmHandle:u32, Payload:Value) -> futures::future::BoxFuture<'a, ()> {
215		use CommonLibrary::SourceControlManagement::SourceControlManagementProvider::SourceControlManagementProvider;
216
217		let RunTime = self.RunTime().clone();
218
219		Box::pin(async move {
220			if let Err(E) = RunTime.Environment.UpdateSourceControlGroup(ScmHandle, Payload).await {
221				dev_log!(
222					"grpc",
223					"warn: [VineHost] UpdateSourceControlGroup scm={} failed: {}",
224					ScmHandle,
225					E
226				);
227			}
228		})
229	}
230
231	fn RegisterLanguageProvider(&self, Handle:u32, TypeName:&str, Payload:&Value) -> bool {
232		use CommonLibrary::LanguageFeature::DTO::ProviderType::ProviderType as PT;
233		use serde_json::json;
234
235		use crate::ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO;
236
237		let ProvType:Option<PT> = match TypeName {
238			"authentication" => Some(PT::Authentication),
239
240			"call_hierarchy" => Some(PT::CallHierarchy),
241
242			"code_actions" => Some(PT::CodeAction),
243
244			"code_lens" => Some(PT::CodeLens),
245
246			"color" => Some(PT::Color),
247
248			"completion_item" => Some(PT::Completion),
249
250			"debug_adapter" => Some(PT::DebugAdapter),
251
252			"debug_configuration" => Some(PT::DebugConfiguration),
253
254			"declaration" => Some(PT::Declaration),
255
256			"definition" => Some(PT::Definition),
257
258			"document_drop_edit" => Some(PT::DocumentDropEdit),
259
260			"document_formatting" => Some(PT::DocumentFormatting),
261
262			"document_highlight" => Some(PT::DocumentHighlight),
263
264			"document_link" => Some(PT::DocumentLink),
265
266			"document_paste_edit" => Some(PT::DocumentPasteEdit),
267
268			"document_range_formatting" => Some(PT::DocumentRangeFormatting),
269
270			"document_symbol" => Some(PT::DocumentSymbol),
271
272			"evaluatable_expression" => Some(PT::EvaluatableExpression),
273
274			"external_uri_opener" => Some(PT::ExternalUriOpener),
275
276			"file_decoration" => Some(PT::FileDecoration),
277
278			"file_system" => Some(PT::FileSystem),
279
280			"folding_range" => Some(PT::FoldingRange),
281
282			"hover" => Some(PT::Hover),
283
284			"implementation" => Some(PT::Implementation),
285
286			"inlay_hints" => Some(PT::InlayHint),
287
288			"inline_completion_item" => Some(PT::InlineCompletion),
289
290			"inline_edit" => Some(PT::InlineEdit),
291
292			"inline_values" => Some(PT::InlineValues),
293
294			"linked_editing_range" => Some(PT::LinkedEditingRange),
295
296			"mapped_edits" => Some(PT::MappedEdits),
297
298			"multi_document_highlight" => Some(PT::MultiDocumentHighlight),
299
300			"notebook_content" => Some(PT::NotebookContent),
301
302			"notebook_serializer" => Some(PT::NotebookSerializer),
303
304			"on_type_formatting" => Some(PT::OnTypeFormatting),
305
306			"reference" => Some(PT::References),
307
308			"remote_authority_resolver" => Some(PT::RemoteAuthorityResolver),
309
310			"rename" => Some(PT::Rename),
311
312			"resource_label_formatter" => Some(PT::ResourceLabelFormatter),
313
314			"scm" => Some(PT::SourceControl),
315
316			"scm_resource_group" => Some(PT::ScmResourceGroup),
317
318			"selection_range" => Some(PT::SelectionRange),
319
320			"semantic_tokens" => Some(PT::SemanticTokens),
321
322			"signature_help" => Some(PT::SignatureHelp),
323
324			"task" => Some(PT::Task),
325
326			"terminal_link" => Some(PT::TerminalLink),
327
328			"terminal_profile" => Some(PT::TerminalProfile),
329
330			"text_document_content" => Some(PT::TextDocumentContent),
331
332			"type_definition" => Some(PT::TypeDefinition),
333
334			"type_hierarchy" => Some(PT::TypeHierarchy),
335
336			"uri_handler" => Some(PT::UriHandler),
337
338			"workspace_symbol" => Some(PT::WorkspaceSymbol),
339
340			_ => None,
341		};
342
343		let Some(ProviderType) = ProvType else { return false };
344
345		let Selector = Payload
346			.get("languageSelector")
347			.or_else(|| Payload.get("language_selector"))
348			.and_then(Value::as_str)
349			.unwrap_or("*");
350
351		let ExtId = Payload
352			.get("extensionId")
353			.or_else(|| Payload.get("extension_id"))
354			.and_then(Value::as_str)
355			.unwrap_or("");
356
357		let Scheme = Payload.get("scheme").and_then(Value::as_str).unwrap_or("");
358
359		let SelectorValue = if !Scheme.is_empty() {
360			json!([{ "scheme": Scheme, "language": Selector }])
361		} else {
362			json!([{ "language": Selector }])
363		};
364
365		let Dto = ProviderRegistrationDTO {
366			Handle,
367
368			ProviderType,
369
370			Selector:SelectorValue,
371
372			SideCarIdentifier:"cocoon-main".to_string(),
373
374			ExtensionIdentifier:json!(ExtId),
375
376			Options:Payload.get("options").cloned(),
377		};
378
379		self.RunTime()
380			.Environment
381			.ApplicationState
382			.Extension
383			.ProviderRegistration
384			.RegisterProvider(Handle, Dto);
385
386		true
387	}
388
389	fn UpdateScmGroupMarkers(&self, ScmHandle:u32, GroupId:&str, ResourceStates:&Value) {
390		use std::collections::HashMap;
391
392		use CommonLibrary::SourceControlManagement::DTO::SourceControlManagementResourceDTO::SourceControlManagementResourceDTO;
393
394		if let Ok(mut Resources) = self
395			.RunTime()
396			.Environment
397			.ApplicationState
398			.Feature
399			.Markers
400			.SourceControlManagementResources
401			.lock()
402		{
403			let GroupsForProvider = Resources.entry(ScmHandle).or_insert_with(HashMap::new);
404
405			let mut DtoList:Vec<SourceControlManagementResourceDTO> = Vec::new();
406
407			if let Some(Array) = ResourceStates.as_array() {
408				for Raw in Array {
409					let ResourceUri = Raw
410						.get("resourceUri")
411						.or_else(|| Raw.get("sourceUri"))
412						.or_else(|| Raw.get("uri"))
413						.cloned()
414						.unwrap_or(Value::Null);
415
416					if ResourceUri.is_null() {
417						continue;
418					}
419
420					let Decorations = Raw
421						.get("decorations")
422						.cloned()
423						.unwrap_or_else(|| Value::Object(serde_json::Map::new()));
424
425					DtoList.push(SourceControlManagementResourceDTO {
426						ProviderHandle:ScmHandle,
427						GroupIdentifier:GroupId.to_string(),
428						ResourceURI:ResourceUri,
429						Decorations,
430					});
431				}
432			}
433
434			GroupsForProvider.insert(GroupId.to_string(), DtoList);
435		}
436	}
437}