From bddb1a9ddecc420f58e6993ae598437b221a124e Mon Sep 17 00:00:00 2001 From: Timon Date: Tue, 30 Jul 2019 18:48:50 +0200 Subject: [PATCH] Crossterm Performance Update (#361) --- src/backend/crossterm.rs | 73 +++++++++++++++++++++++++--------------- src/view/scroll/core.rs | 21 ++++-------- src/view/scroll/mod.rs | 32 +++++------------- src/view/scroll/raw.rs | 41 ++++++---------------- src/views/text_area.rs | 4 +-- 5 files changed, 72 insertions(+), 99 deletions(-) diff --git a/src/backend/crossterm.rs b/src/backend/crossterm.rs index c3f0562..cfd52e2 100644 --- a/src/backend/crossterm.rs +++ b/src/backend/crossterm.rs @@ -7,15 +7,17 @@ use crate::vec::Vec2; use crate::{backend, theme}; use crossterm::{ - cursor, input, terminal, AlternateScreen, AsyncReader, Attribute, - ClearType, Color, Colored, InputEvent as CInputEvent, - KeyEvent as CKeyEvent, MouseButton as CMouseButton, - MouseEvent as CMouseEvent, Terminal, TerminalCursor, + cursor, execute, input, queue, terminal, AlternateScreen, AsyncReader, + Attribute, Clear, ClearType, Color, Colored, Command, Goto, Hide, + InputEvent as CInputEvent, KeyEvent as CKeyEvent, + MouseButton as CMouseButton, MouseEvent as CMouseEvent, Output, RawScreen, + SetAttr, SetBg, SetFg, Show, Terminal, TerminalCursor, }; use crate::event::{Event, Key, MouseButton, MouseEvent}; +use core::borrow::BorrowMut; use std::cell::{Cell, RefCell}; -use std::io::{self, Stdout, Write}; +use std::io::{self, BufWriter, Stdout, StdoutLock, Write}; /// Backend using crossterm pub struct Backend { @@ -24,7 +26,7 @@ pub struct Backend { // reader to read user input async. async_reader: AsyncReader, _alternate_screen: AlternateScreen, - stdout: RefCell, + stdout: RefCell>, cursor: TerminalCursor, terminal: Terminal, } @@ -48,22 +50,31 @@ impl Backend { last_button: None, async_reader, _alternate_screen, - stdout: RefCell::new(io::stdout()), + stdout: RefCell::new(BufWriter::new(io::stdout())), terminal: terminal(), cursor: cursor(), })) } fn apply_colors(&self, colors: theme::ColorPair) { - with_color(colors.front, |c| self.write(Colored::Fg(*c))); - with_color(colors.back, |c| self.write(Colored::Bg(*c))); + with_color(colors.front, |c| { + queue!(self.stdout.borrow_mut(), SetFg(*c)) + }); + with_color(colors.back, |c| { + queue!(self.stdout.borrow_mut(), SetBg(*c)) + }); } fn write(&self, content: T) where T: std::fmt::Display, { - write!(self.stdout.borrow_mut(), "{}", format!("{}", content)).unwrap() + write!(self.stdout.borrow_mut(), "{}", format!("{}", content)) + .unwrap(); + } + + fn set_attr(&self, attr: Attribute) { + queue!(self.stdout.borrow_mut(), SetAttr(attr)); } fn map_key(&mut self, event: CInputEvent) -> Event { @@ -164,11 +175,16 @@ impl backend::Backend for Backend { } fn finish(&mut self) { - self.cursor.goto(1, 1).unwrap(); - self.terminal.clear(ClearType::All).unwrap(); - self.write(Attribute::Reset); + execute!( + self.stdout.borrow_mut(), + Goto(0, 0), + Clear(ClearType::All), + SetBg(Color::Reset), + SetFg(Color::Reset), + SetAttr(Attribute::Reset), + Show + ); input().disable_mouse_mode().unwrap(); - cursor().show().unwrap(); } fn refresh(&mut self) { @@ -182,11 +198,12 @@ impl backend::Backend for Backend { fn screen_size(&self) -> Vec2 { let size = self.terminal.terminal_size(); - Vec2::from(size) + (1, 1) + Vec2::from(size) } fn print_at(&self, pos: Vec2, text: &str) { - self.cursor.goto(pos.x as u16, pos.y as u16).unwrap(); + queue!(self.stdout.borrow_mut(), Goto(pos.x as u16, pos.y as u16)) + .unwrap(); self.write(text); } @@ -194,7 +211,7 @@ impl backend::Backend for Backend { if repetitions > 0 { let mut out = self.stdout.borrow_mut(); - self.cursor.goto(pos.x as u16, pos.y as u16).unwrap(); + queue!(out, Goto(pos.x as u16, pos.y as u16)).unwrap(); // as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout. // Current work aground is writing bytes instead of a string to the terminal. @@ -214,7 +231,7 @@ impl backend::Backend for Backend { back: color, }); - self.terminal.clear(ClearType::All).unwrap(); + queue!(self.stdout.borrow_mut(), Clear(ClearType::All)).unwrap(); } fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair { @@ -231,22 +248,22 @@ impl backend::Backend for Backend { fn set_effect(&self, effect: theme::Effect) { match effect { theme::Effect::Simple => (), - theme::Effect::Reverse => self.write(Attribute::Reverse), - theme::Effect::Bold => self.write(Attribute::Bold), - theme::Effect::Italic => self.write(Attribute::Italic), - theme::Effect::Strikethrough => self.write(Attribute::CrossedOut), - theme::Effect::Underline => self.write(Attribute::Underlined), + theme::Effect::Reverse => self.set_attr(Attribute::Reverse), + theme::Effect::Bold => self.set_attr(Attribute::Bold), + theme::Effect::Italic => self.set_attr(Attribute::Italic), + theme::Effect::Strikethrough => self.set_attr(Attribute::CrossedOut), + theme::Effect::Underline => self.set_attr(Attribute::Underlined), } } fn unset_effect(&self, effect: theme::Effect) { match effect { theme::Effect::Simple => (), - theme::Effect::Reverse => self.write(Attribute::Reverse), - theme::Effect::Bold => self.write(Attribute::NoBold), - theme::Effect::Italic => self.write(Attribute::NoItalic), - theme::Effect::Strikethrough => self.write(Attribute::NotCrossedOut), - theme::Effect::Underline => self.write(Attribute::Underlined), + theme::Effect::Reverse => self.set_attr(Attribute::Reverse), + theme::Effect::Bold => self.set_attr(Attribute::NoBold), + theme::Effect::Italic => self.set_attr(Attribute::NoItalic), + theme::Effect::Strikethrough => self.set_attr(Attribute::NotCrossedOut), + theme::Effect::Underline => self.set_attr(Attribute::Underlined), } } } diff --git a/src/view/scroll/core.rs b/src/view/scroll/core.rs index d80b820..532d112 100644 --- a/src/view/scroll/core.rs +++ b/src/view/scroll/core.rs @@ -97,8 +97,7 @@ impl Core { /// Returns a sub-printer ready to draw the content. pub fn sub_printer<'a, 'b>( - &self, - printer: &Printer<'a, 'b>, + &self, printer: &Printer<'a, 'b>, ) -> Printer<'a, 'b> { // Draw scrollbar? let scrolling = self.is_scrolling(); @@ -185,9 +184,7 @@ impl Core { /// Handle an event after processing by the content. pub fn on_inner_event( - &mut self, - event: Event, - inner_result: EventResult, + &mut self, event: Event, inner_result: EventResult, important_area: Rect, ) -> EventResult { match inner_result { @@ -365,9 +362,7 @@ impl Core { /// Performs `View::call_on_any()` pub fn call_on_any<'a, F>( - &mut self, - selector: &Selector<'_>, - cb: AnyCb<'a>, + &mut self, selector: &Selector<'_>, cb: AnyCb<'a>, inner_call_on_any: F, ) where F: FnOnce(&Selector, AnyCb), @@ -377,9 +372,7 @@ impl Core { /// Performs `View::focus_view()` pub fn focus_view( - &mut self, - selector: &Selector<'_>, - inner_focus_view: F, + &mut self, selector: &Selector<'_>, inner_focus_view: F, ) -> Result<(), ()> where F: FnOnce(&Selector) -> Result<(), ()>, @@ -413,8 +406,7 @@ impl Core { /// Sets the padding between content and scrollbar. pub fn set_scrollbar_padding>( - &mut self, - scrollbar_padding: V, + &mut self, scrollbar_padding: V, ) { self.scrollbar_padding = scrollbar_padding.into(); } @@ -423,8 +415,7 @@ impl Core { /// /// Chainable variant. pub fn scrollbar_padding>( - self, - scrollbar_padding: V, + self, scrollbar_padding: V, ) -> Self { self.with(|s| s.set_scrollbar_padding(scrollbar_padding)) } diff --git a/src/view/scroll/mod.rs b/src/view/scroll/mod.rs index 2f569bc..187fff2 100644 --- a/src/view/scroll/mod.rs +++ b/src/view/scroll/mod.rs @@ -54,9 +54,7 @@ impl Default for ScrollStrategy { /// } /// ``` pub fn on_event( - scroller: &mut T, - event: Event, - on_event: OnEvent, + scroller: &mut T, event: Event, on_event: OnEvent, important_area: ImportantArea, ) -> EventResult where @@ -75,9 +73,7 @@ where /// Performs `View::important_area` on a `scroll::Scroller`. pub fn important_area( - scroller: &T, - size: Vec2, - mut important_area: ImportantArea, + scroller: &T, size: Vec2, mut important_area: ImportantArea, ) -> Rect where T: Scroller, @@ -96,10 +92,7 @@ where /// Performs `View::layout` on a `scroll::Scroller`. pub fn layout( - scroller: &mut T, - size: Vec2, - needs_relayout: bool, - layout: Layout, + scroller: &mut T, size: Vec2, needs_relayout: bool, layout: Layout, required_size: RequiredSize, ) where T: Scroller, @@ -118,9 +111,7 @@ pub fn layout( /// Performs `View::required_size` on a `scroll::Scroller`. pub fn required_size( - scroller: &mut T, - size: Vec2, - needs_relayout: bool, + scroller: &mut T, size: Vec2, needs_relayout: bool, required_size: RequiredSize, ) -> Vec2 where @@ -149,9 +140,7 @@ where /// /// This is an alternative to `scroll::draw()` when you just need to print individual lines. pub fn draw_lines( - scroller: &T, - printer: &Printer, - mut line_drawer: LineDrawer, + scroller: &T, printer: &Printer, mut line_drawer: LineDrawer, ) where T: Scroller, LineDrawer: FnMut(&T, &Printer, usize), @@ -170,11 +159,8 @@ pub fn draw_lines( /// /// `left_border` will be called for each row to draw the left border for the given line number. pub fn draw_frame( - scroller: &T, - printer: &Printer, - mut left_border: LeftBorder, - mut top_border: TopBorder, - mut right_border: RightBorder, + scroller: &T, printer: &Printer, mut left_border: LeftBorder, + mut top_border: TopBorder, mut right_border: RightBorder, mut bottom_border: BottomBorder, ) where T: Scroller, @@ -224,9 +210,7 @@ pub fn draw_frame( /// /// It will print a box with the appropriate `├`, `┤` and so on. pub fn draw_box_frame( - scroller: &T, - printer: &Printer, - is_h_delim: IsHDelim, + scroller: &T, printer: &Printer, is_h_delim: IsHDelim, is_v_delim: IsVDelim, ) where T: Scroller, diff --git a/src/view/scroll/raw.rs b/src/view/scroll/raw.rs index 1ed0aba..3da714c 100644 --- a/src/view/scroll/raw.rs +++ b/src/view/scroll/raw.rs @@ -11,9 +11,7 @@ use crate::Vec2; /// Implements `View::draw` over the `model`. pub fn draw( - printer: &Printer, - model: &Model, - mut get_scroller: GetScroller, + printer: &Printer, model: &Model, mut get_scroller: GetScroller, inner_draw: Draw, ) where Model: ?Sized, @@ -32,12 +30,8 @@ pub fn draw( /// /// Returns (Inner size, Outer size, New scrolling) fn sizes_when_scrolling( - constraint: Vec2, - scrolling: XY, - strict: bool, - model: &mut Model, - get_scroller: &mut GetScroller, - required_size: &mut RequiredSize, + constraint: Vec2, scrolling: XY, strict: bool, model: &mut Model, + get_scroller: &mut GetScroller, required_size: &mut RequiredSize, ) -> (Vec2, Vec2, XY) where Model: ?Sized, @@ -83,12 +77,8 @@ where /// /// Returns (Inner size, Outer size) fn sizes( - constraint: Vec2, - strict: bool, - needs_relayout: bool, - model: &mut Model, - get_scroller: &mut GetScroller, - required_size: &mut RequiredSize, + constraint: Vec2, strict: bool, needs_relayout: bool, model: &mut Model, + get_scroller: &mut GetScroller, required_size: &mut RequiredSize, ) -> (Vec2, Vec2) where Model: ?Sized, @@ -151,11 +141,8 @@ where /// Implements `View::layout` on the given model. pub fn layout( - size: Vec2, - needs_relayout: bool, - model: &mut Model, - mut get_scroller: GetScroller, - mut required_size: RequiredSize, + size: Vec2, needs_relayout: bool, model: &mut Model, + mut get_scroller: GetScroller, mut required_size: RequiredSize, mut layout: Layout, ) where Model: ?Sized, @@ -185,11 +172,8 @@ pub fn layout( /// Implements `View::required_size` on the given model. pub fn required_size( - constraint: Vec2, - needs_relayout: bool, - model: &mut Model, - mut get_scroller: GetScroller, - mut required_size: RequiredSize, + constraint: Vec2, needs_relayout: bool, model: &mut Model, + mut get_scroller: GetScroller, mut required_size: RequiredSize, ) -> Vec2 where Model: ?Sized, @@ -210,11 +194,8 @@ where /// Implements `View::on_event` on the given model. pub fn on_event( - event: Event, - model: &mut Model, - mut get_scroller: GetScroller, - mut on_event: OnEvent, - mut important_area: ImportantArea, + event: Event, model: &mut Model, mut get_scroller: GetScroller, + mut on_event: OnEvent, mut important_area: ImportantArea, ) -> EventResult where Model: ?Sized, diff --git a/src/views/text_area.rs b/src/views/text_area.rs index cd33192..83ab61f 100644 --- a/src/views/text_area.rs +++ b/src/views/text_area.rs @@ -303,8 +303,8 @@ impl TextArea { } fn backspace(&mut self) { - self.move_left(); - self.delete(); + self.move_left(); + self.delete(); } fn delete(&mut self) {