diff --git a/git-branchless-record/tests/test_record.rs b/git-branchless-record/tests/test_record.rs index 22a8c11b5..520402206 100644 --- a/git-branchless-record/tests/test_record.rs +++ b/git-branchless-record/tests/test_record.rs @@ -63,6 +63,7 @@ fn test_record_unstaged_changes_interactive() -> eyre::Result<()> { PtyAction::Write("f"), // expand files PtyAction::WaitUntilContains("contents1"), PtyAction::Write("q"), + PtyAction::Write(" "), // confirm quit dialog ], )?; } diff --git a/scm-record/src/ui.rs b/scm-record/src/ui.rs index 7503f8606..ea69f6bd9 100644 --- a/scm-record/src/ui.rs +++ b/scm-record/src/ui.rs @@ -999,9 +999,11 @@ impl<'state, 'input> Recorder<'state, 'input> { // Render quit dialog if the user made changes. (None, Event::QuitCancel | Event::QuitInterrupt) => { + let num_commit_messages = self.num_user_commit_messages()?; let num_changed_files = self.num_user_file_changes()?; - if num_changed_files > 0 { + if num_commit_messages > 0 || num_changed_files > 0 { StateUpdate::SetQuitDialog(Some(QuitDialog { + num_commit_messages, num_changed_files, focused_button: QuitDialogButtonId::Quit, })) @@ -1030,6 +1032,7 @@ impl<'state, 'input> Recorder<'state, 'input> { // Press the appropriate dialog button. (Some(quit_dialog), Event::ToggleItem | Event::ToggleItemAndAdvance) => { let QuitDialog { + num_commit_messages: _, num_changed_files: _, focused_button, } = quit_dialog; @@ -1144,6 +1147,24 @@ impl<'state, 'input> Recorder<'state, 'input> { } } + fn num_user_commit_messages(&self) -> Result { + let RecordState { + files: _, + commits, + is_read_only: _, + } = &self.state; + Ok(commits + .iter() + .map(|commit| { + let Commit { message } = commit; + match message { + Some(message) if !message.is_empty() => 1, + _ => 0, + } + }) + .sum()) + } + fn num_user_file_changes(&self) -> Result { let RecordState { files, @@ -3033,6 +3054,7 @@ impl Component for SectionLineView<'_> { #[derive(Clone, Debug, PartialEq, Eq)] struct QuitDialog { + num_commit_messages: usize, num_changed_files: usize, focused_button: QuitDialogButtonId, } @@ -3046,18 +3068,42 @@ impl Component for QuitDialog { fn draw(&self, viewport: &mut Viewport, _x: isize, _y: isize) { let Self { + num_commit_messages, num_changed_files, focused_button, } = self; let title = "Quit"; - let body = format!( - "You have changes to {num_changed_files} {}. Are you sure you want to quit?", - if *num_changed_files == 1 { - "file" - } else { - "files" + let alert_items = { + let mut result = Vec::new(); + if *num_commit_messages > 0 { + result.push(format!( + "{num_commit_messages} {}", + if *num_commit_messages == 1 { + "message" + } else { + "messages" + } + )); } - ); + if *num_changed_files > 0 { + result.push(format!( + "{num_changed_files} {}", + if *num_changed_files == 1 { + "file" + } else { + "files" + } + )); + } + result + }; + let alert = if alert_items.is_empty() { + // Shouldn't happen. + "".to_string() + } else { + format!("You have changes to {}. ", alert_items.join(" and ")) + }; + let body = format!("{alert}Are you sure you want to quit?",); let quit_button = Button { id: ComponentId::QuitDialogButton(QuitDialogButtonId::Quit), diff --git a/scm-record/tests/test_scm_record.rs b/scm-record/tests/test_scm_record.rs index f7fa31ed0..b8e4ba122 100644 --- a/scm-record/tests/test_scm_record.rs +++ b/scm-record/tests/test_scm_record.rs @@ -2548,3 +2548,89 @@ fn test_commit_message_view() -> eyre::Result<()> { Ok(()) } + +#[test] +fn test_quit_dialog_when_commit_message_provided() -> eyre::Result<()> { + let mut state = example_contents(); + state.commits = vec![Commit { + message: Some("hello".to_string()), + }]; + + let changed_message_and_files = TestingScreenshot::default(); + let changed_message_only = TestingScreenshot::default(); + let mut input = TestingInput { + width: 80, + height: 24, + events: Box::new( + [ + Event::QuitInterrupt, + changed_message_and_files.event(), + Event::QuitCancel, + Event::ToggleAllUniform, // toggle all + Event::ToggleAllUniform, // toggle none + Event::QuitInterrupt, + changed_message_only.event(), + Event::QuitInterrupt, + ] + .into_iter(), + ), + commit_messages: [].into_iter().collect(), + }; + let recorder = Recorder::new(state, &mut input); + assert_matches!(recorder.run(), Err(RecordError::Cancelled)); + + insta::assert_display_snapshot!(changed_message_and_files, @r###" + "[File] [Edit] [Select] [View] " + " " + "[Edit message] • hello " + " " + "(~) foo/bar (+)" + "[×] baz [+]" + " " + " " + " " + " ┌Quit─────────────────────────────────────────────────────────────────────┐ " + " │You have changes to 1 message and 2 files. Are you sure you want to quit?│ " + " │ │ " + " └─────────────────────────────────────────────────────────[Go Back]─(Quit)┘ " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "###); + insta::assert_display_snapshot!(changed_message_only, @r###" + "[File] [Edit] [Select] [View] " + " " + "[Edit message] • hello " + " " + "( ) foo/bar (+)" + "[ ] baz [+]" + " " + " " + " " + " ┌Quit─────────────────────────────────────────────────────────┐ " + " │You have changes to 1 message. Are you sure you want to quit?│ " + " │ │ " + " └─────────────────────────────────────────────[Go Back]─(Quit)┘ " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "###); + + Ok(()) +}