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.active_window_mut().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.active_window_mut()
39            .pending_file_opens
40            .push(super::PendingFileOpen {
41                path,
42                line,
43                column,
44                end_line,
45                end_column,
46                message,
47                wait_id,
48            });
49    }
50
51    /// Process pending file opens (called from the event loop).
52    ///
53    /// Opens files that were queued during startup, using the same error handling
54    /// as interactive file opens. Returns true if any files were processed.
55    pub fn process_pending_file_opens(&mut self) -> bool {
56        if self.active_window_mut().pending_file_opens.is_empty() {
57            return false;
58        }
59
60        // Take all pending files to process
61        let pending = std::mem::take(&mut self.active_window_mut().pending_file_opens);
62        let mut processed_any = false;
63
64        for pending_file in pending {
65            tracing::info!(
66                "[SYNTAX DEBUG] Processing pending file open: {:?}",
67                pending_file.path
68            );
69
70            match self.open_file(&pending_file.path) {
71                Ok(_) => {
72                    // Navigate to line/column or select range if specified
73                    if let (Some(line), Some(end_line)) = (pending_file.line, pending_file.end_line)
74                    {
75                        self.select_range(
76                            line,
77                            pending_file.column,
78                            end_line,
79                            pending_file.end_column,
80                        );
81                    } else if let Some(line) = pending_file.line {
82                        self.goto_line_col(line, pending_file.column);
83                    }
84                    // Show hover message popup if specified
85                    let has_popup = pending_file.message.is_some();
86                    if let Some(ref msg) = pending_file.message {
87                        self.show_file_message_popup(msg);
88                    }
89                    // Track wait ID for --wait support
90                    if let Some(wait_id) = pending_file.wait_id {
91                        let buffer_id = self.active_buffer();
92                        self.active_window_mut()
93                            .wait_tracking
94                            .insert(buffer_id, (wait_id, has_popup));
95                    }
96                    processed_any = true;
97                }
98                Err(e) => {
99                    // Check if this is a large file encoding confirmation error
100                    // Show prompt instead of crashing
101                    if let Some(confirmation) =
102                        e.downcast_ref::<crate::model::buffer::LargeFileEncodingConfirmation>()
103                    {
104                        self.start_large_file_encoding_confirmation(confirmation);
105                    } else {
106                        // For other errors, show status message (consistent with file browser)
107                        self.set_status_message(
108                            t!("file.error_opening", error = e.to_string()).to_string(),
109                        );
110                    }
111                    processed_any = true;
112                }
113            }
114        }
115
116        // Apply hot exit recovery if flagged (one-shot after CLI files are opened)
117        if processed_any && self.active_window_mut().pending_hot_exit_recovery {
118            self.active_window_mut().pending_hot_exit_recovery = false;
119            match self.apply_hot_exit_recovery() {
120                Ok(count) if count > 0 => {
121                    tracing::info!("Hot exit: restored unsaved changes for {} buffer(s)", count);
122                }
123                Ok(_) => {}
124                Err(e) => {
125                    tracing::warn!("Failed to apply hot exit recovery: {}", e);
126                }
127            }
128        }
129
130        processed_any
131    }
132
133    /// Take and return completed wait IDs (for --wait support).
134    pub fn take_completed_waits(&mut self) -> Vec<u64> {
135        std::mem::take(&mut self.active_window_mut().completed_waits)
136    }
137
138    /// Remove wait tracking for a given wait_id (e.g., when waiting client disconnects).
139    pub fn remove_wait_tracking(&mut self, wait_id: u64) {
140        self.active_window_mut()
141            .wait_tracking
142            .retain(|_, (wid, _)| *wid != wait_id);
143    }
144}