summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2021-03-29 20:55:47 -0230
committerTim Oram <dev@mitmaro.ca>2021-03-29 21:09:45 -0230
commit59c05c40a364b5f9c589190b1f3741cfc361f39a (patch)
tree890c76eee229e8787526c523558916ea97a2cec7
parente4207e2c139a0ed135973f525b42adad1f9aacb5 (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.rs44
-rw-r--r--src/components/confirm/tests.rs46
-rw-r--r--src/components/mod.rs2
-rw-r--r--src/confirm_abort/mod.rs73
-rw-r--r--src/confirm_rebase/mod.rs73
-rw-r--r--src/process/modules.rs4
-rw-r--r--src/view/mod.rs25
-rw-r--r--src/view/testutil.rs6
-rw-r--r--src/view/view_data.rs25
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);