1use rust_i18n::t;
6
7use super::normalize_path;
8use super::BufferId;
9use super::BufferMetadata;
10use super::Editor;
11use crate::input::keybindings::Action;
12use crate::primitives::path_utils::expand_tilde;
13use crate::services::plugins::hooks::HookArgs;
14use crate::view::prompt::PromptType;
15
16pub enum PromptResult {
18 Done,
20 ExecuteAction(Action),
22 EarlyReturn,
24}
25
26impl Editor {
27 pub fn handle_prompt_confirm_input(
31 &mut self,
32 input: String,
33 prompt_type: PromptType,
34 selected_index: Option<usize>,
35 ) -> PromptResult {
36 match prompt_type {
37 PromptType::OpenFile => {
38 let expanded_path = expand_tilde(&input);
40 let resolved_path = if expanded_path.is_absolute() {
41 normalize_path(&expanded_path)
42 } else {
43 normalize_path(&self.working_dir.join(&expanded_path))
44 };
45
46 if let Err(e) = self.open_file(&resolved_path) {
47 self.set_status_message(
48 t!("file.error_opening", error = e.to_string()).to_string(),
49 );
50 } else {
51 self.set_status_message(
52 t!("buffer.opened", name = resolved_path.display().to_string()).to_string(),
53 );
54 }
55 }
56 PromptType::SwitchProject => {
57 let expanded_path = expand_tilde(&input);
59 let resolved_path = if expanded_path.is_absolute() {
60 normalize_path(&expanded_path)
61 } else {
62 normalize_path(&self.working_dir.join(&expanded_path))
63 };
64
65 if resolved_path.is_dir() {
66 self.change_working_dir(resolved_path);
67 } else {
68 self.set_status_message(
69 t!(
70 "file.not_directory",
71 path = resolved_path.display().to_string()
72 )
73 .to_string(),
74 );
75 }
76 }
77 PromptType::SaveFileAs => {
78 self.handle_save_file_as(&input);
79 }
80 PromptType::Search => {
81 self.perform_search(&input);
82 }
83 PromptType::ReplaceSearch => {
84 self.perform_search(&input);
85 self.start_prompt(
86 t!("replace.prompt", search = &input).to_string(),
87 PromptType::Replace {
88 search: input.clone(),
89 },
90 );
91 }
92 PromptType::Replace { search } => {
93 if self.search_confirm_each {
94 self.start_interactive_replace(&search, &input);
95 } else {
96 self.perform_replace(&search, &input);
97 }
98 }
99 PromptType::QueryReplaceSearch => {
100 self.perform_search(&input);
101 self.start_prompt(
102 t!("replace.query_prompt", search = &input).to_string(),
103 PromptType::QueryReplace {
104 search: input.clone(),
105 },
106 );
107 }
108 PromptType::QueryReplace { search } => {
109 if self.search_confirm_each {
110 self.start_interactive_replace(&search, &input);
111 } else {
112 self.perform_replace(&search, &input);
113 }
114 }
115 PromptType::Command => {
116 let commands = self.command_registry.read().unwrap().get_all();
117 if let Some(cmd) = commands.iter().find(|c| c.get_localized_name() == input) {
118 let action = cmd.action.clone();
119 let cmd_name = cmd.get_localized_name();
120 self.command_registry
121 .write()
122 .unwrap()
123 .record_usage(&cmd_name);
124 return PromptResult::ExecuteAction(action);
125 } else {
126 self.set_status_message(
127 t!("error.unknown_command", input = &input).to_string(),
128 );
129 }
130 }
131 PromptType::GotoLine => match input.trim().parse::<usize>() {
132 Ok(line_num) if line_num > 0 => {
133 self.goto_line_col(line_num, None);
134 self.set_status_message(t!("goto.jumped", line = line_num).to_string());
135 }
136 Ok(_) => {
137 self.set_status_message(t!("goto.line_must_be_positive").to_string());
138 }
139 Err(_) => {
140 self.set_status_message(t!("error.invalid_line", input = &input).to_string());
141 }
142 },
143 PromptType::QuickOpen => {
144 return self.handle_quick_open_confirm(&input, selected_index);
146 }
147 PromptType::SetBackgroundFile => {
148 if let Err(e) = self.load_ansi_background(&input) {
149 self.set_status_message(
150 t!("error.background_load_failed", error = e.to_string()).to_string(),
151 );
152 }
153 }
154 PromptType::SetBackgroundBlend => match input.trim().parse::<f32>() {
155 Ok(val) => {
156 let clamped = val.clamp(0.0, 1.0);
157 self.background_fade = clamped;
158 self.set_status_message(
159 t!(
160 "error.background_blend_set",
161 value = format!("{:.2}", clamped)
162 )
163 .to_string(),
164 );
165 }
166 Err(_) => {
167 self.set_status_message(t!("error.invalid_blend", input = &input).to_string());
168 }
169 },
170 PromptType::SetComposeWidth => {
171 self.handle_set_compose_width(&input);
172 }
173 PromptType::RecordMacro => {
174 self.handle_register_input(
175 &input,
176 |editor, c| editor.toggle_macro_recording(c),
177 "Macro",
178 );
179 }
180 PromptType::PlayMacro => {
181 self.handle_register_input(&input, |editor, c| editor.play_macro(c), "Macro");
182 }
183 PromptType::SetBookmark => {
184 self.handle_register_input(&input, |editor, c| editor.set_bookmark(c), "Bookmark");
185 }
186 PromptType::JumpToBookmark => {
187 self.handle_register_input(
188 &input,
189 |editor, c| editor.jump_to_bookmark(c),
190 "Bookmark",
191 );
192 }
193 PromptType::Plugin { custom_type } => {
194 tracing::info!(
195 "prompt_confirmed: dispatching hook for prompt_type='{}', input='{}', selected_index={:?}",
196 custom_type, input, selected_index
197 );
198 self.plugin_manager.run_hook(
199 "prompt_confirmed",
200 HookArgs::PromptConfirmed {
201 prompt_type: custom_type.clone(),
202 input,
203 selected_index,
204 },
205 );
206 tracing::info!(
207 "prompt_confirmed: hook dispatched for prompt_type='{}'",
208 custom_type
209 );
210 }
211 PromptType::ConfirmRevert => {
212 let input_lower = input.trim().to_lowercase();
213 let revert_key = t!("prompt.key.revert").to_string().to_lowercase();
214 if input_lower == revert_key || input_lower == "revert" {
215 if let Err(e) = self.revert_file() {
216 self.set_status_message(
217 t!("file.revert_failed", error = e.to_string()).to_string(),
218 );
219 }
220 } else {
221 self.set_status_message(t!("buffer.revert_cancelled").to_string());
222 }
223 }
224 PromptType::ConfirmSaveConflict => {
225 let input_lower = input.trim().to_lowercase();
226 if input_lower == "o" || input_lower == "overwrite" {
227 if let Err(e) = self.save() {
228 self.set_status_message(
229 t!("file.save_failed", error = e.to_string()).to_string(),
230 );
231 }
232 } else {
233 self.set_status_message(t!("buffer.save_cancelled").to_string());
234 }
235 }
236 PromptType::ConfirmSudoSave { info } => {
237 let input_lower = input.trim().to_lowercase();
238 if input_lower == "y" || input_lower == "yes" {
239 self.cancel_prompt();
241
242 let result = (|| -> anyhow::Result<()> {
244 let data = self.filesystem.read_file(&info.temp_path)?;
245 self.filesystem.sudo_write(
246 &info.dest_path,
247 &data,
248 info.mode,
249 info.uid,
250 info.gid,
251 )?;
252 let _ = self.filesystem.remove_file(&info.temp_path);
254 Ok(())
255 })();
256
257 match result {
258 Ok(_) => {
259 if let Err(e) = self
260 .active_state_mut()
261 .buffer
262 .finalize_external_save(info.dest_path.clone())
263 {
264 tracing::warn!("Failed to finalize sudo save: {}", e);
265 self.set_status_message(
266 t!("prompt.sudo_save_failed", error = e.to_string())
267 .to_string(),
268 );
269 } else if let Err(e) = self.finalize_save(Some(info.dest_path)) {
270 tracing::warn!("Failed to finalize save after sudo: {}", e);
271 self.set_status_message(
272 t!("prompt.sudo_save_failed", error = e.to_string())
273 .to_string(),
274 );
275 }
276 }
277 Err(e) => {
278 tracing::warn!("Sudo save failed: {}", e);
279 self.set_status_message(
280 t!("prompt.sudo_save_failed", error = e.to_string()).to_string(),
281 );
282 let _ = self.filesystem.remove_file(&info.temp_path);
284 }
285 }
286 } else {
287 self.set_status_message(t!("buffer.save_cancelled").to_string());
288 let _ = self.filesystem.remove_file(&info.temp_path);
290 }
291 }
292 PromptType::ConfirmOverwriteFile { path } => {
293 let input_lower = input.trim().to_lowercase();
294 if input_lower == "o" || input_lower == "overwrite" {
295 self.perform_save_file_as(path);
296 } else {
297 self.set_status_message(t!("buffer.save_cancelled").to_string());
298 }
299 }
300 PromptType::ConfirmCloseBuffer { buffer_id } => {
301 if self.handle_confirm_close_buffer(&input, buffer_id) {
302 return PromptResult::EarlyReturn;
303 }
304 }
305 PromptType::ConfirmQuitWithModified => {
306 let input_lower = input.trim().to_lowercase();
307 let discard_key = t!("prompt.key.discard").to_string().to_lowercase();
308 if input_lower == discard_key || input_lower == "discard" {
309 self.should_quit = true;
310 } else {
311 self.set_status_message(t!("buffer.close_cancelled").to_string());
312 }
313 }
314 PromptType::LspRename {
315 original_text,
316 start_pos,
317 end_pos: _,
318 overlay_handle,
319 } => {
320 self.perform_lsp_rename(input, original_text, start_pos, overlay_handle);
321 }
322 PromptType::FileExplorerRename {
323 original_path,
324 original_name,
325 is_new_file,
326 } => {
327 self.perform_file_explorer_rename(original_path, original_name, input, is_new_file);
328 }
329 PromptType::ConfirmDeleteFile { path, is_dir } => {
330 let input_lower = input.trim().to_lowercase();
331 if input_lower == "y" || input_lower == "yes" {
332 self.perform_file_explorer_delete(path, is_dir);
333 } else {
334 self.set_status_message(t!("explorer.delete_cancelled").to_string());
335 }
336 }
337 PromptType::StopLspServer => {
338 self.handle_stop_lsp_server(&input);
339 }
340 PromptType::SelectTheme { .. } => {
341 self.apply_theme(input.trim());
342 }
343 PromptType::SelectKeybindingMap => {
344 self.apply_keybinding_map(input.trim());
345 }
346 PromptType::SelectCursorStyle => {
347 self.apply_cursor_style(input.trim());
348 }
349 PromptType::SelectLocale => {
350 self.apply_locale(input.trim());
351 }
352 PromptType::CopyWithFormattingTheme => {
353 self.copy_selection_with_theme(input.trim());
354 }
355 PromptType::SwitchToTab => {
356 if let Ok(id) = input.trim().parse::<usize>() {
357 self.switch_to_tab(BufferId(id));
358 }
359 }
360 PromptType::QueryReplaceConfirm => {
361 if let Some(c) = input.chars().next() {
364 let _ = self.handle_interactive_replace_key(c);
365 }
366 }
367 PromptType::SetTabSize => {
368 self.handle_set_tab_size(&input);
369 }
370 PromptType::SetLineEnding => {
371 self.handle_set_line_ending(&input);
372 }
373 PromptType::SetLanguage => {
374 self.handle_set_language(&input);
375 }
376 PromptType::ShellCommand { replace } => {
377 self.handle_shell_command(&input, replace);
378 }
379 PromptType::AsyncPrompt => {
380 if let Some(callback_id) = self.pending_async_prompt_callback.take() {
382 let json = serde_json::to_string(&input).unwrap_or_else(|_| "null".to_string());
384 self.plugin_manager.resolve_callback(callback_id, json);
385 }
386 }
387 }
388 PromptResult::Done
389 }
390
391 fn handle_save_file_as(&mut self, input: &str) {
393 let expanded_path = expand_tilde(input);
395 let full_path = if expanded_path.is_absolute() {
396 normalize_path(&expanded_path)
397 } else {
398 normalize_path(&self.working_dir.join(&expanded_path))
399 };
400
401 let current_file_path = self
403 .active_state()
404 .buffer
405 .file_path()
406 .map(|p| p.to_path_buf());
407 let is_different_file = current_file_path.as_ref() != Some(&full_path);
408
409 if is_different_file && full_path.is_file() {
410 let filename = full_path
412 .file_name()
413 .map(|n| n.to_string_lossy().to_string())
414 .unwrap_or_else(|| full_path.display().to_string());
415 self.start_prompt(
416 t!("buffer.overwrite_confirm", name = &filename).to_string(),
417 PromptType::ConfirmOverwriteFile { path: full_path },
418 );
419 return;
420 }
421
422 self.perform_save_file_as(full_path);
424 }
425
426 pub(crate) fn perform_save_file_as(&mut self, full_path: std::path::PathBuf) {
428 let before_idx = self.active_event_log().current_index();
429 let before_len = self.active_event_log().len();
430 tracing::debug!(
431 "SaveFileAs BEFORE: event_log index={}, len={}",
432 before_idx,
433 before_len
434 );
435
436 match self.active_state_mut().buffer.save_to_file(&full_path) {
437 Ok(()) => {
438 let after_save_idx = self.active_event_log().current_index();
439 let after_save_len = self.active_event_log().len();
440 tracing::debug!(
441 "SaveFileAs AFTER buffer.save_to_file: event_log index={}, len={}",
442 after_save_idx,
443 after_save_len
444 );
445
446 let metadata = BufferMetadata::with_file(full_path.clone(), &self.working_dir);
447 self.buffer_metadata.insert(self.active_buffer(), metadata);
448
449 if let Some(state) = self.buffers.get_mut(&self.active_buffer()) {
452 if state.language == "text" {
453 if let Some(filename) = full_path.file_name().and_then(|n| n.to_str()) {
454 state.set_language_from_name(filename, &self.grammar_registry);
455 }
456 }
457 }
458
459 self.active_event_log_mut().mark_saved();
460 tracing::debug!(
461 "SaveFileAs AFTER mark_saved: event_log index={}, len={}",
462 self.active_event_log().current_index(),
463 self.active_event_log().len()
464 );
465
466 if let Ok(metadata) = self.filesystem.metadata(&full_path) {
467 if let Some(mtime) = metadata.modified {
468 self.file_mod_times.insert(full_path.clone(), mtime);
469 }
470 }
471
472 self.notify_lsp_save();
473
474 self.emit_event(
475 crate::model::control_event::events::FILE_SAVED.name,
476 serde_json::json!({"path": full_path.display().to_string()}),
477 );
478
479 self.plugin_manager.run_hook(
480 "after_file_save",
481 crate::services::plugins::hooks::HookArgs::AfterFileSave {
482 buffer_id: self.active_buffer(),
483 path: full_path.clone(),
484 },
485 );
486
487 if let Some(buffer_to_close) = self.pending_close_buffer.take() {
488 if let Err(e) = self.force_close_buffer(buffer_to_close) {
489 self.set_status_message(
490 t!("file.saved_cannot_close", error = e.to_string()).to_string(),
491 );
492 } else {
493 self.set_status_message(t!("buffer.saved_and_closed").to_string());
494 }
495 } else {
496 self.set_status_message(
497 t!("file.saved_as", path = full_path.display().to_string()).to_string(),
498 );
499 }
500 }
501 Err(e) => {
502 self.pending_close_buffer = None;
503 self.set_status_message(t!("file.error_saving", error = e.to_string()).to_string());
504 }
505 }
506 }
507
508 fn handle_set_compose_width(&mut self, input: &str) {
510 let buffer_id = self.active_buffer();
511 let active_split = self.split_manager.active_split();
512 let trimmed = input.trim();
513
514 if trimmed.is_empty() {
515 if let Some(state) = self.buffers.get_mut(&buffer_id) {
516 state.compose_width = None;
517 }
518 if let Some(vs) = self.split_view_states.get_mut(&active_split) {
519 vs.compose_width = None;
520 }
521 self.set_status_message(t!("settings.compose_width_cleared").to_string());
522 } else {
523 match trimmed.parse::<u16>() {
524 Ok(val) if val > 0 => {
525 if let Some(state) = self.buffers.get_mut(&buffer_id) {
526 state.compose_width = Some(val);
527 }
528 if let Some(vs) = self.split_view_states.get_mut(&active_split) {
529 vs.compose_width = Some(val);
530 }
531 self.set_status_message(
532 t!("settings.compose_width_set", value = val).to_string(),
533 );
534 }
535 _ => {
536 self.set_status_message(
537 t!("error.invalid_compose_width", input = input).to_string(),
538 );
539 }
540 }
541 }
542 }
543
544 fn handle_set_tab_size(&mut self, input: &str) {
546 let buffer_id = self.active_buffer();
547 let trimmed = input.trim();
548
549 match trimmed.parse::<usize>() {
550 Ok(val) if val > 0 => {
551 if let Some(state) = self.buffers.get_mut(&buffer_id) {
552 state.tab_size = val;
553 }
554 self.set_status_message(t!("settings.tab_size_set", value = val).to_string());
555 }
556 Ok(_) => {
557 self.set_status_message(t!("settings.tab_size_positive").to_string());
558 }
559 Err(_) => {
560 self.set_status_message(t!("error.invalid_tab_size", input = input).to_string());
561 }
562 }
563 }
564
565 fn handle_set_line_ending(&mut self, input: &str) {
567 use crate::model::buffer::LineEnding;
568
569 let trimmed = input.trim();
571 let code = trimmed.split_whitespace().next().unwrap_or(trimmed);
572
573 let line_ending = match code.to_uppercase().as_str() {
574 "LF" => Some(LineEnding::LF),
575 "CRLF" => Some(LineEnding::CRLF),
576 "CR" => Some(LineEnding::CR),
577 _ => None,
578 };
579
580 match line_ending {
581 Some(le) => {
582 self.active_state_mut().buffer.set_line_ending(le);
583 self.set_status_message(
584 t!("settings.line_ending_set", value = le.display_name()).to_string(),
585 );
586 }
587 None => {
588 self.set_status_message(t!("error.unknown_line_ending", input = input).to_string());
589 }
590 }
591 }
592
593 fn handle_set_language(&mut self, input: &str) {
595 use crate::primitives::highlight_engine::HighlightEngine;
596 use crate::primitives::highlighter::Language;
597
598 let trimmed = input.trim();
599
600 if trimmed == "Plain Text" || trimmed.to_lowercase() == "text" {
602 let buffer_id = self.active_buffer();
603 if let Some(state) = self.buffers.get_mut(&buffer_id) {
604 state.language = "Plain Text".to_string();
605 state.highlighter = HighlightEngine::None;
606 self.set_status_message("Language set to Plain Text".to_string());
607 }
608 return;
609 }
610
611 if self.grammar_registry.find_syntax_by_name(trimmed).is_some() {
614 let ts_language = Language::from_name(trimmed);
618
619 let buffer_id = self.active_buffer();
620 if let Some(state) = self.buffers.get_mut(&buffer_id) {
621 state.language = trimmed.to_string();
622 state.highlighter =
623 HighlightEngine::for_syntax_name(trimmed, &self.grammar_registry, ts_language);
624 if let Some(lang) = ts_language {
626 state.reference_highlighter.set_language(&lang);
627 }
628 self.set_status_message(format!("Language set to {}", trimmed));
629 }
630 } else {
631 self.set_status_message(format!("Unknown language: {}", input));
632 }
633 }
634
635 fn handle_register_input<F>(&mut self, input: &str, action: F, register_type: &str)
637 where
638 F: FnOnce(&mut Self, char),
639 {
640 if let Some(c) = input.trim().chars().next() {
641 if c.is_ascii_digit() {
642 action(self, c);
643 } else {
644 self.set_status_message(
645 t!("register.must_be_digit", "type" = register_type).to_string(),
646 );
647 }
648 } else {
649 self.set_status_message(t!("register.not_specified").to_string());
650 }
651 }
652
653 fn handle_confirm_close_buffer(&mut self, input: &str, buffer_id: BufferId) -> bool {
655 let input_lower = input.trim().to_lowercase();
656 let save_key = t!("prompt.key.save").to_string().to_lowercase();
657 let discard_key = t!("prompt.key.discard").to_string().to_lowercase();
658
659 let first_char = input_lower.chars().next();
660 let save_first = save_key.chars().next();
661 let discard_first = discard_key.chars().next();
662
663 if first_char == save_first {
664 let has_path = self
666 .buffers
667 .get(&buffer_id)
668 .map(|s| s.buffer.file_path().is_some())
669 .unwrap_or(false);
670
671 if has_path {
672 let old_active = self.active_buffer();
673 self.set_active_buffer(buffer_id);
674 if let Err(e) = self.save() {
675 self.set_status_message(
676 t!("file.save_failed", error = e.to_string()).to_string(),
677 );
678 self.set_active_buffer(old_active);
679 return true; }
681 self.set_active_buffer(old_active);
682 if let Err(e) = self.force_close_buffer(buffer_id) {
683 self.set_status_message(
684 t!("file.cannot_close", error = e.to_string()).to_string(),
685 );
686 } else {
687 self.set_status_message(t!("buffer.saved_and_closed").to_string());
688 }
689 } else {
690 self.pending_close_buffer = Some(buffer_id);
691 self.start_prompt_with_initial_text(
692 t!("file.save_as_prompt").to_string(),
693 PromptType::SaveFileAs,
694 String::new(),
695 );
696 }
697 } else if first_char == discard_first {
698 if let Err(e) = self.force_close_buffer(buffer_id) {
700 self.set_status_message(t!("file.cannot_close", error = e.to_string()).to_string());
701 } else {
702 self.set_status_message(t!("buffer.changes_discarded").to_string());
703 }
704 } else {
705 self.set_status_message(t!("buffer.close_cancelled").to_string());
706 }
707 false
708 }
709
710 fn handle_stop_lsp_server(&mut self, input: &str) {
712 let language = input.trim();
713 if language.is_empty() {
714 return;
715 }
716
717 if let Some(lsp) = &mut self.lsp {
718 if lsp.shutdown_server(language) {
719 if let Some(lsp_config) = self.config.lsp.get_mut(language) {
720 lsp_config.auto_start = false;
721 if let Err(e) = self.save_config() {
722 tracing::warn!(
723 "Failed to save config after disabling LSP auto-start: {}",
724 e
725 );
726 } else {
727 let config_path = self.dir_context.config_path();
728 self.emit_event(
729 "config_changed",
730 serde_json::json!({
731 "path": config_path.to_string_lossy(),
732 }),
733 );
734 }
735 }
736 self.set_status_message(t!("lsp.server_stopped", language = language).to_string());
737 } else {
738 self.set_status_message(
739 t!("lsp.server_not_found", language = language).to_string(),
740 );
741 }
742 }
743 }
744
745 fn handle_quick_open_confirm(
747 &mut self,
748 input: &str,
749 selected_index: Option<usize>,
750 ) -> PromptResult {
751 if input.starts_with('>') {
753 let query = &input[1..];
755 return self.handle_quick_open_command(query, selected_index);
756 }
757
758 if input.starts_with('#') {
759 let query = &input[1..];
761 return self.handle_quick_open_buffer(query, selected_index);
762 }
763
764 if input.starts_with(':') {
765 let line_str = &input[1..];
767 if let Ok(line_num) = line_str.parse::<usize>() {
768 if line_num > 0 {
769 self.goto_line_col(line_num, None);
770 self.set_status_message(t!("goto.jumped", line = line_num).to_string());
771 } else {
772 self.set_status_message(t!("goto.line_must_be_positive").to_string());
773 }
774 } else {
775 self.set_status_message(t!("error.invalid_line", input = line_str).to_string());
776 }
777 return PromptResult::Done;
778 }
779
780 self.handle_quick_open_file(input, selected_index)
782 }
783
784 fn handle_quick_open_command(
786 &mut self,
787 query: &str,
788 selected_index: Option<usize>,
789 ) -> PromptResult {
790 let suggestions = {
791 let registry = self.command_registry.read().unwrap();
792 let selection_active = self.has_active_selection();
793 let active_buffer_mode = self
794 .buffer_metadata
795 .get(&self.active_buffer())
796 .and_then(|m| m.virtual_mode());
797
798 registry.filter(
799 query,
800 self.key_context,
801 &self.keybindings,
802 selection_active,
803 &self.active_custom_contexts,
804 active_buffer_mode,
805 )
806 };
807
808 if let Some(idx) = selected_index {
809 if let Some(suggestion) = suggestions.get(idx) {
810 if suggestion.disabled {
811 self.set_status_message(t!("status.command_not_available").to_string());
812 return PromptResult::Done;
813 }
814
815 let commands = self.command_registry.read().unwrap().get_all();
817 if let Some(cmd) = commands
818 .iter()
819 .find(|c| c.get_localized_name() == suggestion.text)
820 {
821 let action = cmd.action.clone();
822 let cmd_name = cmd.get_localized_name();
823 self.command_registry
824 .write()
825 .unwrap()
826 .record_usage(&cmd_name);
827 return PromptResult::ExecuteAction(action);
828 }
829 }
830 }
831
832 self.set_status_message(t!("status.no_selection").to_string());
833 PromptResult::Done
834 }
835
836 fn handle_quick_open_buffer(
838 &mut self,
839 query: &str,
840 selected_index: Option<usize>,
841 ) -> PromptResult {
842 let suggestions = self.get_buffer_suggestions(query);
844
845 if let Some(idx) = selected_index {
846 if let Some(suggestion) = suggestions.get(idx) {
847 if let Some(value) = &suggestion.value {
848 if let Ok(buffer_id) = value.parse::<usize>() {
849 let buffer_id = crate::model::event::BufferId(buffer_id);
850 if self.buffers.contains_key(&buffer_id) {
851 self.set_active_buffer(buffer_id);
852 if let Some(name) = self.active_state().buffer.file_path() {
853 self.set_status_message(
854 t!("buffer.switched", name = name.display().to_string())
855 .to_string(),
856 );
857 }
858 return PromptResult::Done;
859 }
860 }
861 }
862 }
863 }
864
865 self.set_status_message(t!("status.no_selection").to_string());
866 PromptResult::Done
867 }
868
869 fn handle_quick_open_file(
871 &mut self,
872 input: &str,
873 selected_index: Option<usize>,
874 ) -> PromptResult {
875 let suggestions = self.get_file_suggestions(input);
877
878 if let Some(idx) = selected_index {
879 if let Some(suggestion) = suggestions.get(idx) {
880 if let Some(path_str) = &suggestion.value {
881 let path = std::path::PathBuf::from(path_str);
882 let full_path = if path.is_absolute() {
883 path
884 } else {
885 self.working_dir.join(&path)
886 };
887
888 self.file_provider.record_access(path_str);
890
891 if let Err(e) = self.open_file(&full_path) {
892 self.set_status_message(
893 t!("file.error_opening", error = e.to_string()).to_string(),
894 );
895 } else {
896 self.set_status_message(
897 t!("buffer.opened", name = full_path.display().to_string()).to_string(),
898 );
899 }
900 return PromptResult::Done;
901 }
902 }
903 }
904
905 self.set_status_message(t!("status.no_selection").to_string());
906 PromptResult::Done
907 }
908}