core/components/shared/
editable_line.rs

1use display::DisplayColor;
2use input::{KeyCode, KeyEvent, KeyModifiers};
3use unicode_segmentation::UnicodeSegmentation;
4use view::LineSegment;
5
6use crate::events::Event;
7
8#[derive(Debug, PartialEq, Eq)]
9pub(crate) enum EditAction {
10	CursorMove,
11	ContentUpdate,
12	None,
13}
14
15pub(crate) struct EditableLine {
16	content: String,
17	cursor_position: usize,
18	label: Option<LineSegment>,
19	read_only: bool,
20}
21
22impl EditableLine {
23	pub(crate) const fn new() -> Self {
24		Self {
25			content: String::new(),
26			cursor_position: 0,
27			label: None,
28			read_only: false,
29		}
30	}
31
32	pub(crate) fn set_label(&mut self, label: LineSegment) {
33		self.label = Some(label);
34	}
35
36	pub(crate) fn set_content(&mut self, content: &str) {
37		self.content = String::from(content);
38		self.cursor_position = UnicodeSegmentation::graphemes(content, true).count();
39	}
40
41	pub(crate) fn set_read_only(&mut self, read_only: bool) {
42		self.read_only = read_only;
43	}
44
45	pub(crate) fn clear(&mut self) {
46		self.content.clear();
47		self.cursor_position = 0;
48	}
49
50	pub(crate) fn get_content(&self) -> &str {
51		self.content.as_str()
52	}
53
54	pub(crate) const fn cursor_position(&self) -> usize {
55		self.cursor_position
56	}
57
58	pub(crate) fn line_segments(&self) -> Vec<LineSegment> {
59		if self.read_only {
60			return vec![LineSegment::new(self.get_content())];
61		}
62
63		let line = self.content.as_str();
64		let pointer = self.cursor_position;
65
66		let graphemes = UnicodeSegmentation::graphemes(line, true);
67
68		let start = graphemes.clone().take(pointer).collect::<String>();
69		let indicator = graphemes.clone().skip(pointer).take(1).collect::<String>();
70		let end = graphemes.skip(pointer + 1).collect::<String>();
71
72		let mut segments = vec![];
73		if let Some(label) = self.label.as_ref() {
74			segments.push(label.clone());
75		}
76		if !start.is_empty() {
77			segments.push(LineSegment::new(start.as_str()));
78		}
79		segments.push(
80			if indicator.is_empty() {
81				LineSegment::new_with_color_and_style(" ", DisplayColor::Normal, false, true, false)
82			}
83			else {
84				LineSegment::new_with_color_and_style(indicator.as_str(), DisplayColor::Normal, false, true, false)
85			},
86		);
87		if !end.is_empty() {
88			segments.push(LineSegment::new(end.as_str()));
89		}
90
91		segments
92	}
93
94	pub(crate) fn handle_event(&mut self, event: Event) -> EditAction {
95		if self.read_only {
96			return EditAction::None;
97		}
98		match event {
99			Event::Key(KeyEvent {
100				code: KeyCode::Backspace,
101				modifiers: KeyModifiers::NONE,
102			}) => {
103				if self.cursor_position == 0 {
104					EditAction::None
105				}
106				else {
107					let start = UnicodeSegmentation::graphemes(self.content.as_str(), true)
108						.take(self.cursor_position - 1)
109						.collect::<String>();
110					let end = UnicodeSegmentation::graphemes(self.content.as_str(), true)
111						.skip(self.cursor_position)
112						.collect::<String>();
113					self.content = format!("{start}{end}");
114					self.cursor_position -= 1;
115					EditAction::ContentUpdate
116				}
117			},
118			Event::Key(KeyEvent {
119				code: KeyCode::Delete,
120				modifiers: KeyModifiers::NONE,
121			}) => {
122				let length = UnicodeSegmentation::graphemes(self.content.as_str(), true).count();
123				if self.cursor_position == length {
124					EditAction::None
125				}
126				else {
127					let start = UnicodeSegmentation::graphemes(self.content.as_str(), true)
128						.take(self.cursor_position)
129						.collect::<String>();
130					let end = UnicodeSegmentation::graphemes(self.content.as_str(), true)
131						.skip(self.cursor_position + 1)
132						.collect::<String>();
133					self.content = format!("{start}{end}");
134					EditAction::ContentUpdate
135				}
136			},
137			Event::Key(KeyEvent {
138				code: KeyCode::Home,
139				modifiers: KeyModifiers::NONE,
140			}) => {
141				if self.cursor_position == 0 {
142					EditAction::None
143				}
144				else {
145					self.cursor_position = 0;
146					EditAction::CursorMove
147				}
148			},
149			Event::Key(KeyEvent {
150				code: KeyCode::End,
151				modifiers: KeyModifiers::NONE,
152			}) => {
153				let new_position = UnicodeSegmentation::graphemes(self.content.as_str(), true).count();
154				if new_position == self.cursor_position {
155					EditAction::None
156				}
157				else {
158					self.cursor_position = new_position;
159					EditAction::CursorMove
160				}
161			},
162			Event::Key(KeyEvent {
163				code: KeyCode::Right,
164				modifiers: KeyModifiers::NONE,
165			}) => {
166				let length = UnicodeSegmentation::graphemes(self.content.as_str(), true).count();
167				if self.cursor_position < length {
168					self.cursor_position += 1;
169					EditAction::CursorMove
170				}
171				else {
172					EditAction::None
173				}
174			},
175			Event::Key(KeyEvent {
176				code: KeyCode::Left,
177				modifiers: KeyModifiers::NONE,
178			}) => {
179				if self.cursor_position == 0 {
180					EditAction::None
181				}
182				else {
183					self.cursor_position -= 1;
184					EditAction::CursorMove
185				}
186			},
187			Event::Key(KeyEvent {
188				code: KeyCode::Char(c),
189				modifiers: KeyModifiers::NONE,
190			}) => {
191				let start = UnicodeSegmentation::graphemes(self.content.as_str(), true)
192					.take(self.cursor_position)
193					.collect::<String>();
194				let end = UnicodeSegmentation::graphemes(self.content.as_str(), true)
195					.skip(self.cursor_position)
196					.collect::<String>();
197				self.content = format!("{start}{c}{end}");
198				self.cursor_position += 1;
199				EditAction::ContentUpdate
200			},
201			_ => EditAction::None,
202		}
203	}
204}
205
206#[cfg(test)]
207mod tests {
208	use view::{assert_rendered_output, ViewData, ViewLine};
209
210	use super::*;
211
212	macro_rules! view_data_from_editable_line {
213		($editable_line:expr) => {{
214			let segments = $editable_line.line_segments();
215			&ViewData::new(|updater| updater.push_line(ViewLine::from(segments)))
216		}};
217	}
218
219	fn handle_events(module: &mut EditableLine, events: &[Event]) {
220		for event in events {
221			_ = module.handle_event(*event);
222		}
223	}
224
225	#[test]
226	fn with_label() {
227		let mut editable_line = EditableLine::new();
228		editable_line.set_content("foobar");
229		editable_line.set_label(LineSegment::new("Label: "));
230		assert_rendered_output!(
231			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
232			view_data_from_editable_line!(&editable_line),
233			"{BODY}",
234			"{Normal}Label: foobar{Normal,Underline} "
235		);
236	}
237
238	#[test]
239	fn move_cursor_end() {
240		let mut editable_line = EditableLine::new();
241		editable_line.set_content("foobar");
242		_ = editable_line.handle_event(Event::from(KeyCode::Right));
243		assert_rendered_output!(
244			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
245			view_data_from_editable_line!(&editable_line),
246			"{BODY}",
247			"{Normal}foobar{Normal,Underline} "
248		);
249	}
250
251	#[test]
252	fn move_cursor_1_left() {
253		let mut editable_line = EditableLine::new();
254		editable_line.set_content("foobar");
255		_ = editable_line.handle_event(Event::from(KeyCode::Left));
256		assert_rendered_output!(
257			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
258			view_data_from_editable_line!(&editable_line),
259			"{BODY}",
260			"{Normal}fooba{Normal,Underline}r"
261		);
262	}
263
264	#[test]
265	fn move_cursor_2_from_start() {
266		let mut editable_line = EditableLine::new();
267		editable_line.set_content("foobar");
268		handle_events(&mut editable_line, &[Event::from(KeyCode::Left); 2]);
269		assert_rendered_output!(
270			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
271			view_data_from_editable_line!(&editable_line),
272			"{BODY}",
273			"{Normal}foob{Normal,Underline}a{Normal}r"
274		);
275	}
276
277	#[test]
278	fn move_cursor_1_from_start() {
279		let mut editable_line = EditableLine::new();
280		editable_line.set_content("foobar");
281		handle_events(&mut editable_line, &[Event::from(KeyCode::Left); 5]);
282		assert_rendered_output!(
283			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
284			view_data_from_editable_line!(&editable_line),
285			"{BODY}",
286			"{Normal}f{Normal,Underline}o{Normal}obar"
287		);
288	}
289
290	#[test]
291	fn move_cursor_to_start() {
292		let mut editable_line = EditableLine::new();
293		editable_line.set_content("foobar");
294		handle_events(&mut editable_line, &[Event::from(KeyCode::Left); 6]);
295		assert_rendered_output!(
296			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
297			view_data_from_editable_line!(&editable_line),
298			"{BODY}",
299			"{Normal,Underline}f{Normal}oobar"
300		);
301	}
302
303	#[test]
304	fn move_cursor_to_home() {
305		let mut editable_line = EditableLine::new();
306		editable_line.set_content("foobar");
307		_ = editable_line.handle_event(Event::from(KeyCode::Home));
308		assert_rendered_output!(
309			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
310			view_data_from_editable_line!(&editable_line),
311			"{BODY}",
312			"{Normal,Underline}f{Normal}oobar"
313		);
314	}
315
316	#[test]
317	fn move_cursor_right() {
318		let mut editable_line = EditableLine::new();
319		editable_line.set_content("foobar");
320		handle_events(&mut editable_line, &[
321			Event::from(KeyCode::Left),
322			Event::from(KeyCode::Left),
323			Event::from(KeyCode::Left),
324			Event::from(KeyCode::Right),
325		]);
326		assert_rendered_output!(
327			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
328			view_data_from_editable_line!(&editable_line),
329			"{BODY}",
330			"{Normal}foob{Normal,Underline}a{Normal}r"
331		);
332	}
333
334	#[test]
335	fn move_cursor_to_end() {
336		let mut editable_line = EditableLine::new();
337		editable_line.set_content("foobar");
338		handle_events(&mut editable_line, &[
339			Event::from(KeyCode::Left),
340			Event::from(KeyCode::Left),
341			Event::from(KeyCode::Left),
342			Event::from(KeyCode::End),
343		]);
344		assert_rendered_output!(
345			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
346			view_data_from_editable_line!(&editable_line),
347			"{BODY}",
348			"{Normal}foobar{Normal,Underline} "
349		);
350	}
351
352	#[test]
353	fn move_cursor_on_empty_content() {
354		let mut editable_line = EditableLine::new();
355		handle_events(&mut editable_line, &[
356			Event::from(KeyCode::Left),
357			Event::from(KeyCode::Right),
358			Event::from(KeyCode::End),
359			Event::from(KeyCode::Home),
360		]);
361		assert_rendered_output!(
362			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
363			view_data_from_editable_line!(&editable_line),
364			"{BODY}",
365			"{Normal,Underline} "
366		);
367	}
368
369	#[test]
370	fn move_cursor_attempt_past_start() {
371		let mut editable_line = EditableLine::new();
372		editable_line.set_content("foobar");
373		handle_events(&mut editable_line, &[Event::from(KeyCode::Left); 10]);
374		assert_rendered_output!(
375			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
376			view_data_from_editable_line!(&editable_line),
377			"{BODY}",
378			"{Normal,Underline}f{Normal}oobar"
379		);
380	}
381
382	#[test]
383	fn move_cursor_attempt_past_end() {
384		let mut editable_line = EditableLine::new();
385		editable_line.set_content("foobar");
386		handle_events(&mut editable_line, &[Event::from(KeyCode::Right); 10]);
387		assert_rendered_output!(
388			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
389			view_data_from_editable_line!(&editable_line),
390			"{BODY}",
391			"{Normal}foobar{Normal,Underline} "
392		);
393	}
394
395	#[test]
396	fn multiple_width_unicode_single_width() {
397		let mut editable_line = EditableLine::new();
398		editable_line.set_content("a🗳b");
399		handle_events(&mut editable_line, &[Event::from(KeyCode::Left); 2]);
400		assert_rendered_output!(
401			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
402			view_data_from_editable_line!(&editable_line),
403			"{BODY}",
404			"{Normal}a{Normal,Underline}🗳{Normal}b"
405		);
406	}
407
408	#[test]
409	fn multiple_width_unicode_emoji() {
410		let mut editable_line = EditableLine::new();
411		editable_line.set_content("a😀b");
412		handle_events(&mut editable_line, &[Event::from(KeyCode::Left); 2]);
413		assert_rendered_output!(
414			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
415			view_data_from_editable_line!(&editable_line),
416			"{BODY}",
417			"{Normal}a{Normal,Underline}😀{Normal}b"
418		);
419	}
420
421	#[test]
422	fn add_character_end() {
423		let mut editable_line = EditableLine::new();
424		editable_line.set_content("abcd");
425		_ = editable_line.handle_event(Event::from('x'));
426		assert_rendered_output!(
427			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
428			view_data_from_editable_line!(&editable_line),
429			"{BODY}",
430			"{Normal}abcdx{Normal,Underline} "
431		);
432	}
433
434	#[test]
435	fn add_character_one_from_end() {
436		let mut editable_line = EditableLine::new();
437		editable_line.set_content("abcd");
438		handle_events(&mut editable_line, &[Event::from(KeyCode::Left), Event::from('x')]);
439		assert_rendered_output!(
440			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
441			view_data_from_editable_line!(&editable_line),
442			"{BODY}",
443			"{Normal}abcx{Normal,Underline}d"
444		);
445	}
446
447	#[test]
448	fn add_character_one_from_start() {
449		let mut editable_line = EditableLine::new();
450		editable_line.set_content("abcd");
451		handle_events(&mut editable_line, &[
452			Event::from(KeyCode::Left),
453			Event::from(KeyCode::Left),
454			Event::from(KeyCode::Left),
455			Event::from('x'),
456		]);
457		assert_rendered_output!(
458			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
459			view_data_from_editable_line!(&editable_line),
460			"{BODY}",
461			"{Normal}ax{Normal,Underline}b{Normal}cd"
462		);
463	}
464
465	#[test]
466	fn add_character_at_start() {
467		let mut editable_line = EditableLine::new();
468		editable_line.set_content("abcd");
469		handle_events(&mut editable_line, &[
470			Event::from(KeyCode::Left),
471			Event::from(KeyCode::Left),
472			Event::from(KeyCode::Left),
473			Event::from(KeyCode::Left),
474			Event::from('x'),
475		]);
476		assert_rendered_output!(
477			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
478			view_data_from_editable_line!(&editable_line),
479			"{BODY}",
480			"{Normal}x{Normal,Underline}a{Normal}bcd"
481		);
482	}
483
484	#[test]
485	fn add_character_uppercase() {
486		let mut editable_line = EditableLine::new();
487		editable_line.set_content("abcd");
488		_ = editable_line.handle_event(Event::from(KeyCode::Char('X')));
489		assert_rendered_output!(
490			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
491			view_data_from_editable_line!(&editable_line),
492			"{BODY}",
493			"{Normal}abcdX{Normal,Underline} "
494		);
495	}
496
497	#[test]
498	fn backspace_at_end() {
499		let mut editable_line = EditableLine::new();
500		editable_line.set_content("abcd");
501		_ = editable_line.handle_event(Event::from(KeyCode::Backspace));
502		assert_rendered_output!(
503			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
504			view_data_from_editable_line!(&editable_line),
505			"{BODY}",
506			"{Normal}abc{Normal,Underline} "
507		);
508	}
509
510	#[test]
511	fn backspace_one_from_end() {
512		let mut editable_line = EditableLine::new();
513		editable_line.set_content("abcd");
514		handle_events(&mut editable_line, &[
515			Event::from(KeyCode::Left),
516			Event::from(KeyCode::Backspace),
517		]);
518		assert_rendered_output!(
519			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
520			view_data_from_editable_line!(&editable_line),
521			"{BODY}",
522			"{Normal}ab{Normal,Underline}d"
523		);
524	}
525
526	#[test]
527	fn backspace_one_from_start() {
528		let mut editable_line = EditableLine::new();
529		editable_line.set_content("abcd");
530		handle_events(&mut editable_line, &[
531			Event::from(KeyCode::Left),
532			Event::from(KeyCode::Left),
533			Event::from(KeyCode::Left),
534			Event::from(KeyCode::Backspace),
535		]);
536		assert_rendered_output!(
537			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
538			view_data_from_editable_line!(&editable_line),
539			"{BODY}",
540			"{Normal,Underline}b{Normal}cd"
541		);
542	}
543
544	#[test]
545	fn backspace_at_start() {
546		let mut editable_line = EditableLine::new();
547		editable_line.set_content("abcd");
548		handle_events(&mut editable_line, &[
549			Event::from(KeyCode::Left),
550			Event::from(KeyCode::Left),
551			Event::from(KeyCode::Left),
552			Event::from(KeyCode::Left),
553			Event::from(KeyCode::Backspace),
554		]);
555		assert_rendered_output!(
556			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
557			view_data_from_editable_line!(&editable_line),
558			"{BODY}",
559			"{Normal,Underline}a{Normal}bcd"
560		);
561	}
562
563	#[test]
564	fn delete_at_end() {
565		let mut editable_line = EditableLine::new();
566		editable_line.set_content("abcd");
567		_ = editable_line.handle_event(Event::from(KeyCode::Delete));
568		assert_rendered_output!(
569			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
570			view_data_from_editable_line!(&editable_line),
571			"{BODY}",
572			"{Normal}abcd{Normal,Underline} "
573		);
574	}
575
576	#[test]
577	fn delete_last_character() {
578		let mut editable_line = EditableLine::new();
579		editable_line.set_content("abcd");
580		handle_events(&mut editable_line, &[
581			Event::from(KeyCode::Left),
582			Event::from(KeyCode::Delete),
583		]);
584		assert_rendered_output!(
585			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
586			view_data_from_editable_line!(&editable_line),
587			"{BODY}",
588			"{Normal}abc{Normal,Underline} "
589		);
590	}
591
592	#[test]
593	fn delete_second_character() {
594		let mut editable_line = EditableLine::new();
595		editable_line.set_content("abcd");
596		handle_events(&mut editable_line, &[
597			Event::from(KeyCode::Left),
598			Event::from(KeyCode::Left),
599			Event::from(KeyCode::Left),
600			Event::from(KeyCode::Delete),
601		]);
602		assert_rendered_output!(
603			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
604			view_data_from_editable_line!(&editable_line),
605			"{BODY}",
606			"{Normal}a{Normal,Underline}c{Normal}d"
607		);
608	}
609
610	#[test]
611	fn delete_first_character() {
612		let mut editable_line = EditableLine::new();
613		editable_line.set_content("abcd");
614		handle_events(&mut editable_line, &[
615			Event::from(KeyCode::Left),
616			Event::from(KeyCode::Left),
617			Event::from(KeyCode::Left),
618			Event::from(KeyCode::Left),
619			Event::from(KeyCode::Delete),
620		]);
621		assert_rendered_output!(
622			Options AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE,
623			view_data_from_editable_line!(&editable_line),
624			"{BODY}",
625			"{Normal,Underline}b{Normal}cd"
626		);
627	}
628
629	#[test]
630	fn ignore_other_input() {
631		let mut editable_line = EditableLine::new();
632		_ = editable_line.handle_event(Event::from(KeyCode::Null));
633	}
634
635	#[test]
636	fn ignore_input_on_readonly() {
637		let mut editable_line = EditableLine::new();
638		editable_line.set_content("abcd");
639		editable_line.set_read_only(true);
640		_ = editable_line.handle_event(Event::from(KeyCode::Home));
641		assert_eq!(editable_line.cursor_position(), 4);
642	}
643
644	#[test]
645	fn set_get_content() {
646		let mut editable_line = EditableLine::new();
647		editable_line.set_content("abcd");
648		assert_eq!(editable_line.cursor_position(), 4);
649		assert_eq!(editable_line.get_content(), "abcd");
650	}
651
652	#[test]
653	fn set_readonly() {
654		let mut editable_line = EditableLine::new();
655		editable_line.set_content("abcd");
656		editable_line.set_read_only(true);
657		assert_eq!(editable_line.line_segments().len(), 1);
658	}
659
660	#[test]
661	fn clear_content() {
662		let mut editable_line = EditableLine::new();
663		editable_line.set_content("abcd");
664		editable_line.clear();
665		assert_eq!(editable_line.cursor_position(), 0);
666		assert_eq!(editable_line.get_content(), "");
667	}
668
669	#[test]
670	fn handle_event_edit_action_backspace_with_content() {
671		let mut editable_line = EditableLine::new();
672		editable_line.set_content("abcd");
673		assert_eq!(
674			editable_line.handle_event(Event::from(KeyCode::Backspace)),
675			EditAction::ContentUpdate
676		);
677	}
678
679	#[test]
680	fn handle_event_edit_action_backspace_without_content() {
681		let mut editable_line = EditableLine::new();
682		editable_line.set_content("");
683		assert_eq!(
684			editable_line.handle_event(Event::from(KeyCode::Backspace)),
685			EditAction::None
686		);
687	}
688
689	#[test]
690	fn handle_event_edit_action_delete_with_content() {
691		let mut editable_line = EditableLine::new();
692		editable_line.set_content("abcd");
693		editable_line.cursor_position = 0;
694		assert_eq!(
695			editable_line.handle_event(Event::from(KeyCode::Delete)),
696			EditAction::ContentUpdate
697		);
698	}
699
700	#[test]
701	fn handle_event_edit_action_delete_without_content() {
702		let mut editable_line = EditableLine::new();
703		editable_line.set_content("");
704		assert_eq!(
705			editable_line.handle_event(Event::from(KeyCode::Delete)),
706			EditAction::None
707		);
708	}
709
710	#[test]
711	fn handle_event_edit_action_home_with_cursor_change() {
712		let mut editable_line = EditableLine::new();
713		editable_line.set_content("abcd");
714		assert_eq!(
715			editable_line.handle_event(Event::from(KeyCode::Home)),
716			EditAction::CursorMove
717		);
718	}
719
720	#[test]
721	fn handle_event_edit_action_home_without_cursor_change() {
722		let mut editable_line = EditableLine::new();
723		editable_line.set_content("abcd");
724		editable_line.cursor_position = 0;
725		assert_eq!(editable_line.handle_event(Event::from(KeyCode::Home)), EditAction::None);
726	}
727
728	#[test]
729	fn handle_event_edit_action_end_with_cursor_change() {
730		let mut editable_line = EditableLine::new();
731		editable_line.set_content("abcd");
732		editable_line.cursor_position = 0;
733		assert_eq!(
734			editable_line.handle_event(Event::from(KeyCode::End)),
735			EditAction::CursorMove
736		);
737	}
738
739	#[test]
740	fn handle_event_edit_action_end_without_cursor_change() {
741		let mut editable_line = EditableLine::new();
742		editable_line.set_content("abcd");
743		assert_eq!(editable_line.handle_event(Event::from(KeyCode::End)), EditAction::None);
744	}
745
746	#[test]
747	fn handle_event_edit_action_right_with_cursor_change() {
748		let mut editable_line = EditableLine::new();
749		editable_line.set_content("abcd");
750		editable_line.cursor_position = 0;
751		assert_eq!(
752			editable_line.handle_event(Event::from(KeyCode::Right)),
753			EditAction::CursorMove
754		);
755	}
756
757	#[test]
758	fn handle_event_edit_action_right_without_cursor_change() {
759		let mut editable_line = EditableLine::new();
760		editable_line.set_content("abcd");
761		assert_eq!(
762			editable_line.handle_event(Event::from(KeyCode::Right)),
763			EditAction::None
764		);
765	}
766
767	#[test]
768	fn handle_event_edit_action_left_with_cursor_change() {
769		let mut editable_line = EditableLine::new();
770		editable_line.set_content("abcd");
771		assert_eq!(
772			editable_line.handle_event(Event::from(KeyCode::Left)),
773			EditAction::CursorMove
774		);
775	}
776
777	#[test]
778	fn handle_event_edit_action_left_without_cursor_change() {
779		let mut editable_line = EditableLine::new();
780		editable_line.set_content("abcd");
781		editable_line.cursor_position = 0;
782		assert_eq!(editable_line.handle_event(Event::from(KeyCode::Left)), EditAction::None);
783	}
784
785	#[test]
786	fn handle_event_edit_action_new_character() {
787		let mut editable_line = EditableLine::new();
788		editable_line.set_content("abcd");
789		assert_eq!(
790			editable_line.handle_event(Event::from(KeyCode::Char('a'))),
791			EditAction::ContentUpdate
792		);
793	}
794
795	#[test]
796	fn handle_event_edit_action_other() {
797		let mut editable_line = EditableLine::new();
798		editable_line.set_content("abcd");
799		assert_eq!(editable_line.handle_event(Event::from(KeyCode::Esc)), EditAction::None);
800	}
801}