@@ -51,13 +51,17 @@ use {
51
51
// a POLL_WAIT of zero means that every single event is treated as soon as it
52
52
// arrives. This doesn't allow for the possibility of more than 1 event
53
53
// happening at the same time.
54
- const POLL_WAIT : u64 = 10 ;
55
- // Since a paste event is multiple Event::Key events happening at the same time, we specify
56
- // how many events should be in the crossterm_events vector before it is considered
57
- // a paste. 10 events in 10 milliseconds is conservative enough (unlikely somebody
58
- // will type more than 10 characters in 10 milliseconds)
54
+ const POLL_WAIT : Duration = Duration :: from_millis ( 100 ) ;
55
+ // Since a paste event is multiple `Event::Key` events happening at the same
56
+ // time, we specify how many events should be in the `crossterm_events` vector
57
+ // before it is considered a paste. 10 events is conservative enough.
59
58
const EVENTS_THRESHOLD : usize = 10 ;
60
59
60
+ /// Maximum time Reedline will block on input before yielding control to
61
+ /// external printers.
62
+ #[ cfg( feature = "external_printer" ) ]
63
+ const EXTERNAL_PRINTER_WAIT : Duration = Duration :: from_millis ( 100 ) ;
64
+
61
65
/// Determines if inputs should be used to extend the regular line buffer,
62
66
/// traverse the history in the standard prompt or edit the search string in the
63
67
/// reverse search
@@ -695,12 +699,7 @@ impl Reedline {
695
699
696
700
self . repaint ( prompt) ?;
697
701
698
- let mut crossterm_events: Vec < ReedlineRawEvent > = vec ! [ ] ;
699
- let mut reedline_events: Vec < ReedlineEvent > = vec ! [ ] ;
700
-
701
702
loop {
702
- let mut paste_enter_state = false ;
703
-
704
703
#[ cfg( feature = "external_printer" ) ]
705
704
if let Some ( ref external_printer) = self . external_printer {
706
705
// get messages from printer as crlf separated "lines"
@@ -716,76 +715,81 @@ impl Reedline {
716
715
}
717
716
}
718
717
719
- let mut latest_resize = None ;
720
- loop {
721
- // There could be multiple events queued up!
722
- // pasting text, resizes, blocking this thread (e.g. during debugging)
723
- // We should be able to handle all of them as quickly as possible without causing unnecessary output steps.
724
- if !event:: poll ( Duration :: from_millis ( POLL_WAIT ) ) ? {
725
- break ;
718
+ // Helper function that returns true if the input is complete and
719
+ // can be sent to the hosting application.
720
+ fn completed ( events : & [ Event ] ) -> bool {
721
+ if let Some ( event) = events. last ( ) {
722
+ matches ! (
723
+ event,
724
+ Event :: Key ( KeyEvent {
725
+ code: KeyCode :: Enter ,
726
+ modifiers: KeyModifiers :: NONE ,
727
+ ..
728
+ } )
729
+ )
730
+ } else {
731
+ false
726
732
}
733
+ }
727
734
728
- match event:: read ( ) ? {
729
- Event :: Resize ( x, y) => {
730
- latest_resize = Some ( ( x, y) ) ;
731
- }
732
- enter @ Event :: Key ( KeyEvent {
733
- code : KeyCode :: Enter ,
734
- modifiers : KeyModifiers :: NONE ,
735
- ..
736
- } ) => {
737
- let enter = ReedlineRawEvent :: convert_from ( enter) ;
738
- if let Some ( enter) = enter {
739
- crossterm_events. push ( enter) ;
740
- // Break early to check if the input is complete and
741
- // can be send to the hosting application. If
742
- // multiple complete entries are submitted, events
743
- // are still in the crossterm queue for us to
744
- // process.
745
- paste_enter_state = crossterm_events. len ( ) > EVENTS_THRESHOLD ;
746
- break ;
747
- }
748
- }
749
- x => {
750
- let raw_event = ReedlineRawEvent :: convert_from ( x) ;
751
- if let Some ( evt) = raw_event {
752
- crossterm_events. push ( evt) ;
753
- }
754
- }
755
- }
735
+ let mut events: Vec < Event > = vec ! [ ] ;
736
+
737
+ // If the `external_printer` feature is enabled, we need to
738
+ // periodically yield so that external printers get a chance to
739
+ // print. Otherwise, we can just block until we receive an event.
740
+ #[ cfg( feature = "external_printer" ) ]
741
+ if event:: poll ( EXTERNAL_PRINTER_WAIT ) ? {
742
+ events. push ( crossterm:: event:: read ( ) ?) ;
756
743
}
744
+ #[ cfg( not( feature = "external_printer" ) ) ]
745
+ events. push ( crossterm:: event:: read ( ) ?) ;
757
746
758
- if let Some ( ( x, y) ) = latest_resize {
759
- reedline_events. push ( ReedlineEvent :: Resize ( x, y) ) ;
747
+ // Receive all events in the queue without blocking. Will stop when
748
+ // a line of input is completed.
749
+ while !completed ( & events) && event:: poll ( Duration :: from_millis ( 0 ) ) ? {
750
+ events. push ( crossterm:: event:: read ( ) ?) ;
760
751
}
761
752
762
- // Accelerate pasted text by fusing `EditCommand`s
763
- //
764
- // (Text should only be `EditCommand::InsertChar`s)
765
- let mut last_edit_commands = None ;
766
- for event in crossterm_events. drain ( ..) {
767
- match ( & mut last_edit_commands, self . edit_mode . parse_event ( event) ) {
768
- ( None , ReedlineEvent :: Edit ( ec) ) => {
769
- last_edit_commands = Some ( ec) ;
770
- }
771
- ( None , other_event) => {
772
- reedline_events. push ( other_event) ;
773
- }
774
- ( Some ( ref mut last_ecs) , ReedlineEvent :: Edit ( ec) ) => {
775
- last_ecs. extend ( ec) ;
776
- }
777
- ( ref mut a @ Some ( _) , other_event) => {
778
- reedline_events. push ( ReedlineEvent :: Edit ( a. take ( ) . unwrap ( ) ) ) ;
753
+ // If we believe there's text pasting or resizing going on, batch
754
+ // more events at the cost of a slight delay.
755
+ if events. len ( ) > EVENTS_THRESHOLD
756
+ || events. iter ( ) . any ( |e| matches ! ( e, Event :: Resize ( _, _) ) )
757
+ {
758
+ while !completed ( & events) && event:: poll ( POLL_WAIT ) ? {
759
+ events. push ( crossterm:: event:: read ( ) ?) ;
760
+ }
761
+ }
779
762
780
- reedline_events. push ( other_event) ;
763
+ // Convert `Event` into `ReedlineEvent`. Also, fuse consecutive
764
+ // `ReedlineEvent::EditCommand` into one. Also, if there're multiple
765
+ // `ReedlineEvent::Resize`, only keep the last one.
766
+ let mut reedline_events: Vec < ReedlineEvent > = vec ! [ ] ;
767
+ let mut edits = vec ! [ ] ;
768
+ let mut resize = None ;
769
+ for event in events {
770
+ if let Ok ( event) = ReedlineRawEvent :: try_from ( event) {
771
+ match self . edit_mode . parse_event ( event) {
772
+ ReedlineEvent :: Edit ( edit) => edits. extend ( edit) ,
773
+ ReedlineEvent :: Resize ( x, y) => resize = Some ( ( x, y) ) ,
774
+ event => {
775
+ if !edits. is_empty ( ) {
776
+ reedline_events
777
+ . push ( ReedlineEvent :: Edit ( std:: mem:: take ( & mut edits) ) ) ;
778
+ }
779
+ reedline_events. push ( event) ;
780
+ }
781
781
}
782
782
}
783
783
}
784
- if let Some ( ec) = last_edit_commands {
785
- reedline_events. push ( ReedlineEvent :: Edit ( ec) ) ;
784
+ if !edits. is_empty ( ) {
785
+ reedline_events. push ( ReedlineEvent :: Edit ( edits) ) ;
786
+ }
787
+ if let Some ( ( x, y) ) = resize {
788
+ reedline_events. push ( ReedlineEvent :: Resize ( x, y) ) ;
786
789
}
787
790
788
- for event in reedline_events. drain ( ..) {
791
+ // Handle reedline events.
792
+ for event in reedline_events {
789
793
match self . handle_event ( prompt, event) ? {
790
794
EventStatus :: Exits ( signal) => {
791
795
// Check if we are merely suspended (to process an ExecuteHostCommand event)
@@ -798,9 +802,7 @@ impl Reedline {
798
802
return Ok ( signal) ;
799
803
}
800
804
EventStatus :: Handled => {
801
- if !paste_enter_state {
802
- self . repaint ( prompt) ?;
803
- }
805
+ self . repaint ( prompt) ?;
804
806
}
805
807
EventStatus :: Inapplicable => {
806
808
// Nothing changed, no need to repaint
0 commit comments