1#![allow(unused_imports)]
2
3use std::sync::{
62 Arc,
63 atomic::{AtomicBool, Ordering},
64};
65
66use tauri::{App, Manager, RunEvent, Wry};
67use Echo::Scheduler::{Scheduler::Scheduler, SchedulerBuilder::SchedulerBuilder};
68
69use crate::dev_log;
70use crate::{
71 ApplicationState::State::ApplicationState::ApplicationState,
73 Binary::Build::DnsCommands::{
74 StartupTime::init_dns_startup_time,
75 dns_get_forward_allowlist::dns_get_forward_allowlist,
76 dns_get_health_status::dns_get_health_status,
77 dns_get_server_info::dns_get_server_info,
78 dns_get_zone_info::dns_get_zone_info,
79 dns_health_check::dns_health_check,
80 dns_resolve::dns_resolve,
81 dns_test_resolution::dns_test_resolution,
82 },
83 Binary::Build::LocalhostPlugin::LocalhostPlugin as LocalhostPluginFn,
85 Binary::Build::LoggingPlugin::LoggingPlugin as LoggingPluginFn,
86 Binary::Build::Scheme::{self, DnsPort, init_service_registry, land_scheme_handler, register_land_service},
87 Binary::Build::ServiceRegistry::ServiceRegistry as ServiceRegistryFn,
88 Binary::Build::TauriBuild::TauriBuild as TauriBuildFn,
89 Binary::Build::WindowBuild::WindowBuild as WindowBuildFn,
90 Binary::Extension::ExtensionPopulate::ExtensionPopulate as ExtensionPopulateFn,
91 Binary::Extension::ScanPathConfigure::ScanPathConfigure as ScanPathConfigureFn,
92 Binary::Initialize::CliParse::Parse as CliParseFn,
93 Binary::Initialize::LogLevel::Resolve as ResolveLogLevel,
94 Binary::Initialize::PortSelector::BuildUrl as BuildPortUrl,
95 Binary::Initialize::PortSelector::Select as SelectPort,
96 Binary::Initialize::StateBuild::Build as BuildStateFn,
97 Binary::Register::AdvancedFeaturesRegister::AdvancedFeaturesRegister as AdvancedFeaturesRegisterFn,
98 Binary::Register::CommandRegister::CommandRegister as CommandRegisterFn,
99 Binary::Register::IPCServerRegister::IPCServerRegister as IPCServerRegisterFn,
100 Binary::Register::StatusReporterRegister::StatusReporterRegister as StatusReporterRegisterFn,
101 Binary::Register::WindSyncRegister::WindSyncRegister as WindSyncRegisterFn,
102 Binary::Service::CocoonStart::CocoonStart as CocoonStartFn,
103 Binary::Service::ConfigurationInitialize::ConfigurationInitialize as ConfigurationInitializeFn,
104 Binary::Service::VineStart::VineStart as VineStartFn,
105 Binary::Shutdown::RuntimeShutdown::RuntimeShutdown as RuntimeShutdownFn,
106 Binary::Shutdown::SchedulerShutdown::SchedulerShutdown as SchedulerShutdownFn,
107 Command,
108 Environment::MountainEnvironment::MountainEnvironment,
109 ProcessManagement::InitializationData,
110 RunTime::ApplicationRunTime::ApplicationRunTime,
111 Track,
112};
113use super::AppLifecycle::AppLifecycleSetup;
114
115macro_rules! TraceStep {
120 ($($arg:tt)*) => {{
121 dev_log!("lifecycle", $($arg)*);
122 }};
123}
124
125pub fn Fn() {
139 crate::IPC::DevLog::InitEager::Fn();
148
149 crate::Environment::Utility::EnhanceShellEnvironment::Fn();
162
163 {
173 fn LoadEnvFile(Path:&std::path::Path) -> bool {
174 let Ok(Content) = std::fs::read_to_string(Path) else {
175 return false;
176 };
177 for Line in Content.lines() {
178 let Trimmed = Line.trim();
179 if Trimmed.is_empty() || Trimmed.starts_with('#') {
180 continue;
181 }
182 if let Some((Key, Value)) = Trimmed.split_once('=') {
183 let CleanKey = Key.trim();
184 let CleanValue = Value.trim().trim_matches('"').trim_matches('\'');
185 if std::env::var_os(CleanKey).is_none() {
186 unsafe { std::env::set_var(CleanKey, CleanValue) };
190 }
191 }
192 }
193 true
194 }
195
196 let mut Candidates:Vec<std::path::PathBuf> = Vec::new();
197 if let Ok(Cwd) = std::env::current_dir() {
198 Candidates.push(Cwd.join(".env.Land"));
199 if let Some(Parent) = Cwd.parent() {
200 Candidates.push(Parent.join(".env.Land"));
201 }
202 Candidates.push(Cwd.join(".env.Land.Sample"));
203 if let Some(Parent) = Cwd.parent() {
204 Candidates.push(Parent.join(".env.Land.Sample"));
205 }
206 }
207 if let Ok(Exe) = std::env::current_exe() {
209 let Ancestors:Vec<&std::path::Path> = Exe.ancestors().collect();
210 for Candidate in Ancestors.iter().take(6) {
211 Candidates.push(Candidate.join(".env.Land"));
212 Candidates.push(Candidate.join(".env.Land.Sample"));
213 }
214 }
215
216 let mut Loaded = false;
217 for Candidate in Candidates {
218 if Candidate.exists() && LoadEnvFile(&Candidate) {
219 crate::dev_log!("lifecycle", "[Boot] [Env] Loaded env from {}", Candidate.display());
220 Loaded = true;
221 break;
222 }
223 }
224 if !Loaded {
225 crate::dev_log!(
226 "lifecycle",
227 "[Boot] [Env] No .env.Land / .env.Land.Sample found - using defaults"
228 );
229 }
230 }
231
232 crate::LandFixTier::LogResolvedTiers();
236
237 {
248 let NamedProfile = option_env!("Profile").unwrap_or("unknown");
249
250 let Workbench = option_env!("Pack").unwrap_or("Unknown");
251
252 let Bundle = option_env!("Bundle").unwrap_or("");
253
254 let Compiler = option_env!("Compiler").unwrap_or("default");
255
256 dev_log!(
257 "lifecycle",
258 "[LandFix:Profile] Active profile={} workbench={} bundle={} compiler={}",
259 NamedProfile,
260 Workbench,
261 Bundle,
262 Compiler
263 );
264 }
265
266 TraceStep!("[Boot] [Runtime] Building Tokio runtime...");
270
271 let Runtime = tokio::runtime::Builder::new_multi_thread()
272 .enable_all()
273 .build()
274 .expect("FATAL: Cannot build Tokio runtime.");
275
276 TraceStep!("[Boot] [Runtime] Tokio runtime built.");
277
278 Runtime.block_on(async {
279 crate::Binary::Build::PostHogPlugin::HydrateRuntimeEnvironment::Fn();
289
290 crate::Binary::Build::PostHogPlugin::Initialize::Fn().await;
296
297 CommonLibrary::Telemetry::Initialize::Fn(CommonLibrary::Telemetry::Tier::Tier::Mountain).await;
306
307 let _WorkspaceConfigurationPath = CliParseFn();
311 let _InitialFolders:Vec<String> = vec![];
312
313 dev_log!("lifecycle", "[Boot] [State] Building ApplicationState...");
317
318 let AppState = ApplicationState::default();
320
321 {
329 let InitialFolderPaths = crate::Binary::Initialize::CliParse::ParseWorkspaceFolders();
330 if InitialFolderPaths.is_empty() {
331 dev_log!(
332 "lifecycle",
333 "[Boot] [Workspace] No initial folders resolved - editor will open in \"no folder\" mode."
334 );
335 } else {
336 use crate::ApplicationState::DTO::WorkspaceFolderStateDTO::WorkspaceFolderStateDTO;
337 let mut Folders:Vec<WorkspaceFolderStateDTO> = Vec::new();
338 for (Index, Path) in InitialFolderPaths.iter().enumerate() {
339 let Uri = match url::Url::from_directory_path(Path) {
340 Ok(U) => U,
341 Err(()) => {
342 dev_log!(
343 "lifecycle",
344 "warn: [Boot] [Workspace] Failed to build URL for {}; skipping",
345 Path.display()
346 );
347 continue;
348 },
349 };
350 let Name = Path
351 .file_name()
352 .and_then(|N| N.to_str())
353 .map(str::to_string)
354 .unwrap_or_else(|| Path.display().to_string());
355 match WorkspaceFolderStateDTO::New(Uri, Name, Index) {
356 Ok(Dto) => Folders.push(Dto),
357 Err(Error) => {
358 dev_log!(
359 "lifecycle",
360 "warn: [Boot] [Workspace] Failed to build folder DTO for {}: {}",
361 Path.display(),
362 Error
363 );
364 },
365 }
366 }
367 if !Folders.is_empty() {
368 AppState.Workspace.SetWorkspaceFolders(Folders);
373 dev_log!(
374 "lifecycle",
375 "[Boot] [Workspace] Seeded {} workspace folder(s).",
376 InitialFolderPaths.len()
377 );
378 }
379 }
380 }
381
382 dev_log!(
383 "lifecycle",
384 "[Boot] [State] ApplicationState created with {} workspace folders.",
385 AppState.Workspace.WorkspaceFolders.lock().map(|f| f.len()).unwrap_or(0)
386 );
387
388 let AppStateArcForClosure = Arc::new(AppState.clone());
390
391 let Scheduler = Arc::new(SchedulerBuilder::Create().Build());
395 let SchedulerForClosure = Scheduler.clone();
396 TraceStep!("[Boot] [Echo] Scheduler handles prepared.");
397
398 let ServerPort = SelectPort();
402 let LocalhostUrl = BuildPortUrl(ServerPort);
403
404 let log_level = ResolveLogLevel();
408
409 let Builder = TauriBuildFn();
413
414 Builder
415 .plugin(LoggingPluginFn(log_level))
416 .plugin(LocalhostPluginFn(ServerPort))
417 .manage(AppStateArcForClosure.clone())
418 .setup({
419 let LocalhostUrl = LocalhostUrl.clone();
420 let ServerPortForClosure = ServerPort;
421 move |app:&mut App| {
422 dev_log!("lifecycle", "[Lifecycle] [Setup] Setup hook started.");
423 dev_log!("lifecycle", "[Lifecycle] [Setup] LocalhostUrl={}", LocalhostUrl);
424
425 dev_log!(
429 "lifecycle",
430 "[Lifecycle] [Setup] Initializing ServiceRegistry for land:// scheme..."
431 );
432 let service_registry = ServiceRegistryFn::new();
433 init_service_registry(service_registry.clone());
434
435 dev_log!(
440 "lifecycle",
441 "[Lifecycle] [Setup] Registering code.editor.land service on port {}",
442 ServerPortForClosure
443 );
444 register_land_service("code.editor.land", ServerPortForClosure);
445
446 register_land_service("api.editor.land", ServerPortForClosure);
448
449 register_land_service("assets.editor.land", ServerPortForClosure);
451
452 app.manage(service_registry);
454 dev_log!(
455 "lifecycle",
456 "[Lifecycle] [Setup] ServiceRegistry initialized and services registered."
457 );
458
459 dev_log!("lifecycle", "[Lifecycle] [Setup] Starting DNS server on preferred port 5380...");
465 let dns_port = Mist::start(5380).unwrap_or_else(|e| {
466 dev_log!(
467 "lifecycle",
468 "warn: [Lifecycle] [Setup] Failed to start DNS server on port 5380: {}",
469 e
470 );
471 Mist::start(0).unwrap_or_else(|e| {
473 dev_log!(
474 "lifecycle",
475 "error: [Lifecycle] [Setup] Completely failed to start DNS server: {}",
476 e
477 );
478 0 })
480 });
481
482 if dns_port == 0 {
483 dev_log!(
484 "lifecycle",
485 "warn: [Lifecycle] [Setup] DNS server failed to start, land:// protocol will not be \
486 available"
487 );
488 } else {
489 dev_log!(
490 "lifecycle",
491 "[Lifecycle] [Setup] DNS server started successfully on port {}",
492 dns_port
493 );
494 crate::Binary::Build::DnsCommands::StartupTime::init_dns_startup_time();
496 }
497
498 app.manage(DnsPort(dns_port));
500
501 let AppHandle = app.handle().clone();
502 TraceStep!("[Lifecycle] [Setup] AppHandle acquired.");
503
504 let AppStateArcFromClosure = AppStateArcForClosure.clone();
508
509 if let Err(e) = AppLifecycleSetup(
510 app,
511 AppHandle.clone(),
512 LocalhostUrl.clone(),
513 SchedulerForClosure.clone(),
514 AppStateArcFromClosure,
515 ) {
516 dev_log!("lifecycle", "error: [Lifecycle] [Setup] Failed to setup lifecycle: {}", e);
517 }
518
519 Ok(())
520 }
521 })
522 .register_asynchronous_uri_scheme_protocol("land", |_ctx, request, responder| {
523 let response = crate::Binary::Build::Scheme::land_scheme_handler(&request);
525 responder.respond(response);
526 })
527 .register_asynchronous_uri_scheme_protocol("vscode-file", |ctx, request, responder| {
528 let AppHandle = ctx.app_handle().clone();
531 std::thread::spawn(move || {
532 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &request);
533 responder.respond(response);
534 });
535 })
536 .register_asynchronous_uri_scheme_protocol("vscode-webview", |ctx, request, responder| {
537 let AppHandle = ctx.app_handle().clone();
547 std::thread::spawn(move || {
548 let response = crate::Binary::Build::Scheme::VscodeWebviewSchemeHandler(&AppHandle, &request);
549 responder.respond(response);
550 });
551 })
552 .register_asynchronous_uri_scheme_protocol("vscode-webview-resource", |ctx, request, responder| {
553 let AppHandle = ctx.app_handle().clone();
566 std::thread::spawn(move || {
567 let Original = request.uri().to_string();
568 let RewrittenUri = match Original.strip_prefix("vscode-webview-resource://") {
569 Some(After) => {
570 let Rest = After.find('/').map(|I| &After[I..]).unwrap_or("/");
571 format!("vscode-file://vscode-app{}", Rest)
572 },
573 None => "vscode-file://vscode-app/".to_string(),
574 };
575 crate::dev_log!(
576 "scheme-assets",
577 "[LandFix:VscodeWebviewResource] {} -> {}",
578 Original,
579 RewrittenUri
580 );
581 let mut Builder = tauri::http::request::Request::builder().uri(&RewrittenUri);
582 for (Name, Value) in request.headers().iter() {
583 Builder = Builder.header(Name, Value);
584 }
585 let Forwarded = Builder
586 .method(request.method().clone())
587 .body(request.body().clone())
588 .unwrap_or_else(|_| request.clone());
589 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &Forwarded);
590 responder.respond(response);
591 });
592 })
593 .register_asynchronous_uri_scheme_protocol("vscode-resource", |ctx, request, responder| {
594 let AppHandle = ctx.app_handle().clone();
598 std::thread::spawn(move || {
599 let Original = request.uri().to_string();
600 let RewrittenUri = match Original.strip_prefix("vscode-resource://") {
601 Some(After) => {
602 let Rest = After.find('/').map(|I| &After[I..]).unwrap_or("/");
603 format!("vscode-file://vscode-app{}", Rest)
604 },
605 None => "vscode-file://vscode-app/".to_string(),
606 };
607 crate::dev_log!("scheme-assets", "[LandFix:VscodeResource] {} -> {}", Original, RewrittenUri);
608 let mut Builder = tauri::http::request::Request::builder().uri(&RewrittenUri);
609 for (Name, Value) in request.headers().iter() {
610 Builder = Builder.header(Name, Value);
611 }
612 let Forwarded = Builder
613 .method(request.method().clone())
614 .body(request.body().clone())
615 .unwrap_or_else(|_| request.clone());
616 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &Forwarded);
617 responder.respond(response);
618 });
619 })
620 .plugin(tauri_plugin_dialog::init())
621 .plugin(tauri_plugin_fs::init())
622 .invoke_handler(tauri::generate_handler![
623 crate::Binary::Tray::SwitchTrayIcon::SwitchTrayIcon,
624 crate::Binary::IPC::WorkbenchConfigurationCommand::MountainGetWorkbenchConfiguration,
625 Command::TreeView::GetTreeViewChildren::GetTreeViewChildren,
626 Command::LanguageFeature::MountainProvideHover::MountainProvideHover,
627 Command::LanguageFeature::MountainProvideCompletions::MountainProvideCompletions,
628 Command::LanguageFeature::MountainProvideDefinition::MountainProvideDefinition,
629 Command::LanguageFeature::MountainProvideReferences::MountainProvideReferences,
630 Command::SourceControlManagement::GetAllSourceControlManagementState::GetAllSourceControlManagementState,
631 Command::Keybinding::GetResolvedKeybinding::GetResolvedKeybinding,
632 Track::FrontendCommand::DispatchFrontendCommand::DispatchFrontendCommand,
633 Track::UIRequest::ResolveUIRequest::ResolveUIRequest,
634 Track::Webview::MountainWebviewPostMessageFromGuest::MountainWebviewPostMessageFromGuest,
635 crate::Binary::IPC::MessageReceiveCommand::MountainIPCReceiveMessage,
636 crate::Binary::IPC::StatusGetCommand::MountainIPCGetStatus,
637 crate::Binary::IPC::InvokeCommand::MountainIPCInvoke,
638 crate::Binary::IPC::WindConfigurationCommand::MountainGetWindDesktopConfiguration,
639 crate::Binary::IPC::ConfigurationUpdateCommand::MountainUpdateConfigurationFromWind,
640 crate::Binary::IPC::ConfigurationSyncCommand::MountainSynchronizeConfiguration,
641 crate::Binary::IPC::ConfigurationStatusCommand::MountainGetConfigurationStatus,
642 crate::Binary::IPC::IPCStatusCommand::MountainGetIPCStatus,
643 crate::Binary::IPC::IPCStatusHistoryCommand::MountainGetIPCStatusHistory,
644 crate::Binary::IPC::IPCStatusReportingStartCommand::MountainStartIPCStatusReporting,
645 crate::Binary::IPC::PerformanceStatsCommand::MountainGetPerformanceStats,
646 crate::Binary::IPC::CacheStatsCommand::MountainGetCacheStats,
647 crate::Binary::IPC::CollaborationSessionCommand::MountainCreateCollaborationSession,
648 crate::Binary::IPC::CollaborationSessionCommand::MountainGetCollaborationSessions,
649 crate::Binary::IPC::DocumentSyncCommand::MountainAddDocumentForSync,
650 crate::Binary::IPC::DocumentSyncCommand::MountainGetSyncStatus,
651 crate::Binary::IPC::UpdateSubscriptionCommand::MountainSubscribeToUpdates,
652 crate::Binary::IPC::ConfigurationDataCommand::GetConfigurationData,
653 crate::Binary::IPC::ConfigurationDataCommand::SaveConfigurationData,
654 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceOpenFolder,
655 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceListFolders,
656 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceCloseAllFolders,
657 crate::Binary::Build::DnsCommands::dns_get_server_info::dns_get_server_info,
658 crate::Binary::Build::DnsCommands::dns_get_zone_info::dns_get_zone_info,
659 crate::Binary::Build::DnsCommands::dns_get_forward_allowlist::dns_get_forward_allowlist,
660 crate::Binary::Build::DnsCommands::dns_get_health_status::dns_get_health_status,
661 crate::Binary::Build::DnsCommands::dns_resolve::dns_resolve,
662 crate::Binary::Build::DnsCommands::dns_test_resolution::dns_test_resolution,
663 crate::Binary::Build::DnsCommands::dns_health_check::dns_health_check,
664 crate::Binary::IPC::ProcessCommand::process_get_exec_path::process_get_exec_path,
666 crate::Binary::IPC::ProcessCommand::process_get_platform::process_get_platform,
667 crate::Binary::IPC::ProcessCommand::process_get_arch::process_get_arch,
668 crate::Binary::IPC::ProcessCommand::process_get_pid::process_get_pid,
669 crate::Binary::IPC::ProcessCommand::process_get_shell_env::process_get_shell_env,
670 crate::Binary::IPC::ProcessCommand::process_get_memory_info::process_get_memory_info,
671 crate::Binary::IPC::HealthCommand::cocoon_extension_host_health::cocoon_extension_host_health,
673 crate::Binary::IPC::HealthCommand::cocoon_search_service_health::cocoon_search_service_health,
674 crate::Binary::IPC::HealthCommand::cocoon_debug_service_health::cocoon_debug_service_health,
675 crate::Binary::IPC::HealthCommand::shared_process_service_health::shared_process_service_health,
676 crate::Binary::IPC::RenderDevLogCommand::RenderDevLog,
677 crate::Binary::IPC::VineSubscribeCommand::vine_subscribe_notifications,
686 crate::Binary::IPC::VineSubscribeCommand::vine_subscriber_count,
687 ])
688 .build(tauri::generate_context!())
689 .expect("FATAL: Error while building Mountain Tauri application")
690 .run(move |app_handle:&tauri::AppHandle, event:tauri::RunEvent| {
691 if cfg!(debug_assertions) {
693 match &event {
694 RunEvent::MainEventsCleared => {},
695 RunEvent::WindowEvent { .. } => {},
696 _ => dev_log!("lifecycle", "[Lifecycle] [RunEvent] {:?}", event),
697 }
698 }
699
700 if let RunEvent::ExitRequested { api, .. } = event {
701 static SHUTTING_DOWN:AtomicBool = AtomicBool::new(false);
709 if SHUTTING_DOWN.swap(true, Ordering::SeqCst) {
710 return;
711 }
712
713 dev_log!(
714 "lifecycle",
715 "warn: [Lifecycle] [Shutdown] Exit requested. Starting graceful shutdown..."
716 );
717 api.prevent_exit();
718
719 let SchedulerHandle = Scheduler.clone();
720 let app_handle_clone = app_handle.clone();
721
722 tokio::spawn(async move {
723 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Shutting down ApplicationRunTime...");
724 let _ = RuntimeShutdownFn(&app_handle_clone).await;
725
726 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Stopping Echo scheduler...");
727 let _ = SchedulerShutdownFn(SchedulerHandle).await;
728
729 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Done. Exiting process.");
730 app_handle_clone.exit(0);
731 });
732 }
733 });
734
735 dev_log!("lifecycle", "[Lifecycle] [Exit] Mountain application has shut down.");
736 });
737}