Mountain/Command/
Bootstrap.rs

1// File: Mountain/Source/Command/Bootstrap.rs
2// Role: Registers all native, Rust-implemented commands and providers at
3// startup. Responsibilities:
4//   - Centralize the registration of all built-in functionality.
5//   - Populate the `CommandRegistry` with native command handlers.
6//   - Populate the `ActiveTreeViews` registry with native tree data providers.
7
8//! # Bootstrap Commands & Providers
9//!
10//! Registers all native, Rust-implemented commands and providers into the
11//! application's state at startup.
12
13#![allow(non_snake_case, non_camel_case_types)]
14
15use std::{future::Future, pin::Pin, sync::Arc};
16
17use Common::{
18	DTO::WorkSpaceEditDTO::WorkSpaceEditDTO,
19	Document::OpenDocument::OpenDocument,
20	Effect::ApplicationRunTime::ApplicationRunTime as _,
21	Environment::Requires::Requires,
22	Error::CommonError::CommonError,
23	LanguageFeature::LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
24	UserInterface::ShowOpenDialog::ShowOpenDialog,
25	WorkSpace::ApplyWorkSpaceEdit::ApplyWorkSpaceEdit,
26};
27use log::info;
28use serde_json::{Value, json};
29use tauri::{AppHandle, WebviewWindow, Wry};
30use url::Url;
31
32use crate::{
33	ApplicationState::{
34		ApplicationState::{ApplicationState, MapLockError},
35		DTO::TreeViewStateDTO::TreeViewStateDTO,
36	},
37	Environment::CommandProvider::CommandHandler,
38	FileSystem::FileExplorerViewProvider::FileExplorerViewProvider,
39	RunTime::ApplicationRunTime::ApplicationRunTime,
40};
41
42// --- Command Implementations ---
43
44/// A simple native command that logs a message.
45fn CommandHelloWorld(
46	_ApplicationHandle:AppHandle<Wry>,
47
48	_Window:WebviewWindow<Wry>,
49
50	_RunTime:Arc<ApplicationRunTime>,
51
52	_Argument:Value,
53) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
54	Box::pin(async move {
55		info!("[Native Command] Hello from Mountain!");
56
57		Ok(json!("Hello from Mountain's native command!"))
58	})
59}
60
61/// A native command that orchestrates the "Open File" dialog flow.
62fn CommandOpenFile(
63	_ApplicationHandle:AppHandle<Wry>,
64
65	_Window:WebviewWindow<Wry>,
66
67	RunTime:Arc<ApplicationRunTime>,
68
69	_Argument:Value,
70) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
71	Box::pin(async move {
72		info!("[Native Command] Executing Open File...");
73
74		let DialogResult = RunTime.Run(ShowOpenDialog(None)).await.map_err(|Error| Error.to_string())?;
75
76		if let Some(Paths) = DialogResult {
77			if let Some(Path) = Paths.first() {
78				// We have a path, now open the document.
79				let URI = Url::from_file_path(Path).map_err(|_| "Invalid file path".to_string())?;
80
81				let OpenDocumentEffect = OpenDocument(json!({ "external": URI.to_string() }), None, None);
82
83				RunTime.Run(OpenDocumentEffect).await.map_err(|Error| Error.to_string())?;
84			}
85		}
86
87		Ok(Value::Null)
88	})
89}
90
91/// A native command that orchestrates the "Format Document" action.
92fn CommandFormatDocument(
93	_ApplicationHandle:AppHandle<Wry>,
94
95	_Window:WebviewWindow<Wry>,
96
97	RunTime:Arc<ApplicationRunTime>,
98
99	_Argument:Value,
100) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
101	Box::pin(async move {
102		info!("[Native Command] Executing Format Document...");
103
104		let AppState = &RunTime.Environment.ApplicationState;
105
106		let URIString = AppState
107			.ActiveDocumentURI
108			.lock()
109			.map_err(MapLockError)
110			.map_err(|Error| Error.to_string())?
111			.clone()
112			.ok_or("No active document URI found in state".to_string())?;
113
114		let URI = Url::parse(&URIString).map_err(|_| "Invalid URI in window state".to_string())?;
115
116		// Example formatting options
117		let Options = json!({ "tabSize": 4, "insertSpaces": true });
118
119		// 1. Get the formatting edits from the language feature provider.
120		let LanguageProvider:Arc<dyn LanguageFeatureProviderRegistry> = RunTime.Environment.Require();
121
122		let EditsOption = LanguageProvider
123			.ProvideDocumentFormattingEdits(URI.clone(), Options)
124			.await
125			.map_err(|Error| Error.to_string())?;
126
127		if let Some(Edits) = EditsOption {
128			if Edits.is_empty() {
129				info!("[Native Command] No formatting changes to apply.");
130
131				return Ok(Value::Null);
132			}
133
134			// 2. Convert the text edits into a WorkSpaceEdit.
135			let WorkSpaceEdit = WorkSpaceEditDTO {
136				Edits:vec![(
137					serde_json::to_value(&URI).map_err(|Error| Error.to_string())?,
138					Edits
139						.into_iter()
140						.map(serde_json::to_value)
141						.collect::<Result<Vec<_>, _>>()
142						.map_err(|Error| Error.to_string())?,
143				)],
144			};
145
146			// 3. Apply the workspace edit.
147			info!("[Native Command] Applying formatting edits...");
148
149			RunTime
150				.Run(ApplyWorkSpaceEdit(WorkSpaceEdit))
151				.await
152				.map_err(|Error| Error.to_string())?;
153		} else {
154			info!("[Native Command] No formatting provider found for this document.");
155		}
156
157		Ok(Value::Null)
158	})
159}
160
161// --- Registration Function ---
162
163/// Registers all native commands and providers with the application state.
164pub fn RegisterNativeCommands(
165	AppHandle:&AppHandle<Wry>,
166
167	ApplicationState:&Arc<ApplicationState>,
168) -> Result<(), CommonError> {
169	// --- Command Registration ---
170	let mut CommandRegistry = ApplicationState.CommandRegistry.lock().map_err(MapLockError)?;
171
172	info!("[Bootstrap] Registering native commands...");
173
174	CommandRegistry.insert("mountain.helloWorld".to_string(), CommandHandler::Native(CommandHelloWorld));
175
176	CommandRegistry.insert("mountain.openFile".to_string(), CommandHandler::Native(CommandOpenFile));
177
178	CommandRegistry.insert(
179		"workbench.action.files.openFile".to_string(),
180		CommandHandler::Native(CommandOpenFile),
181	);
182
183	CommandRegistry.insert(
184		"editor.action.formatDocument".to_string(),
185		CommandHandler::Native(CommandFormatDocument),
186	);
187
188	info!("[Bootstrap] {} native commands registered.", CommandRegistry.len());
189
190	drop(CommandRegistry);
191
192	// --- Tree View Provider Registration ---
193	let mut TreeViewRegistry = ApplicationState.ActiveTreeViews.lock().map_err(MapLockError)?;
194
195	info!("[Bootstrap] Registering native tree view providers...");
196
197	let ExplorerViewID = "workbench.view.explorer".to_string();
198
199	let ExplorerProvider = Arc::new(FileExplorerViewProvider::New(AppHandle.clone()));
200
201	TreeViewRegistry.insert(
202		ExplorerViewID.clone(),
203		TreeViewStateDTO {
204			ViewIdentifier:ExplorerViewID,
205
206			Provider:Some(ExplorerProvider),
207
208			// This is a native provider
209			SideCarIdentifier:None,
210
211			CanSelectMany:true,
212
213			HasHandleDrag:false,
214
215			HasHandleDrop:false,
216
217			Message:None,
218
219			Title:Some("Explorer".to_string()),
220
221			Description:None,
222		},
223	);
224
225	info!("[Bootstrap] {} native tree view providers registered.", TreeViewRegistry.len());
226
227	Ok(())
228}