1use std::{future::Future, pin::Pin, sync::Arc};
45
46use CommonLibrary::{
47 Command::CommandExecutor::CommandExecutor,
48 Error::CommonError::CommonError,
49 IPC::DTO::ProxyTarget::ProxyTarget,
50};
51use async_trait::async_trait;
52use serde_json::{Value, json};
53use tauri::{AppHandle, Manager, Runtime, WebviewWindow};
54
55use super::MountainEnvironment::MountainEnvironment;
56use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, Vine::Client, dev_log};
57
58pub enum CommandHandler<R:Runtime + 'static> {
63 Native(
65 fn(
66 AppHandle<R>,
67
68 WebviewWindow<R>,
69
70 Arc<ApplicationRunTime>,
71
72 Value,
73 ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>>,
74 ),
75
76 Proxied { SideCarIdentifier:String, CommandIdentifier:String },
78}
79
80impl<R:Runtime> Clone for CommandHandler<R> {
81 fn clone(&self) -> Self {
82 match self {
83 Self::Native(Function) => Self::Native(*Function),
84
85 Self::Proxied { SideCarIdentifier, CommandIdentifier } => {
86 Self::Proxied {
87 SideCarIdentifier:SideCarIdentifier.clone(),
88
89 CommandIdentifier:CommandIdentifier.clone(),
90 }
91 },
92 }
93 }
94}
95
96#[async_trait]
97impl CommandExecutor for MountainEnvironment {
98 async fn ExecuteCommand(&self, CommandIdentifier:String, Argument:Value) -> Result<Value, CommonError> {
101 let HandlerInfoOption = self
102 .ApplicationState
103 .Extension
104 .Registry
105 .CommandRegistry
106 .lock()
107 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
108 .get(&CommandIdentifier)
109 .cloned();
110
111 match HandlerInfoOption {
112 Some(CommandHandler::Native(Function)) => {
113 dev_log!(
119 "commands-verbose",
120 "[CommandProvider] Executing NATIVE command '{}'.",
121 CommandIdentifier
122 );
123
124 let RunTime:Arc<ApplicationRunTime> =
125 self.ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
126
127 let MainWindow = self.ApplicationHandle.get_webview_window("main").ok_or_else(|| {
128 CommonError::UserInterfaceInteraction {
129 Reason:"Main window not found for command execution".into(),
130 }
131 })?;
132
133 Function(self.ApplicationHandle.clone(), MainWindow, RunTime, Argument)
134 .await
135 .map_err(|Error| CommonError::CommandExecution { CommandIdentifier, Reason:Error })
136 },
137
138 Some(CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier: ProxiedCommandIdentifier }) => {
139 dev_log!(
140 "commands-verbose",
141 "[CommandProvider] Executing PROXIED command '{}' on sidecar '{}'.",
142 CommandIdentifier,
143 SideCarIdentifier
144 );
145
146 let RPCParameters = json!([ProxiedCommandIdentifier, Argument]);
147
148 let RPCMethod = format!("{}$ExecuteContributedCommand", ProxyTarget::ExtHostCommands.GetTargetPrefix());
149
150 Client::SendRequest::Fn(&SideCarIdentifier, RPCMethod, RPCParameters, 30000)
151 .await
152 .map_err(|Error| CommonError::IPCError { Description:Error.to_string() })
153 },
154
155 None => {
156 if CommandIdentifier.ends_with(".focus")
167 || CommandIdentifier.ends_with(".resetViewLocation")
168 || CommandIdentifier.ends_with(".removeView")
169 {
170 crate::IPC::DevLog::DebugOnce::Fn(
177 "commands",
178 &format!("view-action-noop:{}", CommandIdentifier),
179 &format!(
180 "[CommandProvider] View-action command '{}' not registered; treating as no-op \
181 (auto-generated by view registry in stock VS Code).",
182 CommandIdentifier
183 ),
184 );
185
186 return Ok(Value::Null);
187 }
188
189 if matches!(
208 CommandIdentifier.as_str(),
209 "getTelemetrySenderObject" | "testing.clearTestResults"
210 ) {
211 crate::IPC::DevLog::DebugOnce::Fn(
216 "commands",
217 &format!("workbench-internal-noop:{}", CommandIdentifier),
218 &format!(
219 "[CommandProvider] Workbench-internal command '{}' not registered; treating as no-op \
220 (Land has no backing service).",
221 CommandIdentifier
222 ),
223 );
224
225 return Ok(Value::Null);
226 }
227
228 if CommandIdentifier.starts_with("_typescript.")
246 || CommandIdentifier.starts_with("_extensionHost.")
247 || CommandIdentifier.starts_with("_workbench.registerWebview")
248 || CommandIdentifier.ends_with(".activationCompleted")
249 || CommandIdentifier.ends_with(".activated")
250 || CommandIdentifier.ends_with(".ready")
251 {
252 dev_log!(
253 "commands",
254 "[CommandProvider] Activation-race command '{}' not yet in registry; returning null \
255 (extension will retry post-activation).",
256 CommandIdentifier
257 );
258
259 return Ok(Value::Null);
260 }
261
262 if LookupCommandContributingExtension(self, &CommandIdentifier) {
273 dev_log!(
274 "commands",
275 "[CommandProvider] Lazy activation for command '{}' - firing onCommand:{0}",
276 CommandIdentifier
277 );
278
279 let Event = format!("onCommand:{}", CommandIdentifier);
280
281 let ActivationResult = Client::SendRequest::Fn(
282 &"cocoon-main".to_string(),
283 "$activateByEvent".to_string(),
284 json!({ "activationEvent": Event }),
285 30_000,
286 )
287 .await;
288
289 if let Err(Error) = ActivationResult {
290 dev_log!(
291 "commands",
292 "warn: [CommandProvider] onCommand:{} activation failed: {}",
293 CommandIdentifier,
294 Error
295 );
296 }
297
298 tokio::time::sleep(std::time::Duration::from_millis(20)).await;
302
303 let PostActivationHandler = self
304 .ApplicationState
305 .Extension
306 .Registry
307 .CommandRegistry
308 .lock()
309 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
310 .get(&CommandIdentifier)
311 .cloned();
312
313 if let Some(Handler) = PostActivationHandler {
314 match Handler {
315 CommandHandler::Native(Function) => {
316 let MainWindow =
317 self.ApplicationHandle.get_webview_window("main").ok_or_else(|| {
318 CommonError::IPCError {
319 Description:"Could not find main window for lazy-activated native command"
320 .to_string(),
321 }
322 })?;
323
324 let RunTime =
325 self.ApplicationHandle.try_state::<Arc<ApplicationRunTime>>().ok_or_else(|| {
326 CommonError::IPCError {
327 Description:"ApplicationRunTime unavailable for lazy-activated native \
328 command"
329 .to_string(),
330 }
331 })?;
332
333 return Function(
334 self.ApplicationHandle.clone(),
335 MainWindow,
336 (*RunTime).clone(),
337 Argument,
338 )
339 .await
340 .map_err(|Error| CommonError::CommandExecution { CommandIdentifier, Reason:Error });
341 },
342
343 CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier: ProxiedId } => {
344 let RPCParameters = json!([ProxiedId, Argument]);
345
346 let RPCMethod = format!(
347 "{}$ExecuteContributedCommand",
348 ProxyTarget::ExtHostCommands.GetTargetPrefix()
349 );
350
351 return Client::SendRequest::Fn(&SideCarIdentifier, RPCMethod, RPCParameters, 30_000)
352 .await
353 .map_err(|Error| CommonError::IPCError { Description:Error.to_string() });
354 },
355 }
356 }
357 }
358
359 dev_log!(
360 "commands",
361 "error: [CommandProvider] Command '{}' not found in registry.",
362 CommandIdentifier
363 );
364
365 Err(CommonError::CommandNotFound { Identifier:CommandIdentifier })
366 },
367 }
368 }
369
370 async fn RegisterCommand(&self, SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
372 dev_log!(
373 "commands",
374 "[CommandProvider] Registering PROXY command '{}' from sidecar '{}'",
375 CommandIdentifier,
376 SideCarIdentifier
377 );
378
379 let mut Registry = self
380 .ApplicationState
381 .Extension
382 .Registry
383 .CommandRegistry
384 .lock()
385 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
386
387 Registry.insert(
388 CommandIdentifier.clone(),
389 CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier },
390 );
391
392 Ok(())
393 }
394
395 async fn UnregisterCommand(&self, _SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
397 dev_log!("commands", "[CommandProvider] Unregistering command '{}'", CommandIdentifier);
398
399 self.ApplicationState
400 .Extension
401 .Registry
402 .CommandRegistry
403 .lock()
404 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
405 .remove(&CommandIdentifier);
406
407 Ok(())
408 }
409
410 async fn GetAllCommands(&self) -> Result<Vec<String>, CommonError> {
412 dev_log!("commands", "[CommandProvider] Getting all command identifiers.");
413
414 let Registry = self
415 .ApplicationState
416 .Extension
417 .Registry
418 .CommandRegistry
419 .lock()
420 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
421
422 Ok(Registry.keys().cloned().collect())
423 }
424}
425
426fn LookupCommandContributingExtension(Environment:&MountainEnvironment, CommandIdentifier:&str) -> bool {
433 let Event = format!("onCommand:{}", CommandIdentifier);
434
435 let Guard = match Environment
436 .ApplicationState
437 .Extension
438 .ScannedExtensions
439 .ScannedExtensions
440 .lock()
441 {
442 Ok(G) => G,
443
444 Err(_) => return false,
445 };
446
447 for Description in Guard.values() {
448 if let Some(Events) = &Description.ActivationEvents {
449 if Events.iter().any(|E| E == &Event) {
450 return true;
451 }
452 }
453 }
454
455 false
456}