Skip to main content

vtcode_tui/core_tui/session/
palette.rs

1/// Palette management operations for Session
2///
3/// This module handles file palette interactions including:
4/// - Loading and closing palettes
5/// - Checking and handling triggers
6/// - Key event handling for palette navigation
7/// - Reference insertion
8use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
9use std::path::PathBuf;
10
11use super::{
12    Session,
13    file_palette::{FilePalette, extract_file_reference},
14};
15use crate::ui::tui::session::slash;
16
17impl Session {
18    /// Load the file palette with files from the workspace
19    pub(super) fn load_file_palette(&mut self, files: Vec<String>, workspace: PathBuf) {
20        let mut palette = FilePalette::new(workspace);
21        palette.load_files(files);
22        self.file_palette = Some(palette);
23        self.file_palette_active = false;
24        self.check_file_reference_trigger();
25    }
26
27    /// Check if the current input should trigger the file palette
28    pub fn check_file_reference_trigger(&mut self) {
29        if let Some(palette) = self.file_palette.as_mut() {
30            if let Some((_start, _end, query)) =
31                extract_file_reference(self.input_manager.content(), self.input_manager.cursor())
32            {
33                palette.set_filter(query);
34                if !self.file_palette_active {
35                    self.ensure_inline_lists_visible_for_trigger();
36                    self.file_palette_active = true;
37                    self.needs_full_clear = true;
38                    self.mark_dirty();
39                }
40            } else if self.file_palette_active {
41                self.close_file_palette();
42            }
43        }
44    }
45
46    /// Close the file palette and clean up resources
47    pub(super) fn close_file_palette(&mut self) {
48        self.file_palette_active = false;
49        self.needs_full_clear = true;
50
51        // Clean up resources when closing to free memory
52        if let Some(palette) = self.file_palette.as_mut() {
53            palette.set_filter(String::new());
54        }
55    }
56
57    /// Handle key events for the file palette
58    ///
59    /// Returns true if the key was handled by the palette
60    pub(super) fn handle_file_palette_key(&mut self, key: &KeyEvent) -> bool {
61        if !self.file_palette_active {
62            return false;
63        }
64
65        let Some(palette) = self.file_palette.as_mut() else {
66            return false;
67        };
68
69        match key.code {
70            KeyCode::Up => {
71                palette.move_selection_up();
72                self.mark_dirty();
73                true
74            }
75            KeyCode::Down => {
76                palette.move_selection_down();
77                self.mark_dirty();
78                true
79            }
80            KeyCode::Tab => {
81                palette.select_best_match();
82                self.mark_dirty();
83                true
84            }
85            KeyCode::Enter => {
86                let selected_path = palette.get_selected().map(|e| e.relative_path.clone());
87                if let Some(path) = selected_path {
88                    self.insert_file_reference(&path);
89                    self.close_file_palette();
90                    self.mark_dirty();
91                    true // Selection made: consume event
92                } else {
93                    // No selection: close palette and fall through to normal submit
94                    self.close_file_palette();
95                    self.mark_dirty();
96                    false // Let normal Enter handling proceed
97                }
98            }
99            KeyCode::Esc => {
100                self.close_file_palette();
101                self.mark_dirty();
102                true
103            }
104            KeyCode::Char('n') if key.modifiers.contains(KeyModifiers::CONTROL) => {
105                palette.move_selection_down();
106                self.mark_dirty();
107                true
108            }
109            KeyCode::Char('p') if key.modifiers.contains(KeyModifiers::CONTROL) => {
110                palette.move_selection_up();
111                self.mark_dirty();
112                true
113            }
114            _ => false,
115        }
116    }
117
118    /// Insert a file reference into the input at the current position
119    pub(super) fn insert_file_reference(&mut self, file_path: &str) {
120        if let Some((start, end, _)) =
121            extract_file_reference(self.input_manager.content(), self.input_manager.cursor())
122        {
123            let before = &self.input_manager.content()[..start];
124            let after = &self.input_manager.content()[end..];
125            let reference_alias = format!("@{}", file_path);
126            let new_content = format!("{}{} {}", before, reference_alias, after);
127            let new_cursor = start + reference_alias.len() + 1;
128
129            self.input_manager.set_content(new_content);
130            self.input_manager.set_cursor(new_cursor);
131            slash::update_slash_suggestions(self);
132        }
133    }
134}