External panelize: command history with Up/Down arrow navigation
Added command history to ExternalPanelizeDialog: - history: Vec<String> stores previously-run commands (deduped) - history_idx: Option<usize> tracks position during navigation - Up arrow: navigate to older entries (backward through history) - Down arrow: navigate to newer entries (forward), exit to live input - Typing any character exits history mode - Successful command runs push to history (dedup consecutive) - Render hint shows '↑↓ history' indicator Self-contained — no changes to Input widget required. 1433 tests pass, zero warnings.
This commit is contained in:
@@ -46,6 +46,10 @@ pub struct ExternalPanelizeDialog {
|
||||
last_error: Option<String>,
|
||||
/// Last stderr captured from the command (for the status line).
|
||||
last_stderr: Option<String>,
|
||||
/// Previous successfully-run commands, most recent last.
|
||||
history: Vec<String>,
|
||||
/// Current position in history when navigating (None = live input).
|
||||
history_idx: Option<usize>,
|
||||
}
|
||||
|
||||
impl ExternalPanelizeDialog {
|
||||
@@ -59,6 +63,8 @@ impl ExternalPanelizeDialog {
|
||||
command,
|
||||
last_error: None,
|
||||
last_stderr: None,
|
||||
history: Vec::new(),
|
||||
history_idx: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,13 +91,35 @@ impl ExternalPanelizeDialog {
|
||||
match key {
|
||||
Key::ESCAPE => ExternalPanelizeOutcome::Cancel,
|
||||
Key::ENTER => self.run_command(),
|
||||
// Up/Down arrow — navigate command history.
|
||||
k if k.code == 0x2191 || k.code == 0x2193 => {
|
||||
self.navigate_history(k.code == 0x2191);
|
||||
ExternalPanelizeOutcome::Running
|
||||
}
|
||||
_ => {
|
||||
let _ = self.command.handle_key(key);
|
||||
self.history_idx = None; // exit history mode on typing
|
||||
ExternalPanelizeOutcome::Running
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Navigate command history. `up` = true means go backward (older
|
||||
/// entries), false = go forward (newer).
|
||||
fn navigate_history(&mut self, up: bool) {
|
||||
if self.history.is_empty() {
|
||||
return;
|
||||
}
|
||||
let new_idx = match self.history_idx {
|
||||
Some(i) if up => i.saturating_sub(1),
|
||||
Some(i) => (i + 1).min(self.history.len() - 1),
|
||||
None if up => self.history.len() - 1,
|
||||
None => return, // at live input, down does nothing
|
||||
};
|
||||
self.history_idx = Some(new_idx);
|
||||
self.command.set_text(self.history[new_idx].clone());
|
||||
}
|
||||
|
||||
/// Run the current command (if non-empty) and translate the result
|
||||
/// into an [`ExternalPanelizeOutcome`].
|
||||
fn run_command(&mut self) -> ExternalPanelizeOutcome {
|
||||
@@ -113,6 +141,10 @@ impl ExternalPanelizeDialog {
|
||||
self.last_error = Some("command produced no paths".to_string());
|
||||
ExternalPanelizeOutcome::Empty
|
||||
} else {
|
||||
// Push to history on success, dedup consecutive entries.
|
||||
if self.history.last() != Some(&cmd) {
|
||||
self.history.push(cmd);
|
||||
}
|
||||
self.last_error = None;
|
||||
ExternalPanelizeOutcome::Apply(paths)
|
||||
}
|
||||
@@ -143,6 +175,11 @@ impl ExternalPanelizeDialog {
|
||||
.split(inner);
|
||||
|
||||
let hint = Line::from(vec![
|
||||
Span::styled("↑↓", Style::default().fg(theme.info)),
|
||||
Span::styled(
|
||||
" history ",
|
||||
Style::default().fg(theme.hidden),
|
||||
),
|
||||
Span::styled("Enter", Style::default().fg(theme.warning)),
|
||||
Span::styled(
|
||||
format!(" {} ", crate::locale::t("dialog_action_select")),
|
||||
|
||||
Reference in New Issue
Block a user