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