1use crate::CrLf;
6use crate::UnicodeString;
7use crate::clear::*;
8use crate::cli::Cli;
9use crate::cursor::*;
10use crate::error::Error;
11use crate::keys::Key;
12use crate::result::Result;
13use cfg_if::cfg_if;
14use futures::*;
15pub use pad::PadStr;
16use regex::Regex;
17use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
18use std::sync::{Arc, LockResult, Mutex, MutexGuard};
19use workflow_core::channel::{Channel, DuplexChannel, Receiver, Sender, unbounded};
20use workflow_core::task::spawn;
21use workflow_log::log_error;
22
23const DEFAULT_PARA_WIDTH: usize = 80;
24
25pub struct Modifiers {
28 pub alt: bool,
30 pub shift: bool,
32 pub ctrl: bool,
34 pub meta: bool,
36}
37pub type LinkMatcherHandlerFn = Arc<Box<dyn Fn(Modifiers, &str)>>;
40
41#[derive(Debug, Clone)]
43pub enum Event {
44 Copy,
46 Paste,
48}
49pub type EventHandlerFn = Arc<Box<dyn Fn(Event)>>;
51
52mod options;
53pub use options::Options;
54pub use options::TargetElement;
55
56pub mod bindings;
57pub mod xterm;
59pub use xterm::{Theme, ThemeOption};
60
61cfg_if! {
62 if #[cfg(target_arch = "wasm32")] {
63 use crate::terminal::xterm::Xterm as Interface;
66
67 } else if #[cfg(feature = "termion")] {
68 pub mod termion;
69 use crate::terminal::termion::Termion as Interface;
70 } else {
71 pub mod crossterm;
73 use crate::terminal::crossterm::Crossterm as Interface;
74 pub use crate::terminal::crossterm::{disable_raw_mode,init_panic_hook};
75 }
76}
77
78#[derive(Debug)]
81pub struct Inner {
82 pub buffer: UnicodeString,
84 history: Vec<UnicodeString>,
85 pub cursor: usize,
87 history_index: usize,
88}
89
90impl Default for Inner {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl Inner {
97 pub fn new() -> Self {
99 Inner {
100 buffer: UnicodeString::default(),
101 history: vec![],
102 cursor: 0,
103 history_index: 0,
104 }
105 }
106
107 pub fn reset_line_buffer(&mut self) {
109 self.buffer.clear();
110 self.cursor = 0;
111 }
112}
113
114#[derive(Clone)]
115struct UserInput {
116 prompt: Arc<Mutex<Option<String>>>,
117 buffer: Arc<Mutex<UnicodeString>>,
118 enabled: Arc<AtomicBool>,
119 secret: Arc<AtomicBool>,
120 kbhit: Arc<AtomicBool>,
121 terminate: Arc<AtomicBool>,
122 sender: Sender<String>,
123 receiver: Receiver<String>,
124}
125
126impl UserInput {
127 pub fn new() -> Self {
128 let (sender, receiver) = unbounded();
129 UserInput {
130 prompt: Arc::new(Mutex::new(None)),
131 buffer: Arc::new(Mutex::new(UnicodeString::default())),
132 enabled: Arc::new(AtomicBool::new(false)),
133 secret: Arc::new(AtomicBool::new(false)),
134 kbhit: Arc::new(AtomicBool::new(false)),
135 terminate: Arc::new(AtomicBool::new(false)),
136 sender,
137 receiver,
138 }
139 }
140
141 pub fn get_prompt(&self) -> Option<String> {
142 self.prompt.lock().unwrap().clone()
143 }
144
145 pub fn get_buffer(&self) -> String {
146 self.buffer.lock().unwrap().clone().to_string()
147 }
148
149 pub fn open(&self, secret: bool, kbhit: bool, prompt: Option<String>) -> Result<()> {
150 *self.prompt.lock().unwrap() = prompt;
151 self.enabled.store(true, Ordering::SeqCst);
152 self.secret.store(secret, Ordering::SeqCst);
153 self.kbhit.store(kbhit, Ordering::SeqCst);
154 self.terminate.store(false, Ordering::SeqCst);
155 Ok(())
156 }
157
158 pub fn close(&self) -> Result<()> {
159 let s = {
160 self.prompt.lock().unwrap().take();
161 let mut buffer = self.buffer.lock().unwrap();
162 let s = buffer.clone();
163 buffer.clear();
164 s
165 };
166
167 self.enabled.store(false, Ordering::SeqCst);
168 self.terminate.store(true, Ordering::SeqCst);
169 self.sender.try_send(s.to_string()).unwrap();
170 Ok(())
171 }
172
173 pub async fn capture(
174 &self,
175 secret: bool,
176 kbhit: bool,
177 prompt: Option<String>,
178 term: &Arc<Terminal>,
179 ) -> Result<String> {
180 self.open(secret, kbhit, prompt)?;
181
182 let term = term.clone();
183 let terminate = self.terminate.clone();
184
185 cfg_if! {
186
187 if #[cfg(target_arch = "wasm32")] {
191 workflow_core::task::dispatch(async move {
192 let _result = term.term().intake(&terminate).await;
193 });
194 } else {
195 workflow_core::task::spawn(async move {
196 let _result = term.term().intake(&terminate).await;
197 });
198 }
199 }
200
201 let string = self.receiver.recv().await?;
202 Ok(string)
203 }
204
205 fn is_enabled(&self) -> bool {
206 self.enabled.load(Ordering::SeqCst)
207 }
208
209 fn is_secret(&self) -> bool {
210 self.secret.load(Ordering::SeqCst)
211 }
212
213 fn is_kbhit(&self) -> bool {
214 self.kbhit.load(Ordering::SeqCst)
215 }
216
217 fn ingest(&self, key: Key, term: &Arc<Terminal>) -> Result<()> {
218 match key {
219 Key::Ctrl('c') => {
220 self.close()?;
221 term.abort();
222 }
223 Key::Char(ch) => {
224 self.buffer.lock().unwrap().push(ch);
225 if !self.is_secret() {
226 term.write(ch);
227 }
228 if self.is_kbhit() {
229 term.crlf();
230 self.close()?;
231 }
232 }
233 Key::Backspace => {
234 self.buffer.lock().unwrap().pop();
235 if !self.is_secret() {
236 term.write("\x08 \x08");
237 }
238 }
239 Key::Enter => {
240 term.crlf();
242 self.close()?;
243 }
244 _ => {}
245 }
246 Ok(())
247 }
248
249 #[allow(dead_code)]
250 pub fn inject<S: ToString>(&self, text: S, term: &Terminal) -> Result<()> {
251 self.inject_impl(text.to_string().into(), term)?;
252 Ok(())
253 }
254
255 fn inject_impl(&self, text: UnicodeString, term: &Terminal) -> Result<()> {
256 let mut buffer = self.buffer.lock().unwrap();
257 if !self.is_secret() {
258 text.iter().for_each(|ch| {
259 term.write(ch);
260 });
261 }
262 buffer.extend(text);
263 Ok(())
264 }
265}
266
267#[derive(Clone)]
269pub struct Terminal {
270 inner: Arc<Mutex<Inner>>,
271 pub running: Arc<AtomicBool>,
273 pub prompt: Arc<Mutex<String>>,
275 pub term: Arc<Interface>,
277 pub handler: Arc<dyn Cli>,
279 pub terminate: Arc<AtomicBool>,
281 user_input: UserInput,
282 pub pipe_raw: Channel<String>,
284 pub pipe_crlf: Channel<String>,
286 pub pipe_ctl: DuplexChannel<()>,
288 pub para_width: Arc<AtomicUsize>,
290}
291
292impl Terminal {
293 pub fn try_new(handler: Arc<dyn Cli>, prompt: &str) -> Result<Self> {
295 let term = Arc::new(Interface::try_new()?);
296
297 let terminal = Self {
298 inner: Arc::new(Mutex::new(Inner::new())),
299 running: Arc::new(AtomicBool::new(false)),
300 prompt: Arc::new(Mutex::new(prompt.to_string())),
301 term,
302 handler,
303 terminate: Arc::new(AtomicBool::new(false)),
304 user_input: UserInput::new(),
305 pipe_raw: Channel::unbounded(),
306 pipe_crlf: Channel::unbounded(),
307 pipe_ctl: DuplexChannel::oneshot(),
308 para_width: Arc::new(AtomicUsize::new(DEFAULT_PARA_WIDTH)),
309 };
310
311 Ok(terminal)
312 }
313
314 pub fn try_new_with_options(
317 handler: Arc<dyn Cli>,
318 options: Options,
320 ) -> Result<Self> {
321 let term = Arc::new(Interface::try_new_with_options(&options)?);
322
323 let terminal = Self {
324 inner: Arc::new(Mutex::new(Inner::new())),
325 running: Arc::new(AtomicBool::new(false)),
326 prompt: Arc::new(Mutex::new(options.prompt())),
327 term,
328 handler,
329 terminate: Arc::new(AtomicBool::new(false)),
330 user_input: UserInput::new(),
331 pipe_raw: Channel::unbounded(),
332 pipe_crlf: Channel::unbounded(),
333 pipe_ctl: DuplexChannel::oneshot(),
334 para_width: Arc::new(AtomicUsize::new(DEFAULT_PARA_WIDTH)),
335 };
336
337 Ok(terminal)
338 }
339
340 pub async fn init(self: &Arc<Self>) -> Result<()> {
342 self.term.init(self).await?;
343
344 self.handler.clone().init(self)?;
345
346 Ok(())
347 }
348
349 pub fn inner(&self) -> LockResult<MutexGuard<'_, Inner>> {
351 self.inner.lock()
352 }
353
354 pub fn history(&self) -> Vec<UnicodeString> {
356 let data = self.inner().unwrap();
357 data.history.clone()
358 }
359
360 pub fn reset_line_buffer(&self) {
362 self.inner().unwrap().reset_line_buffer();
363 }
364
365 pub fn get_prompt(&self) -> String {
367 if let Some(prompt) = self.handler.prompt() {
368 prompt
369 } else {
370 self.prompt.lock().unwrap().clone()
371 }
372 }
373
374 pub fn prompt(&self) {
376 let mut data = self.inner().unwrap();
377 data.cursor = 0;
378 data.buffer.clear();
379 self.term().write(self.get_prompt());
380 }
381
382 pub fn crlf(&self) {
384 self.term().write("\n\r".to_string());
385 }
386
387 pub fn write<S>(&self, s: S)
389 where
390 S: ToString,
391 {
392 self.term().write(s);
393 }
394
395 pub fn writeln<S>(&self, s: S)
397 where
398 S: ToString,
399 {
400 if self.is_running() {
401 if self.user_input.is_enabled() {
402 if let Some(prompt) = self.user_input.get_prompt() {
403 self.write(format!("{}{}\n\r", ClearLine, s.to_string()));
404 self.write(prompt);
405 if !self.user_input.secret.load(Ordering::SeqCst) {
406 self.write(self.user_input.get_buffer());
407 }
408 }
409 } else {
410 self.write(format!("{}\n\r", s.to_string()));
411 }
412 } else {
413 self.write(format!("{}{}\n\r", ClearLine, s.to_string()));
414 let data = self.inner().unwrap();
415 let p = format!("{}{}", self.get_prompt(), data.buffer);
416 self.write(p);
417 let l = data.buffer.len() - data.cursor;
418 for _ in 0..l {
419 self.write("\x08".to_string());
420 }
421 }
422 }
423
424 pub fn refresh_prompt(&self) {
428 if !self.is_running() {
429 self.write(format!("{}", ClearLine));
430 let data = self.inner().unwrap();
431 let p = format!("{}{}", self.get_prompt(), data.buffer);
432 self.write(p);
433 let l = data.buffer.len() - data.cursor;
434 for _ in 0..l {
435 self.write("\x08".to_string());
436 }
437 }
438 }
439
440 pub fn para<S>(&self, text: S)
443 where
444 S: Into<String>,
445 {
446 let width = self
447 .term()
448 .cols()
449 .unwrap_or_else(|| self.para_width.load(Ordering::SeqCst));
450 let options = textwrap::Options::new(width).line_ending(textwrap::LineEnding::CRLF);
451
452 textwrap::wrap(text.into().as_str(), options)
453 .into_iter()
454 .for_each(|line| self.writeln(line));
455 }
456
457 pub fn para_with_options<'a, S, Opt>(&self, width_or_options: Opt, text: S)
460 where
461 S: Into<String>,
462 Opt: Into<textwrap::Options<'a>>,
463 {
464 textwrap::wrap(text.into().crlf().as_str(), width_or_options.into())
467 .into_iter()
468 .for_each(|line| self.writeln(line));
469 }
470
471 pub fn help<S: ToString, H: ToString>(
476 &self,
477 list: &[(S, H)],
478 separator: Option<&str>,
479 ) -> Result<()> {
480 let mut list = list
481 .iter()
482 .map(|(cmd, help)| (cmd.to_string(), help.to_string()))
483 .collect::<Vec<_>>();
484 list.sort_by_key(|(cmd, _)| cmd.to_string());
485 let separator = separator.unwrap_or(" ");
486 let term_width: usize = self.cols().unwrap_or(80);
487 let cmd_width = list.iter().map(|(c, _)| c.len()).fold(0, |a, b| a.max(b)) + 2;
488 let help_width = term_width - cmd_width - 2 - 4 - separator.len();
489 let cmd_space = "".pad_to_width(cmd_width);
490 self.writeln("");
491 for (cmd, help) in list {
492 let mut first = true;
493 let options =
494 textwrap::Options::new(help_width).line_ending(textwrap::LineEnding::CRLF);
495 textwrap::wrap(help.as_str(), options)
496 .into_iter()
497 .for_each(|line| {
498 if first {
499 self.writeln(format!(
500 "{:>4}{}{}{}",
501 "",
502 cmd.pad_to_width(cmd_width),
503 separator,
504 line
505 ));
506 first = false;
507 } else {
508 self.writeln(format!("{:>4}{cmd_space}{}{}", "", separator, line));
509 }
510 });
511 }
512 self.writeln("");
513
514 Ok(())
515 }
516
517 pub fn term(&self) -> Arc<Interface> {
519 Arc::clone(&self.term)
520 }
521
522 async fn pipe_start(self: &Arc<Self>) -> Result<()> {
523 let self_ = self.clone();
524 spawn(async move {
525 loop {
526 select! {
527 _ = self_.pipe_ctl.request.receiver.recv().fuse() => {
528 break;
529 },
530 raw = self_.pipe_raw.receiver.recv().fuse() => {
531 raw.map(|text|self_.write(text)).unwrap_or_else(|err|log_error!("Error writing from raw pipe: {err}"));
532 },
533 text = self_.pipe_crlf.receiver.recv().fuse() => {
534 text.map(|text|self_.writeln(text)).unwrap_or_else(|err|log_error!("Error writing from crlf pipe: {err}"));
535 },
536 }
537 }
538
539 self_
540 .pipe_ctl
541 .response
542 .sender
543 .send(())
544 .await
545 .unwrap_or_else(|err| log_error!("Error posting shutdown ctl: {err}"));
546 });
547 Ok(())
548 }
549
550 async fn pipe_stop(self: &Arc<Self>) -> Result<()> {
551 self.pipe_ctl.signal(()).await?;
552 Ok(())
553 }
554
555 fn pipe_abort(self: &Arc<Self>) -> Result<()> {
556 self.pipe_ctl.request.try_send(())?;
557 Ok(())
558 }
559
560 pub async fn run(self: &Arc<Self>) -> Result<()> {
564 self.pipe_start().await?;
567 self.term().run().await
568 }
569
570 pub async fn exit(self: &Arc<Self>) {
572 self.terminate.store(true, Ordering::SeqCst);
573 self.pipe_stop().await.unwrap_or_else(|err| panic!("{err}"));
574 self.term.exit();
575 }
576
577 pub fn abort(self: &Arc<Self>) {
579 self.terminate.store(true, Ordering::SeqCst);
580 self.pipe_abort().unwrap_or_else(|err| panic!("{err}"));
581 self.term.exit();
582 }
583
584 pub async fn ask(self: &Arc<Terminal>, secret: bool, prompt: &str) -> Result<String> {
588 self.reset_line_buffer();
589 self.term().write(prompt.to_string());
590 self.user_input
591 .capture(secret, false, Some(prompt.to_string()), self)
592 .await
593 }
594
595 pub async fn kbhit(self: &Arc<Terminal>, prompt: Option<&str>) -> Result<String> {
598 self.reset_line_buffer();
599 if let Some(prompt) = prompt {
600 self.term().write(prompt.to_string());
601 }
602 self.user_input
603 .capture(true, true, prompt.map(String::from), self)
604 .await
605 }
606
607 pub fn inject_unicode_string(&self, text: UnicodeString) -> Result<()> {
609 let mut data = self.inner()?;
610 self.inject_impl(&mut data, text)?;
611 Ok(())
612 }
613
614 pub fn inject<S: ToString>(&self, text: S) -> Result<()> {
616 let mut data = self.inner()?;
617 self.inject_impl(&mut data, text.to_string().into())
618 }
619
620 fn inject_impl(&self, data: &mut Inner, text: UnicodeString) -> Result<()> {
621 if self.user_input.is_enabled() {
622 self.user_input.inject_impl(text, self)
623 } else {
624 let len = text.len();
625 data.buffer.insert(data.cursor, text);
626 self.trail(data.cursor, &data.buffer, true, false, len);
627 data.cursor += len;
628 Ok(())
629 }
630 }
631
632 pub fn inject_char(&self, ch: char) -> Result<()> {
635 let mut data = self.inner()?;
636 self.inject_char_impl(&mut data, ch)?;
637 Ok(())
638 }
639
640 fn inject_char_impl(&self, data: &mut Inner, ch: char) -> Result<()> {
641 data.buffer.insert_char(data.cursor, ch);
642 self.trail(data.cursor, &data.buffer, true, false, 1);
643 data.cursor += 1;
644 Ok(())
645 }
646
647 async fn ingest(self: &Arc<Terminal>, key: Key, _term_key: String) -> Result<()> {
648 if self.user_input.is_enabled() {
649 self.user_input.ingest(key, self)?;
650 return Ok(());
651 }
652
653 match key {
654 Key::Backspace => {
655 let mut data = self.inner()?;
656 if data.cursor == 0 {
657 return Ok(());
658 }
659 self.write("\x08".to_string());
660 data.cursor -= 1;
661 let idx = data.cursor;
662 data.buffer.remove(idx);
663 self.trail(data.cursor, &data.buffer, true, true, 0);
664 }
665 Key::ArrowUp => {
666 let mut data = self.inner()?;
667 if data.history_index == 0 {
668 return Ok(());
669 }
670 let current_buffer = data.buffer.clone();
671 let index = data.history_index;
672 if data.history.len() <= index {
674 data.history.push(current_buffer);
675 } else {
676 data.history[index] = current_buffer;
677 }
678 data.history_index -= 1;
679
680 data.buffer = data.history[data.history_index].clone();
681 self.write(format!("{}{}{}", ClearLine, self.get_prompt(), data.buffer));
682 data.cursor = data.buffer.len();
683 }
684 Key::ArrowDown => {
685 let mut data = self.inner()?;
686 let len = data.history.len();
687 if data.history_index >= len {
688 return Ok(());
689 }
690 let index = data.history_index;
691 data.history[index] = data.buffer.clone();
692 data.history_index += 1;
693 if data.history_index == len {
694 data.buffer.clear();
695 } else {
696 data.buffer = data.history[data.history_index].clone();
697 }
698
699 self.write(format!("{}{}{}", ClearLine, self.get_prompt(), data.buffer));
700 data.cursor = data.buffer.len();
701 }
702 Key::ArrowLeft => {
703 let mut data = self.inner()?;
704 if data.cursor == 0 {
705 return Ok(());
706 }
707 data.cursor -= 1;
708 self.write(Left(1));
709 }
710 Key::ArrowRight => {
711 let mut data = self.inner()?;
712 if data.cursor < data.buffer.len() {
713 data.cursor += 1;
714 self.write(Right(1));
715 }
716 }
717 Key::Enter => {
718 let cmd = {
719 let mut data = self.inner()?;
720 let buffer = data.buffer.clone();
721 let length = data.history.len();
722
723 data.buffer.clear();
724 data.cursor = 0;
725
726 if !buffer.is_empty() {
727 let cmd = buffer.clone();
728
729 if length == 0 || !data.history[length - 1].is_empty() {
730 data.history_index = length;
731 } else {
732 data.history_index = length - 1;
733 }
734 let index = data.history_index;
735 if length <= index {
736 data.history.push(buffer);
737 } else {
738 data.history[index] = buffer;
739 }
740 data.history_index += 1;
741
742 Some(cmd)
743 } else {
744 None
745 }
746 };
747
748 self.crlf();
749
750 if let Some(cmd) = cmd {
751 self.running.store(true, Ordering::SeqCst);
752 self.exec(cmd).await.ok();
753 self.running.store(false, Ordering::SeqCst);
754 } else {
755 self.prompt();
756 }
757 }
758 Key::Alt(_c) => {
759 return Ok(());
760 }
761 Key::Ctrl('c') => {
762 cfg_if! {
763 if #[cfg(not(target_arch = "wasm32"))] {
764 self.exit().await;
765 }
766 }
767 return Ok(());
768 }
769 Key::Ctrl(_c) => {
770 return Ok(());
771 }
772 Key::Char(ch) => {
773 self.inject_char(ch)?;
774 }
775 _ => {
776 return Ok(());
777 }
778 }
779
780 Ok(())
781 }
782
783 fn trail(
784 &self,
785 cursor: usize,
786 buffer: &UnicodeString,
787 rewind: bool,
788 erase_last: bool,
789 offset: usize,
790 ) {
791 let mut tail = UnicodeString::from(&buffer.0[cursor..]); if erase_last {
793 tail.push(' ');
794 }
795 self.write(&tail);
796 if rewind {
797 let mut l = tail.len();
798 if offset > 0 {
799 l -= offset;
800 }
801 for _ in 0..l {
802 self.write("\x08"); }
804 }
805 }
806
807 #[inline]
813 pub fn is_running(&self) -> bool {
814 self.running.load(Ordering::SeqCst)
815 }
816
817 pub async fn exec<S: ToString>(self: &Arc<Terminal>, cmd: S) -> Result<()> {
821 if let Err(err) = self
822 .handler
823 .clone()
824 .digest(self.clone(), cmd.to_string())
825 .await
826 {
827 self.writeln(err);
828 }
829 if self.terminate.load(Ordering::SeqCst) {
830 self.term().exit();
831 } else {
832 self.prompt();
833 }
834 Ok(())
835 }
836
837 pub fn set_theme(&self, _theme: Theme) -> Result<()> {
839 #[cfg(target_arch = "wasm32")]
840 self.term.set_theme(_theme)?;
841 Ok(())
842 }
843
844 pub fn update_theme(&self) -> Result<()> {
846 #[cfg(target_arch = "wasm32")]
847 self.term.update_theme()?;
848 Ok(())
849 }
850
851 pub fn clipboard_copy(&self) -> Result<()> {
854 #[cfg(target_arch = "wasm32")]
855 self.term.clipboard_copy()?;
856 Ok(())
857 }
858
859 pub fn clipboard_paste(&self) -> Result<()> {
862 #[cfg(target_arch = "wasm32")]
863 self.term.clipboard_paste()?;
864 Ok(())
865 }
866
867 pub fn increase_font_size(&self) -> Result<Option<f64>> {
869 self.term.increase_font_size()
870 }
871
872 pub fn decrease_font_size(&self) -> Result<Option<f64>> {
874 self.term.decrease_font_size()
875 }
876
877 pub fn set_font_size(&self, font_size: f64) -> Result<()> {
879 self.term.set_font_size(font_size)
880 }
881
882 pub fn get_font_size(&self) -> Result<Option<f64>> {
884 self.term.get_font_size()
885 }
886
887 pub fn cols(&self) -> Option<usize> {
889 self.term.cols()
890 }
891
892 pub async fn select<T>(self: &Arc<Terminal>, prompt: &str, list: &[T]) -> Result<Option<T>>
897 where
898 T: std::fmt::Display + Clone, {
900 if list.is_empty() {
901 Ok(None)
902 } else if list.len() == 1 {
903 Ok(list.first().cloned())
904 } else {
905 let mut selection = None;
906 while selection.is_none() {
907 list.iter().enumerate().for_each(|(seq, item)| {
908 self.writeln(format!("{seq}: {item}"));
909 });
910
911 let text = self
912 .ask(
913 false,
914 &format!("{prompt} [{}..{}] or <enter> to abort: ", 0, list.len() - 1),
915 )
916 .await?
917 .trim()
918 .to_string();
919 if text.is_empty() {
920 self.writeln("aborting...");
921 return Err(Error::UserAbort);
922 } else {
923 match text.parse::<usize>() {
924 Ok(seq) if seq < list.len() => selection = list.get(seq).cloned(),
925 _ => {}
926 };
927 }
928 }
929
930 Ok(selection)
931 }
932 }
933
934 pub fn register_event_handler(self: &Arc<Self>, _handler: EventHandlerFn) -> Result<()> {
937 #[cfg(target_arch = "wasm32")]
938 self.term.register_event_handler(_handler)?;
939 Ok(())
940 }
941
942 pub fn register_link_matcher(
945 &self,
946 _regexp: &js_sys::RegExp,
947 _handler: LinkMatcherHandlerFn,
948 ) -> Result<()> {
949 cfg_if! {
950 if #[cfg(target_arch = "wasm32")] {
951 self.term.register_link_matcher(_regexp, _handler)?;
952 }
953 }
954 Ok(())
955 }
956}
957
958pub fn parse(s: &str) -> Vec<String> {
960 let regex = Regex::new(r"\s+").unwrap();
961 let s = regex.replace_all(s.trim(), " ");
962 s.split(' ').map(|s| s.to_string()).collect::<Vec<String>>()
963}