Don't visually freeze the UI on an onslaught of input.

This commit is contained in:
Nathan Vegdahl 2020-02-11 08:33:22 +09:00
parent d1de3aacb3
commit 6e8f132563
4 changed files with 133 additions and 2 deletions

90
Cargo.lock generated
View File

@ -10,6 +10,7 @@ dependencies = [
"gag", "gag",
"ropey", "ropey",
"smallvec 1.2.0", "smallvec 1.2.0",
"time",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
] ]
@ -291,6 +292,35 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" 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]] [[package]]
name = "rand" name = "rand"
version = "0.7.3" version = "0.7.3"
@ -356,6 +386,17 @@ dependencies = [
"smallvec 0.6.13", "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]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.0.0" version = "1.0.0"
@ -410,6 +451,17 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 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]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.1.0" version = "3.1.0"
@ -433,6 +485,38 @@ dependencies = [
"unicode-width", "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]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.6.0" version = "1.6.0"
@ -445,6 +529,12 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.8.1" version = "0.8.1"

View File

@ -23,6 +23,7 @@ clap = "2"
smallvec = "1" smallvec = "1"
crossterm = "0.14" crossterm = "0.14"
gag = "0.1.10" gag = "0.1.10"
time = "0.2"
# Local crate dependencies # Local crate dependencies
[dependencies.backend] [dependencies.backend]

View File

@ -15,7 +15,7 @@ use crate::{
editor::Editor, editor::Editor,
formatter::{block_count, block_index_and_range, char_range_from_block_index, LineFormatter}, 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}, string_utils::{line_ending_to_str, rope_slice_is_line_ending, LineEnding},
utils::{digit_count, RopeGraphemes}, utils::{digit_count, RopeGraphemes, Timer},
}; };
use self::{ use self::{
@ -24,6 +24,7 @@ use self::{
}; };
const EMPTY_MOD: KeyModifiers = KeyModifiers::empty(); const EMPTY_MOD: KeyModifiers = KeyModifiers::empty();
const UPDATE_TICK_MS: u64 = 10;
// Color theme. // Color theme.
// Styles are (FG, BG). // Styles are (FG, BG).
@ -80,6 +81,7 @@ const STYLE_INFO: Style = Style(
macro_rules! ui_loop { macro_rules! ui_loop {
($term_ui:ident,draw $draw:block,key_press($key:ident) $key_press:block) => { ($term_ui:ident,draw $draw:block,key_press($key:ident) $key_press:block) => {
let mut stop = false; let mut stop = false;
let mut timer = Timer::new();
// Draw the editor to screen for the first time // 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 // want to re-draw on e.g. async syntax highlighting updates, or
// update based on a file being modified outside our process. // update based on a file being modified outside our process.
loop { 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() { match crossterm::event::read().unwrap() {
Event::Key($key) => { Event::Key($key) => {
let (status, state_changed) = || -> (LoopStatus, bool) { $key_press }(); let (status, state_changed) = || -> (LoopStatus, bool) { $key_press }();
@ -120,6 +122,15 @@ macro_rules! ui_loop {
break; 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 { } else {
break; break;
} }

View File

@ -1,4 +1,5 @@
use ropey::{iter::Chunks, str_utils::byte_to_char_idx, RopeSlice}; use ropey::{iter::Chunks, str_utils::byte_to_char_idx, RopeSlice};
use time::Instant;
use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete}; use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
use unicode_width::UnicodeWidthStr; 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;