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.file_palette_active = true;
36                    self.needs_full_clear = true;
37                    self.mark_dirty();
38                }
39            } else if self.file_palette_active {
40                self.close_file_palette();
41            }
42        }
43    }
44
45    /// Close the file palette and clean up resources
46    pub(super) fn close_file_palette(&mut self) {
47        self.file_palette_active = false;
48        self.needs_full_clear = true;
49
50        // Clean up resources when closing to free memory
51        if let Some(palette) = self.file_palette.as_mut() {
52            palette.set_filter(String::new());
53        }
54    }
55
56    /// Handle key events for the file palette
57    ///
58    /// Returns true if the key was handled by the palette
59    pub(super) fn handle_file_palette_key(&mut self, key: &KeyEvent) -> bool {
60        if !self.file_palette_active {
61            return false;
62        }
63
64        let Some(palette) = self.file_palette.as_mut() else {
65            return false;
66        };
67
68        match key.code {
69            KeyCode::Up => {
70                palette.move_selection_up();
71                self.mark_dirty();
72                true
73            }
74            KeyCode::Down => {
75                palette.move_selection_down();
76                self.mark_dirty();
77                true
78            }
79            KeyCode::Tab => {
80                palette.select_best_match();
81                self.mark_dirty();
82                true
83            }
84            KeyCode::Enter => {
85                let selected_path = palette.get_selected().map(|e| e.relative_path.clone());
86                if let Some(path) = selected_path {
87                    self.insert_file_reference(&path);
88                    self.close_file_palette();
89                    self.mark_dirty();
90                    true // Selection made: consume event
91                } else {
92                    // No selection: close palette and fall through to normal submit
93                    self.close_file_palette();
94                    self.mark_dirty();
95                    false // Let normal Enter handling proceed
96                }
97            }
98            KeyCode::Esc => {
99                self.close_file_palette();
100                self.mark_dirty();
101                true
102            }
103            KeyCode::Char('n') if key.modifiers.contains(KeyModifiers::CONTROL) => {
104                palette.move_selection_down();
105                self.mark_dirty();
106                true
107            }
108            KeyCode::Char('p') if key.modifiers.contains(KeyModifiers::CONTROL) => {
109                palette.move_selection_up();
110                self.mark_dirty();
111                true
112            }
113            _ => false,
114        }
115    }
116
117    /// Insert a file reference into the input at the current position
118    pub(super) fn insert_file_reference(&mut self, file_path: &str) {
119        if let Some((start, end, _)) =
120            extract_file_reference(self.input_manager.content(), self.input_manager.cursor())
121        {
122            let before = &self.input_manager.content()[..start];
123            let after = &self.input_manager.content()[end..];
124            let reference_alias = format!("@{}", file_path);
125            let new_content = format!("{}{} {}", before, reference_alias, after);
126            let new_cursor = start + reference_alias.len() + 1;
127
128            self.input_manager.set_content(new_content);
129            self.input_manager.set_cursor(new_cursor);
130            slash::update_slash_suggestions(self);
131        }
132    }
133}