diff options
author | Tim Oram <dev@mitmaro.ca> | 2021-03-29 20:55:47 -0230 |
---|---|---|
committer | Tim Oram <dev@mitmaro.ca> | 2021-03-29 21:09:45 -0230 |
commit | 59c05c40a364b5f9c589190b1f3741cfc361f39a (patch) | |
tree | 890c76eee229e8787526c523558916ea97a2cec7 | |
parent | e4207e2c139a0ed135973f525b42adad1f9aacb5 (diff) |
Add confirm component
Add a confirm component and migrate the confirm and abort rebase modules
to use it.
-rw-r--r-- | src/components/confirm/mod.rs | 44 | ||||
-rw-r--r-- | src/components/confirm/tests.rs | 46 | ||||
-rw-r--r-- | src/components/mod.rs | 2 | ||||
-rw-r--r-- | src/confirm_abort/mod.rs | 73 | ||||
-rw-r--r-- | src/confirm_rebase/mod.rs | 73 | ||||
-rw-r--r-- | src/process/modules.rs | 4 | ||||
-rw-r--r-- | src/view/mod.rs | 25 | ||||
-rw-r--r-- | src/view/testutil.rs | 6 | ||||
-rw-r--r-- | src/view/view_data.rs | 25 |
9 files changed, 165 insertions, 133 deletions
diff --git a/src/components/confirm/mod.rs b/src/components/confirm/mod.rs new file mode 100644 index 0000000..68f268b --- /dev/null +++ b/src/components/confirm/mod.rs @@ -0,0 +1,44 @@ +#[cfg(test)] +mod tests; + +use crate::{ + input::Input, + process::util::handle_view_data_scroll, + view::{view_data::ViewData, view_line::ViewLine}, +}; + +pub struct Confirm { + view_data: ViewData, +} + +impl Confirm { + pub fn new(prompt: &str, confirm_yes: &[String], confirm_no: &[String]) -> Self { + let mut view_data = ViewData::new(); + view_data.set_show_title(true); + view_data.push_line(ViewLine::from(format!( + "{} ({}/{})? ", + prompt, + confirm_yes.join(","), + confirm_no.join(",") + ))); + Self { view_data } + } + + pub fn get_view_data(&mut self, view_width: usize, view_height: usize) -> &ViewData { + self.view_data.set_view_size(view_width, view_height); + &self.view_data + } + + pub fn handle_input(&mut self, input: Input) -> Option<bool> { + if handle_view_data_scroll(input, &mut self.view_data).is_none() { + match input { + Input::Yes => Some(true), + Input::No => Some(false), + _ => None, + } + } + else { + None + } + } +} diff --git a/src/components/confirm/tests.rs b/src/components/confirm/tests.rs new file mode 100644 index 0000000..f4c4dba --- /dev/null +++ b/src/components/confirm/tests.rs @@ -0,0 +1,46 @@ +use rstest::rstest; + +use super::*; +use crate::assert_rendered_output; + +#[test] +fn render() { + let mut module = Confirm::new("Prompt message", &[String::from("y"), String::from("Z")], &[ + String::from("n"), + String::from("X"), + ]); + assert_rendered_output!( + module.get_view_data(100, 100), + "{TITLE}", + "{BODY}", + "{Normal}Prompt message (y,Z/n,X)? " + ); +} + +#[test] +fn handle_input_yes() { + let mut module = Confirm::new("Prompt message", &[], &[]); + assert!(module.handle_input(Input::Yes).unwrap()); +} + +#[test] +fn handle_input_no() { + let mut module = Confirm::new("Prompt message", &[], &[]); + assert!(!module.handle_input(Input::No).unwrap()); +} + +#[rstest( + input, + case::other(Input::Character('x')), + case::resize(Input::Resize), + case::scroll_left(Input::ScrollLeft), + case::scroll_right(Input::ScrollRight), + case::scroll_down(Input::ScrollDown), + case::scroll_up(Input::ScrollUp), + case::scroll_jump_down(Input::ScrollJumpDown), + case::scroll_jump_up(Input::ScrollJumpUp) +)] +fn input_standard(input: Input) { + let mut module = Confirm::new("Prompt message", &[], &[]); + assert!(module.handle_input(input).is_none()); +} diff --git a/src/components/mod.rs b/src/components/mod.rs index bb3d4dc..d5327c9 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,7 +1,9 @@ mod choice; +mod confirm; mod edit; mod help; pub use choice::Choice; +pub use confirm::Confirm; pub use edit::Edit; pub use help::Help; diff --git a/src/confirm_abort/mod.rs b/src/confirm_abort/mod.rs index 25b16d0..1c8aada 100644 --- a/src/confirm_abort/mod.rs +++ b/src/confirm_abort/mod.rs @@ -1,44 +1,41 @@ use crate::{ - input::{input_handler::InputMode, Input}, + components::Confirm, + input::input_handler::InputMode, process::{exit_status::ExitStatus, process_module::ProcessModule, process_result::ProcessResult, state::State}, todo_file::TodoFile, view::{view_data::ViewData, View}, }; pub struct ConfirmAbort { - view_data: ViewData, + dialog: Confirm, } impl ProcessModule for ConfirmAbort { fn build_view_data(&mut self, view: &View<'_>, _: &TodoFile) -> &ViewData { - let view_width = view.get_view_size().width(); - let view_height = view.get_view_size().height(); - self.view_data.set_view_size(view_width, view_height); - self.view_data.rebuild(); - &self.view_data + let view_size = view.get_view_size(); + self.dialog.get_view_data(view_size.width(), view_size.height()) } fn handle_input(&mut self, view: &mut View<'_>, rebase_todo: &mut TodoFile) -> ProcessResult { let input = view.get_input(InputMode::Confirm); let mut result = ProcessResult::new().input(input); - match input { - Input::Yes => { + if let Some(confirmed) = self.dialog.handle_input(input) { + if confirmed { rebase_todo.set_lines(vec![]); result = result.exit_status(ExitStatus::Good); - }, - Input::No => { + } + else { result = result.state(State::List); - }, - _ => {}, + } } result } } impl ConfirmAbort { - pub(crate) fn new() -> Self { + pub(crate) fn new(confirm_yes: &[String], confirm_no: &[String]) -> Self { Self { - view_data: ViewData::new_confirm("Are you sure you want to abort"), + dialog: Confirm::new("Are you sure you want to abort", confirm_yes, confirm_no), } } } @@ -49,6 +46,7 @@ mod tests { use crate::{ assert_process_result, assert_rendered_output, + input::Input, process::testutil::{process_module_test, TestContext, ViewState}, }; @@ -60,9 +58,17 @@ mod tests { ViewState::default(), &[], |test_context: TestContext<'_>| { - let mut module = ConfirmAbort::new(); + let mut module = ConfirmAbort::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, + ); let view_data = test_context.build_view_data(&mut module); - assert_rendered_output!(view_data, "{TITLE}", "{PROMPT}", "Are you sure you want to abort"); + assert_rendered_output!( + view_data, + "{TITLE}", + "{BODY}", + "{Normal}Are you sure you want to abort (y/n)? " + ); }, ); } @@ -75,7 +81,10 @@ mod tests { ViewState::default(), &[Input::Yes], |mut test_context: TestContext<'_>| { - let mut module = ConfirmAbort::new(); + let mut module = ConfirmAbort::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, + ); assert_process_result!( test_context.handle_input(&mut module), input = Input::Yes, @@ -94,25 +103,10 @@ mod tests { ViewState::default(), &[Input::No], |mut test_context: TestContext<'_>| { - let mut module = ConfirmAbort::new(); - assert_process_result!( - test_context.handle_input(&mut module), - input = Input::No, - state = State::List + let mut module = ConfirmAbort::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, ); - }, - ); - } - - #[test] - #[serial_test::serial] - fn handle_input_any_key() { - process_module_test( - &["pick aaa comment"], - ViewState::default(), - &[Input::Character('x')], - |mut test_context: TestContext<'_>| { - let mut module = ConfirmAbort::new(); assert_process_result!( test_context.handle_input(&mut module), input = Input::No, @@ -124,13 +118,16 @@ mod tests { #[test] #[serial_test::serial] - fn handle_input_resize() { + fn handle_input_no_match_key() { process_module_test( &["pick aaa comment"], ViewState::default(), &[Input::Resize], |mut test_context: TestContext<'_>| { - let mut module = ConfirmAbort::new(); + let mut module = ConfirmAbort::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, + ); assert_process_result!(test_context.handle_input(&mut module), input = Input::Resize); }, ); diff --git a/src/confirm_rebase/mod.rs b/src/confirm_rebase/mod.rs index f3b29fa..5c9a5b7 100644 --- a/src/confirm_rebase/mod.rs +++ b/src/confirm_rebase/mod.rs @@ -1,43 +1,40 @@ use crate::{ - input::{input_handler::InputMode, Input}, + components::Confirm, + input::input_handler::InputMode, process::{exit_status::ExitStatus, process_module::ProcessModule, process_result::ProcessResult, state::State}, todo_file::TodoFile, view::{view_data::ViewData, View}, }; pub struct ConfirmRebase { - view_data: ViewData, + dialog: Confirm, } impl ProcessModule for ConfirmRebase { fn build_view_data(&mut self, view: &View<'_>, _: &TodoFile) -> &ViewData { - let view_width = view.get_view_size().width(); - let view_height = view.get_view_size().height(); - self.view_data.set_view_size(view_width, view_height); - self.view_data.rebuild(); - &self.view_data + let view_size = view.get_view_size(); + self.dialog.get_view_data(view_size.width(), view_size.height()) } fn handle_input(&mut self, view: &mut View<'_>, _: &mut TodoFile) -> ProcessResult { let input = view.get_input(InputMode::Confirm); let mut result = ProcessResult::new().input(input); - match input { - Input::Yes => { + if let Some(confirmed) = self.dialog.handle_input(input) { + if confirmed { result = result.exit_status(ExitStatus::Good); - }, - Input::No => { + } + else { result = result.state(State::List); - }, - _ => {}, + } } result } } impl ConfirmRebase { - pub(crate) fn new() -> Self { + pub(crate) fn new(confirm_yes: &[String], confirm_no: &[String]) -> Self { Self { - view_data: ViewData::new_confirm("Are you sure you want to rebase"), + dialog: Confirm::new("Are you sure you want to rebase", confirm_yes, confirm_no), } } } @@ -48,6 +45,7 @@ mod tests { use crate::{ assert_process_result, assert_rendered_output, + input::Input, process::testutil::{process_module_test, TestContext, ViewState}, }; @@ -59,9 +57,17 @@ mod tests { ViewState::default(), &[], |test_context: TestContext<'_>| { - let mut module = ConfirmRebase::new(); + let mut module = ConfirmRebase::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, + ); let view_data = test_context.build_view_data(&mut module); - assert_rendered_output!(view_data, "{TITLE}", "{PROMPT}", "Are you sure you want to rebase"); + assert_rendered_output!( + view_data, + "{TITLE}", + "{BODY}", + "{Normal}Are you sure you want to rebase (y/n)? " + ); }, ); } @@ -74,7 +80,10 @@ mod tests { ViewState::default(), &[Input::Yes], |mut test_context: TestContext<'_>| { - let mut module = ConfirmRebase::new(); + let mut module = ConfirmRebase::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, + ); assert_process_result!( test_context.handle_input(&mut module), input = Input::Yes, @@ -93,25 +102,10 @@ mod tests { ViewState::default(), &[Input::No], |mut test_context: TestContext<'_>| { - let mut module = ConfirmRebase::new(); - assert_process_result!( - test_context.handle_input(&mut module), - input = Input::No, - state = State::List + let mut module = ConfirmRebase::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, ); - }, - ); - } - - #[test] - #[serial_test::serial] - fn handle_input_any_key() { - process_module_test( - &["pick aaa comment"], - ViewState::default(), - &[Input::Character('x')], - |mut test_context: TestContext<'_>| { - let mut module = ConfirmRebase::new(); assert_process_result!( test_context.handle_input(&mut module), input = Input::No, @@ -123,13 +117,16 @@ mod tests { #[test] #[serial_test::serial] - fn handle_input_resize() { + fn handle_input_no_match_key() { process_module_test( &["pick aaa comment"], ViewState::default(), &[Input::Resize], |mut test_context: TestContext<'_>| { - let mut module = ConfirmRebase::new(); + let mut module = ConfirmRebase::new( + &test_context.config.key_bindings.confirm_yes, + &test_context.config.key_bindings.confirm_no, + ); assert_process_result!(test_context.handle_input(&mut module), input = Input::Resize); }, ); diff --git a/src/process/modules.rs b/src/process/modules.rs index a057530..6e14596 100644 --- a/src/process/modules.rs +++ b/src/process/modules.rs @@ -31,8 +31,8 @@ pub struct Modules<'m> { impl<'m> Modules<'m> { pub fn new(config: &'m Config) -> Self { Modules { - confirm_abort: ConfirmAbort::new(), - confirm_rebase: ConfirmRebase::new(), + confirm_abort: ConfirmAbort::new(&config.key_bindings.confirm_yes, &config.key_bindings.confirm_no), + confirm_rebase: ConfirmRebase::new(&config.key_bindings.confirm_yes, &config.key_bindings.confirm_no), error: Error::new(), external_editor: ExternalEditor::new(config.git.editor.as_str()), insert: Insert::new(), diff --git a/src/view/mod.rs b/src/view/mod.rs index 0cc8eb2..e086eb2 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -52,20 +52,6 @@ impl<'v> View<'v> { self.display.next_line()?; } - if let Some(ref prompt) = *view_data.get_prompt() { - self.display.set_style(false, false, false)?; - self.display.next_line()?; - self.display.draw_str(&format!( - "{} ({}/{})? ", - prompt, - self.config.key_bindings.confirm_yes.join(","), - self.config.key_bindings.confirm_no.join(",") - ))?; - self.display.next_line()?; - self.display.refresh()?; - return Ok(()); - } - let leading_lines = view_data.get_leading_lines(); let lines = view_data.get_lines(); let trailing_lines = view_data.get_trailing_lines(); @@ -291,17 +277,6 @@ mod tests { #[test] #[serial_test::serial] - fn render_prompt() { - view_module_test(Size::new(35, 10), |mut test_context| { - let view_data = ViewData::new_confirm("This is a prompt"); - test_context.view.render(&view_data).unwrap(); - let expected = vec!["Git Interactive Rebase Tool ", "\nThis is a prompt (y/n)? "]; - TestContext::assert_output(&expected); - }); - } - - #[test] - #[serial_test::serial] fn render_leading_lines() { view_module_test(Size::new(30, 10), |mut test_context| { let mut view_data = ViewData::new(); diff --git a/src/view/testutil.rs b/src/view/testutil.rs index 3119782..010ff32 100644 --- a/src/view/testutil.rs +++ b/src/view/testutil.rs @@ -102,12 +102,6 @@ fn render_view_data(view_data: &ViewData) -> Vec<String> { lines.push(String::from("{EMPTY}")); } - if let Some(ref prompt) = *view_data.get_prompt() { - lines.push(String::from("{PROMPT}")); - lines.push(prompt.to_owned()); - return lines; - } - let leading_lines = view_data.get_leading_lines(); if !leading_lines.is_empty() { lines.push(String::from("{LEADING}")); diff --git a/src/view/view_data.rs b/src/view/view_data.rs index 418e4f1..699c12c 100644 --- a/src/view/view_data.rs +++ b/src/view/view_data.rs @@ -13,7 +13,6 @@ pub struct ViewData { trailing_lines_cache: Option<Vec<ViewLine>>, show_title: bool, show_help: bool, - prompt: Option<String>, max_leading_line_length: usize, max_line_length: usize, max_trailing_line_length: usize, @@ -31,7 +30,6 @@ impl ViewData { max_leading_line_length: 0, max_line_length: 0, max_trailing_line_length: 0, - prompt: None, scroll_position: ScrollPosition::new(), show_help: false, show_title: false, @@ -41,13 +39,6 @@ impl ViewData { } } - pub(crate) fn new_confirm(prompt: &str) -> Self { - let mut inst = Self::new(); - inst.set_show_title(true); - inst.prompt = Some(String::from(prompt)); - inst - } - pub(crate) fn reset(&mut self) { self.clear(); self.scroll_position.reset(); @@ -60,7 +51,6 @@ impl ViewData { self.lines_cache = None; self.trailing_lines.clear(); self.trailing_lines_cache = None; - self.prompt = None; } pub(crate) fn clear_body(&mut self) { @@ -269,14 +259,7 @@ impl ViewData { } pub(crate) fn is_empty(&self) -> bool { - self.lines.is_empty() - && self.leading_lines.is_empty() - && self.trailing_lines.is_empty() - && self.prompt.is_none() - } - - pub(super) const fn get_prompt(&self) -> &Option<String> { - &self.prompt + self.lines.is_empty() && self.leading_lines.is_empty() && self.trailing_lines.is_empty() } pub(super) const fn get_leading_lines(&self) -> &Vec<ViewLine> { @@ -604,12 +587,6 @@ mod tests { } #[test] - fn with_prompt() { - let view_data = ViewData::new_confirm("This is a prompt"); - assert_rendered_output!(view_data, "{TITLE}", "{PROMPT}", "This is a prompt"); - } - - #[test] fn clear() { let mut view_data = create_mocked_view_data(); view_data.set_view_size(100, 3); |