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 log::info;
128use serde_json::{Value, json};
129use tauri::{AppHandle, WebviewWindow, Wry};
130use url::Url;
131
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 info!("[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 info!("[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 info!("[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 info!("[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 info!("[Native Command] Applying formatting edits...");
246
247 RunTime
248 .Run(ApplyWorkspaceEdit(WorkspaceEdit))
249 .await
250 .map_err(|Error| Error.to_string())?;
251 } else {
252 info!("[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 info!("[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 info!("[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 info!("[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 info!("[Native Command] Closing document: {}", URI);
330
331 Ok(Value::Null)
332 })
333}
334
335fn CommandReloadWindow(
337 _ApplicationHandle:AppHandle<Wry>,
338
339 _Window:WebviewWindow<Wry>,
340
341 _RunTime:Arc<ApplicationRunTime>,
342
343 _Argument:Value,
344) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
345 Box::pin(async move {
346 info!("[Native Command] Executing Reload Window...");
347
348 Ok(json!({ "success": true }))
354 })
355}
356
357fn ValidateCommandParameters(CommandName:&str, Arguments:&Value) -> Result<(), String> {
359 match CommandName {
360 "mountain.openFile" | "workbench.action.files.openFile" => {
361 Ok(())
363 },
364 "editor.action.formatDocument" => {
365 Ok(())
367 },
368 _ => Ok(()),
369 }
370}
371
372pub fn RegisterNativeCommands(
376 AppHandle:&AppHandle<Wry>,
377
378 ApplicationState:&Arc<ApplicationState>,
379) -> Result<(), CommonError> {
380 let mut CommandRegistry = ApplicationState
382 .Extension
383 .Registry
384 .CommandRegistry
385 .lock()
386 .map_err(MapLockError)?;
387
388 info!("[Bootstrap] Registering native commands...");
389
390 CommandRegistry.insert("mountain.helloWorld".to_string(), CommandHandler::Native(CommandHelloWorld));
392
393 CommandRegistry.insert("mountain.openFile".to_string(), CommandHandler::Native(CommandOpenFile));
394
395 CommandRegistry.insert(
396 "workbench.action.files.openFile".to_string(),
397 CommandHandler::Native(CommandOpenFile),
398 );
399
400 CommandRegistry.insert(
401 "editor.action.formatDocument".to_string(),
402 CommandHandler::Native(CommandFormatDocument),
403 );
404
405 CommandRegistry.insert(
406 "workbench.action.files.save".to_string(),
407 CommandHandler::Native(CommandSaveDocument),
408 );
409
410 CommandRegistry.insert(
411 "workbench.action.closeActiveEditor".to_string(),
412 CommandHandler::Native(CommandCloseDocument),
413 );
414
415 CommandRegistry.insert(
416 "workbench.action.reloadWindow".to_string(),
417 CommandHandler::Native(CommandReloadWindow),
418 );
419
420 info!("[Bootstrap] {} native commands registered.", CommandRegistry.len());
421
422 drop(CommandRegistry);
423
424 info!("[Bootstrap] Validating registered commands...");
426 let mut TreeViewRegistry = ApplicationState
435 .Feature
436 .TreeViews
437 .ActiveTreeViews
438 .lock()
439 .map_err(MapLockError)?;
440
441 info!("[Bootstrap] Registering native tree view providers...");
442
443 let ExplorerViewID = "workbench.view.explorer".to_string();
444
445 let ExplorerProvider = Arc::new(FileExplorerViewProvider::New(AppHandle.clone()));
446
447 TreeViewRegistry.insert(
448 ExplorerViewID.clone(),
449 TreeViewStateDTO {
450 ViewIdentifier:ExplorerViewID,
451
452 Provider:Some(ExplorerProvider),
453
454 SideCarIdentifier:None,
456
457 CanSelectMany:true,
458
459 HasHandleDrag:false,
460
461 HasHandleDrop:false,
462
463 Message:None,
464
465 Title:Some("Explorer".to_string()),
466
467 Description:None,
468 },
469 );
470
471 info!("[Bootstrap] {} native tree view providers registered.", TreeViewRegistry.len());
472
473 Ok(())
474}