1use std::path::PathBuf;
8use std::sync::{Arc, RwLock};
9
10use rust_i18n::t;
11
12use crate::input::command_registry::CommandRegistry;
13use crate::input::commands::Suggestion;
14use crate::input::keybindings::KeyContext;
15use crate::input::quick_open::{BufferInfo, QuickOpenContext};
16use crate::services::async_bridge::AsyncMessage;
17use crate::services::plugins::PluginManager;
18use crate::view::prompt::{Prompt, PromptType};
19
20use super::file_open;
21use super::Editor;
22
23impl Editor {
24 pub fn start_prompt(&mut self, message: String, prompt_type: PromptType) {
28 self.start_prompt_with_suggestions(message, prompt_type, Vec::new());
29 }
30
31 pub(super) fn start_search_prompt(
36 &mut self,
37 message: String,
38 prompt_type: PromptType,
39 use_selection_range: bool,
40 ) {
41 self.pending_search_range = None;
43
44 let selection_range = self.active_cursors().primary().selection_range();
45
46 let selected_text = if let Some(range) = selection_range.clone() {
47 let state = self.active_state_mut();
48 let text = state.get_text_range(range.start, range.end);
49 if !text.contains('\n') && !text.is_empty() {
50 Some(text)
51 } else {
52 None
53 }
54 } else {
55 None
56 };
57
58 if use_selection_range {
59 self.pending_search_range = selection_range;
60 }
61
62 let from_history = selected_text.is_none();
64 let default_text = selected_text.or_else(|| {
65 self.get_prompt_history("search")
66 .and_then(|h| h.last().map(|s| s.to_string()))
67 });
68
69 self.start_prompt(message, prompt_type);
71
72 if let Some(text) = default_text {
74 if let Some(ref mut prompt) = self.prompt {
75 prompt.set_input(text.clone());
76 prompt.selection_anchor = Some(0);
77 prompt.cursor_pos = text.len();
78 }
79 if from_history {
80 self.get_or_create_prompt_history("search").init_at_last();
81 }
82 self.update_search_highlights(&text);
83 }
84 }
85
86 pub fn start_prompt_with_suggestions(
88 &mut self,
89 message: String,
90 prompt_type: PromptType,
91 suggestions: Vec<Suggestion>,
92 ) {
93 self.on_editor_focus_lost();
95
96 match prompt_type {
99 PromptType::Search | PromptType::ReplaceSearch | PromptType::QueryReplaceSearch => {
100 self.clear_search_highlights();
101 }
102 _ => {}
103 }
104
105 let needs_suggestions = matches!(
107 prompt_type,
108 PromptType::OpenFile | PromptType::SwitchProject | PromptType::SaveFileAs
109 );
110
111 self.prompt = Some(Prompt::with_suggestions(message, prompt_type, suggestions));
112
113 if needs_suggestions {
115 self.update_prompt_suggestions();
116 }
117 }
118
119 pub fn start_prompt_with_initial_text(
121 &mut self,
122 message: String,
123 prompt_type: PromptType,
124 initial_text: String,
125 ) {
126 self.on_editor_focus_lost();
128
129 self.prompt = Some(Prompt::with_initial_text(
130 message,
131 prompt_type,
132 initial_text,
133 ));
134 }
135
136 pub fn start_quick_open(&mut self) {
138 self.start_quick_open_with_prefix(">");
139 }
140
141 pub fn start_quick_open_with_prefix(&mut self, prefix: &str) {
143 self.on_editor_focus_lost();
144 self.status_message = None;
145 self.goto_line_preview = None;
146
147 let mut prompt = Prompt::with_suggestions(String::new(), PromptType::QuickOpen, vec![]);
148 prompt.input = prefix.to_string();
149 prompt.cursor_pos = prefix.len();
150 self.prompt = Some(prompt);
151
152 self.update_quick_open_suggestions(prefix);
153 }
154
155 pub(super) fn build_quick_open_context(&self) -> QuickOpenContext {
157 let open_buffers = self
158 .buffers
159 .iter()
160 .filter_map(|(buffer_id, state)| {
161 let path = state.buffer.file_path()?;
162 let name = path
163 .file_name()
164 .map(|n| n.to_string_lossy().to_string())
165 .unwrap_or_else(|| format!("Buffer {}", buffer_id.0));
166 Some(BufferInfo {
167 id: buffer_id.0,
168 path: path.display().to_string(),
169 name,
170 modified: state.buffer.is_modified(),
171 })
172 })
173 .collect();
174
175 let has_lsp_config = {
176 let language = self
177 .buffers
178 .get(&self.active_buffer())
179 .map(|s| s.language.as_str());
180 language
181 .and_then(|lang| self.lsp.as_ref().and_then(|lsp| lsp.get_config(lang)))
182 .is_some()
183 };
184
185 QuickOpenContext {
186 cwd: self.working_dir.display().to_string(),
187 open_buffers,
188 active_buffer_id: self.active_buffer().0,
189 active_buffer_path: self
190 .active_state()
191 .buffer
192 .file_path()
193 .map(|p| p.display().to_string()),
194 has_selection: self.has_active_selection(),
195 key_context: self.key_context.clone(),
196 custom_contexts: self.active_custom_contexts.clone(),
197 buffer_mode: self
198 .buffer_metadata
199 .get(&self.active_buffer())
200 .and_then(|m| m.virtual_mode())
201 .map(|s| s.to_string()),
202 has_lsp_config,
203 relative_line_numbers: self.config.editor.relative_line_numbers,
204 }
205 }
206
207 pub(super) fn update_quick_open_suggestions(&mut self, input: &str) {
209 let context = self.build_quick_open_context();
210 let suggestions = if let Some((provider, query)) =
211 self.quick_open_registry.get_provider_for_input(input)
212 {
213 provider.suggestions(query, &context)
214 } else {
215 vec![]
216 };
217
218 if let Some(prompt) = &mut self.prompt {
219 prompt.suggestions = suggestions;
220 prompt.selected_suggestion = if prompt.suggestions.is_empty() {
221 None
222 } else {
223 Some(0)
224 };
225 }
226
227 let input = input.trim();
235 let target = Self::parse_quick_open_goto_line_target(input);
236 self.apply_goto_line_preview(target);
237 }
238
239 pub(super) fn parse_quick_open_goto_line_target(input: &str) -> Option<usize> {
242 let rest = input.strip_prefix(':')?;
243 match crate::input::quick_open::parse_goto_line_input(rest) {
244 Some(crate::input::quick_open::GotoLineTarget::Absolute(n)) => Some(n),
245 _ => None,
246 }
247 }
248
249 pub(super) fn apply_goto_line_preview(&mut self, target_line: Option<usize>) {
256 if let Some(line) = target_line {
257 self.save_goto_line_preview_snapshot();
258 self.goto_line_col(line, None);
259 let new_position = self.active_cursors().primary().position;
262 if let Some(snap) = self.goto_line_preview.as_mut() {
263 snap.last_jump_position = new_position;
264 }
265 } else {
266 self.restore_goto_line_preview_snapshot();
267 }
268 }
269
270 pub(super) fn save_goto_line_preview_snapshot(&mut self) {
274 if self.goto_line_preview.is_some() {
275 return;
276 }
277
278 let buffer_id = self.active_buffer();
279 let split_id = self.split_manager.active_split();
280 let (cursor_id, position, anchor, sticky_column) = {
281 let cursors = self.active_cursors();
282 let primary = cursors.primary();
283 (
284 cursors.primary_id(),
285 primary.position,
286 primary.anchor,
287 primary.sticky_column,
288 )
289 };
290 let (viewport_top_byte, viewport_top_view_line_offset, viewport_left_column) = {
291 let vp = self.active_viewport();
292 (vp.top_byte, vp.top_view_line_offset, vp.left_column)
293 };
294
295 self.goto_line_preview = Some(super::GotoLinePreviewSnapshot {
296 buffer_id,
297 split_id,
298 cursor_id,
299 position,
300 anchor,
301 sticky_column,
302 viewport_top_byte,
303 viewport_top_view_line_offset,
304 viewport_left_column,
305 last_jump_position: position,
309 });
310 }
311
312 pub(super) fn restore_goto_line_preview_snapshot(&mut self) {
321 let Some(snap) = self.goto_line_preview.take() else {
322 return;
323 };
324
325 if self.active_buffer() != snap.buffer_id
328 || self.split_manager.active_split() != snap.split_id
329 {
330 return;
331 }
332
333 let cursors = self.active_cursors();
334 let current = cursors.primary();
335
336 if current.position != snap.last_jump_position {
340 return;
341 }
342 let event = crate::model::event::Event::MoveCursor {
343 cursor_id: snap.cursor_id,
344 old_position: current.position,
345 new_position: snap.position,
346 old_anchor: current.anchor,
347 new_anchor: snap.anchor,
348 old_sticky_column: current.sticky_column,
349 new_sticky_column: snap.sticky_column,
350 };
351
352 let state = self.buffers.get_mut(&snap.buffer_id).unwrap();
353 let view_state = self.split_view_states.get_mut(&snap.split_id).unwrap();
354 state.apply(&mut view_state.cursors, &event);
355
356 let vp = &mut view_state.viewport;
357 vp.top_byte = snap.viewport_top_byte;
358 vp.top_view_line_offset = snap.viewport_top_view_line_offset;
359 vp.left_column = snap.viewport_left_column;
360 vp.set_skip_ensure_visible();
363 }
364
365 pub(super) fn cancel_search_prompt_if_active(&mut self) {
368 if let Some(ref prompt) = self.prompt {
369 if matches!(
370 prompt.prompt_type,
371 PromptType::Search
372 | PromptType::ReplaceSearch
373 | PromptType::Replace { .. }
374 | PromptType::QueryReplaceSearch
375 | PromptType::QueryReplace { .. }
376 | PromptType::QueryReplaceConfirm
377 ) {
378 self.prompt = None;
379 self.interactive_replace_state = None;
381 let ns = self.search_namespace.clone();
383 let state = self.active_state_mut();
384 state.overlays.clear_namespace(&ns, &mut state.marker_list);
385 }
386 }
387 }
388
389 pub(super) fn prefill_open_file_prompt(&mut self) {
391 if let Some(prompt) = self.prompt.as_mut() {
395 if prompt.prompt_type == PromptType::OpenFile {
396 prompt.input.clear();
397 prompt.cursor_pos = 0;
398 prompt.selection_anchor = None;
399 }
400 }
401 }
402
403 pub(super) fn init_file_open_state(&mut self) {
409 let buffer_id = self.active_buffer();
411
412 let initial_dir = if self.is_terminal_buffer(buffer_id) {
415 self.get_terminal_id(buffer_id)
416 .and_then(|tid| self.terminal_manager.get(tid))
417 .and_then(|handle| handle.cwd())
418 .unwrap_or_else(|| self.working_dir.clone())
419 } else {
420 self.active_state()
421 .buffer
422 .file_path()
423 .and_then(|path| path.parent())
424 .map(|p| p.to_path_buf())
425 .unwrap_or_else(|| self.working_dir.clone())
426 };
427
428 let show_hidden = self.config.file_browser.show_hidden;
430 self.file_open_state = Some(file_open::FileOpenState::new(
431 initial_dir.clone(),
432 show_hidden,
433 self.authority.filesystem.clone(),
434 ));
435
436 self.load_file_open_directory(initial_dir);
438 self.load_file_open_shortcuts_async();
439 }
440
441 pub(super) fn init_folder_open_state(&mut self) {
446 let initial_dir = self.working_dir.clone();
448
449 let show_hidden = self.config.file_browser.show_hidden;
451 self.file_open_state = Some(file_open::FileOpenState::new(
452 initial_dir.clone(),
453 show_hidden,
454 self.authority.filesystem.clone(),
455 ));
456
457 self.load_file_open_directory(initial_dir);
459 self.load_file_open_shortcuts_async();
460 }
461
462 pub fn change_working_dir(&mut self, new_path: PathBuf) {
472 let new_path = new_path.canonicalize().unwrap_or(new_path);
474
475 self.request_restart(new_path);
478 }
479
480 pub(super) fn load_file_open_directory(&mut self, path: PathBuf) {
482 if let Some(state) = &mut self.file_open_state {
484 state.current_dir = path.clone();
485 state.loading = true;
486 state.error = None;
487 state.update_shortcuts();
488 }
489
490 if let Some(ref runtime) = self.tokio_runtime {
492 let fs_manager = self.fs_manager.clone();
493 let sender = self.async_bridge.as_ref().map(|b| b.sender());
494
495 runtime.spawn(async move {
496 let result = fs_manager.list_dir_with_metadata(path).await;
497 if let Some(sender) = sender {
498 #[allow(clippy::let_underscore_must_use)]
500 let _ = sender.send(AsyncMessage::FileOpenDirectoryLoaded(result));
501 }
502 });
503 } else {
504 if let Some(state) = &mut self.file_open_state {
506 state.set_error("Async runtime not available".to_string());
507 }
508 }
509 }
510
511 pub(super) fn handle_file_open_directory_loaded(
513 &mut self,
514 result: std::io::Result<Vec<crate::services::fs::DirEntry>>,
515 ) {
516 match result {
517 Ok(entries) => {
518 if let Some(state) = &mut self.file_open_state {
519 state.set_entries(entries);
520 }
521 let filter = self
523 .prompt
524 .as_ref()
525 .map(|p| p.input.clone())
526 .unwrap_or_default();
527 if !filter.is_empty() {
528 if let Some(state) = &mut self.file_open_state {
529 state.apply_filter(&filter);
530 }
531 }
532 }
533 Err(e) => {
534 if let Some(state) = &mut self.file_open_state {
535 state.set_error(e.to_string());
536 }
537 }
538 }
539 }
540
541 pub(super) fn load_file_open_shortcuts_async(&mut self) {
545 if let Some(ref runtime) = self.tokio_runtime {
546 let filesystem = self.authority.filesystem.clone();
547 let sender = self.async_bridge.as_ref().map(|b| b.sender());
548
549 runtime.spawn(async move {
550 let shortcuts = tokio::task::spawn_blocking(move || {
552 file_open::FileOpenState::build_shortcuts_async(&*filesystem)
553 })
554 .await
555 .unwrap_or_default();
556
557 if let Some(sender) = sender {
558 #[allow(clippy::let_underscore_must_use)]
560 let _ = sender.send(AsyncMessage::FileOpenShortcutsLoaded(shortcuts));
561 }
562 });
563 }
564 }
565
566 pub(super) fn handle_file_open_shortcuts_loaded(
568 &mut self,
569 shortcuts: Vec<file_open::NavigationShortcut>,
570 ) {
571 if let Some(state) = &mut self.file_open_state {
572 state.merge_async_shortcuts(shortcuts);
573 }
574 }
575
576 pub fn cancel_prompt(&mut self) {
578 let theme_to_restore = if let Some(ref prompt) = self.prompt {
580 if let PromptType::SelectTheme { original_theme } = &prompt.prompt_type {
581 Some(original_theme.clone())
582 } else {
583 None
584 }
585 } else {
586 None
587 };
588
589 if let Some(ref prompt) = self.prompt {
591 if let Some(key) = Self::prompt_type_to_history_key(&prompt.prompt_type) {
593 if let Some(history) = self.prompt_histories.get_mut(&key) {
594 history.reset_navigation();
595 }
596 }
597 match &prompt.prompt_type {
598 PromptType::Search | PromptType::ReplaceSearch | PromptType::QueryReplaceSearch => {
599 self.clear_search_highlights();
600 }
601 PromptType::Plugin { custom_type } => {
602 use crate::services::plugins::hooks::HookArgs;
604 self.plugin_manager.run_hook(
605 "prompt_cancelled",
606 HookArgs::PromptCancelled {
607 prompt_type: custom_type.clone(),
608 input: prompt.input.clone(),
609 },
610 );
611 }
612 PromptType::LspRename { overlay_handle, .. } => {
613 let remove_overlay_event = crate::model::event::Event::RemoveOverlay {
615 handle: overlay_handle.clone(),
616 };
617 self.apply_event_to_active_buffer(&remove_overlay_event);
618 }
619 PromptType::OpenFile | PromptType::SwitchProject | PromptType::SaveFileAs => {
620 self.file_open_state = None;
622 self.file_browser_layout = None;
623 }
624 PromptType::AsyncPrompt => {
625 if let Some(callback_id) = self.pending_async_prompt_callback.take() {
627 self.plugin_manager
628 .resolve_callback(callback_id, "null".to_string());
629 }
630 }
631 PromptType::QuickOpen => {
632 if let Some((provider, _)) = self.quick_open_registry.get_provider_for_input("")
634 {
635 if let Some(fp) = provider
636 .as_any()
637 .downcast_ref::<crate::input::quick_open::providers::FileProvider>(
638 ) {
639 fp.cancel_loading();
640 }
641 }
642 self.restore_goto_line_preview_snapshot();
645 }
646 PromptType::GotoLine => {
647 self.restore_goto_line_preview_snapshot();
650 }
651 _ => {}
652 }
653 }
654
655 self.prompt = None;
656 self.pending_search_range = None;
657 self.status_message = Some(t!("search.cancelled").to_string());
658
659 if let Some(original_theme) = theme_to_restore {
661 self.preview_theme(&original_theme);
662 }
663 }
664
665 pub fn handle_prompt_scroll(&mut self, delta: i32) -> bool {
668 if let Some(ref mut prompt) = self.prompt {
669 if prompt.suggestions.is_empty() {
670 return false;
671 }
672
673 let current = prompt.selected_suggestion.unwrap_or(0);
674 let len = prompt.suggestions.len();
675
676 let new_selected = if delta < 0 {
679 current.saturating_sub((-delta) as usize)
681 } else {
682 (current + delta as usize).min(len.saturating_sub(1))
684 };
685
686 prompt.selected_suggestion = Some(new_selected);
687
688 if !matches!(prompt.prompt_type, PromptType::Plugin { .. }) {
690 if let Some(suggestion) = prompt.suggestions.get(new_selected) {
691 prompt.input = suggestion.get_value().to_string();
692 prompt.cursor_pos = prompt.input.len();
693 }
694 }
695
696 return true;
697 }
698 false
699 }
700
701 pub fn confirm_prompt(&mut self) -> Option<(String, PromptType, Option<usize>)> {
706 if let Some(prompt) = self.prompt.take() {
707 let selected_index = prompt.selected_suggestion;
708 let mut final_input = if prompt.sync_input_on_navigate {
710 prompt.input.clone()
713 } else if matches!(
714 prompt.prompt_type,
715 PromptType::OpenFile
716 | PromptType::SwitchProject
717 | PromptType::SaveFileAs
718 | PromptType::StopLspServer
719 | PromptType::RestartLspServer
720 | PromptType::SelectTheme { .. }
721 | PromptType::SelectLocale
722 | PromptType::SwitchToTab
723 | PromptType::SetLanguage
724 | PromptType::SetEncoding
725 | PromptType::SetLineEnding
726 | PromptType::Plugin { .. }
727 ) {
728 if let Some(selected_idx) = prompt.selected_suggestion {
730 if let Some(suggestion) = prompt.suggestions.get(selected_idx) {
731 if suggestion.disabled {
733 self.set_status_message(
734 t!(
735 "error.command_not_available",
736 command = suggestion.text.clone()
737 )
738 .to_string(),
739 );
740 return None;
741 }
742 suggestion.get_value().to_string()
744 } else {
745 prompt.input.clone()
746 }
747 } else {
748 prompt.input.clone()
749 }
750 } else {
751 prompt.input.clone()
752 };
753
754 if matches!(
756 prompt.prompt_type,
757 PromptType::StopLspServer | PromptType::RestartLspServer
758 ) {
759 let is_valid = prompt
760 .suggestions
761 .iter()
762 .any(|s| s.text == final_input || s.get_value() == final_input);
763 if !is_valid {
764 self.prompt = Some(prompt);
766 self.set_status_message(
767 t!("error.no_lsp_match", input = final_input.clone()).to_string(),
768 );
769 return None;
770 }
771 }
772
773 if matches!(prompt.prompt_type, PromptType::RemoveRuler) {
777 if prompt.input.is_empty() {
778 if let Some(selected_idx) = prompt.selected_suggestion {
780 if let Some(suggestion) = prompt.suggestions.get(selected_idx) {
781 final_input = suggestion.get_value().to_string();
782 }
783 } else {
784 self.prompt = Some(prompt);
785 return None;
786 }
787 } else {
788 let typed = prompt.input.trim().to_string();
790 let matched = prompt.suggestions.iter().find(|s| s.get_value() == typed);
791 if let Some(suggestion) = matched {
792 final_input = suggestion.get_value().to_string();
793 } else {
794 self.prompt = Some(prompt);
796 return None;
797 }
798 }
799 }
800
801 if let Some(key) = Self::prompt_type_to_history_key(&prompt.prompt_type) {
803 let history = self.get_or_create_prompt_history(&key);
804 history.push(final_input.clone());
805 history.reset_navigation();
806 }
807
808 Some((final_input, prompt.prompt_type, selected_index))
809 } else {
810 None
811 }
812 }
813
814 pub fn is_prompting(&self) -> bool {
816 self.prompt.is_some()
817 }
818
819 pub(super) fn get_or_create_prompt_history(
821 &mut self,
822 key: &str,
823 ) -> &mut crate::input::input_history::InputHistory {
824 self.prompt_histories.entry(key.to_string()).or_default()
825 }
826
827 pub(super) fn get_prompt_history(
829 &self,
830 key: &str,
831 ) -> Option<&crate::input::input_history::InputHistory> {
832 self.prompt_histories.get(key)
833 }
834
835 pub(super) fn prompt_type_to_history_key(
837 prompt_type: &crate::view::prompt::PromptType,
838 ) -> Option<String> {
839 use crate::view::prompt::PromptType;
840 match prompt_type {
841 PromptType::Search | PromptType::ReplaceSearch | PromptType::QueryReplaceSearch => {
842 Some("search".to_string())
843 }
844 PromptType::Replace { .. } | PromptType::QueryReplace { .. } => {
845 Some("replace".to_string())
846 }
847 PromptType::GotoLine => Some("goto_line".to_string()),
848 PromptType::Plugin { custom_type } => Some(format!("plugin:{}", custom_type)),
849 _ => None,
850 }
851 }
852
853 pub fn editor_mode(&self) -> Option<String> {
856 self.editor_mode.clone()
857 }
858
859 pub fn command_registry(&self) -> &Arc<RwLock<CommandRegistry>> {
861 &self.command_registry
862 }
863
864 pub fn plugin_manager(&self) -> &PluginManager {
866 &self.plugin_manager
867 }
868
869 pub fn plugin_manager_mut(&mut self) -> &mut PluginManager {
871 &mut self.plugin_manager
872 }
873
874 pub fn file_explorer_is_focused(&self) -> bool {
876 self.key_context == KeyContext::FileExplorer
877 }
878
879 pub fn prompt_input(&self) -> Option<&str> {
881 self.prompt.as_ref().map(|p| p.input.as_str())
882 }
883
884 pub fn has_active_selection(&self) -> bool {
886 self.active_cursors().primary().selection_range().is_some()
887 }
888
889 pub fn prompt_mut(&mut self) -> Option<&mut Prompt> {
891 self.prompt.as_mut()
892 }
893
894 pub fn set_status_message(&mut self, message: String) {
896 tracing::info!(target: "status", "{}", message);
897 self.plugin_status_message = None;
898 self.status_message = Some(message);
899 }
900
901 pub fn get_status_message(&self) -> Option<&String> {
903 self.plugin_status_message
904 .as_ref()
905 .or(self.status_message.as_ref())
906 }
907
908 pub fn get_plugin_errors(&self) -> &[String] {
911 &self.plugin_errors
912 }
913
914 pub fn clear_plugin_errors(&mut self) {
916 self.plugin_errors.clear();
917 }
918
919 pub fn update_prompt_suggestions(&mut self) {
921 let (prompt_type, input) = if let Some(prompt) = &self.prompt {
923 (prompt.prompt_type.clone(), prompt.input.clone())
924 } else {
925 return;
926 };
927
928 match prompt_type {
929 PromptType::QuickOpen => {
930 self.update_quick_open_suggestions(&input);
932 }
933 PromptType::Search | PromptType::ReplaceSearch | PromptType::QueryReplaceSearch => {
934 self.update_search_highlights(&input);
936 if let Some(history) = self.prompt_histories.get_mut("search") {
938 history.reset_navigation();
939 }
940 }
941 PromptType::Replace { .. } | PromptType::QueryReplace { .. } => {
942 if let Some(history) = self.prompt_histories.get_mut("replace") {
944 history.reset_navigation();
945 }
946 }
947 PromptType::GotoLine => {
948 if let Some(history) = self.prompt_histories.get_mut("goto_line") {
950 history.reset_navigation();
951 }
952 let target = match crate::input::quick_open::parse_goto_line_input(input.trim()) {
957 Some(crate::input::quick_open::GotoLineTarget::Absolute(n)) => Some(n),
958 _ => None,
959 };
960 self.apply_goto_line_preview(target);
961 }
962 PromptType::OpenFile | PromptType::SwitchProject | PromptType::SaveFileAs => {
963 self.update_file_open_filter();
965 }
966 PromptType::Plugin { custom_type } => {
967 let key = format!("plugin:{}", custom_type);
969 if let Some(history) = self.prompt_histories.get_mut(&key) {
970 history.reset_navigation();
971 }
972 use crate::services::plugins::hooks::HookArgs;
974 self.plugin_manager.run_hook(
975 "prompt_changed",
976 HookArgs::PromptChanged {
977 prompt_type: custom_type,
978 input,
979 },
980 );
981 if let Some(prompt) = &mut self.prompt {
986 prompt.filter_suggestions(false);
987 }
988 }
989 PromptType::SwitchToTab
990 | PromptType::SelectTheme { .. }
991 | PromptType::StopLspServer
992 | PromptType::RestartLspServer
993 | PromptType::SetLanguage
994 | PromptType::SetEncoding
995 | PromptType::SetLineEnding => {
996 if let Some(prompt) = &mut self.prompt {
997 prompt.filter_suggestions(false);
998 }
999 }
1000 PromptType::SelectLocale => {
1001 if let Some(prompt) = &mut self.prompt {
1003 prompt.filter_suggestions(true);
1004 }
1005 }
1006 _ => {}
1007 }
1008 }
1009}