Skip to content

Commit 332edaa

Browse files
committed
refactor: add interactive review and edit flow to generate_commit
Moves terminal detection earlier to support new interactive review step where users can edit the commit message before confirmation. Restructures the final confirmation logic to handle auto-commit, clipboard, dry-run, and interactive review as distinct paths.
1 parent 8e305c1 commit 332edaa

1 file changed

Lines changed: 48 additions & 29 deletions

File tree

src/app.rs

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::io::IsTerminal;
66
use std::path::PathBuf;
77

88
use console::style;
9-
use dialoguer::Confirm;
9+
use dialoguer::{Confirm, Editor, Select};
1010
use globset::{Glob, GlobSetBuilder};
1111
use tokio::signal;
1212
use tokio::sync::mpsc;
@@ -186,10 +186,10 @@ impl App {
186186
// Finish analysis spinner before any interactive prompts
187187
progress.finish();
188188

189+
let is_interactive = std::io::stdout().is_terminal() && std::io::stdin().is_terminal();
190+
189191
// Step 3.5: Split detection
190192
if !self.cli.no_split {
191-
let is_interactive = std::io::stdout().is_terminal() && std::io::stdin().is_terminal();
192-
193193
if is_interactive && !self.cli.yes {
194194
let suggestion = CommitSplitter::analyze(&changes, &symbols);
195195

@@ -360,13 +360,45 @@ impl App {
360360
}
361361

362362
// Step 6: Select message
363-
let message = if candidates.len() == 1 {
363+
let mut message = if candidates.len() == 1 {
364364
candidates.into_iter().next().unwrap()
365365
} else {
366366
self.select_candidate(&candidates)?
367367
};
368368

369-
// Step 7: Clipboard / dry-run / confirm and commit
369+
// Step 6.5: Interactive Review / Edit
370+
if !self.cli.yes && is_interactive && !self.cli.dry_run && !self.cli.clipboard {
371+
loop {
372+
eprintln!("\n{}", style("Commit message:").bold());
373+
eprintln!("{}", style(&message).green());
374+
eprintln!();
375+
376+
let options = &["Commit", "Edit", "Cancel"];
377+
let selection = Select::new()
378+
.with_prompt("What would you like to do?")
379+
.items(options)
380+
.default(0)
381+
.interact()
382+
.map_err(|e| Error::Dialog(e.to_string()))?;
383+
384+
match selection {
385+
0 => break, // Commit
386+
1 => {
387+
if let Some(edited) = Editor::new()
388+
.edit(&message)
389+
.map_err(|e| Error::Dialog(e.to_string()))?
390+
{
391+
if !edited.trim().is_empty() {
392+
message = edited;
393+
}
394+
}
395+
}
396+
_ => return Err(Error::Cancelled),
397+
}
398+
}
399+
}
400+
401+
// Step 7: Clipboard / dry-run / commit
370402
if self.cli.clipboard {
371403
Self::copy_to_clipboard(&message)?;
372404
eprintln!("{} Copied to clipboard!", style("✓").green().bold());
@@ -379,31 +411,18 @@ impl App {
379411
return Ok(());
380412
}
381413

382-
let is_interactive = std::io::stdout().is_terminal() && std::io::stdin().is_terminal();
383-
384-
if !self.cli.yes {
385-
if !is_interactive {
386-
eprintln!("{}", style("warning:").yellow().bold());
387-
eprintln!(" Not a terminal. Use --yes to auto-confirm in scripts/hooks.");
388-
println!("{}", message);
389-
return Ok(());
390-
}
391-
392-
// For single candidate (already shown via streaming), just confirm
393-
if num_candidates == 1 {
394-
eprintln!("\n{}", style("Generated commit message:").bold());
395-
eprintln!("{}", style(&message).green());
396-
eprintln!();
397-
}
398-
399-
let confirm = Confirm::new()
400-
.with_prompt("Create commit with this message?")
401-
.default(true)
402-
.interact()?;
414+
// Auto-commit if --yes is set
415+
if self.cli.yes {
416+
git.commit(&message).await?;
417+
eprintln!("{} Committed!", style("✓").green().bold());
418+
return Ok(());
419+
}
403420

404-
if !confirm {
405-
return Err(Error::Cancelled);
406-
}
421+
if !is_interactive {
422+
eprintln!("{}", style("warning:").yellow().bold());
423+
eprintln!(" Not a terminal. Use --yes to auto-confirm in scripts/hooks.");
424+
println!("{}", message);
425+
return Ok(());
407426
}
408427

409428
git.commit(&message).await?;

0 commit comments

Comments
 (0)