diff options
author | Tim Oram <dev@mitmaro.ca> | 2019-07-02 22:20:51 -0230 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-02 22:20:51 -0230 |
commit | 39138ea76182cca1b8ee0fb9a5687296630caaa4 (patch) | |
tree | 292b4157bafe31ffc7520da3c6c1f17fb9266844 | |
parent | 549cc55083748fe341b1ef564efb2f6896dc486a (diff) | |
parent | 8947002607684d6ba7fa6d99511800efe82c1c26 (diff) |
Merge pull request #146 from MitMaro/tim/move-to-modules
Move the rest of the code into modules
-rw-r--r-- | src/application.rs | 250 | ||||
-rw-r--r-- | src/confirm_abort/confirm_abort.rs | 2 | ||||
-rw-r--r-- | src/confirm_rebase/confirm_rebase.rs | 2 | ||||
-rw-r--r-- | src/constants.rs | 16 | ||||
-rw-r--r-- | src/edit/edit.rs | 2 | ||||
-rw-r--r-- | src/error/error.rs | 2 | ||||
-rw-r--r-- | src/exiting/exiting.rs | 17 | ||||
-rw-r--r-- | src/exiting/mod.rs | 4 | ||||
-rw-r--r-- | src/external_editor/external_editor.rs | 4 | ||||
-rw-r--r-- | src/git_interactive.rs | 2 | ||||
-rw-r--r-- | src/help/help.rs | 105 | ||||
-rw-r--r-- | src/help/mod.rs | 4 | ||||
-rw-r--r-- | src/list/list.rs | 299 | ||||
-rw-r--r-- | src/list/mod.rs | 6 | ||||
-rw-r--r-- | src/list/utils.rs | 15 | ||||
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/process/process.rs | 2 | ||||
-rw-r--r-- | src/process/state.rs | 3 | ||||
-rw-r--r-- | src/show_commit/show_commit.rs | 4 | ||||
-rw-r--r-- | src/view/view.rs | 265 | ||||
-rw-r--r-- | src/window_size_error/mod.rs | 4 | ||||
-rw-r--r-- | src/window_size_error/window_size_error.rs | 61 |
22 files changed, 585 insertions, 488 deletions
diff --git a/src/application.rs b/src/application.rs index 5278bc4..e58eb6e 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,30 +1,33 @@ -use crate::action::Action; -use crate::git_interactive::GitInteractive; - use crate::config::Config; use crate::confirm_abort::ConfirmAbort; use crate::confirm_rebase::ConfirmRebase; -use crate::constants::{LIST_HELP_LINES, VISUAL_MODE_HELP_LINES}; use crate::edit::Edit; use crate::error::Error; +use crate::exiting::Exiting; use crate::external_editor::ExternalEditor; -use crate::input::{Input, InputHandler}; -use crate::process::{ExitStatus, HandleInputResult, HandleInputResultBuilder, ProcessModule, ProcessResult, State}; +use crate::git_interactive::GitInteractive; +use crate::help::Help; +use crate::input::InputHandler; +use crate::list::List; +use crate::process::{HandleInputResult, ProcessModule, ProcessResult, State}; use crate::show_commit::ShowCommit; use crate::view::View; -use core::borrow::Borrow; +use crate::window_size_error::WindowSizeError; pub struct Application<'a> { - config: &'a Config, confirm_abort: ConfirmAbort, confirm_rebase: ConfirmRebase, edit: Edit, error: Error, + exiting: Exiting, external_editor: ExternalEditor<'a>, git_interactive: GitInteractive, + help: Help, input_handler: &'a InputHandler<'a>, + list: List<'a>, show_commit: ShowCommit, view: View<'a>, + window_size_error: WindowSizeError, } impl<'a> Application<'a> { @@ -36,36 +39,34 @@ impl<'a> Application<'a> { ) -> Self { Self { - config, confirm_abort: ConfirmAbort::new(), confirm_rebase: ConfirmRebase::new(), edit: Edit::new(), error: Error::new(), + exiting: Exiting::new(), external_editor: ExternalEditor::new(config), git_interactive, + help: Help::new(), input_handler, + list: List::new(config), show_commit: ShowCommit::new(), view, + window_size_error: WindowSizeError::new(), } } - fn get_cursor_index(&self) -> usize { - *self.git_interactive.get_selected_line_index() - 1 - } - pub fn activate(&mut self, state: State) { match state { State::ConfirmAbort => self.confirm_abort.activate(state, &self.git_interactive), State::ConfirmRebase => self.confirm_rebase.activate(state, &self.git_interactive), State::Edit => self.edit.activate(state, &self.git_interactive), State::Error { .. } => self.error.activate(state, &self.git_interactive), - State::Exiting => {}, + State::Exiting => self.exiting.activate(state, &self.git_interactive), State::ExternalEditor => self.external_editor.activate(state, &self.git_interactive), - State::Help(_) => {}, - State::List => {}, + State::Help(_) => self.help.activate(state, &self.git_interactive), + State::List(_) => self.list.activate(state, &self.git_interactive), State::ShowCommit => self.show_commit.activate(state, &self.git_interactive), - State::VisualMode => {}, - State::WindowSizeError(_) => {}, + State::WindowSizeError(_) => self.window_size_error.activate(state, &self.git_interactive), } } @@ -75,13 +76,12 @@ impl<'a> Application<'a> { State::ConfirmRebase => self.confirm_rebase.deactivate(), State::Edit => self.edit.deactivate(), State::Error { .. } => self.error.deactivate(), - State::Exiting => {}, + State::Exiting => self.exiting.deactivate(), State::ExternalEditor => self.external_editor.deactivate(), - State::Help(_) => {}, - State::List => {}, + State::Help(_) => self.help.deactivate(), + State::List(_) => self.list.deactivate(), State::ShowCommit => self.show_commit.deactivate(), - State::VisualMode => {}, - State::WindowSizeError(_) => {}, + State::WindowSizeError(_) => self.window_size_error.deactivate(), } } @@ -91,23 +91,15 @@ impl<'a> Application<'a> { State::ConfirmRebase => self.confirm_rebase.process(&mut self.git_interactive), State::Edit => self.edit.process(&mut self.git_interactive), State::Error { .. } => self.error.process(&mut self.git_interactive), - State::Exiting => ProcessResult::new(), + State::Exiting => self.exiting.process(&mut self.git_interactive), State::ExternalEditor => self.external_editor.process(&mut self.git_interactive), - State::Help(_) => ProcessResult::new(), - State::List => self.process_list(), + State::Help(_) => self.help.process(&mut self.git_interactive), + State::List(_) => self.list.process_with_view(&mut self.git_interactive, &self.view), State::ShowCommit => self.show_commit.process(&mut self.git_interactive), - State::VisualMode => self.process_list(), - State::WindowSizeError(_) => ProcessResult::new(), + State::WindowSizeError(_) => self.window_size_error.process(&mut self.git_interactive), } } - pub fn process_list(&mut self) -> ProcessResult { - let lines = self.git_interactive.get_lines(); - let selected_index = self.get_cursor_index(); - self.view.update_main_top(lines.len(), selected_index); - ProcessResult::new() - } - pub fn check_window_size(&self) -> bool { self.view.check_window_size() } @@ -119,53 +111,16 @@ impl<'a> Application<'a> { State::ConfirmRebase => self.confirm_rebase.render(&self.view, &self.git_interactive), State::Edit => self.edit.render(&self.view, &self.git_interactive), State::Error { .. } => self.error.render(&self.view, &self.git_interactive), - State::Exiting => self.draw_exiting(), + State::Exiting => self.exiting.render(&self.view, &self.git_interactive), State::ExternalEditor => self.external_editor.render(&self.view, &self.git_interactive), - State::Help(help_state) => self.draw_help(help_state.borrow()), - State::List => self.draw_main(false), - State::VisualMode => self.draw_main(true), + State::Help(_) => self.help.render(&self.view, &self.git_interactive), + State::List(_) => self.list.render(&self.view, &self.git_interactive), State::ShowCommit => self.show_commit.render(&self.view, &self.git_interactive), - State::WindowSizeError(_) => self.draw_window_size_error(), + State::WindowSizeError(_) => self.window_size_error.render(&self.view, &self.git_interactive), } self.view.refresh() } - fn draw_main(&self, visual_mode: bool) { - self.view.draw_main( - self.git_interactive.get_lines(), - self.get_cursor_index(), - if visual_mode { - Some(self.git_interactive.get_visual_start_index() - 1) - } - else { - None - }, - ); - } - - fn draw_help(&self, help_state: &State) { - self.view.draw_help( - if *help_state == State::List { - LIST_HELP_LINES - } - else { - VISUAL_MODE_HELP_LINES - }, - ); - } - - fn draw_exiting(&self) { - self.view.draw_exiting(); - } - - fn draw_window_size_error(&self) { - self.view.draw_window_size_error(); - } - - pub fn get_input(&self) -> Input { - self.input_handler.get_input() - } - pub fn handle_input(&mut self, state: State) -> HandleInputResult { match state { State::ConfirmAbort => { @@ -178,150 +133,31 @@ impl<'a> Application<'a> { }, State::Edit => self.edit.handle_input(&self.input_handler, &mut self.git_interactive), State::Error { .. } => self.error.handle_input(&self.input_handler, &mut self.git_interactive), - State::Exiting => HandleInputResult::new(Input::Other), + State::Exiting => { + self.exiting + .handle_input(&self.input_handler, &mut self.git_interactive) + }, State::ExternalEditor => { self.external_editor .handle_input(&self.input_handler, &mut self.git_interactive) }, - State::Help(help_state) => self.handle_help_input(help_state.borrow()), - State::List => self.handle_list_input(), - State::VisualMode => self.handle_visual_mode_input(), + State::Help(_) => { + self.help + .handle_input_with_view(&self.input_handler, &mut self.git_interactive, &self.view) + }, + State::List(_) => self.list.handle_input(&self.input_handler, &mut self.git_interactive), State::ShowCommit => { self.show_commit .handle_input_with_view(&self.input_handler, &mut self.git_interactive, &self.view) }, - State::WindowSizeError(_) => self.handle_window_size_error_input(), - } - } - - fn handle_help_input(&mut self, help_state: &State) -> HandleInputResult { - let help_lines = if *help_state == State::List { - LIST_HELP_LINES - } - else { - VISUAL_MODE_HELP_LINES - }; - let input = self.get_input(); - let mut result = HandleInputResultBuilder::new(input); - match input { - Input::MoveCursorDown => { - self.view.update_help_top(false, false, help_lines); - }, - Input::MoveCursorUp => { - self.view.update_help_top(true, false, help_lines); - }, - Input::Resize => { - self.view.update_help_top(true, true, help_lines); - }, - _ => { - result = result.state(help_state.clone()); - }, - } - result.build() - } - - fn handle_visual_mode_input(&mut self) -> HandleInputResult { - let input = self.get_input(); - let mut result = HandleInputResultBuilder::new(input); - match input { - Input::MoveCursorDown => { - self.git_interactive.move_cursor_down(1); - }, - Input::MoveCursorUp => { - self.git_interactive.move_cursor_up(1); - }, - Input::MoveCursorPageDown => { - self.git_interactive.move_cursor_down(5); - }, - Input::MoveCursorPageUp => { - self.git_interactive.move_cursor_up(5); - }, - Input::ActionDrop => self.git_interactive.set_visual_range_action(Action::Drop), - Input::ActionEdit => self.git_interactive.set_visual_range_action(Action::Edit), - Input::ActionFixup => self.git_interactive.set_visual_range_action(Action::Fixup), - Input::ActionPick => self.git_interactive.set_visual_range_action(Action::Pick), - Input::ActionReword => self.git_interactive.set_visual_range_action(Action::Reword), - Input::ActionSquash => self.git_interactive.set_visual_range_action(Action::Squash), - Input::SwapSelectedDown => self.git_interactive.swap_visual_range_down(), - Input::SwapSelectedUp => self.git_interactive.swap_visual_range_up(), - Input::ToggleVisualMode => { - result = result.state(State::List); - }, - Input::Help => { - self.view.update_help_top(false, true, VISUAL_MODE_HELP_LINES); - result = result.help(State::VisualMode); - }, - _ => {}, - } - result.build() - } - - pub fn handle_list_input(&mut self) -> HandleInputResult { - let input = self.get_input(); - let mut result = HandleInputResultBuilder::new(input); - match input { - Input::Help => { - self.view.update_help_top(false, true, LIST_HELP_LINES); - result = result.help(State::List); - }, - Input::ShowCommit => { - if !self.git_interactive.get_selected_line_hash().is_empty() { - result = result.state(State::ShowCommit); - } - }, - Input::Abort => { - result = result.state(State::ConfirmAbort); - }, - Input::ForceAbort => { - self.git_interactive.clear(); - result = result.exit_status(ExitStatus::Good).state(State::Exiting); - }, - Input::Rebase => { - result = result.state(State::ConfirmRebase); - }, - Input::ForceRebase => { - result = result.exit_status(ExitStatus::Good).state(State::Exiting); - }, - Input::ActionBreak => self.git_interactive.toggle_break(), - Input::ActionDrop => self.set_selected_line_action(Action::Drop), - Input::ActionEdit => self.set_selected_line_action(Action::Edit), - Input::ActionFixup => self.set_selected_line_action(Action::Fixup), - Input::ActionPick => self.set_selected_line_action(Action::Pick), - Input::ActionReword => self.set_selected_line_action(Action::Reword), - Input::ActionSquash => self.set_selected_line_action(Action::Squash), - Input::Edit => { - if *self.git_interactive.get_selected_line_action() == Action::Exec { - result = result.state(State::Edit); - } - }, - Input::SwapSelectedDown => self.git_interactive.swap_selected_down(), - Input::SwapSelectedUp => self.git_interactive.swap_selected_up(), - Input::MoveCursorDown => self.git_interactive.move_cursor_down(1), - Input::MoveCursorUp => self.git_interactive.move_cursor_up(1), - Input::MoveCursorPageDown => self.git_interactive.move_cursor_down(5), - Input::MoveCursorPageUp => self.git_interactive.move_cursor_up(5), - Input::ToggleVisualMode => { - self.git_interactive.start_visual_mode(); - result = result.state(State::VisualMode); + State::WindowSizeError(_) => { + self.window_size_error + .handle_input(&self.input_handler, &mut self.git_interactive) }, - Input::OpenInEditor => result = result.state(State::ExternalEditor), - _ => {}, } - result.build() - } - - pub fn handle_window_size_error_input(&mut self) -> HandleInputResult { - HandleInputResult::new(self.get_input()) } pub fn write_file(&self) -> Result<(), String> { self.git_interactive.write_file() } - - fn set_selected_line_action(&mut self, action: Action) { - self.git_interactive.set_selected_line_action(action); - if self.config.auto_select_next { - self.git_interactive.move_cursor_down(1); - } - } } diff --git a/src/confirm_abort/confirm_abort.rs b/src/confirm_abort/confirm_abort.rs index 0caa8c9..9fe3464 100644 --- a/src/confirm_abort/confirm_abort.rs +++ b/src/confirm_abort/confirm_abort.rs @@ -20,7 +20,7 @@ impl ProcessModule for ConfirmAbort { result = result.exit_status(ExitStatus::Good).state(State::Exiting); }, Input::No => { - result = result.state(State::List); + result = result.state(State::List(false)); }, _ => {}, } diff --git a/src/confirm_rebase/confirm_rebase.rs b/src/confirm_rebase/confirm_rebase.rs index 1189ff2..f341429 100644 --- a/src/confirm_rebase/confirm_rebase.rs +++ b/src/confirm_rebase/confirm_rebase.rs @@ -19,7 +19,7 @@ impl ProcessModule for ConfirmRebase { result = result.exit_status(ExitStatus::Good).state(State::Exiting); }, Input::No => { - result = result.state(State::List); + result = result.state(State::List(false)); }, _ => {}, } diff --git a/src/constants.rs b/src/constants.rs index a51ef37..1b6e40e 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -6,22 +6,22 @@ pub const TITLE_HELP_INDICATOR: &str = "Help: ?"; pub const TITLE_HELP_INDICATOR_LENGTH: i32 = 7; pub const LIST_FOOTER_FULL: &str = " up, down, q/Q, w/W, c, j, k, b, p, r, e, s, f, d, E, !, ?"; -pub const LIST_FOOTER_FULL_WIDTH: i32 = 58; +pub const LIST_FOOTER_FULL_WIDTH: usize = 58; pub const LIST_FOOTER_COMPACT: &str = "up,dn.q/Q,w/W,c,j,k,b,p,r,e,s,f,d,E,!,?"; -pub const LIST_FOOTER_COMPACT_WIDTH: i32 = 39; +pub const LIST_FOOTER_COMPACT_WIDTH: usize = 39; pub const VISUAL_MODE_FOOTER_FULL: &str = "(VISUAL) up, down, j, k, p, r, e, s, f, d, ?"; -pub const VISUAL_MODE_FOOTER_FULL_WIDTH: i32 = 44; +pub const VISUAL_MODE_FOOTER_FULL_WIDTH: usize = 44; pub const VISUAL_MODE_FOOTER_COMPACT: &str = "(V) up,down,j,k,p,r,e,s,f,d,?"; -pub const VISUAL_MODE_FOOTER_COMPACT_WIDTH: i32 = 29; +pub const VISUAL_MODE_FOOTER_COMPACT_WIDTH: usize = 29; pub const HEIGHT_ERROR_MESSAGE: &str = "Window too small, increase height to continue\n"; -pub const MINIMUM_WINDOW_HEIGHT_ERROR_WIDTH: i32 = 45; +pub const MINIMUM_WINDOW_HEIGHT_ERROR_WIDTH: usize = 45; pub const SHORT_ERROR_MESSAGE: &str = "Window too small\n"; -pub const SHORT_ERROR_MESSAGE_WIDTH: i32 = 16; +pub const SHORT_ERROR_MESSAGE_WIDTH: usize = 16; -pub const MINIMUM_WINDOW_HEIGHT: i32 = 5; // title + pad top + line + pad bottom + help -pub const MINIMUM_COMPACT_WINDOW_WIDTH: i32 = 20; //">s ccc mmmmmmmmmmmmm".len() +pub const MINIMUM_WINDOW_HEIGHT: usize = 5; // title + pad top + line + pad bottom + help +pub const MINIMUM_COMPACT_WINDOW_WIDTH: usize = 20; //">s ccc mmmmmmmmmmmmm".len() pub const MINIMUM_FULL_WINDOW_WIDTH: usize = 34; // " > squash cccccccc mmmmmmmmmmmmm %".len() pub const NAME: &str = "interactive-rebase-tool"; diff --git a/src/edit/edit.rs b/src/edit/edit.rs index e7a6fa9..d1dfd6f 100644 --- a/src/edit/edit.rs +++ b/src/edit/edit.rs @@ -35,7 +35,7 @@ impl ProcessModule for Edit { EditState::Active => {}, EditState::Finish => { git_interactive.edit_selected_line(self.content.as_str()); - result = result.state(State::List); + result = result.state(State::List(false)); }, }; result.build() diff --git a/src/error/error.rs b/src/error/error.rs index 2ae68c6..84c4eca 100644 --- a/src/error/error.rs +++ b/src/error/error.rs @@ -49,7 +49,7 @@ impl Error { pub fn new() -> Self { Self { error_message: String::from(""), - return_state: State::List, + return_state: State::List(false), } } } diff --git a/src/exiting/exiting.rs b/src/exiting/exiting.rs new file mode 100644 index 0000000..ef13fa4 --- /dev/null +++ b/src/exiting/exiting.rs @@ -0,0 +1,17 @@ +use crate::git_interactive::GitInteractive; +use crate::process::ProcessModule; +use crate::view::View; + +pub struct Exiting {} + +impl ProcessModule for Exiting { + fn render(&self, view: &View, _git_interactive: &GitInteractive) { + view.draw_str("Exiting...") + } +} + +impl Exiting { + pub fn new() -> Self { + Self {} + } +} diff --git a/src/exiting/mod.rs b/src/exiting/mod.rs new file mode 100644 index 0000000..2d7e7f4 --- /dev/null +++ b/src/exiting/mod.rs @@ -0,0 +1,4 @@ +#[allow(clippy::module_inception)] +mod exiting; + +pub use self::exiting::Exiting; diff --git a/src/external_editor/external_editor.rs b/src/external_editor/external_editor.rs index b7fd186..fe793e8 100644 --- a/src/external_editor/external_editor.rs +++ b/src/external_editor/external_editor.rs @@ -110,7 +110,7 @@ impl<'e> ExternalEditor<'e> { self.state = ExternalEditorState::Error; } else { - result = result.state(State::List); + result = result.state(State::List(false)); } result.build() } @@ -133,7 +133,7 @@ impl<'e> ExternalEditor<'e> { match input { Input::Resize => {}, _ => { - result = result.state(State::List); + result = result.state(State::List(false)); }, } result.build() diff --git a/src/git_interactive.rs b/src/git_interactive.rs index 5099751..3a86501 100644 --- a/src/git_interactive.rs +++ b/src/git_interactive.rs @@ -59,7 +59,7 @@ impl GitInteractive { lines, selected_commit_stats: None, selected_line_index: 1, - visual_index_start: 0, + visual_index_start: 1, }) } diff --git a/src/help/help.rs b/src/help/help.rs new file mode 100644 index 0000000..3eda151 --- /dev/null +++ b/src/help/help.rs @@ -0,0 +1,105 @@ +use crate::constants::{LIST_HELP_LINES, VISUAL_MODE_HELP_LINES}; +use crate::git_interactive::GitInteractive; +use crate::input::{Input, InputHandler}; +use crate::process::{HandleInputResult, HandleInputResultBuilder, ProcessModule, State}; +use crate::scroll::ScrollPosition; +use crate::view::{LineSegment, View, ViewLine}; +use crate::window::WindowColor; + +fn get_help_lines(return_state: &State) -> &[(&str, &str)] { + if let State::List(visual_mode) = *return_state { + if visual_mode { + VISUAL_MODE_HELP_LINES + } + else { + LIST_HELP_LINES + } + } + else { + &[] + } +} + +pub struct Help { + scroll_position: ScrollPosition, + return_state: State, +} + +impl ProcessModule for Help { + fn activate(&mut self, state: State, _git_interactive: &GitInteractive) { + self.scroll_position.reset(); + if let State::Help(return_state) = state { + self.return_state = *return_state; + } + else { + panic!("Help module activated when not expected"); + } + } + + fn render(&self, view: &View, _git_interactive: &GitInteractive) { + let (view_width, view_height) = view.get_view_size(); + + let mut view_lines: Vec<ViewLine> = vec![]; + + for line in get_help_lines(&self.return_state) { + view_lines.push(ViewLine::new(vec![ + LineSegment::new_with_color(format!(" {:4} ", line.0).as_str(), WindowColor::IndicatorColor), + LineSegment::new(line.1), + ])); + } + + view.draw_title(false); + + view.set_color(WindowColor::Foreground); + view.set_style(false, true, false); + view.draw_str(" Key Action"); + if view_width > 13 { + let padding = " ".repeat(view_width - 13); + view.draw_str(padding.as_str()); + } + + view.draw_view_lines(view_lines, self.scroll_position.get_position(), view_height - 3); + + view.set_color(WindowColor::IndicatorColor); + view.draw_str("Any key to close"); + } +} + +impl Help { + pub fn new() -> Self { + Self { + return_state: State::List(false), + scroll_position: ScrollPosition::new(3, 6, 3), + } + } + + // TODO refactor to remove need for view + pub fn handle_input_with_view( + &mut self, + input_handler: &InputHandler, + _git_interactive: &mut GitInteractive, + view: &View, + ) -> HandleInputResult + { + let (_, window_height) = view.get_view_size(); + let input = input_handler.get_input(); + let mut result = HandleInputResultBuilder::new(input); + match input { + Input::MoveCursorDown => { + self.scroll_position + .scroll_down(window_height, get_help_lines(&self.return_state).len()); + }, + Input::MoveCursorUp => { + self.scroll_position + .scroll_up(window_height, get_help_lines(&self.return_state).len()); + }, + Input::Resize => { + self.scroll_position.reset(); + }, + _ => { + result = result.state(self.return_state.clone()); + }, + } + result.build() + } +} diff --git a/src/help/mod.rs b/src/help/mod.rs new file mode 100644 index 0000000..013e492 --- /dev/null +++ b/src/help/mod.rs @@ -0,0 +1,4 @@ +#[allow(clippy::module_inception)] +mod help; + +pub use self::help::Help; diff --git a/src/list/list.rs b/src/list/list.rs new file mode 100644 index 0000000..7676837 --- /dev/null +++ b/src/list/list.rs @@ -0,0 +1,299 @@ +use crate::action::Action; +use crate::config::Config; +use crate::constants::{ + LIST_FOOTER_COMPACT, + LIST_FOOTER_COMPACT_WIDTH, + LIST_FOOTER_FULL, + LIST_FOOTER_FULL_WIDTH, + MINIMUM_FULL_WINDOW_WIDTH, + VISUAL_MODE_FOOTER_COMPACT, + VISUAL_MODE_FOOTER_COMPACT_WIDTH, + VISUAL_MODE_FOOTER_FULL, + VISUAL_MODE_FOOTER_FULL_WIDTH, +}; +use crate::git_interactive::GitInteractive; +use crate::input::{Input, InputHandler}; +use crate::line::Line; +use crate::list::get_action_color; +use crate::process::{ExitStatus, HandleInputResult, HandleInputResultBuilder, ProcessModule, ProcessResult, State}; +use crate::scroll::ScrollPosition; +use crate::view::{LineSegment, View, ViewLine}; +use crate::window::WindowColor; +use std::cmp; + +#[derive(Debug, PartialEq)] +enum ListState { + Normal, + Visual, +} + +pub struct List<'l> { + config: &'l Config, + scroll_position: ScrollPosition, + state: ListState, +} + +impl<'l> ProcessModule for List<'l> { + fn handle_input( + &mut self, + input_handler: &InputHandler, + git_interactive: &mut GitInteractive, + ) -> HandleInputResult + { + match self.state { + ListState::Normal => self.handle_normal_mode_input(input_handler, git_interactive), + ListState::Visual => self.handle_visual_mode_input(input_handler, git_interactive), + } + } + + #[allow(clippy::nonminimal_bool)] + fn render(&self, view: &View, git_interactive: &GitInteractive) { + let (view_width, view_height) = view.get_view_size(); + + let is_visual_mode = self.state == ListState::Visual; + let visual_index = git_interactive.get_visual_start_index() - 1; + + let mut view_lines: Vec<ViewLine> = vec![]; + + let selected_index = *git_interactive.get_selected_line_index() - 1; + + for (index, line) in git_interactive.get_lines().iter().enumerate() { + view_lines.push(ViewLine::new(self.get_todo_line_segments( + line, + selected_index == index, + is_visual_mode + && ((visual_index <= selected_index && index >= visual_index && index <= selected_index) + || (visual_index > selected_index && index >= selected_index && index <= visual_index)), + view_width, + ))); + } + + view.draw_title(true); + + view.draw_view_lines(view_lines, self.scroll_position.get_position(), view_height - 2); + + view.set_color(WindowColor::Foreground); + view.set_style(true, false, false); + if is_visual_mode { + if view_width >= VISUAL_MODE_FOOTER_FULL_WIDTH { + view.draw_str(VISUAL_MODE_FOOTER_FULL); + } + else if view_width >= VISUAL_MODE_FOOTER_COMPACT_WIDTH { + view.draw_str(VISUAL_MODE_FOOTER_COMPACT); + } + else { + view.draw_str("(Visual) Help: ?"); + } + } + else if view_width >= LIST_FOOTER_FULL_WIDTH { + view.draw_str(LIST_FOOTER_FULL); + } + else if view_width >= LIST_FOOTER_COMPACT_WIDTH { + view.draw_str(LIST_FOOTER_COMPACT); + } + else { + view.draw_str("Help: ?"); + } + view.set_style(false, false, false); + } +} + +impl<'l> List<'l> { + pub fn new(config: &'l Config) -> Self { + Self { + config, + scroll_position: ScrollPosition::new(2, 1, 1), + state: ListState::Normal, + } + } + + fn set_selected_line_action(&self, git_interactive: &mut GitInteractive, action: Action) { + git_interactive.set_selected_line_action(action); + if self.config.auto_select_next { + git_interactive.move_cursor_down(1); + } + } + + pub fn process_with_view(&mut self, git_interactive: &mut GitInteractive, view: &View) -> ProcessResult { + let (_, view_height) = view.get_view_size(); + let lines = git_interactive.get_lines(); + let selected_index = *git_interactive.get_selected_line_index() - 1; + self.scroll_position + .ensure_cursor_visible(selected_index, view_height, lines.len()); + ProcessResult::new() + } + + fn handle_normal_mode_input( + &mut self, + input_handler: &InputHandler, + git_interactive: &mut GitInteractive, + ) -> HandleInputResult + { + let input = input_handler.get_input(); + let mut result = HandleInputResultBuilder::new(input); + match input { + Input::Help => { + result = result.help(State::List(false)); + }, + Input::ShowCommit => { + if !git_interactive.get_selected_line_hash().is_empty() { + result = result.state(State::ShowCommit); + } + }, + Input::Abort => { + result = result.state(State::ConfirmAbort); + }, + Input::ForceAbort => { + git_interactive.clear(); + result = result.exit_status(ExitStatus::Good).state(State::Exiting); + }, + Input::Rebase => { + result = result.state(State::ConfirmRebase); + }, + Input::ForceRebase => { + result = result.exit_status(ExitStatus::Good).state(State::Exiting); + }, + Input::ActionBreak => git_interactive.toggle_break(), + Input::ActionDrop => self.set_selected_line_action(git_interactive, Action::Drop), + Input::ActionEdit => self.set_selected_line_action(git_interactive, Action::Edit), + Input::ActionFixup => self.set_selected_line_action(git_interactive, Action::Fixup), + Input::ActionPick => self.set_selected_line_action(git_interactive, Action::Pick), + Input::ActionReword => self.set_selected_line_action(git_interactive, Action::Reword), + Input::ActionSquash => self.set_selected_line_action(git_interactive, Action::Squash), + Input::Edit => { + if *git_interactive.get_selected_line_action() == Action::Exec { + result = result.state(State::Edit); + } + }, + Input::SwapSelectedDown => git_interactive.swap_selected_down(), + Input::SwapSelectedUp => git_interactive.swap_selected_up(), + Input::MoveCursorDown => git_interactive.move_cursor_down(1), + Input::MoveCursorUp => git_interactive.move_cursor_up(1), + Input::MoveCursorPageDown => git_interactive.move_cursor_down(5), + Input::MoveCursorPageUp => git_interactive.move_cursor_up(5), + Input::ToggleVisualMode => { + git_interactive.start_visual_mode(); + self.state = ListState::Visual; + result = result.state(State::List(true)); + }, + Input::OpenInEditor => result = result.state(State::ExternalEditor), + _ => {}, + } + result.build() + } + + fn handle_visual_mode_input( + &mut self, + input_handler: &InputHandler, + g |