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 return Ok(Value::Null);
225 }
226
227 if CommandIdentifier.starts_with("_typescript.")
245 || CommandIdentifier.starts_with("_extensionHost.")
246 || CommandIdentifier.starts_with("_workbench.registerWebview")
247 || CommandIdentifier.ends_with(".activationCompleted")
248 || CommandIdentifier.ends_with(".activated")
249 || CommandIdentifier.ends_with(".ready")
250 {
251 dev_log!(
252 "commands",
253 "[CommandProvider] Activation-race command '{}' not yet in registry; returning null \
254 (extension will retry post-activation).",
255 CommandIdentifier
256 );
257 return Ok(Value::Null);
258 }
259
260 if LookupCommandContributingExtension(self, &CommandIdentifier) {
271 dev_log!(
272 "commands",
273 "[CommandProvider] Lazy activation for command '{}' - firing onCommand:{0}",
274 CommandIdentifier
275 );
276 let Event = format!("onCommand:{}", CommandIdentifier);
277 let ActivationResult = Client::SendRequest::Fn(
278 &"cocoon-main".to_string(),
279 "$activateByEvent".to_string(),
280 json!({ "activationEvent": Event }),
281 30_000,
282 )
283 .await;
284 if let Err(Error) = ActivationResult {
285 dev_log!(
286 "commands",
287 "warn: [CommandProvider] onCommand:{} activation failed: {}",
288 CommandIdentifier,
289 Error
290 );
291 }
292 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
296 let PostActivationHandler = self
297 .ApplicationState
298 .Extension
299 .Registry
300 .CommandRegistry
301 .lock()
302 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
303 .get(&CommandIdentifier)
304 .cloned();
305 if let Some(Handler) = PostActivationHandler {
306 match Handler {
307 CommandHandler::Native(Function) => {
308 let MainWindow =
309 self.ApplicationHandle.get_webview_window("main").ok_or_else(|| {
310 CommonError::IPCError {
311 Description:"Could not find main window for lazy-activated native command"
312 .to_string(),
313 }
314 })?;
315 let RunTime =
316 self.ApplicationHandle.try_state::<Arc<ApplicationRunTime>>().ok_or_else(|| {
317 CommonError::IPCError {
318 Description:"ApplicationRunTime unavailable for lazy-activated native \
319 command"
320 .to_string(),
321 }
322 })?;
323 return Function(
324 self.ApplicationHandle.clone(),
325 MainWindow,
326 (*RunTime).clone(),
327 Argument,
328 )
329 .await
330 .map_err(|Error| CommonError::CommandExecution { CommandIdentifier, Reason:Error });
331 },
332 CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier: ProxiedId } => {
333 let RPCParameters = json!([ProxiedId, Argument]);
334 let RPCMethod = format!(
335 "{}$ExecuteContributedCommand",
336 ProxyTarget::ExtHostCommands.GetTargetPrefix()
337 );
338 return Client::SendRequest::Fn(&SideCarIdentifier, RPCMethod, RPCParameters, 30_000)
339 .await
340 .map_err(|Error| CommonError::IPCError { Description:Error.to_string() });
341 },
342 }
343 }
344 }
345
346 dev_log!(
347 "commands",
348 "error: [CommandProvider] Command '{}' not found in registry.",
349 CommandIdentifier
350 );
351
352 Err(CommonError::CommandNotFound { Identifier:CommandIdentifier })
353 },
354 }
355 }
356
357 async fn RegisterCommand(&self, SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
359 dev_log!(
360 "commands",
361 "[CommandProvider] Registering PROXY command '{}' from sidecar '{}'",
362 CommandIdentifier,
363 SideCarIdentifier
364 );
365
366 let mut Registry = self
367 .ApplicationState
368 .Extension
369 .Registry
370 .CommandRegistry
371 .lock()
372 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
373
374 Registry.insert(
375 CommandIdentifier.clone(),
376 CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier },
377 );
378
379 Ok(())
380 }
381
382 async fn UnregisterCommand(&self, _SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
384 dev_log!("commands", "[CommandProvider] Unregistering command '{}'", CommandIdentifier);
385
386 self.ApplicationState
387 .Extension
388 .Registry
389 .CommandRegistry
390 .lock()
391 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
392 .remove(&CommandIdentifier);
393
394 Ok(())
395 }
396
397 async fn GetAllCommands(&self) -> Result<Vec<String>, CommonError> {
399 dev_log!("commands", "[CommandProvider] Getting all command identifiers.");
400
401 let Registry = self
402 .ApplicationState
403 .Extension
404 .Registry
405 .CommandRegistry
406 .lock()
407 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
408
409 Ok(Registry.keys().cloned().collect())
410 }
411}
412
413fn LookupCommandContributingExtension(Environment:&MountainEnvironment, CommandIdentifier:&str) -> bool {
420 let Event = format!("onCommand:{}", CommandIdentifier);
421 let Guard = match Environment
422 .ApplicationState
423 .Extension
424 .ScannedExtensions
425 .ScannedExtensions
426 .lock()
427 {
428 Ok(G) => G,
429 Err(_) => return false,
430 };
431 for Description in Guard.values() {
432 if let Some(Events) = &Description.ActivationEvents {
433 if Events.iter().any(|E| E == &Event) {
434 return true;
435 }
436 }
437 }
438 false
439}