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