Skip to content

Commit

Permalink
Avoid consuming CPU when waiting for input. (#651)
Browse files Browse the repository at this point in the history
When reading input, start by calling `event::read`, which efficiently blocks
until input arrives, rather than polling for input every 100
milliseconds.

This preserves the existing `POLL_WAIT` polling behavior by moving the
poll after the first `read` call returns.

Fixes #521.
  • Loading branch information
sunfishcode authored Nov 1, 2023
1 parent 973dbb5 commit 2b6790c
Showing 1 changed file with 58 additions and 61 deletions.
119 changes: 58 additions & 61 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,77 +725,74 @@ impl Reedline {
}
}

if event::poll(Duration::from_millis(100))? {
let mut latest_resize = None;

// There could be multiple events queued up!
// pasting text, resizes, blocking this thread (e.g. during debugging)
// We should be able to handle all of them as quickly as possible without causing unnecessary output steps.
while event::poll(Duration::from_millis(POLL_WAIT))? {
match event::read()? {
Event::Resize(x, y) => {
latest_resize = Some((x, y));
}
enter @ Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
..
}) => {
let enter = ReedlineRawEvent::convert_from(enter);
match enter {
Some(enter) => {
crossterm_events.push(enter);
// Break early to check if the input is complete and
// can be send to the hosting application. If
// multiple complete entries are submitted, events
// are still in the crossterm queue for us to
// process.
paste_enter_state = crossterm_events.len() > EVENTS_THRESHOLD;
break;
}
None => continue,
}
let mut latest_resize = None;
loop {
match event::read()? {
Event::Resize(x, y) => {
latest_resize = Some((x, y));
}
enter @ Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
..
}) => {
let enter = ReedlineRawEvent::convert_from(enter);
if let Some(enter) = enter {
crossterm_events.push(enter);
// Break early to check if the input is complete and
// can be send to the hosting application. If
// multiple complete entries are submitted, events
// are still in the crossterm queue for us to
// process.
paste_enter_state = crossterm_events.len() > EVENTS_THRESHOLD;
break;
}
x => {
let raw_event = ReedlineRawEvent::convert_from(x);
match raw_event {
Some(evt) => crossterm_events.push(evt),
None => continue,
}
}
x => {
let raw_event = ReedlineRawEvent::convert_from(x);
if let Some(evt) = raw_event {
crossterm_events.push(evt);
}
}
}

if let Some((x, y)) = latest_resize {
reedline_events.push(ReedlineEvent::Resize(x, y));
// There could be multiple events queued up!
// pasting text, resizes, blocking this thread (e.g. during debugging)
// We should be able to handle all of them as quickly as possible without causing unnecessary output steps.
if !event::poll(Duration::from_millis(POLL_WAIT))? {
break;
}
}

// Accelerate pasted text by fusing `EditCommand`s
//
// (Text should only be `EditCommand::InsertChar`s)
let mut last_edit_commands = None;
for event in crossterm_events.drain(..) {
match (&mut last_edit_commands, self.edit_mode.parse_event(event)) {
(None, ReedlineEvent::Edit(ec)) => {
last_edit_commands = Some(ec);
}
(None, other_event) => {
reedline_events.push(other_event);
}
(Some(ref mut last_ecs), ReedlineEvent::Edit(ec)) => {
last_ecs.extend(ec);
}
(ref mut a @ Some(_), other_event) => {
reedline_events.push(ReedlineEvent::Edit(a.take().unwrap()));
if let Some((x, y)) = latest_resize {
reedline_events.push(ReedlineEvent::Resize(x, y));
}

reedline_events.push(other_event);
}
// Accelerate pasted text by fusing `EditCommand`s
//
// (Text should only be `EditCommand::InsertChar`s)
let mut last_edit_commands = None;
for event in crossterm_events.drain(..) {
match (&mut last_edit_commands, self.edit_mode.parse_event(event)) {
(None, ReedlineEvent::Edit(ec)) => {
last_edit_commands = Some(ec);
}
(None, other_event) => {
reedline_events.push(other_event);
}
(Some(ref mut last_ecs), ReedlineEvent::Edit(ec)) => {
last_ecs.extend(ec);
}
(ref mut a @ Some(_), other_event) => {
reedline_events.push(ReedlineEvent::Edit(a.take().unwrap()));

reedline_events.push(other_event);
}
}
if let Some(ec) = last_edit_commands {
reedline_events.push(ReedlineEvent::Edit(ec));
}
};
}
if let Some(ec) = last_edit_commands {
reedline_events.push(ReedlineEvent::Edit(ec));
}

for event in reedline_events.drain(..) {
match self.handle_event(prompt, event)? {
Expand Down

0 comments on commit 2b6790c

Please sign in to comment.