Skip to main content

fresh/app/
file_open_queue.rs

1//! Pending-file-open queue and --wait tracking on `Editor`.
2//!
3//! CLI file arguments are queued and processed after the TUI starts so
4//! they go through the same code path as interactive opens (with proper
5//! encoding-prompt handling). Wait tracking lets the CLI block until
6//! a popup-based wait is dismissed.
7
8use std::path::PathBuf;
9
10use rust_i18n::t;
11
12use super::Editor;
13
14impl Editor {
15    /// Queue a file to be opened after the TUI starts.
16    ///
17    /// This is used for CLI file arguments to ensure they go through the same
18    /// code path as interactive file opens, providing consistent error handling
19    /// (e.g., encoding confirmation prompts are shown in the UI instead of crashing).
20    /// Schedule hot exit recovery to run after the next batch of pending file opens.
21    pub fn schedule_hot_exit_recovery(&mut self) {
22        if self.config.editor.hot_exit {
23            self.pending_hot_exit_recovery = true;
24        }
25    }
26
27    #[allow(clippy::too_many_arguments)]
28    pub fn queue_file_open(
29        &mut self,
30        path: PathBuf,
31        line: Option<usize>,
32        column: Option<usize>,
33        end_line: Option<usize>,
34        end_column: Option<usize>,
35        message: Option<String>,
36        wait_id: Option<u64>,
37    ) {
38        self.pending_file_opens.push(super::PendingFileOpen {
39            path,
40            line,
41            column,
42            end_line,
43            end_column,
44            message,
45            wait_id,
46        });
47    }
48
49    /// Process pending file opens (called from the event loop).
50    ///
51    /// Opens files that were queued during startup, using the same error handling
52    /// as interactive file opens. Returns true if any files were processed.
53    pub fn process_pending_file_opens(&mut self) -> bool {
54        if self.pending_file_opens.is_empty() {
55            return false;
56        }
57
58        // Take all pending files to process
59        let pending = std::mem::take(&mut self.pending_file_opens);
60        let mut processed_any = false;
61
62        for pending_file in pending {
63            tracing::info!(
64                "[SYNTAX DEBUG] Processing pending file open: {:?}",
65                pending_file.path
66            );
67
68            match self.open_file(&pending_file.path) {
69                Ok(_) => {
70                    // Navigate to line/column or select range if specified
71                    if let (Some(line), Some(end_line)) = (pending_file.line, pending_file.end_line)
72                    {
73                        self.select_range(
74                            line,
75                            pending_file.column,
76                            end_line,
77                            pending_file.end_column,
78                        );
79                    } else if let Some(line) = pending_file.line {
80                        self.goto_line_col(line, pending_file.column);
81                    }
82                    // Show hover message popup if specified
83                    let has_popup = pending_file.message.is_some();
84                    if let Some(ref msg) = pending_file.message {
85                        self.show_file_message_popup(msg);
86                    }
87                    // Track wait ID for --wait support
88                    if let Some(wait_id) = pending_file.wait_id {
89                        let buffer_id = self.active_buffer();
90                        self.wait_tracking.insert(buffer_id, (wait_id, has_popup));
91                    }
92                    processed_any = true;
93                }
94                Err(e) => {
95                    // Check if this is a large file encoding confirmation error
96                    // Show prompt instead of crashing
97                    if let Some(confirmation) =
98                        e.downcast_ref::<crate::model::buffer::LargeFileEncodingConfirmation>()
99                    {
100                        self.start_large_file_encoding_confirmation(confirmation);
101                    } else {
102                        // For other errors, show status message (consistent with file browser)
103                        self.set_status_message(
104                            t!("file.error_opening", error = e.to_string()).to_string(),
105                        );
106                    }
107                    processed_any = true;
108                }
109            }
110        }
111
112        // Apply hot exit recovery if flagged (one-shot after CLI files are opened)
113        if processed_any && self.pending_hot_exit_recovery {
114            self.pending_hot_exit_recovery = false;
115            match self.apply_hot_exit_recovery() {
116                Ok(count) if count > 0 => {
117                    tracing::info!("Hot exit: restored unsaved changes for {} buffer(s)", count);
118                }
119                Ok(_) => {}
120                Err(e) => {
121                    tracing::warn!("Failed to apply hot exit recovery: {}", e);
122                }
123            }
124        }
125
126        processed_any
127    }
128
129    /// Take and return completed wait IDs (for --wait support).
130    pub fn take_completed_waits(&mut self) -> Vec<u64> {
131        std::mem::take(&mut self.completed_waits)
132    }
133
134    /// Remove wait tracking for a given wait_id (e.g., when waiting client disconnects).
135    pub fn remove_wait_tracking(&mut self, wait_id: u64) {
136        self.wait_tracking.retain(|_, (wid, _)| *wid != wait_id);
137    }
138}