history : reexecution (issues #27 #28) - expansion !! !N !prefix ^old^new dans le REPL + am history --rerun N avec confirmation (shell ou commande am)

This commit is contained in:
2026-08-17 08:05:58 -04:00
parent 4ef89cde72
commit 81c69db17b
6 changed files with 126 additions and 1 deletions
+52
View File
@@ -124,6 +124,40 @@ pub fn migrate(app: &App) -> Result<usize> {
Ok(count)
}
/// Expand a history reference against the most recent entries
/// (most recent first): !! (last), !N (the Nth most recent), !prefix (the
/// most recent command starting with prefix) and ^old^new (substitution in
/// the last command). Returns None when the line is not a reference.
pub fn expand(entries: &[HistoryEntry], line: &str) -> Option<String> {
let trimmed = line.trim();
if trimmed == "!!" {
return entries.first().map(|e| e.cmd.clone());
}
if let Some(rest) = trimmed.strip_prefix('!') {
if !rest.is_empty() {
if let Ok(n) = rest.parse::<usize>() {
if n >= 1 {
return entries.get(n - 1).map(|e| e.cmd.clone());
}
return None;
}
return entries
.iter()
.find(|e| e.cmd.starts_with(rest))
.map(|e| e.cmd.clone());
}
}
if let Some(body) = trimmed.strip_prefix('^') {
if let Some((old, new)) = body.split_once('^') {
if !old.is_empty() {
let last = entries.first()?;
return Some(last.cmd.replacen(old, new, 1));
}
}
}
None
}
/// History files of a directory, oldest first.
pub fn files(dir: &Path) -> Vec<PathBuf> {
let mut out: Vec<PathBuf> = std::fs::read_dir(dir)
@@ -277,4 +311,22 @@ start group:dev
Some("20260815_143926".to_string())
);
}
#[test]
fn expand_history_references() {
let entries = vec![
HistoryEntry::now("install jcode"),
HistoryEntry::now("start group:dev"),
HistoryEntry::now("list --all"),
];
assert_eq!(expand(&entries, "!!").as_deref(), Some("install jcode"));
assert_eq!(expand(&entries, "!2").as_deref(), Some("start group:dev"));
assert_eq!(expand(&entries, "!9"), None, "out of range");
assert_eq!(expand(&entries, "!li").as_deref(), Some("list --all"));
assert_eq!(
expand(&entries, "^jcode^aider").as_deref(),
Some("install aider")
);
assert_eq!(expand(&entries, "plain line"), None);
}
}