rm_lisa/input/stdin/
mod.rs

1//! Receive input from a tty attached to STDIN.
2//!
3//! Basically allow a user to just type into their terminal, and not have to do
4//! anything else.
5
6mod os_specific_apis;
7mod read_provider;
8pub mod replacement;
9mod state;
10
11use crate::{
12	app_name_to_prefix,
13	errors::LisaError,
14	input::{
15		InputProvider, TerminalInputEvent,
16		autocomplete::AutocompleteProvider,
17		history::HistoryProvider,
18		stdin::{
19			os_specific_apis::{
20				CachedModeType, default_cached_mode, disable_raw_mode, enable_raw_mode,
21				os_pre_reqs, raise_sigint,
22			},
23			read_provider::ReadProvider,
24			replacement::parse_character_replacements,
25			state::{StdinInputState, transaction::InputStateTransaction},
26		},
27	},
28};
29use arboard::Clipboard;
30use fnv::FnvHashMap;
31use parking_lot::Mutex;
32#[cfg(test)]
33use std::collections::VecDeque;
34use std::{
35	env::var as env_var,
36	hash::BuildHasherDefault,
37	io::Stdin,
38	sync::{
39		OnceLock,
40		atomic::{AtomicBool, Ordering},
41	},
42};
43
44/// A function to determine if we should insert a newline instead of sending a
45/// message.
46type NewlineHookFn = dyn Fn(&str) -> bool + Send + Sync;
47
48/// A global clipboard lock.
49///
50/// Clipboards can't always be accessed by multiple places at once, in fact
51/// usually only one thread at a time. Properly abstracting away all clipboards
52/// and not just accessing them with a lock would add so so so so so much
53/// complexity that this is by far the easier option.
54static CLIPBOARD_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
55
56/// Test functionality for overriding the clipboard without modifying it.
57#[cfg(test)]
58static TEST_OVERRIDE_CLIPBOARD: OnceLock<Mutex<VecDeque<String>>> = OnceLock::new();
59
60/// An input provider which reads input from 'STDIN', or standard in.
61pub struct StdinInputProvider<ReadTy: ReadProvider> {
62	/// Allow showing, and hiding the terminal input.
63	active: AtomicBool,
64	/// The current autocomplete provider if one exists.
65	autocomplete_provider: Option<Box<dyn AutocompleteProvider>>,
66	/// The cached
67	cached_mode: CachedModeType,
68	/// The currently active input that users are typing.
69	current_input: StdinInputState,
70	/// Any completed inputs since last checked.
71	completed_inputs: Vec<String>,
72	/// Something that can provide us a history of commands.
73	history_provider: Option<Box<dyn HistoryProvider>>,
74	/// If 'raw mode' has been enabled on our terminal.
75	in_raw_mode: AtomicBool,
76	/// Whether or not we should actually process a newline as a newline.
77	newline_hook: Option<Box<NewlineHookFn>>,
78	/// The provider of the read function.
79	read_provider: ReadTy,
80	/// The replacements as specified by the environment.
81	replacements: FnvHashMap<char, Option<char>>,
82}
83
84impl StdinInputProvider<Stdin> {
85	/// Create a new standard input provider.
86	///
87	/// ## Errors
88	///
89	/// Will error if the user has specified remappings that can't happen (as
90	/// determined by the environment variable `${APP_NAME}_STTY_REPLACEMENTS`).
91	///
92	/// Additionally only error on Windows OS machines, when we fail to load in
93	/// the library for `ReadConsoleEx` and various other functions that are
94	/// necessary.
95	///
96	/// These should always be present in the kernel library.
97	pub fn new(app_name: &'static str) -> Result<Self, LisaError> {
98		os_pre_reqs()?;
99
100		let environment_prefix = app_name_to_prefix(app_name);
101		let replacements =
102			if let Ok(env_var_value) = env_var(format!("{environment_prefix}_STTY_REPLACEMENTS")) {
103				parse_character_replacements(&env_var_value)?
104			} else {
105				FnvHashMap::with_capacity_and_hasher(0, BuildHasherDefault::default())
106			};
107
108		Ok(Self {
109			active: AtomicBool::new(false),
110			autocomplete_provider: None,
111			cached_mode: default_cached_mode(),
112			current_input: StdinInputState::new(),
113			completed_inputs: Vec::with_capacity(1),
114			history_provider: None,
115			in_raw_mode: AtomicBool::new(false),
116			newline_hook: None,
117			read_provider: std::io::stdin(),
118			replacements,
119		})
120	}
121}
122
123impl<ReadTy: ReadProvider> StdinInputProvider<ReadTy> {
124	/// Create a new STDIN Input provider given an explicit read provider.
125	///
126	/// ## Errors
127	///
128	/// Will error if the user has specified remappings that can't happen (as
129	/// determined by the environment variable `${APP_NAME}_STTY_REPLACEMENTS`).
130	///
131	/// Additionally only error on Windows OS machines, when we fail to load in
132	/// the library for `ReadConsoleEx` and various other functions that are
133	/// necessary.
134	///
135	/// These should always be present in the kernel library.
136	pub fn new_with_provider(app_name: &'static str, provider: ReadTy) -> Result<Self, LisaError> {
137		os_pre_reqs()?;
138
139		let environment_prefix = app_name_to_prefix(app_name);
140		let replacements =
141			if let Ok(env_var_value) = env_var(format!("{environment_prefix}_STTY_REPLACEMENTS")) {
142				parse_character_replacements(&env_var_value)?
143			} else {
144				FnvHashMap::with_capacity_and_hasher(0, BuildHasherDefault::default())
145			};
146
147		Ok(Self {
148			active: AtomicBool::new(false),
149			autocomplete_provider: None,
150			cached_mode: default_cached_mode(),
151			current_input: StdinInputState::new(),
152			completed_inputs: Vec::with_capacity(1),
153			history_provider: None,
154			in_raw_mode: AtomicBool::new(false),
155			newline_hook: None,
156			read_provider: provider,
157			replacements,
158		})
159	}
160
161	/// Manually poll the STDIN input without anything else.
162	///
163	/// ***NOTE: THIS WILL CONSUME THE INPUT AND WILL NOT PASS IT ON TO THE
164	/// NORMAL HANDLING ROUTINES. IT WOULD BE YOUR JOB TO HANDLE THIS INPUT.***
165	///
166	/// ## Errors
167	///
168	/// If there is an OS error reading from standard in.
169	pub fn manually_poll_input_and_consume(&self) -> Result<String, LisaError> {
170		self.read_provider.non_blocking_read().map(|string| {
171			string
172				.chars()
173				.filter_map(|character| {
174					if let Some(replacement) = self.replacements.get(&character) {
175						*replacement
176					} else {
177						Some(character)
178					}
179				})
180				.collect::<String>()
181		})
182	}
183
184	/// Set the newline hook.
185	///
186	/// A newline hook tells us if we should actually 'complete' the input. If
187	/// this function returns true the newline is assumed to be marked as
188	/// 'completed', if false input is still continuing.
189	pub fn set_newline_hook(&mut self, hook: Box<dyn Fn(&str) -> bool + Send + Sync>) {
190		_ = self.newline_hook.insert(hook);
191	}
192
193	/// Process the input stream, and turn them into terminal events.
194	///
195	/// This inputs will attempt to optimize inputs constantly cancelling, and
196	/// then starting over and over again. By collapsing all cancel/start events
197	/// to the begin.
198	fn process_inputs(&mut self, item: &str, ansi_supported: bool) -> Vec<TerminalInputEvent> {
199		// Gets appended to everytime an input is cancelled/started/finished.
200		let mut input_events = Vec::with_capacity(0);
201		// Prevent current input from being locked too long...
202		let had_started = self.current_input.started();
203		let append_ledger = self.calculate_append_ledger(&mut input_events, item, ansi_supported);
204
205		let item_events = if let Some(ledger) = append_ledger {
206			if ledger.is_empty() {
207				Vec::with_capacity(0)
208			} else if ledger.len() == 1
209				&& let Some(char) = ledger.chars().next()
210			{
211				vec![TerminalInputEvent::InputAppend(char)]
212			} else if !ledger.is_empty() {
213				vec![TerminalInputEvent::InputMassAppend(ledger)]
214			} else {
215				Vec::with_capacity(0)
216			}
217		} else if !input_events.last().is_some_and(|ie| {
218			matches!(
219				ie,
220				TerminalInputEvent::CursorMoveLeft(_) | TerminalInputEvent::CursorMoveRight(_)
221			)
222		}) {
223			input_events.truncate(0);
224			if had_started {
225				vec![TerminalInputEvent::InputChanged(
226					self.current_input.cursor_position(),
227				)]
228			} else {
229				vec![
230					TerminalInputEvent::InputStarted,
231					TerminalInputEvent::InputChanged(self.current_input.cursor_position()),
232				]
233			}
234		} else {
235			// We started, we can just safely append...
236			let buffer = self.current_input();
237			if buffer.is_empty() {
238				Vec::with_capacity(0)
239			} else {
240				input_events.truncate(0);
241				if had_started {
242					vec![TerminalInputEvent::InputChanged(
243						self.current_input.cursor_position(),
244					)]
245				} else {
246					vec![
247						TerminalInputEvent::InputStarted,
248						TerminalInputEvent::InputChanged(self.current_input.cursor_position()),
249					]
250				}
251			}
252		};
253		input_events.extend(item_events);
254		input_events
255	}
256
257	/// Process all the characters in a string, and produce an 'append' buffer.
258	///
259	/// This append buffer is empty if the input changed, or present with some data
260	/// (potentially empty data).
261	fn calculate_append_ledger(
262		&mut self,
263		input_events: &mut Vec<TerminalInputEvent>,
264		item: &str,
265		ansi_supported: bool,
266	) -> Option<String> {
267		let mut processing = self.current_input.process();
268
269		let mut processed_amount = 0_usize;
270		for character in item.chars() {
271			processed_amount += 1;
272			if Self::route_character(
273				self.autocomplete_provider.as_deref(),
274				self.history_provider.as_deref(),
275				self.newline_hook.as_deref(),
276				ansi_supported,
277				character,
278				&mut processing,
279			) {
280				// we're going down it's fine to drop.
281				std::mem::drop(processing);
282				self.raise_sigint();
283				return None;
284			}
285
286			if let Some((move_left, move_amount, buffer, original_position)) = processing
287				.did_trigger_multibyte_cursor(
288					self.autocomplete_provider.as_deref(),
289					self.history_provider.as_deref(),
290					ansi_supported,
291				) {
292				if let Some(buff) = buffer {
293					if buff.is_empty() {
294						// do nothing!
295					} else if buff.len() == 1 {
296						input_events.push(TerminalInputEvent::InputAppend(
297							buff.chars().next().unwrap_or_else(|| unreachable!()),
298						));
299					} else {
300						input_events.push(TerminalInputEvent::InputMassAppend(buff));
301					}
302				} else if processed_amount > 0 {
303					input_events.push(TerminalInputEvent::InputChanged(original_position));
304				}
305
306				if move_left {
307					input_events.push(TerminalInputEvent::CursorMoveLeft(move_amount));
308				} else {
309					input_events.push(TerminalInputEvent::CursorMoveRight(move_amount));
310				}
311			}
312			if processing.did_trigger_multibyte_paste()
313				&& let Some(data) = Self::get_clipboard()
314			{
315				processing.new_string_action(&data, self.history_provider.as_deref());
316			}
317			if processing.did_trigger_start() {
318				input_events.push(TerminalInputEvent::InputStarted);
319			}
320			if processing.did_trigger_cancel() {
321				if input_events.last() == Some(&TerminalInputEvent::InputStarted) {
322					input_events.pop();
323				} else {
324					input_events.push(TerminalInputEvent::InputCancelled);
325				}
326			}
327			if processing.did_trigger_clear() {
328				input_events.push(TerminalInputEvent::ClearScreen);
329				// Let our displays resynchronize.
330				input_events.push(TerminalInputEvent::InputChanged(
331					processing.cursor_position(),
332				));
333			}
334			if processing.did_trigger_pause() {
335				input_events.push(TerminalInputEvent::ToggleOutputPause);
336			}
337			if let Some((append_buffer, finished)) = processing.did_trigger_finish() {
338				if let Some(hprovider) = self.history_provider.as_ref() {
339					hprovider.insert_command(&finished);
340				}
341				self.completed_inputs.push(finished);
342
343				if let Some(append) = append_buffer {
344					if append.len() == 1
345						&& let Some(character) = append.chars().next()
346					{
347						input_events.push(TerminalInputEvent::InputAppend(character));
348					} else if !append.is_empty() {
349						input_events.push(TerminalInputEvent::InputMassAppend(append));
350					}
351				} else {
352					input_events.push(TerminalInputEvent::InputChanged(
353						processing.cursor_position(),
354					));
355				}
356				input_events.push(TerminalInputEvent::InputFinished);
357			}
358		}
359
360		processing.finished_processing()
361	}
362
363	/// Match a single byte character into the correct processing input state.
364	///
365	/// Returns if we should raise a SIGINT.
366	fn route_character(
367		autocomplete_provider: Option<&dyn AutocompleteProvider>,
368		history_provider: Option<&dyn HistoryProvider>,
369		newline_hook: Option<&NewlineHookFn>,
370		ansi_supported: bool,
371		character: char,
372		processing: &mut InputStateTransaction<'_>,
373	) -> bool {
374		match character {
375			// Some terminals send nul bytes while shift is being held...
376			// don't pass those on.
377			'\u{0}' => {}
378			// Backspace character, and 'Ctrl-H'
379			'\u{7f}' | '\u{8}' => processing.backspace(),
380			// Ctrl-C
381			'\u{3}' => {
382				if !processing.cancel() {
383					return true;
384				}
385			}
386			// Ctrl-D, 'EOF'
387			//
388			// Normally Ctrl-D _doesn't_ raise a sigint, but in a shell
389			// environment closing STDIN, closes the shell. To keep the same
390			// feel we also raise SIGINT which we consider 'exit'.
391			'\u{4}' => {
392				return true;
393			}
394			// Ctrl-V
395			'\u{16}' => {
396				if let Some(data) = Self::get_clipboard() {
397					processing.new_string_action(&data, history_provider);
398				}
399			}
400			// Ctrl-P
401			'\u{10}' => processing.previous_history_action(history_provider),
402			// Ctrl-N
403			'\u{e}' => processing.next_history_action(history_provider),
404			// Ctrl-L
405			'\u{c}' => processing.clear(),
406			// Ctrl-S
407			'\u{13}' => processing.pause_output(),
408			// Ctrl-B
409			'\u{2}' => {
410				if ansi_supported {
411					_ = processing.move_cursor_left(1);
412				}
413			}
414			// Ctrl-F
415			'\u{6}' => {
416				if ansi_supported {
417					_ = processing.move_cursor_right(1);
418				}
419			}
420			// Ctrl-A
421			'\u{1}' => {
422				if ansi_supported {
423					_ = processing.move_cursor_left(processing.cursor_position() + 1);
424				}
425			}
426			// Ctrl-E
427			'\u{5}' => {
428				if ansi_supported {
429					_ = processing.move_cursor_right(processing.current_input().len());
430				}
431			}
432			// Ctrl-U
433			'\u{15}' => {
434				if ansi_supported {
435					processing.uwk_cursor_to_begin();
436				}
437			}
438			// Ctrl-K
439			'\u{b}' => {
440				if ansi_supported {
441					processing.uwk_cursor_to_end();
442				}
443			}
444			// Ctrl-W
445			'\u{17}' => {
446				if ansi_supported {
447					processing.uwk_cursor_word();
448				}
449			}
450			// Ctrl-Y
451			'\u{19}' => {
452				if ansi_supported {
453					processing.paste_uwk();
454				}
455			}
456			// Ctrl-T
457			'\u{14}' => {
458				if ansi_supported {
459					processing.swap_last_two_characters();
460				}
461			}
462			// Ctrl-G
463			'\u{7}' => _ = processing.cancel(),
464			// Ctrl-R
465			'\u{12}' => {
466				if ansi_supported {
467					processing.start_history_search_action(history_provider);
468				}
469			}
470			// Ctrl-J (also 'shift-enter' in git bash).
471			//
472			// If you're looking for 'enter' handling look for '\r'
473			'\n' => processing.complete(),
474			// Enter key is pressed.
475			'\r' => {
476				if let Some(hook) = newline_hook {
477					if (*hook)(processing.current_input().as_ref()) {
478						processing.complete();
479					} else {
480						processing.new_character_action('\n', history_provider);
481					}
482				} else {
483					processing.complete();
484				}
485			}
486			// Tab key, autocomplete, or well... tab.
487			'\t' => processing.tab_action(autocomplete_provider, history_provider, true, false),
488			// User actually typed something...
489			_ => processing.new_character_action(character, history_provider),
490		}
491
492		false
493	}
494
495	fn get_clipboard() -> Option<String> {
496		// Allow tests to override the clipboard as needed.
497		#[cfg(test)]
498		{
499			let mut clipboard = TEST_OVERRIDE_CLIPBOARD
500				.get_or_init(|| Mutex::new(VecDeque::new()))
501				.lock();
502			if let Some(value) = clipboard.pop_front() {
503				return Some(value);
504			}
505		}
506
507		let guard = CLIPBOARD_LOCK.get_or_init(|| Mutex::new(())).lock();
508		// Getting clipboards may fail temporarily but be ready later.
509		//
510		// So we can ignore any failures as a 'temporary' issue.
511		let Ok(mut clipboard) = Clipboard::new() else {
512			return None;
513		};
514		let text = clipboard.get_text().ok();
515		std::mem::drop(guard);
516		text
517	}
518
519	fn enable_raw_mode(&mut self) -> Result<(), LisaError> {
520		if self.in_raw_mode.swap(true, Ordering::AcqRel) {
521			return Ok(());
522		}
523
524		self.cached_mode = enable_raw_mode()?;
525		Ok(())
526	}
527
528	fn disable_raw_mode(&mut self) -> Result<(), LisaError> {
529		if !self.in_raw_mode.swap(false, Ordering::AcqRel) {
530			return Ok(());
531		}
532
533		self.cached_mode = disable_raw_mode(self.cached_mode)?;
534		Ok(())
535	}
536
537	fn raise_sigint(&mut self) {
538		// Sigint means termination for us always.
539		//
540		// Ensure raw mode is disabled.
541		_ = self.disable_raw_mode();
542		if let Some(hprovider) = self.history_provider.as_ref() {
543			hprovider.attempt_to_do_full_sync();
544		}
545		raise_sigint();
546	}
547}
548
549impl<ReadTy: ReadProvider> InputProvider for StdinInputProvider<ReadTy> {
550	fn is_stdin(&self) -> bool {
551		true
552	}
553
554	fn is_active(&self) -> bool {
555		self.active.load(Ordering::Relaxed)
556	}
557	fn set_active(&mut self, active: bool) -> Result<(), LisaError> {
558		if active {
559			self.enable_raw_mode()?;
560		} else {
561			self.disable_raw_mode()?;
562		}
563
564		self.active.store(active, Ordering::SeqCst);
565		Ok(())
566	}
567
568	fn input_in_progress(&self) -> bool {
569		self.current_input.started()
570	}
571	fn current_input(&self) -> String {
572		self.current_input.input()
573	}
574	fn poll_for_input(&mut self, ansi_supported: bool) -> Vec<TerminalInputEvent> {
575		let read_value = self.read_provider.non_blocking_read().map(|string| {
576			string
577				.chars()
578				.filter_map(|character| {
579					if let Some(replacement) = self.replacements.get(&character) {
580						*replacement
581					} else {
582						Some(character)
583					}
584				})
585				.collect::<String>()
586		});
587
588		match read_value {
589			Ok(item) => self.process_inputs(&item, ansi_supported),
590			Err(_cause) => Vec::with_capacity(0),
591		}
592	}
593
594	fn set_autocomplete_provider(&mut self, autocomplete: Box<dyn AutocompleteProvider>) {
595		_ = self.autocomplete_provider.insert(autocomplete);
596	}
597	fn current_autocomplete_suggestion(&self) -> Option<String> {
598		self.autocomplete_provider
599			.as_ref()
600			.and_then(|ap| ap.get_displayable_suggestion(self.current_input.input()))
601	}
602	fn autocomplete_suggestion_pending(&self) -> bool {
603		self.current_input.autocomplete_pending()
604	}
605
606	/// Set the current history provider to show previous commands.
607	fn set_history_provider(&mut self, history: Box<dyn HistoryProvider>) {
608		_ = self.history_provider.insert(history);
609	}
610	fn current_history_search_value(&self) -> Option<String> {
611		self.current_input.current_history_search_value()
612	}
613	fn is_doing_history_search(&self) -> bool {
614		self.current_input.is_doing_history_search()
615	}
616
617	fn inputs(&mut self) -> Vec<String> {
618		self.completed_inputs.drain(..).collect()
619	}
620}
621
622impl<ReadTy: ReadProvider> Drop for StdinInputProvider<ReadTy> {
623	fn drop(&mut self) {
624		if self.in_raw_mode.load(Ordering::Relaxed) {
625			_ = self.disable_raw_mode();
626		}
627	}
628}
629
630/// A multi-byte cursor control character.
631#[derive(Copy, Clone, Debug, PartialEq, Eq)]
632enum MultibyteCursorControl {
633	/// A 'shift-tab' that has occured.
634	ShiftTab,
635	/// A user has hit 'up' on their arrow keys.
636	UpArrow,
637	/// A user has hit 'down' on their arrow keys.
638	DownArrow,
639	/// A user has hit 'left' on their arrow keys.
640	LeftArrow,
641	/// A user has hit 'right' on their arrow keys.
642	RightArrow,
643	/// A user has hit 'left' on the arrow key while holding shift.
644	ShiftLeftArrow,
645	/// A user has hit 'right' ont he arrow key while holding shift.
646	ShiftRightArrow,
647}
648
649#[cfg(test)]
650pub mod test_helpers {
651	use super::*;
652
653	/// Insert the next paste value to get read.
654	pub fn insert_next_paste_value(new_value: String) {
655		let mut clipboard = TEST_OVERRIDE_CLIPBOARD
656			.get_or_init(|| Mutex::new(VecDeque::new()))
657			.lock();
658		clipboard.push_back(new_value);
659	}
660}
661
662#[cfg(test)]
663mod unit_tests {
664	use super::{test_helpers::insert_next_paste_value, *};
665	use crate::input::{
666		autocomplete::test_helpers::AutocompleteSuggestionList, history::SimpleHistoryProvider,
667	};
668	use parking_lot::Mutex;
669	use tokio::sync::mpsc::channel;
670
671	#[test]
672	pub fn always_stdin() {
673		// Test with actual stdin.
674		{
675			let provider = StdinInputProvider::new("test")
676				.expect("Failed to create new stdin input provider!");
677			assert!(provider.is_stdin());
678		}
679
680		// Test with channel.
681		{
682			let (_sender, raw_receiver) = channel(8);
683			let read_provider = Mutex::new(raw_receiver);
684			let provider = StdinInputProvider::new_with_provider("test", read_provider)
685				.expect("Failed to create new stdin input provider!");
686
687			assert!(provider.is_stdin());
688		}
689	}
690
691	/// Validate that manually polling for input removes any erase buffer.
692	#[tokio::test]
693	pub async fn manually_poll_for_input_consumes() {
694		let (sender, raw_receiver) = channel(8);
695		let read_provider = Mutex::new(raw_receiver);
696		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
697			.expect("Failed to create new stdin input provider!");
698		sender
699			.send("abcdefghijklmnopqrstuvwxyz".to_owned())
700			.await
701			.expect("Failed to send example input");
702
703		assert_eq!(
704			provider
705				.manually_poll_input_and_consume()
706				.expect("Failed to manually poll input and consume"),
707			"abcdefghijklmnopqrstuvwxyz",
708		);
709		assert_eq!(provider.poll_for_input(true), Vec::with_capacity(0));
710		assert!(provider.manually_poll_input_and_consume().is_err());
711	}
712
713	#[tokio::test]
714	pub async fn simple_type_enter_with_hooks_and_without_hooks() {
715		// With ANSI support.
716		{
717			let (sender, raw_receiver) = channel(8);
718			let read_provider = Mutex::new(raw_receiver);
719			let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
720				.expect("Failed to create new stdin input provider!");
721
722			sender
723				.send("abcdefghijklmnopqrstuvwxyz(\r".to_owned())
724				.await
725				.expect("Failed to send example input");
726			assert_eq!(
727				provider.poll_for_input(true),
728				vec![
729					TerminalInputEvent::InputStarted,
730					TerminalInputEvent::InputMassAppend("abcdefghijklmnopqrstuvwxyz(".to_owned()),
731					TerminalInputEvent::InputFinished,
732				],
733			);
734
735			provider.set_newline_hook(Box::new(|data: &str| -> bool {
736				data.chars().filter(|character| *character == '(').count()
737					== data.chars().filter(|character| *character == ')').count()
738			}));
739			sender
740				.send("abcdefghijklmnopqrstuvwxyz(\r".to_owned())
741				.await
742				.expect("Failed to send example input");
743			assert_eq!(
744				provider.poll_for_input(true),
745				vec![
746					TerminalInputEvent::InputStarted,
747					TerminalInputEvent::InputMassAppend("abcdefghijklmnopqrstuvwxyz(\n".to_owned()),
748				],
749			);
750			sender
751				.send(")\r".to_owned())
752				.await
753				.expect("Failed to send example input!");
754			assert_eq!(
755				provider.poll_for_input(true),
756				vec![
757					TerminalInputEvent::InputAppend(')'),
758					TerminalInputEvent::InputFinished,
759				],
760			);
761		}
762
763		// Without ANSI support.
764		{
765			let (sender, raw_receiver) = channel(8);
766			let read_provider = Mutex::new(raw_receiver);
767			let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
768				.expect("Failed to create new stdin input provider!");
769
770			sender
771				.send("abcdefghijklmnopqrstuvwxyz(\r".to_owned())
772				.await
773				.expect("Failed to send example input");
774			assert_eq!(
775				provider.poll_for_input(false),
776				vec![
777					TerminalInputEvent::InputStarted,
778					TerminalInputEvent::InputMassAppend("abcdefghijklmnopqrstuvwxyz(".to_owned()),
779					TerminalInputEvent::InputFinished,
780				],
781			);
782
783			provider.set_newline_hook(Box::new(|data: &str| -> bool {
784				data.chars().filter(|character| *character == '(').count()
785					== data.chars().filter(|character| *character == ')').count()
786			}));
787			sender
788				.send("abcdefghijklmnopqrstuvwxyz(\r".to_owned())
789				.await
790				.expect("Failed to send example input");
791			assert_eq!(
792				provider.poll_for_input(false),
793				vec![
794					TerminalInputEvent::InputStarted,
795					TerminalInputEvent::InputMassAppend("abcdefghijklmnopqrstuvwxyz(\n".to_owned()),
796				],
797			);
798			sender
799				.send(")\r".to_owned())
800				.await
801				.expect("Failed to send example input!");
802			assert_eq!(
803				provider.poll_for_input(false),
804				vec![
805					TerminalInputEvent::InputAppend(')'),
806					TerminalInputEvent::InputFinished,
807				],
808			);
809		}
810	}
811
812	#[tokio::test]
813	pub async fn respects_replacements() {
814		unsafe {
815			std::env::set_var(
816				"TEST_TOTALLY_UNIQUE_RESPECTS_REPLACEMENTS_STTY_REPLACEMENTS",
817				"z=0xd",
818			);
819		}
820
821		let mut expected_replacement_map = FnvHashMap::default();
822		expected_replacement_map.insert('z', Some('\r'));
823		// Validate normal constructor also respects it.
824		{
825			let state = StdinInputProvider::new("test-totally-unique-respects-replacements")
826				.expect("Failed to create stdininputprovider");
827			assert_eq!(state.replacements, expected_replacement_map);
828		}
829		// Validate reader constructor respects it, and validate remaps work.
830		{
831			let (sender, raw_receiver) = channel(8);
832			let read_provider = Mutex::new(raw_receiver);
833			let mut provider = StdinInputProvider::new_with_provider(
834				"test-totally-unique-respects-replacements",
835				read_provider,
836			)
837			.expect("Failed to create new stdin input provider!");
838
839			sender
840				.send("abcdefghijklmnopqrstuvwxy(z".to_owned())
841				.await
842				.expect("Failed to send example input");
843			assert_eq!(
844				provider.poll_for_input(false),
845				vec![
846					TerminalInputEvent::InputStarted,
847					TerminalInputEvent::InputMassAppend("abcdefghijklmnopqrstuvwxy(".to_owned()),
848					TerminalInputEvent::InputFinished,
849				],
850			);
851			sender
852				.send("abcdefghijklmnopqrstuvwxy(z".to_owned())
853				.await
854				.expect("Failed to send example input");
855			assert_eq!(
856				provider
857					.manually_poll_input_and_consume()
858					.expect("Failed to manually poll input and consume!"),
859				"abcdefghijklmnopqrstuvwxy(\r".to_owned(),
860			);
861		}
862
863		// Test that invalid stty replacements causes errors.
864		unsafe {
865			std::env::set_var(
866				"TEST_TOTALLY_UNIQUE_RESPECTS_REPLACEMENTS_STTY_REPLACEMENTS",
867				"undef=",
868			);
869		}
870
871		assert!(
872			StdinInputProvider::new("test-totally-unique-respects-replacements").is_err(),
873			"Invalid STTY replacements did not error `StdinInputProvider`",
874		);
875		assert!(
876			StdinInputProvider::new_with_provider(
877				"test-totally-unique-respects-replacements",
878				std::io::stdin(),
879			)
880			.is_err(),
881			"Invalid STTY replacements did not error `StdinInputProvider`",
882		);
883	}
884
885	#[tokio::test]
886	pub async fn append_ledger_to_events() {
887		let (sender, raw_receiver) = channel(8);
888		let read_provider = Mutex::new(raw_receiver);
889		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
890			.expect("Failed to create new stdin input provider!");
891
892		// start the input to get ride of input started event.
893		sender
894			.send("a\u{7f}".to_owned())
895			.await
896			.expect("Failed to start input!");
897		_ = provider.poll_for_input(true);
898		// If append ledger exists, and is empty we get 0 events.
899		sender
900			.send("".to_owned())
901			.await
902			.expect("Failed to send input");
903		assert_eq!(provider.poll_for_input(true), vec![]);
904		// If append ledger is a single character, we get a single append event.
905		sender
906			.send("a".to_owned())
907			.await
908			.expect("Failed to send input!");
909		assert_eq!(
910			provider.poll_for_input(true),
911			vec![TerminalInputEvent::InputAppend('a')]
912		);
913		// If append ledger is multiple characters we get a single mass append.
914		sender
915			.send("bcd".to_owned())
916			.await
917			.expect("Failed to send input!");
918		assert_eq!(
919			provider.poll_for_input(true),
920			vec![TerminalInputEvent::InputMassAppend("bcd".to_owned())],
921		);
922
923		// Trigger change... (append buffer doesn't exist) last input isn't character move.
924		sender
925			.send("\u{7f}a".to_owned())
926			.await
927			.expect("failed to send input!");
928		assert_eq!(
929			provider.poll_for_input(true),
930			vec![TerminalInputEvent::InputChanged(4)],
931		);
932		// Validate even with cursor moves in the middle, and items, still causes a
933		// change.
934		sender
935			.send("\u{7f}abcdef\u{1b}[D\u{7f}c".to_owned())
936			.await
937			.expect("");
938		assert_eq!(
939			provider.poll_for_input(true),
940			vec![TerminalInputEvent::InputChanged(8)],
941		);
942	}
943
944	#[tokio::test]
945	pub async fn cursor_operations_no_happen_when_no_ansi() {
946		let (sender, raw_receiver) = channel(8);
947		let read_provider = Mutex::new(raw_receiver);
948		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
949			.expect("Failed to create new stdin input provider!");
950
951		// first populate it with some input.
952		sender
953			.send("abcdefghijklmnopqrstuvwxyz".to_owned())
954			.await
955			.expect("Failed to send base input data");
956		_ = provider.poll_for_input(false);
957		sender.send(
958			"\u{2}\u{2}\u{2}\u{6}\u{5}\u{1}\u{6}\u{15}\u{19}\u{17}\u{19}\u{b}\u{14}\u{12}\u{1b}[C\u{1b}[C\u{1b}[D\u{1b}[1;2C\u{1b}[1;2D".to_owned()
959		).await.expect("Failed to send input data!");
960		assert_eq!(provider.poll_for_input(false), Vec::with_capacity(0));
961		assert_eq!(provider.current_input.cursor_position(), 26);
962
963		sender
964			.send("\u{3}".to_owned())
965			.await
966			.expect("Failed to send cancel!");
967		_ = provider.poll_for_input(true);
968
969		sender
970			.send("abcdefghijklmnopqrstuvwxyz".to_owned())
971			.await
972			.expect("Failed to send base input data");
973		_ = provider.poll_for_input(false);
974		sender.send(
975			"\u{2}\u{2}\u{2}\u{6}\u{5}\u{1}\u{6}\u{15}\u{19}\u{17}\u{19}\u{b}\u{14}\u{12}\u{1b}[C\u{1b}[C\u{1b}[D\u{1b}[1;2C\u{1b}[1;2D".to_owned()
976		).await.expect("Failed to send input data!");
977		assert_eq!(
978			provider.poll_for_input(true),
979			vec![
980				TerminalInputEvent::InputChanged(26),
981				TerminalInputEvent::CursorMoveLeft(1),
982				TerminalInputEvent::InputChanged(26),
983				TerminalInputEvent::CursorMoveRight(1),
984				TerminalInputEvent::CursorMoveLeft(26),
985			],
986		);
987		assert_eq!(provider.current_input.cursor_position(), 0);
988	}
989
990	#[cfg(feature = "tests-with-signals")]
991	#[tokio::test]
992	pub async fn ctrl_c_and_ctrl_d() {
993		use std::sync::{Arc, atomic::AtomicU8};
994		let (sender, raw_receiver) = channel(8);
995		let read_provider = Mutex::new(raw_receiver);
996		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
997			.expect("Failed to create new stdin input provider!");
998
999		let ctrl_c_handler = Arc::new(AtomicU8::new(0));
1000		let cloned_handler = ctrl_c_handler.clone();
1001		ctrlc::set_handler(move || {
1002			cloned_handler.fetch_add(1, Ordering::SeqCst);
1003		})
1004		.expect("Failed to set ctrl-c handler!");
1005
1006		sender
1007			.send("abcdefghijklmnopqrstuvwxyz\u{3}".to_owned())
1008			.await
1009			.expect("Failed to send first input + ctrl-c");
1010		// Inputs should not send ctrl-c, but instead cancel.
1011		_ = provider.poll_for_input(true);
1012		assert_eq!(ctrl_c_handler.load(Ordering::SeqCst), 0);
1013		sender
1014			.send("abcdefghijklmnopqrstuvwxyz\u{3}".to_owned())
1015			.await
1016			.expect("Failed to send first input + ctrl-c");
1017		_ = provider.poll_for_input(false);
1018		assert_eq!(ctrl_c_handler.load(Ordering::SeqCst), 0);
1019		sender
1020			.send("\u{3}".to_owned())
1021			.await
1022			.expect("Failed to send second ctrl-c!");
1023		_ = provider.poll_for_input(true);
1024		sender
1025			.send("\u{3}".to_owned())
1026			.await
1027			.expect("Failed to send second ctrl-c!");
1028		_ = provider.poll_for_input(false);
1029		// Sleep to give other thread time to update...
1030		tokio::time::sleep(std::time::Duration::from_millis(300)).await;
1031		assert_eq!(ctrl_c_handler.load(Ordering::SeqCst), 2);
1032
1033		sender
1034			.send("\u{4}".to_owned())
1035			.await
1036			.expect("Failed to send second ctrl-c!");
1037		_ = provider.poll_for_input(true);
1038		sender
1039			.send("\u{4}".to_owned())
1040			.await
1041			.expect("Failed to send second ctrl-c!");
1042		_ = provider.poll_for_input(false);
1043		// Sleep to give other thread time to update...
1044		tokio::time::sleep(std::time::Duration::from_millis(300)).await;
1045		assert_eq!(ctrl_c_handler.load(Ordering::SeqCst), 4);
1046	}
1047
1048	#[tokio::test]
1049	pub async fn autocomplete() {
1050		let suggestion = Box::new(AutocompleteSuggestionList(vec![
1051			"command long".to_owned(),
1052			"command short".to_owned(),
1053			"command longer".to_owned(),
1054			"command shorter".to_owned(),
1055			"other command".to_owned(),
1056		]));
1057
1058		let (sender, raw_receiver) = channel(8);
1059		let read_provider = Mutex::new(raw_receiver);
1060		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
1061			.expect("Failed to create new stdin input provider!");
1062		provider.set_autocomplete_provider(suggestion);
1063
1064		sender
1065			.send("command ".to_owned())
1066			.await
1067			.expect("Failed to send start of input");
1068		_ = provider.poll_for_input(true);
1069		assert!(provider.input_in_progress());
1070		assert!(!provider.autocomplete_suggestion_pending());
1071		assert_eq!(
1072			provider.current_autocomplete_suggestion(),
1073			Some("long".to_owned()),
1074		);
1075
1076		sender
1077			.send("\t".to_owned())
1078			.await
1079			.expect("Failed to send tab input!");
1080		// Moves to end of auto complete input, past command.
1081		assert_eq!(
1082			provider.poll_for_input(true),
1083			vec![TerminalInputEvent::InputChanged(12)],
1084		);
1085		assert!(provider.autocomplete_suggestion_pending());
1086		assert_eq!(provider.current_input(), "command long");
1087
1088		// Moves two more, cause we changed to 'command short' over 'command long'
1089		sender
1090			.send("\t".to_owned())
1091			.await
1092			.expect("Failed to send tab input!");
1093		assert_eq!(
1094			provider.poll_for_input(true),
1095			vec![TerminalInputEvent::InputChanged(13)],
1096		);
1097		assert!(provider.autocomplete_suggestion_pending());
1098		assert_eq!(provider.current_input(), "command short");
1099
1100		// Moves one more, cause we changed to 'command longer' over 'command short'
1101		sender
1102			.send("\t".to_owned())
1103			.await
1104			.expect("Failed to send tab input!");
1105		assert_eq!(
1106			provider.poll_for_input(true),
1107			vec![TerminalInputEvent::InputChanged(14)],
1108		);
1109		assert!(provider.autocomplete_suggestion_pending());
1110		assert_eq!(provider.current_input(), "command longer");
1111
1112		sender
1113			.send("\u{1b}[Z".to_owned())
1114			.await
1115			.expect("Failed to send shift tab input!");
1116		assert_eq!(
1117			provider.poll_for_input(true),
1118			vec![TerminalInputEvent::InputChanged(13)],
1119			"Post shift tab Current input is: [{}] @ {}",
1120			provider.current_input(),
1121			provider.current_input.cursor_position(),
1122		);
1123		assert!(provider.autocomplete_suggestion_pending());
1124		assert_eq!(provider.current_input(), "command short");
1125
1126		sender
1127			.send("a".to_owned())
1128			.await
1129			.expect("Failed to send promotion input!");
1130		assert_eq!(
1131			provider.poll_for_input(true),
1132			vec![TerminalInputEvent::InputAppend('a')],
1133		);
1134		assert!(!provider.autocomplete_suggestion_pending());
1135		assert_eq!(provider.current_input(), "command shorta");
1136
1137		// Cancel our current input and reset.
1138		sender
1139			.send("\u{3}".to_owned())
1140			.await
1141			.expect("Failed to send cancellation!");
1142		_ = provider.poll_for_input(true);
1143
1144		// Start a brand new input that can be autocompleted.
1145		sender
1146			.send("command \u{1b}[D\u{1b}[D\u{1b}[D\u{1b}[D\u{1b}[D".to_owned())
1147			.await
1148			.expect("Failed to send start of input");
1149		_ = provider.poll_for_input(true);
1150		assert!(provider.input_in_progress());
1151		assert!(!provider.autocomplete_suggestion_pending());
1152		assert_eq!(
1153			provider.current_autocomplete_suggestion(),
1154			Some("long".to_owned()),
1155		);
1156
1157		sender
1158			.send("\t\u{1b}[C\u{1b}[C\u{1b}[C\u{1b}[C\u{1b}[C".to_owned())
1159			.await
1160			.expect("Failed to start autocomplete input!");
1161		assert_eq!(
1162			provider.poll_for_input(true),
1163			vec![TerminalInputEvent::InputChanged(12)],
1164			"Current input is: [{:?}] @ {}",
1165			provider.current_input(),
1166			provider.current_input.cursor_position(),
1167		);
1168		assert!(!provider.autocomplete_suggestion_pending());
1169		assert_eq!(provider.current_input(), "command long".to_owned());
1170	}
1171
1172	#[tokio::test]
1173	pub async fn history_non_search() {
1174		let history_provider = Box::new(SimpleHistoryProvider::new_in_memory(10));
1175		history_provider.insert_command("previous command");
1176		history_provider.insert_command("short");
1177		history_provider.insert_command("cos sdkversion");
1178
1179		let (sender, raw_receiver) = channel(8);
1180		let read_provider = Mutex::new(raw_receiver);
1181		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
1182			.expect("Failed to create new stdin input provider!");
1183		provider.set_history_provider(history_provider);
1184
1185		// Populate with a newer command.
1186		sender
1187			.send("new command\rtest".to_owned())
1188			.await
1189			.expect("Failed to send newer command");
1190		_ = provider.poll_for_input(false);
1191
1192		sender
1193			.send("\u{1b}[A".to_owned())
1194			.await
1195			.expect("Failed to send up arrow!");
1196		assert_eq!(
1197			provider.poll_for_input(false),
1198			vec![TerminalInputEvent::InputChanged(4)],
1199		);
1200		assert_eq!(provider.current_input(), "new command");
1201
1202		sender
1203			.send("\u{1b}[A".to_owned())
1204			.await
1205			.expect("Failed to send up arrow!");
1206		assert_eq!(
1207			provider.poll_for_input(true),
1208			vec![TerminalInputEvent::InputChanged(4)],
1209			"Current input is: [{:?}] @ {}",
1210			provider.current_input(),
1211			provider.current_input.cursor_position(),
1212		);
1213		assert_eq!(provider.current_input(), "cos sdkversion");
1214
1215		// Move to validate that history changing can move.
1216		sender
1217			.send("\u{1b}[C\u{1b}[C\u{1b}[C\u{1b}[C\u{1b}[C\u{1b}[C".to_owned())
1218			.await
1219			.expect("failed to send some right arrows!");
1220		_ = provider.poll_for_input(true);
1221
1222		sender
1223			.send("\u{1b}[A".to_owned())
1224			.await
1225			.expect("Failed to send up arrow!");
1226		// This should move the cursor cause it's shorter.
1227		assert_eq!(
1228			provider.poll_for_input(true),
1229			vec![TerminalInputEvent::InputChanged(5)],
1230			"Current input is: [{:?}] @ {}",
1231			provider.current_input(),
1232			provider.current_input.cursor_position(),
1233		);
1234		assert_eq!(provider.current_input(), "short");
1235
1236		sender
1237			.send("\u{1b}[A".to_owned())
1238			.await
1239			.expect("Failed to send up arrow!");
1240		// Again doesn't need to move
1241		assert_eq!(
1242			provider.poll_for_input(true),
1243			vec![TerminalInputEvent::InputChanged(5)],
1244			"Current input is: [{:?}] @ {}",
1245			provider.current_input(),
1246			provider.current_input.cursor_position(),
1247		);
1248		assert_eq!(provider.current_input(), "previous command");
1249
1250		// Move the cursor so we need to move again, before going down.
1251		sender
1252			.send("\u{1b}[C\u{1b}[C\u{1b}[B".to_owned())
1253			.await
1254			.expect("Failed to send up arrow!");
1255		assert_eq!(
1256			provider.poll_for_input(true),
1257			vec![TerminalInputEvent::InputChanged(5)],
1258			"Current input is: [{:?}] @ {}",
1259			provider.current_input(),
1260			provider.current_input.cursor_position(),
1261		);
1262		assert_eq!(provider.current_input(), "short");
1263
1264		// Restores previous input, and can move cursor.
1265		sender
1266			.send("\u{1b}[B\u{1b}[B\u{1b}[B\u{1b}[B\u{1b}[B\u{1b}[B".to_owned())
1267			.await
1268			.expect("Failed to send down arrow!");
1269		assert_eq!(
1270			provider.poll_for_input(true),
1271			vec![TerminalInputEvent::InputChanged(4)],
1272		);
1273		assert_eq!(provider.current_input(), "test");
1274		assert_eq!(provider.current_input.cursor_position(), 4);
1275
1276		// Validate can start an input too.
1277		sender
1278			.send("\u{3}".to_owned())
1279			.await
1280			.expect("Failed to send cancel input");
1281		_ = provider.poll_for_input(true);
1282		sender
1283			.send("\u{1b}[A".to_owned())
1284			.await
1285			.expect("Failed to send up arrow!");
1286		assert_eq!(
1287			provider.poll_for_input(true),
1288			vec![
1289				TerminalInputEvent::InputStarted,
1290				TerminalInputEvent::InputChanged(11),
1291			],
1292		);
1293	}
1294
1295	#[tokio::test]
1296	pub async fn history_searching() {
1297		let history_provider = Box::new(SimpleHistoryProvider::new_in_memory(15));
1298		history_provider.insert_command("word swap again");
1299		history_provider.insert_command("word swap more");
1300		history_provider.insert_command("swap word again");
1301		history_provider.insert_command("swap word more");
1302		history_provider.insert_command("comamand longer1");
1303		history_provider.insert_command("command long");
1304		history_provider.insert_command("command longer");
1305		history_provider.insert_command("command short");
1306		history_provider.insert_command("command shorter");
1307		history_provider.insert_command("other");
1308
1309		let (sender, raw_receiver) = channel(8);
1310		let read_provider = Mutex::new(raw_receiver);
1311		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
1312			.expect("Failed to create new stdin input provider!");
1313		provider.set_history_provider(history_provider);
1314
1315		// Type and start history search.
1316		sender
1317			.send("command ".to_owned())
1318			.await
1319			.expect("Failed to start history search");
1320		assert_eq!(
1321			provider.poll_for_input(true),
1322			vec![
1323				TerminalInputEvent::InputStarted,
1324				TerminalInputEvent::InputMassAppend("command ".to_owned()),
1325			],
1326		);
1327		assert!(!provider.is_doing_history_search());
1328		assert!(provider.current_history_search_value().is_none());
1329
1330		sender
1331			.send("\u{12}".to_owned())
1332			.await
1333			.expect("Failed to send ctrl-r to input to start history search!");
1334		assert_eq!(
1335			provider.poll_for_input(true),
1336			vec![TerminalInputEvent::InputChanged(8)],
1337		);
1338		assert!(provider.is_doing_history_search());
1339		assert_eq!(
1340			provider.current_history_search_value(),
1341			Some("shorter".to_owned()),
1342		);
1343		// Isn't like autocomplete, actually treated as a full real value.
1344		assert_eq!(provider.current_input(), "command shorter");
1345
1346		sender
1347			.send("l".to_owned())
1348			.await
1349			.expect("Failed to send key press to terminal");
1350		assert_eq!(
1351			provider.poll_for_input(true),
1352			vec![TerminalInputEvent::InputAppend('l')],
1353		);
1354		assert!(provider.is_doing_history_search());
1355		assert_eq!(
1356			provider.current_history_search_value(),
1357			Some("onger".to_owned()),
1358		);
1359		assert_eq!(provider.current_input(), "command longer");
1360
1361		sender
1362			.send("\u{1b}[D\u{1b}[D\u{2}\u{2}\u{1b}[D\u{1b}[Da".to_owned())
1363			.await
1364			.expect("Failed to send cursor movement and append for history!");
1365		assert_eq!(
1366			provider.poll_for_input(true),
1367			vec![TerminalInputEvent::InputChanged(4)],
1368			"Current Input Is ({:?}) @ {}",
1369			provider.current_input(),
1370			provider.current_input.cursor_position(),
1371		);
1372		assert!(provider.is_doing_history_search());
1373		assert_eq!(
1374			provider.current_history_search_value(),
1375			Some("onger1".to_owned()),
1376		);
1377
1378		sender
1379			.send("\u{1b}[1;2Conger1".to_owned())
1380			.await
1381			.expect("Failed to complete history search!");
1382		assert_eq!(
1383			provider.poll_for_input(true),
1384			vec![TerminalInputEvent::InputChanged(14)],
1385		);
1386		assert!(provider.is_doing_history_search());
1387		assert!(provider.current_history_search_value().is_none());
1388
1389		sender
1390			.send("\u{3}".to_owned())
1391			.await
1392			.expect("Failed to send cancel!");
1393		_ = provider.poll_for_input(true);
1394
1395		sender
1396			.send("word swap\u{12}".to_owned())
1397			.await
1398			.expect("Failed to send history search start!");
1399		assert_eq!(
1400			provider.poll_for_input(true),
1401			vec![
1402				TerminalInputEvent::InputStarted,
1403				TerminalInputEvent::InputChanged(9),
1404			],
1405		);
1406		sender
1407			.send("\u{1b}t a".to_owned())
1408			.await
1409			.expect("Failed to swap words and type a!");
1410		assert_eq!(
1411			provider.poll_for_input(true),
1412			vec![TerminalInputEvent::InputChanged(11)],
1413			"Current input is: [{:?}] @ {}",
1414			provider.current_input.input(),
1415			provider.current_input.cursor_position(),
1416		);
1417		assert!(provider.is_doing_history_search());
1418		assert_eq!(
1419			provider.current_history_search_value(),
1420			Some("gain".to_owned()),
1421		);
1422		assert_eq!(provider.current_input.input(), "swap word again");
1423
1424		sender
1425			.send("\r".to_owned())
1426			.await
1427			.expect("Failed to send enter early!");
1428		assert_eq!(
1429			provider.poll_for_input(true),
1430			vec![TerminalInputEvent::InputFinished],
1431		);
1432		assert_eq!(
1433			provider.completed_inputs.last().map(Clone::clone),
1434			Some("swap word again".to_owned())
1435		);
1436
1437		assert!(!provider.is_doing_history_search());
1438		sender
1439			.send("command \u{12}".to_owned())
1440			.await
1441			.expect("Failed to test history search without ANSI!");
1442		assert_eq!(
1443			provider.poll_for_input(false),
1444			vec![
1445				TerminalInputEvent::InputStarted,
1446				TerminalInputEvent::InputMassAppend("command ".to_owned())
1447			],
1448		);
1449		assert!(!provider.is_doing_history_search());
1450	}
1451
1452	#[tokio::test]
1453	pub async fn paste_control_codes() {
1454		let (sender, raw_receiver) = channel(8);
1455		let read_provider = Mutex::new(raw_receiver);
1456		let mut provider = StdinInputProvider::new_with_provider("test", read_provider)
1457			.expect("Failed to create new stdin input provider!");
1458
1459		insert_next_paste_value("\u{1b}[31m\u{1b}[1;31min red!\u{1b}[0mjust normal!".to_owned());
1460		sender
1461			.send("\u{16}".to_owned())
1462			.await
1463			.expect("Failed to send paste!");
1464		assert_eq!(
1465			provider.poll_for_input(true),
1466			vec![
1467				TerminalInputEvent::InputStarted,
1468				TerminalInputEvent::InputMassAppend(
1469					"\u{1b}[31m\u{1b}[1;31min red!\u{1b}[0mjust normal!".to_owned()
1470				),
1471			],
1472		);
1473		assert_eq!(provider.current_input.cursor_position(), 35);
1474		insert_next_paste_value("\u{16}\u{12}\u{2}\u{1b}[D\u{1b}[D\u{1b}[D".to_owned());
1475		sender
1476			.send("\u{16}".to_owned())
1477			.await
1478			.expect("Failed to send paste!");
1479		assert_eq!(
1480			provider.poll_for_input(true),
1481			vec![TerminalInputEvent::InputMassAppend(
1482				"\u{16}\u{12}\u{2}\u{1b}[D\u{1b}[D\u{1b}[D".to_owned()
1483			)],
1484		);
1485		assert!(!provider.current_input.is_doing_history_search());
1486		assert_eq!(provider.current_input.cursor_position(), 47);
1487	}
1488}