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