From 6e8f13256398a274f2622e9bcb3509acd920644e Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Tue, 11 Feb 2020 08:33:22 +0900 Subject: [PATCH] Don't visually freeze the UI on an onslaught of input. --- Cargo.lock | 90 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/term_ui/mod.rs | 15 ++++++-- src/utils.rs | 29 +++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c715438..58c0e8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ "gag", "ropey", "smallvec 1.2.0", + "time", "unicode-segmentation", "unicode-width", ] @@ -291,6 +292,35 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.7.3" @@ -356,6 +386,17 @@ dependencies = [ "smallvec 0.6.13", ] +[[package]] +name = "rustversion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "scopeguard" version = "1.0.0" @@ -410,6 +451,17 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "tempfile" version = "3.1.0" @@ -433,6 +485,38 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "time" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb8742444475438c500d017736dd7cf3d9c9dd1f0153cfa4af428692484e3ef" +dependencies = [ + "rustversion", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9b6e9f095bc105e183e3cd493d72579be3181ad4004fceb01adbe9eecab2d" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987cfe0537f575b5fc99909de6185f6c19c3ad8889e2275e686a873d0869ba1" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-segmentation" version = "1.6.0" @@ -445,6 +529,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + [[package]] name = "vec_map" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 2aeb18b..e5c92d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ clap = "2" smallvec = "1" crossterm = "0.14" gag = "0.1.10" +time = "0.2" # Local crate dependencies [dependencies.backend] diff --git a/src/term_ui/mod.rs b/src/term_ui/mod.rs index 351ff48..ac0744a 100644 --- a/src/term_ui/mod.rs +++ b/src/term_ui/mod.rs @@ -15,7 +15,7 @@ use crate::{ editor::Editor, formatter::{block_count, block_index_and_range, char_range_from_block_index, LineFormatter}, string_utils::{line_ending_to_str, rope_slice_is_line_ending, LineEnding}, - utils::{digit_count, RopeGraphemes}, + utils::{digit_count, RopeGraphemes, Timer}, }; use self::{ @@ -24,6 +24,7 @@ use self::{ }; const EMPTY_MOD: KeyModifiers = KeyModifiers::empty(); +const UPDATE_TICK_MS: u64 = 10; // Color theme. // Styles are (FG, BG). @@ -80,6 +81,7 @@ const STYLE_INFO: Style = Style( macro_rules! ui_loop { ($term_ui:ident,draw $draw:block,key_press($key:ident) $key_press:block) => { let mut stop = false; + let mut timer = Timer::new(); // Draw the editor to screen for the first time { @@ -97,7 +99,7 @@ macro_rules! ui_loop { // want to re-draw on e.g. async syntax highlighting updates, or // update based on a file being modified outside our process. loop { - if crossterm::event::poll(Duration::from_millis(5)).unwrap() { + if crossterm::event::poll(Duration::from_millis(UPDATE_TICK_MS)).unwrap() { match crossterm::event::read().unwrap() { Event::Key($key) => { let (status, state_changed) = || -> (LoopStatus, bool) { $key_press }(); @@ -120,6 +122,15 @@ macro_rules! ui_loop { break; } } + + // If too much time has passed since the last redraw, + // break so we can draw if needed. This keeps an onslaught + // of input (e.g. when pasting a large piece of text) from + // visually freezing the UI. + if timer.elapsed() >= UPDATE_TICK_MS { + timer.tick(); + break; + } } else { break; } diff --git a/src/utils.rs b/src/utils.rs index 4e79477..4bf7bff 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ use ropey::{iter::Chunks, str_utils::byte_to_char_idx, RopeSlice}; +use time::Instant; use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete}; use unicode_width::UnicodeWidthStr; @@ -190,6 +191,34 @@ impl<'a> Iterator for RopeGraphemes<'a> { //============================================================= +#[derive(Copy, Clone)] +pub struct Timer { + last_instant: Instant, +} + +impl Timer { + pub fn new() -> Timer { + Timer { + last_instant: Instant::now(), + } + } + + /// Marks a new tick time and returns the time elapsed in milliseconds since + /// the last call to tick(). + pub fn tick(&mut self) -> u64 { + let n = self.elapsed(); + self.last_instant = Instant::now(); + n + } + + /// Returns the time elapsed in milliseconds since the last call to tick(). + pub fn elapsed(self) -> u64 { + self.last_instant.elapsed().whole_milliseconds() as u64 + } +} + +//============================================================= + #[cfg(test)] mod tests { use super::*;