1use std::{future::Future, pin::Pin, sync::Arc};
116
117use CommonLibrary::{
118 DTO::WorkspaceEditDTO::WorkspaceEditDTO,
119 Document::OpenDocument::OpenDocument,
120 Effect::ApplicationRunTime::ApplicationRunTime as _,
121 Environment::Requires::Requires,
122 Error::CommonError::CommonError,
123 LanguageFeature::LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
124 UserInterface::ShowOpenDialog::ShowOpenDialog,
125 Workspace::ApplyWorkspaceEdit::ApplyWorkspaceEdit,
126};
127use serde_json::{Value, json};
128use tauri::{AppHandle, WebviewWindow, Wry};
129use url::Url;
130
131use crate::dev_log;
132use crate::{
133 ApplicationState::{ApplicationState, DTO::TreeViewStateDTO::TreeViewStateDTO, MapLockError},
134 Environment::CommandProvider::CommandHandler,
135 FileSystem::FileExplorerViewProvider::FileExplorerViewProvider,
136 RunTime::ApplicationRunTime::ApplicationRunTime,
137};
138
139fn CommandHelloWorld(
143 _ApplicationHandle:AppHandle<Wry>,
144
145 _Window:WebviewWindow<Wry>,
146
147 _RunTime:Arc<ApplicationRunTime>,
148
149 _Argument:Value,
150) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
151 Box::pin(async move {
152 dev_log!("commands", "[Native Command] Hello from Mountain!");
153
154 Ok(json!("Hello from Mountain's native command!"))
155 })
156}
157
158fn CommandOpenFile(
160 _ApplicationHandle:AppHandle<Wry>,
161
162 _Window:WebviewWindow<Wry>,
163
164 RunTime:Arc<ApplicationRunTime>,
165
166 _Argument:Value,
167) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
168 Box::pin(async move {
169 dev_log!("commands", "[Native Command] Executing Open File...");
170
171 let DialogResult = RunTime.Run(ShowOpenDialog(None)).await.map_err(|Error| Error.to_string())?;
172
173 if let Some(Paths) = DialogResult {
174 if let Some(Path) = Paths.first() {
175 let URI = Url::from_file_path(Path).map_err(|_| "Invalid file path".to_string())?;
177
178 let OpenDocumentEffect = OpenDocument(json!({ "external": URI.to_string() }), None, None);
179
180 RunTime.Run(OpenDocumentEffect).await.map_err(|Error| Error.to_string())?;
181 }
182 }
183
184 Ok(Value::Null)
185 })
186}
187
188fn CommandFormatDocument(
190 _ApplicationHandle:AppHandle<Wry>,
191
192 _Window:WebviewWindow<Wry>,
193
194 RunTime:Arc<ApplicationRunTime>,
195
196 _Argument:Value,
197) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
198 Box::pin(async move {
199 dev_log!("commands", "[Native Command] Executing Format Document...");
200
201 let AppState = &RunTime.Environment.ApplicationState;
202
203 let URIString = AppState
204 .Workspace
205 .ActiveDocumentURI
206 .lock()
207 .map_err(MapLockError)
208 .map_err(|Error| Error.to_string())?
209 .clone()
210 .ok_or("No active document URI found in state".to_string())?;
211
212 let URI = Url::parse(&URIString).map_err(|_| "Invalid URI in window state".to_string())?;
213
214 let Options = json!({ "tabSize": 4, "insertSpaces": true });
216
217 let LanguageProvider:Arc<dyn LanguageFeatureProviderRegistry> = RunTime.Environment.Require();
219
220 let EditsOption = LanguageProvider
221 .ProvideDocumentFormattingEdits(URI.clone(), Options)
222 .await
223 .map_err(|Error| Error.to_string())?;
224
225 if let Some(Edits) = EditsOption {
226 if Edits.is_empty() {
227 dev_log!("commands", "[Native Command] No formatting changes to apply.");
228
229 return Ok(Value::Null);
230 }
231
232 let WorkspaceEdit = WorkspaceEditDTO {
234 Edits:vec![(
235 serde_json::to_value(&URI).map_err(|Error| Error.to_string())?,
236 Edits
237 .into_iter()
238 .map(serde_json::to_value)
239 .collect::<Result<Vec<_>, _>>()
240 .map_err(|Error| Error.to_string())?,
241 )],
242 };
243
244 dev_log!("commands", "[Native Command] Applying formatting edits...");
246
247 RunTime
248 .Run(ApplyWorkspaceEdit(WorkspaceEdit))
249 .await
250 .map_err(|Error| Error.to_string())?;
251 } else {
252 dev_log!("commands", "[Native Command] No formatting provider found for this document.");
253 }
254
255 Ok(Value::Null)
256 })
257}
258
259fn CommandSaveDocument(
261 _ApplicationHandle:AppHandle<Wry>,
262
263 _Window:WebviewWindow<Wry>,
264
265 RunTime:Arc<ApplicationRunTime>,
266
267 _Argument:Value,
268) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
269 Box::pin(async move {
270 dev_log!("commands", "[Native Command] Executing Save Document...");
271
272 let AppState = &RunTime.Environment.ApplicationState;
273
274 let URIString = AppState
275 .Workspace
276 .ActiveDocumentURI
277 .lock()
278 .map_err(MapLockError)
279 .map_err(|Error| Error.to_string())?
280 .clone()
281 .ok_or("No active document URI found in state".to_string())?;
282
283 let URI = Url::parse(&URIString).map_err(|_| "Invalid URI in window state".to_string())?;
284
285 dev_log!("commands", "[Native Command] Saving document: {}", URI);
292
293 Ok(Value::Null)
294 })
295}
296
297fn CommandCloseDocument(
299 _ApplicationHandle:AppHandle<Wry>,
300
301 _Window:WebviewWindow<Wry>,
302
303 RunTime:Arc<ApplicationRunTime>,
304
305 _Argument:Value,
306) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
307 Box::pin(async move {
308 dev_log!("commands", "[Native Command] Executing Close Document...");
309
310 let AppState = &RunTime.Environment.ApplicationState;
311
312 let URIString = AppState
313 .Workspace
314 .ActiveDocumentURI
315 .lock()
316 .map_err(MapLockError)
317 .map_err(|Error| Error.to_string())?
318 .clone()
319 .ok_or("No active document URI found in state".to_string())?;
320
321 let URI = Url::parse(&URIString).map_err(|_| "Invalid URI in window state".to_string())?;
322
323 dev_log!("commands", "[Native Command] Closing document: {}", URI);
330
331 Ok(Value::Null)
332 })
333}
334
335fn CommandSetContext(
341 _ApplicationHandle:AppHandle<Wry>,
342
343 _Window:WebviewWindow<Wry>,
344
345 _RunTime:Arc<ApplicationRunTime>,
346
347 Argument:Value,
348) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
349 Box::pin(async move {
350 dev_log!("commands", "[Native Command] setContext: {}", Argument);
351 Ok(Value::Null)
352 })
353}
354
355fn CommandReloadWindow(
357 _ApplicationHandle:AppHandle<Wry>,
358
359 _Window:WebviewWindow<Wry>,
360
361 _RunTime:Arc<ApplicationRunTime>,
362
363 _Argument:Value,
364) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
365 Box::pin(async move {
366 dev_log!("commands", "[Native Command] Executing Reload Window...");
367
368 Ok(json!({ "success": true }))
374 })
375}
376
377fn ValidateCommandParameters(CommandName:&str, Arguments:&Value) -> Result<(), String> {
379 match CommandName {
380 "mountain.openFile" | "workbench.action.files.openFile" => {
381 Ok(())
383 },
384 "editor.action.formatDocument" => {
385 Ok(())
387 },
388 _ => Ok(()),
389 }
390}
391
392pub fn RegisterNativeCommands(
396 AppHandle:&AppHandle<Wry>,
397
398 ApplicationState:&Arc<ApplicationState>,
399) -> Result<(), CommonError> {
400 let mut CommandRegistry = ApplicationState
402 .Extension
403 .Registry
404 .CommandRegistry
405 .lock()
406 .map_err(MapLockError)?;
407
408 dev_log!("commands", "[Bootstrap] Registering native commands...");
409
410 CommandRegistry.insert("mountain.helloWorld".to_string(), CommandHandler::Native(CommandHelloWorld));
412
413 CommandRegistry.insert("mountain.openFile".to_string(), CommandHandler::Native(CommandOpenFile));
414
415 CommandRegistry.insert(
416 "workbench.action.files.openFile".to_string(),
417 CommandHandler::Native(CommandOpenFile),
418 );
419
420 CommandRegistry.insert(
421 "editor.action.formatDocument".to_string(),
422 CommandHandler::Native(CommandFormatDocument),
423 );
424
425 CommandRegistry.insert(
426 "workbench.action.files.save".to_string(),
427 CommandHandler::Native(CommandSaveDocument),
428 );
429
430 CommandRegistry.insert(
431 "workbench.action.closeActiveEditor".to_string(),
432 CommandHandler::Native(CommandCloseDocument),
433 );
434
435 CommandRegistry.insert(
436 "workbench.action.reloadWindow".to_string(),
437 CommandHandler::Native(CommandReloadWindow),
438 );
439
440 CommandRegistry.insert("setContext".to_string(), CommandHandler::Native(CommandSetContext));
444
445 dev_log!("commands", "[Bootstrap] {} native commands registered.", CommandRegistry.len());
446
447 drop(CommandRegistry);
448
449 dev_log!("commands", "[Bootstrap] Validating registered commands...");
451 let mut TreeViewRegistry = ApplicationState
460 .Feature
461 .TreeViews
462 .ActiveTreeViews
463 .lock()
464 .map_err(MapLockError)?;
465
466 dev_log!("commands", "[Bootstrap] Registering native tree view providers...");
467
468 let ExplorerViewID = "workbench.view.explorer".to_string();
469
470 let ExplorerProvider = Arc::new(FileExplorerViewProvider::New(AppHandle.clone()));
471
472 TreeViewRegistry.insert(
473 ExplorerViewID.clone(),
474 TreeViewStateDTO {
475 ViewIdentifier:ExplorerViewID,
476
477 Provider:Some(ExplorerProvider),
478
479 SideCarIdentifier:None,
481
482 CanSelectMany:true,
483
484 HasHandleDrag:false,
485
486 HasHandleDrop:false,
487
488 Message:None,
489
490 Title:Some("Explorer".to_string()),
491
492 Description:None,
493
494 Badge:None,
495 },
496 );
497
498 dev_log!("commands", "[Bootstrap] {} native tree view providers registered.", TreeViewRegistry.len());
499
500 Ok(())
501}