1mod 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
44type NewlineHookFn = dyn Fn(&str) -> bool + Send + Sync;
47
48static CLIPBOARD_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
55
56#[cfg(test)]
58static TEST_OVERRIDE_CLIPBOARD: OnceLock<Mutex<VecDeque<String>>> = OnceLock::new();
59
60pub struct StdinInputProvider<ReadTy: ReadProvider> {
62 active: AtomicBool,
64 autocomplete_provider: Option<Box<dyn AutocompleteProvider>>,
66 cached_mode: CachedModeType,
68 current_input: StdinInputState,
70 completed_inputs: Vec<String>,
72 history_provider: Option<Box<dyn HistoryProvider>>,
74 in_raw_mode: AtomicBool,
76 newline_hook: Option<Box<NewlineHookFn>>,
78 read_provider: ReadTy,
80 replacements: FnvHashMap<char, Option<char>>,
82}
83
84impl StdinInputProvider<Stdin> {
85 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 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 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 pub fn set_autocomplete_provider(&mut self, autocomplete: Box<dyn AutocompleteProvider>) {
186 _ = self.autocomplete_provider.insert(autocomplete);
187 }
188
189 pub fn set_history_provider(&mut self, history: Box<dyn HistoryProvider>) {
191 _ = self.history_provider.insert(history);
192 }
193
194 pub fn set_newline_hook(&mut self, hook: Box<dyn Fn(&str) -> bool + Send + Sync>) {
200 _ = self.newline_hook.insert(hook);
201 }
202
203 fn process_inputs(&mut self, item: &str, ansi_supported: bool) -> Vec<TerminalInputEvent> {
209 let mut input_events = Vec::with_capacity(0);
211 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 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 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 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 } 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 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 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 '\u{0}' => {}
388 '\u{7f}' | '\u{8}' => processing.backspace(),
390 '\u{3}' => {
392 if !processing.cancel() {
393 return true;
394 }
395 }
396 '\u{4}' => {
402 return true;
403 }
404 '\u{16}' => {
406 if let Some(data) = Self::get_clipboard() {
407 processing.new_string_action(&data, history_provider);
408 }
409 }
410 '\u{10}' => processing.previous_history_action(history_provider),
412 '\u{e}' => processing.next_history_action(history_provider),
414 '\u{c}' => processing.clear(),
416 '\u{13}' => processing.pause_output(),
418 '\u{2}' => {
420 if ansi_supported {
421 _ = processing.move_cursor_left(1);
422 }
423 }
424 '\u{6}' => {
426 if ansi_supported {
427 _ = processing.move_cursor_right(1);
428 }
429 }
430 '\u{1}' => {
432 if ansi_supported {
433 _ = processing.move_cursor_left(processing.cursor_position() + 1);
434 }
435 }
436 '\u{5}' => {
438 if ansi_supported {
439 _ = processing.move_cursor_right(processing.current_input().len());
440 }
441 }
442 '\u{15}' => {
444 if ansi_supported {
445 processing.uwk_cursor_to_begin();
446 }
447 }
448 '\u{b}' => {
450 if ansi_supported {
451 processing.uwk_cursor_to_end();
452 }
453 }
454 '\u{17}' => {
456 if ansi_supported {
457 processing.uwk_cursor_word();
458 }
459 }
460 '\u{19}' => {
462 if ansi_supported {
463 processing.paste_uwk();
464 }
465 }
466 '\u{14}' => {
468 if ansi_supported {
469 processing.swap_last_two_characters();
470 }
471 }
472 '\u{7}' => _ = processing.cancel(),
474 '\u{12}' => {
476 if ansi_supported {
477 processing.start_history_search_action(history_provider);
478 }
479 }
480 '\n' => processing.complete(),
484 '\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 '\t' => processing.tab_action(autocomplete_provider, history_provider, true, false),
498 _ => processing.new_character_action(character, history_provider),
500 }
501
502 false
503 }
504
505 fn get_clipboard() -> Option<String> {
506 #[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 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 _ = 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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
635enum MultibyteCursorControl {
636 ShiftTab,
638 UpArrow,
640 DownArrow,
642 LeftArrow,
644 RightArrow,
646 ShiftLeftArrow,
648 ShiftRightArrow,
650}
651
652#[cfg(test)]
653pub mod test_helpers {
654 use super::*;
655
656 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 {
678 let provider = StdinInputProvider::new("test")
679 .expect("Failed to create new stdin input provider!");
680 assert!(provider.is_stdin());
681 }
682
683 {
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 #[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 {
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 {
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 {
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 {
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 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 sender
897 .send("a\u{7f}".to_owned())
898 .await
899 .expect("Failed to start input!");
900 _ = provider.poll_for_input(true);
901 sender
903 .send("".to_owned())
904 .await
905 .expect("Failed to send input");
906 assert_eq!(provider.poll_for_input(true), vec![]);
907 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 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 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 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 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 _ = 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 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 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 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 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 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 sender
1142 .send("\u{3}".to_owned())
1143 .await
1144 .expect("Failed to send cancellation!");
1145 _ = provider.poll_for_input(true);
1146
1147 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 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 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 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 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 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 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 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 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 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}