Skip to content

Commit faddde8

Browse files
committed
Rework read_line_helper()
1 parent 5dd7e0e commit faddde8

File tree

1 file changed

+62
-72
lines changed

1 file changed

+62
-72
lines changed

src/engine.rs

+62-72
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,10 @@ 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.
5958
const EVENTS_THRESHOLD: usize = 10;
6059

6160
/// Determines if inputs should be used to extend the regular line buffer,
@@ -695,12 +694,7 @@ impl Reedline {
695694

696695
self.repaint(prompt)?;
697696

698-
let mut crossterm_events: Vec<ReedlineRawEvent> = vec![];
699-
let mut reedline_events: Vec<ReedlineEvent> = vec![];
700-
701697
loop {
702-
let mut paste_enter_state = false;
703-
704698
#[cfg(feature = "external_printer")]
705699
if let Some(ref external_printer) = self.external_printer {
706700
// get messages from printer as crlf separated "lines"
@@ -716,76 +710,74 @@ impl Reedline {
716710
}
717711
}
718712

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;
726-
}
727-
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-
}
713+
// Helper function that returns true if the input is complete and
714+
// can be sent to the hosting application.
715+
fn completed(events: &[Event]) -> bool {
716+
if let Some(event) = events.last() {
717+
matches!(
718+
event,
719+
Event::Key(KeyEvent {
720+
code: KeyCode::Enter,
721+
modifiers: KeyModifiers::NONE,
722+
..
723+
})
724+
)
725+
} else {
726+
false
755727
}
756728
}
757729

758-
if let Some((x, y)) = latest_resize {
759-
reedline_events.push(ReedlineEvent::Resize(x, y));
730+
let mut events: Vec<Event> = vec![];
731+
732+
// Block until we receive an event.
733+
events.push(crossterm::event::read()?);
734+
735+
// Receive all events in the queue without blocking. Will stop when
736+
// a line of input is completed.
737+
while !completed(&events) && event::poll(Duration::from_millis(0))? {
738+
events.push(crossterm::event::read()?);
760739
}
761740

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()));
741+
// If we believe there's text pasting or resizing going on, batch
742+
// more events at the cost of a slight delay.
743+
if events.len() > EVENTS_THRESHOLD
744+
|| events.iter().any(|e| matches!(e, Event::Resize(_, _)))
745+
{
746+
while !completed(&events) && event::poll(POLL_WAIT)? {
747+
events.push(crossterm::event::read()?);
748+
}
749+
}
779750

780-
reedline_events.push(other_event);
751+
// Convert `Event` into `ReedlineEvent`. Also, fuse consecutive
752+
// `ReedlineEvent::EditCommand` into one. Also, if there're multiple
753+
// `ReedlineEvent::Resize`, only keep the last one.
754+
let mut reedline_events: Vec<ReedlineEvent> = vec![];
755+
let mut edits = vec![];
756+
let mut resize = None;
757+
for event in events {
758+
if let Some(event) = ReedlineRawEvent::convert_from(event) {
759+
match self.edit_mode.parse_event(event) {
760+
ReedlineEvent::Edit(edit) => edits.extend(edit),
761+
ReedlineEvent::Resize(x, y) => resize = Some((x, y)),
762+
event => {
763+
if !edits.is_empty() {
764+
reedline_events
765+
.push(ReedlineEvent::Edit(std::mem::take(&mut edits)));
766+
}
767+
reedline_events.push(event);
768+
}
781769
}
782770
}
783771
}
784-
if let Some(ec) = last_edit_commands {
785-
reedline_events.push(ReedlineEvent::Edit(ec));
772+
if !edits.is_empty() {
773+
reedline_events.push(ReedlineEvent::Edit(edits));
774+
}
775+
if let Some((x, y)) = resize {
776+
reedline_events.push(ReedlineEvent::Resize(x, y));
786777
}
787778

788-
for event in reedline_events.drain(..) {
779+
// Handle reedline events.
780+
for event in reedline_events {
789781
match self.handle_event(prompt, event)? {
790782
EventStatus::Exits(signal) => {
791783
// Check if we are merely suspended (to process an ExecuteHostCommand event)
@@ -798,9 +790,7 @@ impl Reedline {
798790
return Ok(signal);
799791
}
800792
EventStatus::Handled => {
801-
if !paste_enter_state {
802-
self.repaint(prompt)?;
803-
}
793+
self.repaint(prompt)?;
804794
}
805795
EventStatus::Inapplicable => {
806796
// Nothing changed, no need to repaint

0 commit comments

Comments
 (0)