1 use crate::display::display_color::DisplayColor; 2 use crate::git_interactive::GitInteractive; 3 use crate::input::input_handler::InputHandler; 4 use crate::input::Input; 5 use crate::process::handle_input_result::HandleInputResult; 6 use crate::process::process_module::ProcessModule; 7 use crate::process::process_result::{ProcessResult, ProcessResultBuilder}; 8 use crate::process::state::State; 9 use crate::view::View; 10 use unicode_segmentation::UnicodeSegmentation; 11 12 #[derive(Clone, Copy, Debug, PartialEq)] 13 enum EditState { 14 Active, 15 Finish, 16 } 17 18 pub(crate) struct Edit { 19 content: String, 20 cursor_position: usize, 21 state: EditState, 22 } 23 24 impl ProcessModule for Edit { activate(&mut self, _state: State, application: &GitInteractive)25 fn activate(&mut self, _state: State, application: &GitInteractive) { 26 self.state = EditState::Active; 27 self.content = application.get_selected_line_edit_content().clone(); 28 self.cursor_position = UnicodeSegmentation::graphemes(self.content.as_str(), true).count(); 29 } 30 deactivate(&mut self)31 fn deactivate(&mut self) { 32 self.content.clear(); 33 self.cursor_position = 0; 34 } 35 process(&mut self, git_interactive: &mut GitInteractive, _view: &View) -> ProcessResult36 fn process(&mut self, git_interactive: &mut GitInteractive, _view: &View) -> ProcessResult { 37 let mut result = ProcessResultBuilder::new(); 38 match self.state { 39 EditState::Active => {}, 40 EditState::Finish => { 41 git_interactive.edit_selected_line(self.content.as_str()); 42 result = result.state(State::List(false)); 43 }, 44 }; 45 result.build() 46 } 47 handle_input( &mut self, input_handler: &InputHandler, _git_interactive: &mut GitInteractive, _view: &View, ) -> HandleInputResult48 fn handle_input( 49 &mut self, 50 input_handler: &InputHandler, 51 _git_interactive: &mut GitInteractive, 52 _view: &View, 53 ) -> HandleInputResult 54 { 55 if self.state == EditState::Finish { 56 return HandleInputResult::new(Input::Enter); 57 } 58 let mut input; 59 loop { 60 input = input_handler.get_character(); 61 match input { 62 Input::Character(c) => { 63 let start = UnicodeSegmentation::graphemes(self.content.as_str(), true) 64 .take(self.cursor_position) 65 .collect::<String>(); 66 let end = UnicodeSegmentation::graphemes(self.content.as_str(), true) 67 .skip(self.cursor_position) 68 .collect::<String>(); 69 self.content = format!("{}{}{}", start, c, end); 70 self.cursor_position += 1; 71 }, 72 Input::Backspace => { 73 if self.cursor_position == 0 { 74 break; 75 } 76 let start = UnicodeSegmentation::graphemes(self.content.as_str(), true) 77 .take(self.cursor_position - 1) 78 .collect::<String>(); 79 let end = UnicodeSegmentation::graphemes(self.content.as_str(), true) 80 .skip(self.cursor_position) 81 .collect::<String>(); 82 self.content = format!("{}{}", start, end); 83 self.cursor_position -= 1; 84 }, 85 Input::Delete => { 86 let length = UnicodeSegmentation::graphemes(self.content.as_str(), true).count(); 87 if self.cursor_position == length { 88 break; 89 } 90 let start = UnicodeSegmentation::graphemes(self.content.as_str(), true) 91 .take(self.cursor_position) 92 .collect::<String>(); 93 let end = UnicodeSegmentation::graphemes(self.content.as_str(), true) 94 .skip(self.cursor_position + 1) 95 .collect::<String>(); 96 self.content = format!("{}{}", start, end); 97 }, 98 Input::MoveCursorRight => { 99 let length = UnicodeSegmentation::graphemes(self.content.as_str(), true).count(); 100 if self.cursor_position < length { 101 self.cursor_position += 1; 102 } 103 }, 104 Input::MoveCursorLeft => { 105 if self.cursor_position != 0 { 106 self.cursor_position -= 1; 107 } 108 }, 109 Input::Enter => self.state = EditState::Finish, 110 _ => { 111 continue; 112 }, 113 } 114 break; 115 } 116 HandleInputResult::new(input) 117 } 118 render(&self, view: &View, _git_interactive: &GitInteractive)119 fn render(&self, view: &View, _git_interactive: &GitInteractive) { 120 let line = self.content.as_str(); 121 let pointer = self.cursor_position; 122 123 view.draw_title(false); 124 view.set_style(false, true, false); 125 view.set_color(DisplayColor::Normal, false); 126 127 // this could probably be made way more efficient 128 let graphemes = UnicodeSegmentation::graphemes(line, true); 129 let segment_length = graphemes.clone().count(); 130 for (counter, c) in graphemes.enumerate() { 131 if counter == pointer { 132 view.set_style(false, true, false); 133 view.draw_str(c); 134 view.set_style(false, false, false); 135 } 136 else { 137 view.draw_str(c); 138 } 139 } 140 if pointer >= segment_length { 141 view.set_style(false, true, false); 142 view.draw_str(" "); 143 view.set_style(false, false, false); 144 } 145 146 view.draw_str("\n\n"); 147 view.set_color(DisplayColor::IndicatorColor, false); 148 view.draw_str("Enter to finish"); 149 } 150 } 151 152 impl Edit { new() -> Self153 pub(crate) fn new() -> Self { 154 Self { 155 content: String::from(""), 156 cursor_position: 0, 157 state: EditState::Active, 158 } 159 } 160 } 161