@@ -51,13 +51,17 @@ use {
5151// a POLL_WAIT of zero means that every single event is treated as soon as it
5252// arrives. This doesn't allow for the possibility of more than 1 event
5353// 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.
5958const EVENTS_THRESHOLD : usize = 10 ;
6059
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+
6165/// Determines if inputs should be used to extend the regular line buffer,
6266/// traverse the history in the standard prompt or edit the search string in the
6367/// reverse search
@@ -695,12 +699,7 @@ impl Reedline {
695699
696700 self . repaint ( prompt) ?;
697701
698- let mut crossterm_events: Vec < ReedlineRawEvent > = vec ! [ ] ;
699- let mut reedline_events: Vec < ReedlineEvent > = vec ! [ ] ;
700-
701702 loop {
702- let mut paste_enter_state = false ;
703-
704703 #[ cfg( feature = "external_printer" ) ]
705704 if let Some ( ref external_printer) = self . external_printer {
706705 // get messages from printer as crlf separated "lines"
@@ -716,76 +715,81 @@ impl Reedline {
716715 }
717716 }
718717
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
726732 }
733+ }
727734
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 ( ) ?) ;
756743 }
744+ #[ cfg( not( feature = "external_printer" ) ) ]
745+ events. push ( crossterm:: event:: read ( ) ?) ;
757746
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 ( ) ?) ;
760751 }
761752
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+ }
779762
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+ }
781781 }
782782 }
783783 }
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) ) ;
786789 }
787790
788- for event in reedline_events. drain ( ..) {
791+ // Handle reedline events.
792+ for event in reedline_events {
789793 match self . handle_event ( prompt, event) ? {
790794 EventStatus :: Exits ( signal) => {
791795 // Check if we are merely suspended (to process an ExecuteHostCommand event)
@@ -798,9 +802,7 @@ impl Reedline {
798802 return Ok ( signal) ;
799803 }
800804 EventStatus :: Handled => {
801- if !paste_enter_state {
802- self . repaint ( prompt) ?;
803- }
805+ self . repaint ( prompt) ?;
804806 }
805807 EventStatus :: Inapplicable => {
806808 // Nothing changed, no need to repaint
0 commit comments