summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2021-03-26 23:17:14 -0230
committerTim Oram <dev@mitmaro.ca>2021-03-26 23:34:56 -0230
commit38550a49acb8b1953b847685e3f459979480e89a (patch)
tree952ced728d4f033e990ee154715f3bb8c55540db
parentee694d94b8d5c1c9117db6af692a12e8de1bd3ae (diff)
Add insert line support
This adds the ability to insert new exec, label, reset and merge lines to the rebase todo file.
-rw-r--r--CHANGELOG.md1
-rw-r--r--README.md2
-rw-r--r--readme/customization.md2
-rw-r--r--src/components/edit/mod.rs9
-rw-r--r--src/components/edit/tests.rs9
-rw-r--r--src/config/key_bindings.rs2
-rw-r--r--src/config/tests.rs14
-rw-r--r--src/input/input_handler.rs2
-rw-r--r--src/input/mod.rs1
-rw-r--r--src/insert/insert_state.rs5
-rw-r--r--src/insert/line_type.rs40
-rw-r--r--src/insert/mod.rs118
-rw-r--r--src/insert/tests.rs254
-rw-r--r--src/list/mod.rs3
-rw-r--r--src/list/tests.rs19
-rw-r--r--src/list/utils.rs1
-rw-r--r--src/main.rs9
-rw-r--r--src/process/modules.rs5
-rw-r--r--src/process/state.rs1
-rw-r--r--src/process/testutil.rs3
-rw-r--r--src/todo_file/line.rs76
21 files changed, 570 insertions, 6 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a924f18..c37e113 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- Support for multiple key bindings per configuration ([#437](https://github.com/MitMaro/git-interactive-rebase-tool/pull/437))
- Open external editor from visual mode ([#442](https://github.com/MitMaro/git-interactive-rebase-tool/pull/442))
- Delete selected lines from the todo list ([#443](https://github.com/MitMaro/git-interactive-rebase-tool/pull/443))
+- Insert new exec, label, reset or merge line ([#454](https://github.com/MitMaro/git-interactive-rebase-tool/pull/454))
### Fixed
- Most modifier key combinations could not be used as key bindings ([#435](https://github.com/MitMaro/git-interactive-rebase-tool/pull/435))
diff --git a/README.md b/README.md
index dcc69de..5925e90 100644
--- a/README.md
+++ b/README.md
@@ -144,6 +144,8 @@ Key bindings can be configured, see [configuration](readme/customization.md#key-
| `E` | Normal | Edit the command of an exec action |
| `v` | All | Enter and exit visual mode |
| `d` | Diff | Show full commit diff |
+| `I` | Normal | Insert a new line |
+| `Delete` | All | Remove selected lines |
| `Control+z` | All | Undo the previous change |
| `Control+y` | All | Redo the previously undone change |
diff --git a/readme/customization.md b/readme/customization.md
index 7c236f4..85ceef3 100644
--- a/readme/customization.md
+++ b/readme/customization.md
@@ -111,6 +111,7 @@ Most keys can be changed to any printable character or supported special charact
| `inputForceAbort` | Q | String | Key for forcing an abort of the rebase |
| `inputForceRebase` | W | String | Key for forcing a rebase |
| `inputHelp` | ? | String | Key for showing the help |
+| `inputInsertLine` | I | String | Key for inserting a new line |
| `inputMoveDown` | Down | String | Key for moving the cursor down |
| `inputMoveLeft` | Left | String | Key for moving the cursor left |
| `inputMoveRight` | Right | String | Key for moving the cursor right |
@@ -122,6 +123,7 @@ Most keys can be changed to any printable character or supported special charact
| `inputOpenInExternalEditor`| ! | String | Key for opening the external editor |
| `inputRebase` | w | String | Key for rebasing with confirmation |
| `inputRedo` | Control+y| String | Key for redoing the previous undone change |
+| `inputRemoveLine` | Delete | String | Key for removing selected commits |
| `inputShowCommit` | c | String | Key for showing the overview of the selected commit |
| `inputShowDiff` | d | String | Key for showing the diff of the selected commit |
| `inputToggleVisualMode` | v | String | Key for toggling visual mode |
diff --git a/src/components/edit/mod.rs b/src/components/edit/mod.rs
index c50d745..5894c3a 100644
--- a/src/components/edit/mod.rs
+++ b/src/components/edit/mod.rs
@@ -148,7 +148,12 @@ impl Edit {
self.cursor_position = UnicodeSegmentation::graphemes(content, true).count();
}
- pub fn get_content(&self) -> &str {
- self.content.as_str()
+ pub fn clear(&mut self) {
+ self.content.clear();
+ self.cursor_position = 0;
+ }
+
+ pub fn get_content(&self) -> String {
+ self.content.clone()
}
}
diff --git a/src/components/edit/tests.rs b/src/components/edit/tests.rs
index 6430232..f913c9d 100644
--- a/src/components/edit/tests.rs
+++ b/src/components/edit/tests.rs
@@ -522,3 +522,12 @@ fn set_get_content() {
assert_eq!(module.cursor_position, 4);
assert_eq!(module.get_content(), "abcd");
}
+
+#[test]
+fn clear_content() {
+ let mut module = Edit::new();
+ module.set_content("abcd");
+ module.clear();
+ assert_eq!(module.cursor_position, 0);
+ assert_eq!(module.get_content(), "");
+}
diff --git a/src/config/key_bindings.rs b/src/config/key_bindings.rs
index 039b636..d583324 100644
--- a/src/config/key_bindings.rs
+++ b/src/config/key_bindings.rs
@@ -19,6 +19,7 @@ pub struct KeyBindings {
pub(crate) force_abort: Vec<String>,
pub(crate) force_rebase: Vec<String>,
pub(crate) help: Vec<String>,
+ pub(crate) insert_line: Vec<String>,
pub(crate) move_down: Vec<String>,
pub(crate) move_down_step: Vec<String>,
pub(crate) move_left: Vec<String>,
@@ -62,6 +63,7 @@ impl KeyBindings {
force_abort: get_input(git_config, "interactive-rebase-tool.inputForceAbort", "Q")?,
force_rebase: get_input(git_config, "interactive-rebase-tool.inputForceRebase", "W")?,
help: get_input(git_config, "interactive-rebase-tool.inputHelp", "?")?,
+ insert_line: get_input(git_config, "interactive-rebase-tool.insertLine", "I")?,
move_down: get_input(git_config, "interactive-rebase-tool.inputMoveDown", "Down")?,
move_left: get_input(git_config, "interactive-rebase-tool.inputMoveLeft", "Left")?,
move_right: get_input(git_config, "interactive-rebase-tool.inputMoveRight", "Right")?,
diff --git a/src/config/tests.rs b/src/config/tests.rs
index 7aa5655..a429979 100644
--- a/src/config/tests.rs
+++ b/src/config/tests.rs
@@ -977,6 +977,20 @@ fn config_key_bindings_help() {
}
#[test]
+fn config_key_bindings_insert_line_default() {
+ let config = load(|_| {});
+ assert_eq!(config.key_bindings.insert_line, vec![String::from("I")]);
+}
+
+#[test]
+fn config_key_bindings_insert_line() {
+ let config = load(|git_config| {
+ git_config.set_str("interactive-rebase-tool.insertLine", "X").unwrap();
+ });
+ assert_eq!(config.key_bindings.insert_line, vec![String::from("X")]);
+}
+
+#[test]
fn config_key_bindings_move_down_default() {
let config = load(|_| {});
assert_eq!(config.key_bindings.move_down, vec![String::from("Down")]);
diff --git a/src/input/input_handler.rs b/src/input/input_handler.rs
index 60611d0..9ff4efa 100644
--- a/src/input/input_handler.rs
+++ b/src/input/input_handler.rs
@@ -148,6 +148,7 @@ impl<'i> InputHandler<'i> {
i if self.key_bindings.show_commit.contains(&i) => Input::ShowCommit,
i if self.key_bindings.edit.contains(&i) => Input::Edit,
i if self.key_bindings.help.contains(&i) => Input::Help,
+ i if self.key_bindings.insert_line.contains(&i) => Input::InsertLine,
i if self.key_bindings.toggle_visual_mode.contains(&i) => Input::ToggleVisualMode,
i if self.key_bindings.action_break.contains(&i) => Input::ActionBreak,
i if self.key_bindings.action_drop.contains(&i) => Input::ActionDrop,
@@ -414,6 +415,7 @@ mod tests {
case::show_commit(create_key_event!('c'), Input::ShowCommit),
case::edit(create_key_event!('E'), Input::Edit),
case::help(create_key_event!('?'), Input::Help),
+ case::insert_line(create_key_event!('I'), Input::InsertLine),
case::toggle_visual_mode(create_key_event!('v'), Input::ToggleVisualMode),
case::action_break(create_key_event!('b'), Input::ActionBreak),
case::action_drop(create_key_event!('d'), Input::ActionDrop),
diff --git a/src/input/mod.rs b/src/input/mod.rs
index c9e8aea..ed90990 100644
--- a/src/input/mod.rs
+++ b/src/input/mod.rs
@@ -17,6 +17,7 @@ pub enum Input {
ForceAbort,
ForceRebase,
Help,
+ InsertLine,
Kill,
MoveCursorDown,
MoveCursorLeft,
diff --git a/src/insert/insert_state.rs b/src/insert/insert_state.rs
new file mode 100644
index 0000000..599e621
--- /dev/null
+++ b/src/insert/insert_state.rs
@@ -0,0 +1,5 @@
+#[derive(Debug, PartialEq)]
+pub enum InsertState {
+ Prompt,
+ Edit,
+}
diff --git a/src/insert/line_type.rs b/src/insert/line_type.rs
new file mode 100644
index 0000000..761c93f
--- /dev/null
+++ b/src/insert/line_type.rs
@@ -0,0 +1,40 @@
+#[derive(Clone, Debug, PartialEq)]
+pub enum LineType {
+ Exec,
+ Label,
+ Reset,
+ Merge,
+ Cancel,
+}
+
+impl ToString for LineType {
+ fn to_string(&self) -> String {
+ match *self {
+ Self::Exec => String::from("exec"),
+ Self::Label => String::from("label"),
+ Self::Reset => String::from("reset"),
+ Self::Merge => String::from("merge"),
+ Self::Cancel => String::from("<cancel>"),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use rstest::rstest;
+
+ use super::*;
+
+ #[rstest(
+ line_type,
+ expected,
+ case::exec(&LineType::Exec, "exec"),
+ case::exec(&LineType::Label, "label"),
+ case::exec(&LineType::Reset, "reset"),
+ case::exec(&LineType::Merge, "merge"),
+ case::exec(&LineType::Cancel, "<cancel>"),
+ )]
+ fn to_string(line_type: &LineType, expected: &str) {
+ assert_eq!(line_type.to_string(), String::from(expected));
+ }
+}
diff --git a/src/insert/mod.rs b/src/insert/mod.rs
new file mode 100644
index 0000000..9add788
--- /dev/null
+++ b/src/insert/mod.rs
@@ -0,0 +1,118 @@
+mod insert_state;
+mod line_type;
+
+#[cfg(all(unix, test))]
+mod tests;
+
+use crate::{
+ components::{Choice, Edit},
+ input::{input_handler::InputMode, Input},
+ insert::{insert_state::InsertState, line_type::LineType},
+ process::{process_module::ProcessModule, process_result::ProcessResult, state::State},
+ todo_file::{line::Line, TodoFile},
+ view::{view_data::ViewData, view_line::ViewLine, View},
+};
+
+pub struct Insert {
+ action_choices: Choice<LineType>,
+ edit: Edit,
+ edit_view_data: ViewData,
+ line_type: LineType,
+ state: InsertState,
+}
+
+impl ProcessModule for Insert {
+ fn activate(&mut self, _: &TodoFile, _: State) -> ProcessResult {
+ self.state = InsertState::Prompt;
+ self.edit.clear();
+ ProcessResult::new()
+ }
+
+ 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();
+
+ match self.state {
+ InsertState::Prompt => self.action_choices.get_view_data(view_width, view_height),
+ InsertState::Edit => {
+ self.edit_view_data.clear();
+ self.edit_view_data.set_view_size(view_width, view_height);
+ self.edit.update_view_data(&mut self.edit_view_data);
+ &self.edit_view_data
+ },
+ }
+ }
+
+ fn handle_input(&mut self, view: &mut View<'_>, rebase_todo: &mut TodoFile) -> ProcessResult {
+ let mut result = ProcessResult::new();
+ match self.state {
+ InsertState::Prompt => {
+ let input = view.get_input(InputMode::Default);
+ result = result.input(input);
+ if let Some(action) = self.action_choices.handle_input(input) {
+ if action == &LineType::Cancel {
+ result = result.state(State::List);
+ }
+ else {
+ self.line_type = action.clone();
+ self.edit.set_label(format!("{} ", action.to_string()).as_str());
+ self.state = InsertState::Edit;
+ }
+ }
+ },
+ InsertState::Edit => {
+ let input = view.get_input(InputMode::Raw);
+ result = result.input(input);
+ if !self.edit.handle_input(input) && input == Input::Enter {
+ let content = self.edit.get_content();
+ result = result.state(State::List);
+ if !content.is_empty() {
+ let line = match self.line_type {
+ LineType::Exec => Line::new_exec(content.as_str()),
+ LineType::Label => Line::new_label(content.as_str()),
+ LineType::Reset => Line::new_reset(content.as_str()),
+ LineType::Merge => Line::new_merge(content.as_str()),
+ // this should exit in the prompt state and never get here
+ LineType::Cancel => unreachable!(),
+ };
+ let new_line_index = rebase_todo.get_selected_line_index() + 1;
+ rebase_todo.add_line(new_line_index, line);
+ rebase_todo.set_selected_line_index(new_line_index);
+ }
+ }
+ },
+ }
+
+ result
+ }
+}
+
+impl Insert {
+ pub(crate) fn new() -> Self {
+ let mut edit = Edit::new();
+ edit.set_description("Enter contents of the new line. Empty content cancels creation of a new line.");
+
+ let mut action_choices = Choice::new(vec![
+ (LineType::Exec, 'e', String::from("exec <command>")),
+ (LineType::Label, 'l', String::from("label <label>")),
+ (LineType::Reset, 'r', String::from("reset <label>")),
+ (
+ LineType::Merge,
+ 'm',
+ String::from("merge [-C <commit> | -c <commit>] <label> [# <oneline>]"),
+ ),
+ (LineType::Cancel, 'c', String::from("Cancel add line")),
+ ]);
+ action_choices.set_prompt(vec![ViewLine::from("Select the type of line to insert:")]);
+ let mut edit_view_data = ViewData::new();
+ edit_view_data.set_show_title(true);
+
+ Self {
+ state: InsertState::Prompt,
+ edit,
+ edit_view_data,
+ action_choices,
+ line_type: LineType::Exec,
+ }
+ }
+}
diff --git a/src/insert/tests.rs b/src/insert/tests.rs
new file mode 100644
index 0000000..80936e2
--- /dev/null
+++ b/src/insert/tests.rs
@@ -0,0 +1,254 @@
+use super::*;
+use crate::{
+ assert_process_result,
+ assert_rendered_output,
+ process::testutil::{process_module_test, TestContext, ViewState},
+};
+
+#[test]
+#[serial_test::serial]
+fn activate() {
+ process_module_test(&[], ViewState::default(), &[], |test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ assert_process_result!(test_context.activate(&mut module, State::List));
+ });
+}
+
+#[test]
+#[serial_test::serial]
+fn render_prompt() {
+ process_module_test(&[], ViewState::default(), &[], |test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ let view_data = test_context.build_view_data(&mut module);
+ assert_rendered_output!(
+ view_data,
+ "{TITLE}",
+ "{LEADING}",
+ "{Normal}Select the type of line to insert:",
+ "",
+ "{BODY}",
+ "{Normal}e) exec <command>",
+ "{Normal}l) label <label>",
+ "{Normal}r) reset <label>",
+ "{Normal}m) merge [-C <commit> | -c <commit>] <label> [# <oneline>]",
+ "{Normal}c) Cancel add line",
+ "",
+ "{IndicatorColor}Please choose an option."
+ );
+ });
+}
+
+#[test]
+#[serial_test::serial]
+fn prompt_cancel() {
+ process_module_test(
+ &[],
+ ViewState::default(),
+ &[Input::Character('c')],
+ |mut test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ assert_process_result!(
+ test_context.handle_input(&mut module),
+ input = Input::Character('c'),
+ state = State::List
+ );
+ },
+ );
+}
+
+#[test]
+#[serial_test::serial]
+fn edit_render_exec() {
+ process_module_test(
+ &[],
+ ViewState::default(),
+ &[
+ Input::Character('e'),
+ Input::Character('f'),
+ Input::Character('o'),
+ Input::Character('o'),
+ Input::Enter,
+ ],
+ |mut test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ test_context.handle_n_inputs(&mut module, 4);
+ let view_data = test_context.build_view_data(&mut module);
+ assert_rendered_output!(
+ view_data,
+ "{TITLE}",
+ "{LEADING}",
+ "{IndicatorColor}Enter contents of the new line. Empty content cancels creation of a new line.",
+ "",
+ "{BODY}",
+ "{Normal,Dimmed}exec {Normal}foo{Normal,Underline} ",
+ "{TRAILING}",
+ "{IndicatorColor}Enter to finish"
+ );
+ assert_process_result!(
+ test_context.handle_input(&mut module),
+ input = Input::Enter,
+ state = State::List
+ );
+ assert_eq!(test_context.rebase_todo_file.get_line(0).unwrap().to_text(), "exec foo");
+ },
+ );
+}
+
+#[test]
+#[serial_test::serial]
+fn edit_render_label() {
+ process_module_test(
+ &[],
+ ViewState::default(),
+ &[
+ Input::Character('l'),
+ Input::Character('f'),
+ Input::Character('o'),
+ Input::Character('o'),
+ Input::Enter,
+ ],
+ |mut test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ test_context.handle_n_inputs(&mut module, 4);
+ let view_data = test_context.build_view_data(&mut module);
+ assert_rendered_output!(
+ view_data,
+ "{TITLE}",
+ "{LEADING}",
+ "{IndicatorColor}Enter contents of the new line. Empty content cancels creation of a new line.",
+ "",
+ "{BODY}",
+ "{Normal,Dimmed}label {Normal}foo{Normal,Underline} ",
+ "{TRAILING}",
+ "{IndicatorColor}Enter to finish"
+ );
+ assert_process_result!(
+ test_context.handle_input(&mut module),
+ input = Input::Enter,
+ state = State::List
+ );
+ assert_eq!(
+ test_context.rebase_todo_file.get_line(0).unwrap().to_text(),
+ "label foo"
+ );
+ },
+ );
+}
+
+#[test]
+#[serial_test::serial]
+fn edit_render_reset() {
+ process_module_test(
+ &[],
+ ViewState::default(),
+ &[
+ Input::Character('r'),
+ Input::Character('f'),
+ Input::Character('o'),
+ Input::Character('o'),
+ Input::Enter,
+ ],
+ |mut test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ test_context.handle_n_inputs(&mut module, 4);
+ let view_data = test_context.build_view_data(&mut module);
+ assert_rendered_output!(
+ view_data,
+ "{TITLE}",
+ "{LEADING}",
+ "{IndicatorColor}Enter contents of the new line. Empty content cancels creation of a new line.",
+ "",
+ "{BODY}",
+ "{Normal,Dimmed}reset {Normal}foo{Normal,Underline} ",
+ "{TRAILING}",
+ "{IndicatorColor}Enter to finish"
+ );
+ assert_process_result!(
+ test_context.handle_input(&mut module),
+ input = Input::Enter,
+ state = State::List
+ );
+ assert_eq!(
+ test_context.rebase_todo_file.get_line(0).unwrap().to_text(),
+ "reset foo"
+ );
+ },
+ );
+}
+
+#[test]
+#[serial_test::serial]
+fn edit_render_merge() {
+ process_module_test(
+ &[],
+ ViewState::default(),
+ &[
+ Input::Character('m'),
+ Input::Character('f'),
+ Input::Character('o'),
+ Input::Character('o'),
+ Input::Enter,
+ ],
+ |mut test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ test_context.handle_n_inputs(&mut module, 4);
+ let view_data = test_context.build_view_data(&mut module);
+ assert_rendered_output!(
+ view_data,
+ "{TITLE}",
+ "{LEADING}",
+ "{IndicatorColor}Enter contents of the new line. Empty content cancels creation of a new line.",
+ "",
+ "{BODY}",
+ "{Normal,Dimmed}merge {Normal}foo{Normal,Underline} ",
+ "{TRAILING}",
+ "{IndicatorColor}Enter to finish"
+ );
+ assert_process_result!(
+ test_context.handle_input(&mut module),
+ input = Input::Enter,
+ state = State::List
+ );
+ assert_eq!(
+ test_context.rebase_todo_file.get_line(0).unwrap().to_text(),
+ "merge foo"
+ );
+ },
+ );
+}
+
+#[test]
+#[serial_test::serial]
+fn edit_select_next_index() {
+ process_module_test(
+ &["pick aaa c1"],
+ ViewState::default(),
+ &[
+ Input::Character('e'),
+ Input::Character('f'),
+ Input::Character('o'),
+ Input::Character('o'),
+ Input::Enter,
+ ],
+ |mut test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ test_context.handle_all_inputs(&mut module);
+ assert_eq!(test_context.rebase_todo_file.get_selected_line_index(), 1);
+ },
+ );
+}
+
+#[test]
+#[serial_test::serial]
+fn cancel_edit() {
+ process_module_test(
+ &[],
+ ViewState::default(),
+ &[Input::Character('e'), Input::Enter],
+ |mut test_context: TestContext<'_>| {
+ let mut module = Insert::new();
+ test_context.handle_all_inputs(&mut module);
+ assert!(test_context.rebase_todo_file.is_empty());
+ },
+ );
+}
diff --git a/src/list/mod.rs b/src/list/mod.rs
index ad37875..3aa0447 100644
--- a/src/list/mod.rs
+++ b/src/list/mod.rs
@@ -330,6 +330,7 @@ impl<'l> List<'l> {
}
}
},
+ Input::InsertLine => result = result.state(State::Insert),
_ => {},
}
result
@@ -355,7 +356,7 @@ impl<'l> List<'l> {
rebase_todo.update_range(
selected_index,
selected_index,
- &EditContext::new().content(self.edit.get_content()),
+ &EditContext::new().content(self.edit.get_content().as_str()),
);
self.visual_index_start = None;
self.state = ListState::Normal;
diff --git a/src/list/tests.rs b/src/list/tests.rs
index 19e97a1..9a94902 100644
--- a/src/list/tests.rs
+++ b/src/list/tests.rs
@@ -1367,6 +1367,24 @@ fn normal_mode_edit_without_selected_line() {
#[test]
#[serial_test::serial]
+fn normal_mode_insert_line() {
+ process_module_test(
+ &[],
+ ViewState::default(),
+ &[Input::InsertLine],
+ |mut test_context: TestContext<'_>| {
+ let mut module = List::new(test_context.config);
+ assert_process_result!(
+ test_context.handle_input(&mut module),
+ input = Input::InsertLine,
+ state = State::Insert
+ );
+ },
+ );
+}
+
+#[test]
+#[serial_test::serial]
fn normal_mode_open_external_editor() {
process_module_test(
&["pick aaa c1"],
@@ -2755,6 +2773,7 @@ fn normal_mode_help() {
"{IndicatorColor} f {Normal,Dimmed}|{Normal}Set selected commit to be fixed-up",
"{IndicatorColor} d {Normal,Dimmed}|{Normal}Set selected commit to be dropped",
"{IndicatorColor} E {Normal,Dimmed}|{Normal}Edit an exec action's command",
+ "{IndicatorColor} I {Normal,Dimmed}|{Normal}Insert a new line",
"{IndicatorColor} Delete {Normal,Dimmed}|{Normal}Completely remove the selected line",
"{IndicatorColor} Controlz{Normal,Dimmed}|{Normal}Undo the last change",
"{IndicatorColor} Controly{Normal,Dimmed}|{Normal}Redo the previous undone change",
diff --git a/src/list/utils.rs b/src/list/utils.rs
index 7556dc4..2342978 100644
--- a/src/list/utils.rs
+++ b/src/list/utils.rs
@@ -84,6 +84,7 @@ pub(super) fn get_list_normal_mode_help_lines(key_bindings: &KeyBindings) -> Vec
String::from("Set selected commit to be dropped"),
),
(key_bindings.edit.clone(), String::from("Edit an exec action's command")),
+ (key_bindings.insert_line.clone(), String::from("Insert a new line")),
(
key_bindings.remove_line.clone(),
String::from("Completely remove the selected line"),
diff --git a/src/main.rs b/src/main.rs
index 2bc61fc..d065d44 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -29,6 +29,8 @@
#![allow(clippy::unwrap_used)]
#![allow(clippy::wildcard_enum_match_arm)]
#![allow(clippy::similar_names)]
+#![allow(clippy::unreachable)]
+#![allow(clippy::missing_panics_doc)]
mod components;
mod config;
@@ -38,12 +40,16 @@ mod constants;
mod display;
mod external_editor;
mod input;
+mod insert;
mod list;
mod process;
mod show_commit;
mod todo_file;
mod view;
+#[cfg(test)]
+pub mod testutil;
+
use clap::{App, Arg};
use crate::{
@@ -56,9 +62,6 @@ use crate::{
view::View,
};
-#[cfg(test)]
-pub mod testutil;
-
#[derive(Debug)]
pub struct Exit {
message: String,
diff --git a/src/process/modules.rs b/src/process/modules.rs
index 9aac49a..a057530 100644
--- a/src/process/modules.rs
+++ b/src/process/modules.rs
@@ -3,6 +3,7 @@ use crate::{
confirm_abort::ConfirmAbort,
confirm_rebase::ConfirmRebase,
external_editor::ExternalEditor,
+ insert::Insert,
list::List,
process::{
error::Error,
@@ -21,6 +22,7 @@ pub struct Modules<'m> {
pub confirm_rebase: ConfirmRebase,
pub error: Error,
pub external_editor: ExternalEditor,
+ pub insert: Insert,
pub list: List<'m>,
pub show_commit: ShowCommit<'m>,
pub window_size_error: WindowSizeError,
@@ -33,6 +35,7 @@ impl<'m> Modules<'m> {
confirm_rebase: ConfirmRebase::new(),
error: Error::new(),
external_editor: ExternalEditor::new(config.git.editor.as_str()),
+ insert: Insert::new(),
list: List::new(config),
show_commit: ShowCommit::new(config),
window_size_error: WindowSizeError::new(),
@@ -45,6 +48,7 @@ impl<'m> Modules<'m> {
State::ConfirmRebase => &mut self.confirm_rebase as &mut dyn ProcessModule,
State::Error => &mut self.error as &mut dyn ProcessModule,
State::ExternalEditor => &mut self.external_editor as &mut dyn ProcessModule,
+ State::Insert => &mut self.insert as &mut dyn ProcessModule,
State::List => &mut self.list as &mut dyn ProcessModule,
State::ShowCommit => &mut self.show_commit as &mut dyn ProcessModule,
State::WindowSizeError => &mut self.window_size_error as &mut dyn ProcessModule,
@@ -90,6 +94,7 @@ mod tests {
case::confirm_rabase(State::ConfirmRebase),
case::error(State::Error),
case::external_editor(State::ExternalEditor),
+ case::insert(State::Insert),
case::list(State::List),
case::show_commit(State::ShowCommit),
case::window_size_error(State::WindowSizeError)
diff --git a/src/process/state.rs b/src/process/state.rs
index b4bf7d2..4c42f22 100644
--- a/src/process/state.rs
+++ b/src/process/state.rs
@@ -5,6 +5,7 @@ pub enum State {
Error,
ExternalEditor,
List,
+ Insert,
ShowCommit,
WindowSizeError,
}
diff --git a/src/process/testutil.rs b/src/process/testutil.rs
index 02d2454..59e64b3 100644
--- a/src/process/testutil.rs
+++ b/src/process/testutil.rs
@@ -146,6 +146,7 @@ fn map_input_to_event(key_bindings: &KeyBindings, input: Input) -> Event {
Input::ForceAbort => map_str_to_event(key_bindings.force_abort.first().unwrap().as_str()),
Input::ForceRebase => map_str_to_event(key_bindings.force_rebase.first().unwrap().as_str()),
Input::Help => map_str_to_event(key_bindings.help.first().unwrap().as_str()),
+ Input::InsertLine => map_str_to_event(key_bindings.insert_line.first().unwrap().as_str()),
Input::Home | Input::ScrollTop => map_str_to_event("Home"),
Input::Left | Input::ScrollLeft => map_str_to_event("Left"),
Input::MoveCursorDown => map_str_to_event(key_bindings.move_down.first().unwrap().as_str()),
@@ -202,6 +203,7 @@ fn format_process_result(
State::ConfirmRebase => "ConfirmRebase",
State::Error => "Error",
State::ExternalEditor => "ExternalEditor",
+ State::Insert => "Insert",
State::List => "List",
State::ShowCommit => "ShowCommit",
State::WindowSizeError => "WindowSizeError",
@@ -230,6 +232,7 @@ fn format_process_result(
Input::ForceAbort => String::from("ForceAbort"),
Input::ForceRebase => String::from("ForceRebase"),
Input::Help => String::from("Help"),
+ Input::InsertLine => String::from("InsertLine"),
Input::Home => String::from("Home"),
Input::Ignore => String::from("Ignore"),
Input::Insert => String::from("Insert"),
diff --git a/src/todo_file/line.rs b/src/todo_file/line.rs
index 08e109b..e81d437 100644
--- a/src/todo_file/line.rs
+++ b/src/todo_file/line.rs
@@ -31,6 +31,42 @@ impl Line {
}
}
+ pub(crate) fn new_exec(command: &str) -> Self {
+ Self {
+ action: Action::Exec,
+ content: String::from(command),
+ hash: String::from(""),
+ mutated: false,
+ }
+ }
+
+ pub(crate) fn new_merge(command: &str) -> Self {
+ Self {
+ action: Action::Merge,
+ content: String::from(command),
+ hash: String::from(""),
+ mutated: false,
+ }
+ }
+
+ pub(crate) fn new_label(label: &str) -> Self {
+ Self {
+ action: Action::Label,
+ content: String::from(label),
+ hash: String::from(""),
+ mutated: false,
+ }
+ }
+
+ pub(crate) fn new_reset(label: &str) -> Self {
+ Self {
+ action: Action::Reset,
+ content: String::from(label),
+ hash: String::from(""),
+ mutated: false,
+ }
+ }
+
pub(crate) fn new(input_line: &str) -> Result<Self> {
if input_line.starts_with("noop") {
return Ok(Self::new_noop());
@@ -235,6 +271,46 @@ mod tests {
});
}
+ #[test]</