Add command suggestions and enhancements to Command mode
- Introduce `command_suggestions` feature for autocompletion in Command mode. - Implement `render_command_suggestions` to display filtered suggestions in a popup. - Enable navigation through suggestions using Up/Down keys and Tab for completion. - Add dynamic filtering of suggestions based on input buffer. - Improve input handling, ensuring suggestion state resets appropriately when exiting Command mode.
This commit is contained in:
@@ -51,6 +51,8 @@ pub struct ChatApp {
|
||||
pending_key: Option<char>, // For multi-key sequences like gg, dd
|
||||
clipboard: String, // Vim-style clipboard for yank/paste
|
||||
command_buffer: String, // Buffer for command mode input
|
||||
command_suggestions: Vec<String>, // Filtered command suggestions based on current input
|
||||
selected_suggestion: usize, // Index of selected suggestion
|
||||
visual_start: Option<(usize, usize)>, // Visual mode selection start (row, col) for Input panel
|
||||
visual_end: Option<(usize, usize)>, // Visual mode selection end (row, col) for scrollable panels
|
||||
focused_panel: FocusedPanel, // Currently focused panel for scrolling
|
||||
@@ -100,6 +102,8 @@ impl ChatApp {
|
||||
pending_key: None,
|
||||
clipboard: String::new(),
|
||||
command_buffer: String::new(),
|
||||
command_suggestions: Vec::new(),
|
||||
selected_suggestion: 0,
|
||||
visual_start: None,
|
||||
visual_end: None,
|
||||
focused_panel: FocusedPanel::Input,
|
||||
@@ -206,6 +210,76 @@ impl ChatApp {
|
||||
&self.command_buffer
|
||||
}
|
||||
|
||||
pub fn command_suggestions(&self) -> &[String] {
|
||||
&self.command_suggestions
|
||||
}
|
||||
|
||||
pub fn selected_suggestion(&self) -> usize {
|
||||
self.selected_suggestion
|
||||
}
|
||||
|
||||
/// Returns all available commands with their aliases
|
||||
fn get_all_commands() -> Vec<(&'static str, &'static str)> {
|
||||
vec![
|
||||
("quit", "Exit the application"),
|
||||
("q", "Alias for quit"),
|
||||
("clear", "Clear the conversation"),
|
||||
("c", "Alias for clear"),
|
||||
("w", "Alias for write"),
|
||||
("save", "Alias for write"),
|
||||
("load", "Load a saved conversation"),
|
||||
("open", "Alias for load"),
|
||||
("o", "Alias for load"),
|
||||
("sessions", "List saved sessions"),
|
||||
("ls", "Alias for sessions"),
|
||||
("help", "Show help documentation"),
|
||||
("h", "Alias for help"),
|
||||
("model", "Select a model"),
|
||||
("m", "Alias for model"),
|
||||
("new", "Start a new conversation"),
|
||||
("n", "Alias for new"),
|
||||
]
|
||||
}
|
||||
|
||||
/// Update command suggestions based on current input
|
||||
fn update_command_suggestions(&mut self) {
|
||||
let input = self.command_buffer.trim();
|
||||
|
||||
if input.is_empty() {
|
||||
// Show all commands when input is empty
|
||||
self.command_suggestions = Self::get_all_commands()
|
||||
.iter()
|
||||
.map(|(cmd, _)| cmd.to_string())
|
||||
.collect();
|
||||
} else {
|
||||
// Filter commands that start with the input
|
||||
self.command_suggestions = Self::get_all_commands()
|
||||
.iter()
|
||||
.filter_map(|(cmd, _)| {
|
||||
if cmd.starts_with(input) {
|
||||
Some(cmd.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
// Reset selection if out of bounds
|
||||
if self.selected_suggestion >= self.command_suggestions.len() {
|
||||
self.selected_suggestion = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Complete the current command with the selected suggestion
|
||||
fn complete_command(&mut self) {
|
||||
if let Some(suggestion) = self.command_suggestions.get(self.selected_suggestion) {
|
||||
self.command_buffer = suggestion.clone();
|
||||
self.update_command_suggestions();
|
||||
self.status = format!(":{}", self.command_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focused_panel(&self) -> FocusedPanel {
|
||||
self.focused_panel
|
||||
}
|
||||
@@ -416,6 +490,8 @@ impl ChatApp {
|
||||
(KeyCode::Char(':'), KeyModifiers::NONE) => {
|
||||
self.mode = InputMode::Command;
|
||||
self.command_buffer.clear();
|
||||
self.selected_suggestion = 0;
|
||||
self.update_command_suggestions();
|
||||
self.status = ":".to_string();
|
||||
}
|
||||
// Enter editing mode
|
||||
@@ -1000,8 +1076,28 @@ impl ChatApp {
|
||||
(KeyCode::Esc, _) => {
|
||||
self.mode = InputMode::Normal;
|
||||
self.command_buffer.clear();
|
||||
self.command_suggestions.clear();
|
||||
self.reset_status();
|
||||
}
|
||||
(KeyCode::Tab, _) => {
|
||||
// Tab completion
|
||||
self.complete_command();
|
||||
}
|
||||
(KeyCode::Up, _) | (KeyCode::Char('k'), KeyModifiers::CONTROL) => {
|
||||
// Navigate up in suggestions
|
||||
if !self.command_suggestions.is_empty() {
|
||||
self.selected_suggestion = self
|
||||
.selected_suggestion
|
||||
.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
(KeyCode::Down, _) | (KeyCode::Char('j'), KeyModifiers::CONTROL) => {
|
||||
// Navigate down in suggestions
|
||||
if !self.command_suggestions.is_empty() {
|
||||
self.selected_suggestion = (self.selected_suggestion + 1)
|
||||
.min(self.command_suggestions.len().saturating_sub(1));
|
||||
}
|
||||
}
|
||||
(KeyCode::Enter, _) => {
|
||||
// Execute command
|
||||
let cmd = self.command_buffer.trim();
|
||||
@@ -1055,6 +1151,7 @@ impl ChatApp {
|
||||
self.selected_session_index = 0;
|
||||
self.mode = InputMode::SessionBrowser;
|
||||
self.command_buffer.clear();
|
||||
self.command_suggestions.clear();
|
||||
return Ok(AppState::Running);
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -1070,6 +1167,7 @@ impl ChatApp {
|
||||
self.selected_session_index = 0;
|
||||
self.mode = InputMode::SessionBrowser;
|
||||
self.command_buffer.clear();
|
||||
self.command_suggestions.clear();
|
||||
return Ok(AppState::Running);
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -1080,12 +1178,14 @@ impl ChatApp {
|
||||
"h" | "help" => {
|
||||
self.mode = InputMode::Help;
|
||||
self.command_buffer.clear();
|
||||
self.command_suggestions.clear();
|
||||
return Ok(AppState::Running);
|
||||
}
|
||||
"m" | "model" => {
|
||||
self.refresh_models().await?;
|
||||
self.mode = InputMode::ProviderSelection;
|
||||
self.command_buffer.clear();
|
||||
self.command_suggestions.clear();
|
||||
return Ok(AppState::Running);
|
||||
}
|
||||
"n" | "new" => {
|
||||
@@ -1097,15 +1197,18 @@ impl ChatApp {
|
||||
}
|
||||
}
|
||||
self.command_buffer.clear();
|
||||
self.command_suggestions.clear();
|
||||
self.mode = InputMode::Normal;
|
||||
}
|
||||
(KeyCode::Char(c), KeyModifiers::NONE)
|
||||
| (KeyCode::Char(c), KeyModifiers::SHIFT) => {
|
||||
self.command_buffer.push(c);
|
||||
self.update_command_suggestions();
|
||||
self.status = format!(":{}", self.command_buffer);
|
||||
}
|
||||
(KeyCode::Backspace, _) => {
|
||||
self.command_buffer.pop();
|
||||
self.update_command_suggestions();
|
||||
self.status = format!(":{}", self.command_buffer);
|
||||
}
|
||||
_ => {}
|
||||
|
||||
Reference in New Issue
Block a user