diff options
-rw-r--r-- | src/fail.rs | 43 | ||||
-rw-r--r-- | src/file_browser.rs | 6 | ||||
-rw-r--r-- | src/files.rs | 208 | ||||
-rw-r--r-- | src/listview.rs | 12 | ||||
-rw-r--r-- | src/main.rs | 8 | ||||
-rw-r--r-- | src/preview.rs | 103 | ||||
-rw-r--r-- | src/tabview.rs | 2 | ||||
-rw-r--r-- | src/textview.rs | 4 | ||||
-rw-r--r-- | src/widget.rs | 8 |
9 files changed, 234 insertions, 160 deletions
diff --git a/src/fail.rs b/src/fail.rs new file mode 100644 index 0000000..791435b --- /dev/null +++ b/src/fail.rs @@ -0,0 +1,43 @@ +use failure; + +pub type HResult<T> = Result<T, HError>; + +#[derive(Fail, Debug)] +pub enum HError { + #[fail(display = "IO error: {}", error)] + IoError{#[cause] error: std::io::Error}, + #[fail(display = "Mutex failed")] + MutexError, + #[fail(display = "Channel failed: {}", error)] + ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError}, + #[fail(display = "Previewer failed on file: {}", file)] + PreviewFailed{file: String}, + #[fail(display = "StalePreviewer for file: {}", file)] + StalePreviewError{file: String}, + #[fail(display = "Failed: {}", error)] + Error{#[cause] error: failure::Error } +} + +impl From<std::io::Error> for HError { + fn from(error: std::io::Error) -> Self { + HError::IoError { error: error } + } +} + +impl From<failure::Error> for HError { + fn from(error: failure::Error) -> Self { + HError::Error { error: error } + } +} + +impl From<std::sync::mpsc::TryRecvError> for HError { + fn from(error: std::sync::mpsc::TryRecvError) -> Self { + HError::ChannelTryRecvError { error: error } + } +} + +impl<T> From<std::sync::PoisonError<T>> for HError { + fn from(_: std::sync::PoisonError<T>) -> Self { + HError::MutexError + } +} diff --git a/src/file_browser.rs b/src/file_browser.rs index 01eb316..f5809a5 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -3,7 +3,7 @@ use termion::event::Key; use std::error::Error; use std::io::Write; -use crate::coordinates::{Coordinates, Position, Size}; +use crate::coordinates::{Coordinates}; use crate::files::{File, Files}; use crate::listview::ListView; use crate::miller_columns::MillerColumns; @@ -212,10 +212,10 @@ impl Widget for FileBrowser { let ypos = self.get_coordinates().position().y(); let file = self.selected_file(); - let permissions = file.pretty_print_permissions(); + let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".into()); let user = file.pretty_user().unwrap_or("NOUSER".into()); let group = file.pretty_group().unwrap_or("NOGROUP".into()); - let mtime = file.pretty_mtime(); + let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into()); let selection = self.main_column().get_selection(); diff --git a/src/files.rs b/src/files.rs index 7d7b3e4..322f99c 100644 --- a/src/files.rs +++ b/src/files.rs @@ -9,7 +9,9 @@ use users; use chrono::TimeZone; use failure::Error; -use crate::fail::HError; +use crate::fail::HResult; + +use std::sync::{Arc, Mutex}; lazy_static! { @@ -33,26 +35,9 @@ impl Index<usize> for Files { } } -fn get_kind(file: &std::fs::DirEntry) -> Kind { - let file = file.file_type().unwrap(); - if file.is_file() { - return Kind::File; - } - if file.is_dir() { - return Kind::Directory; - } - if file.is_symlink() { - return Kind::Link; - } - Kind::Pipe -} -fn get_color(path: &Path, meta: &std::fs::Metadata) -> Option<lscolors::Color> { - match COLORS.style_for_path_with_metadata(path, Some(&meta)) { - Some(style) => style.clone().foreground, - None => None, - } -} + + impl Files { pub fn new_from_path(path: &Path) -> Result<Files, Error> { @@ -61,23 +46,56 @@ impl Files { let files: Vec<_> = direntries? .iter() .map(|file| { - //let file = file?; let name = file.file_name(); let name = name.to_string_lossy(); - let kind = get_kind(&file); let path = file.path(); - let meta = file.metadata().unwrap(); - let mode = meta.mode(); - let size = meta.len(); - let mtime = meta.mtime(); - let user = meta.uid(); - let group = meta.gid(); - let color = get_color(&path, &meta); - File::new(&name, path, kind, size as usize, mtime, color, mode, - user, group) + File::new(&name, path) + }) + .collect(); + + let mut files = Files { + directory: File::new_from_path(&path)?, + files: files, + sort: SortBy::Name, + dirs_first: true, + reverse: false, + show_hidden: true + }; + + files.sort(); + + if files.files.len() == 0 { + files.files = vec![File::new_placeholder(&path)?]; + } + + Ok(files) + } + + pub fn new_from_path_cancellable(path: &Path, stale: Arc<Mutex<bool>>) -> Result<Files, Error> { + let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect(); + + let files: Vec<_> = direntries? + .iter() + .map(|file| { + if crate::preview::is_stale(&stale).unwrap() { + None + } else { + let name = file.file_name(); + let name = name.to_string_lossy(); + let path = file.path(); + Some(File::new(&name, path)) + } }) + .fuse() + .flatten() .collect(); + if crate::preview::is_stale(&stale).unwrap() { + return Err(crate::fail::HError::StalePreviewError { + file: path.to_string_lossy().to_string() + })?; + } + let mut files = Files { directory: File::new_from_path(&path)?, files: files, @@ -102,19 +120,21 @@ impl Files { .files .sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)), SortBy::Size => { + self.meta_all(); self.files.sort_by(|a, b| { - if a.size == b.size { + if a.meta().unwrap().size() == b.meta().unwrap().size() { return alphanumeric_sort::compare_str(&b.name, &a.name); } - a.size.cmp(&b.size).reverse() + a.meta().unwrap().size().cmp(&b.meta().unwrap().size()).reverse() }); } SortBy::MTime => { + self.meta_all(); self.files.sort_by(|a, b| { - if a.mtime == b.mtime { + if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() { return alphanumeric_sort::compare_str(&a.name, &b.name); } - a.mtime.cmp(&b.mtime) + a.meta().unwrap().mtime().cmp(&b.meta().unwrap().mtime()) }); } }; @@ -169,6 +189,17 @@ impl Files { self.files = files; } + pub fn meta_all(&mut self) { + let len = self.files.len(); + self.meta_upto(len); + } + + pub fn meta_upto(&mut self, to: usize) { + for file in self.files.iter_mut().take(to) { + file.get_meta().ok(); + } + } + pub fn len(&self) -> usize { self.files.len() } @@ -182,8 +213,6 @@ impl Files { pub enum Kind { Directory, File, - Link, - Pipe, Placeholder } @@ -205,17 +234,24 @@ pub enum SortBy { MTime, } -#[derive(Debug, PartialEq, Clone)] + +impl PartialEq for File { + fn eq(&self, other: &File) -> bool { + if self.path == other.path { + true + } else { + false + } + } +} + +#[derive(Debug, Clone)] pub struct File { pub name: String, pub path: PathBuf, - pub size: Option<usize>, pub kind: Kind, - pub mtime: i64, pub color: Option<lscolors::Color>, - pub mode: u32, - pub user: u32, - pub group: u32, + pub meta: Option<std::fs::Metadata>, pub selected: bool // flags: Option<String>, } @@ -224,24 +260,13 @@ impl File { pub fn new( name: &str, path: PathBuf, - kind: Kind, - size: usize, - mtime: i64, - color: Option<lscolors::Color>, - mode: u32, - user: u32, - group: u32 ) -> File { File { name: name.to_string(), + kind: if path.is_dir() { Kind::Directory } else { Kind::File }, path: path, - size: Some(size), - kind: kind, - mtime: mtime, - color: color, - mode: mode, - user: user, - group: group, + meta: None, + color: None, selected: false } } @@ -253,18 +278,7 @@ impl File { .map(|name| name.to_string_lossy().to_string()) .unwrap_or("/".to_string()); - let kind = Kind::Directory; //get_kind(&path); - let meta = &path.metadata()?; - let size = meta.len(); - let user = meta.uid(); - let group = meta.gid(); - let color = get_color(&path, meta); - let mode = meta.mode(); - let mtime = meta.mtime(); - Ok( - File::new(&name, pathbuf, kind, size as usize, mtime, color, mode, user - , group) - ) + Ok(File::new(&name, pathbuf)) } pub fn new_placeholder(path: &Path) -> Result<File, Error> { @@ -274,17 +288,44 @@ impl File { Ok(file) } - pub fn calculate_size(&self) -> (usize, String) { + pub fn meta(&self) -> HResult<std::fs::Metadata> { + match &self.meta { + Some(meta) => Ok(meta.clone()), + None => { Ok(std::fs::metadata(&self.path)?) } + } + } + + pub fn get_meta(&mut self) -> HResult<()> { + if let Some(_) = self.meta { return Ok(()) } + + let meta = std::fs::metadata(&self.path)?; + let color = self.get_color(&meta); + + self.meta = Some(meta); + self.color = color; + Ok(()) + } + + fn get_color(&self, meta: &std::fs::Metadata) -> Option<lscolors::Color> { + match COLORS.style_for_path_with_metadata(&self.path, Some(&meta)) { + Some(style) => style.clone().foreground, + None => None, + } + } + + pub fn calculate_size(&self) -> HResult<(u64, String)> { if self.is_dir() { let dir_iterator = std::fs::read_dir(&self.path); match dir_iterator { - Ok(dir_iterator) => return (dir_iterator.count(), "".to_string()), - Err(_) => return (0, "".to_string()) + Ok(dir_iterator) => return Ok((dir_iterator.count() as u64, + "".to_string())), + Err(_) => return Ok((0, "".to_string())) } } + let mut unit = 0; - let mut size = self.size.unwrap(); + let mut size = self.meta()?.size(); while size > 1024 { size /= 1024; unit += 1; @@ -299,7 +340,7 @@ impl File { _ => "", } .to_string(); - (size, unit) + Ok((size, unit)) } pub fn get_mime(&self) -> Option<String> { @@ -333,8 +374,8 @@ impl File { self.selected } - pub fn pretty_print_permissions(&self) -> String { - let perms: usize = format!("{:o}", self.mode).parse().unwrap(); + pub fn pretty_print_permissions(&self) -> HResult<String> { + let perms: usize = format!("{:o}", self.meta()?.mode()).parse().unwrap(); let perms: usize = perms % 800; let perms = format!("{}", perms); @@ -354,11 +395,12 @@ impl File { _ => format!("---") }).collect(); - perms + Ok(perms) } pub fn pretty_user(&self) -> Option<String> { - let uid = self.user; + if self.meta().is_err() { return None } + let uid = self.meta().unwrap().uid(); let file_user = users::get_user_by_uid(uid)?; let cur_user = users::get_current_username()?; let color = @@ -370,7 +412,8 @@ impl File { } pub fn pretty_group(&self) -> Option<String> { - let gid = self.group; + if self.meta().is_err() { return None } + let gid = self.meta().unwrap().gid(); let file_group = users::get_group_by_gid(gid)?; let cur_group = users::get_current_groupname()?; let color = @@ -381,10 +424,11 @@ impl File { Some(format!("{}{}", color, file_group.name().to_string_lossy())) } - pub fn pretty_mtime(&self) -> String { + pub fn pretty_mtime(&self) -> Option<String> { + if self.meta().is_err() { return None } //let time = chrono::DateTime::from_timestamp(self.mtime, 0); let time: chrono::DateTime<chrono::Local> - = chrono::Local.timestamp(self.mtime, 0); - time.format("%F %R").to_string() + = chrono::Local.timestamp(self.meta().unwrap().mtime(), 0); + Some(time.format("%F %R").to_string()) } } diff --git a/src/listview.rs b/src/listview.rs index 99adaaf..c1e1d30 100644 --- a/src/listview.rs +++ b/src/listview.rs @@ -1,18 +1,14 @@ -use rayon::prelude::*; use termion::event::{Event, Key}; use unicode_width::UnicodeWidthStr; use std::path::{Path, PathBuf}; use std::io::Write; -//use std::sync::mpsc::{channel, Sender, Receiver}; use crate::coordinates::{Coordinates, Position, Size}; use crate::files::{File, Files}; use crate::term; use crate::widget::{Widget}; -// Maybe also buffer drawlist for efficiency when it doesn't change every draw - #[derive(PartialEq)] pub struct ListView<T> where @@ -95,7 +91,7 @@ where fn render_line(&self, file: &File) -> String { let name = &file.name; - let (size, unit) = file.calculate_size(); + let (size, unit) = file.calculate_size().unwrap(); let selection_gap = " ".to_string(); let (name, selection_color) = if file.is_selected() { @@ -137,10 +133,6 @@ where } impl ListView<Files> -where - ListView<Files>: Widget, - Files: std::ops::Index<usize, Output = File>, - Files: std::marker::Sized, { pub fn selected_file(&self) -> &File { let selection = self.selection; @@ -366,6 +358,8 @@ impl Widget for ListView<Files> { self.refresh(); } fn refresh(&mut self) { + let visible_file_num = self.selection + self.get_coordinates().ysize() as usize; + self.content.meta_upto(visible_file_num); self.lines = self.content.len(); self.buffer = self.render(); } diff --git a/src/main.rs b/src/main.rs index 7de9a46..0add6c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,6 @@ extern crate termion; extern crate unicode_width; #[macro_use] extern crate lazy_static; -#[macro_use] extern crate failure; #[macro_use] extern crate failure_derive; @@ -23,7 +22,6 @@ use termion::raw::IntoRawMode; use termion::screen::AlternateScreen; use std::io::{stdout, Write}; -use std::marker::Send; mod coordinates; mod file_browser; @@ -42,16 +40,14 @@ mod async_widget; mod fail; use window::Window; -///use async_widget::AsyncPlug; -use widget::Widget; + fn main() { - let mut bufout = std::io::BufWriter::new(std::io::stdout()); + let bufout = std::io::BufWriter::new(std::io::stdout()); // Need to do this here to actually turn terminal into raw mode... let mut _screen = AlternateScreen::from(Box::new(bufout)); let mut _stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap()); - let filebrowser = crate::file_browser::FileBrowser::new().unwrap(); let mut tabview = crate::tabview::TabView::new(); diff --git a/src/preview.rs b/src/preview.rs index 2d3f82c..dd78c78 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -1,7 +1,5 @@ use failure::Error; -use failure::Fail; -use std::io::Write; use std::sync::Mutex; use std::sync::Arc; @@ -14,14 +12,11 @@ use crate::fail::HError; type HResult<T> = Result<T, HError>; -type HClosure<T> = Box<Fn() -> Result<T, HError> + Send>; -type WClosure = HClosure<Box<dyn Widget>>; +type HClosure<T> = Box<Fn(Arc<Mutex<bool>>) -> Result<T, HError> + Send>; type WidgetO = Box<dyn Widget + Send>; -type WidgetFn = Box<Fn() -> Box<dyn Widget + Send>>; lazy_static! { static ref SUBPROC: Arc<Mutex<Option<u32>>> = { Arc::new(Mutex::new(None)) }; - static ref CURFILE: Arc<Mutex<Option<File>>> = { Arc::new(Mutex::new(None)) }; } fn kill_proc() -> HResult<()> { @@ -33,17 +28,8 @@ fn kill_proc() -> HResult<()> { Ok(()) } -fn is_current(file: &File) -> bool { - match CURFILE.lock().unwrap().as_ref() { - Some(curfile) => curfile == file, - None => true - } -} - -fn set_current(file: &File) -> HResult<()> { - let mut curfile = CURFILE.lock()?; - *curfile = Some(file.clone()); - Ok(()) +pub fn is_stale(stale: &Arc<Mutex<bool>>) -> HResult<bool> { + Ok(*(stale.lock()?)) } enum State<T: Send> { @@ -56,7 +42,7 @@ enum State<T: Send> { struct WillBe<T: Send> { pub state: State<T>, rx: std::sync::mpsc::Receiver<T>, - cancel: bool + stale: Arc<Mutex<bool>> } impl<T: Send + 'static> WillBe<T> where { @@ -65,14 +51,15 @@ impl<T: Send + 'static> WillBe<T> where { let (tx,rx) = std::sync::mpsc::channel(); let mut willbe = WillBe { state: State::Becoming, rx: rx, - cancel: false }; + stale: Arc::new(Mutex::new(false)) }; willbe.run(closure, tx); willbe } fn run(&mut self, closure: HClosure<T>, tx: std::sync::mpsc::Sender<T>) { + let stale = self.stale.clone(); std::thread::spawn(move|| { - let thing = closure(); + let thing = closure(stale); match thing { Ok(thing) => { tx.send(thing).ok(); }, Err(err) => { dbg!(err); } @@ -80,6 +67,11 @@ impl<T: Send + 'static> WillBe<T> where { }); } + pub fn set_stale(&mut self) -> HResult<()> { + *self.stale.lock()? = true; + Ok(()) + } + pub fn check(&mut self) -> Result<(), Error> { match self.state { State::Is(_) => Ok(()), @@ -91,16 +83,9 @@ impl<T: Send + 'static> WillBe<T> where { } } - pub fn wait(mut self) -> Result<T, std::sync::mpsc::RecvError> { + pub fn wait(self) -> Result<T, std::sync::mpsc::RecvError> { self.rx.recv() } - - pub fn take(mut self) -> Option<T> { - match self.state { - State::Is(thing) => Some(thing), - _ => None - } - } } impl<W: Widget + Send> PartialEq for WillBeWidget<W> { @@ -121,10 +106,13 @@ struct WillBeWidget<T: Widget + Send> { impl<T: Widget + Send + 'static> WillBeWidget<T> { fn new(closure: HClosure<T>) -> WillBeWidget<T> { WillBeWidget { - willbe: WillBe::new_become(Box::new(move || closure())), + willbe: WillBe::new_become(Box::new(move |stale| closure(stale))), coordinates: Coordinates::new() } } + pub fn set_stale(&mut self) -> HResult<()> { + self.willbe.set_stale() + } } // impl<T: Widget + Send> WillBeWidget<T> { @@ -177,8 +165,16 @@ impl<T: Widget + Send> Widget for WillBeWidget<T> { } +impl PartialEq for Previewer { + fn eq(&self, other: &Previewer) -> bool { + if self.widget.coordinates == other.widget.coordinates { + true + } else { + false + } + } +} -#[derive(PartialEq)] pub struct Previewer { widget: WillBeWidget<Box<dyn Widget + Send>>, } @@ -186,12 +182,9 @@ pub struct Previewer { impl Previewer { pub fn new() -> Previewer { - let willbe = WillBeWidget::new(Box::new(move || { - Ok(Box::new(crate::textview::TextView { - lines: vec![], - buffer: String::new(), - coordinates: Coordinates::new() - }) as Box<dyn Widget + Send>) + let willbe = WillBeWidget::new(Box::new(move |_| { + Ok(Box::new(crate::textview::TextView::new_blank()) + as Box<dyn Widget + Send>) })); Previewer { widget: willbe } } @@ -206,22 +199,26 @@ impl Previewer { pub fn set_file(&mut self, file: &File) { let coordinates = self.get_coordinates().clone(); let file = file.clone(); - set_current(&file).unwrap(); - self.become_preview(Ok(WillBeWidget::new(Box::new(move || { + self.widget.set_stale().ok(); + + self.become_preview(Ok(WillBeWidget::new(Box::new(move |stale| { kill_proc().unwrap(); + let file = file.clone(); if file.kind == Kind::Directory { - let preview = Previewer::preview_dir(&file, &coordinates); + let preview = Previewer::preview_dir(&file, &coordinates, stale.clone()); return preview; } if file.get_mime() == Some("text".to_string()) { - return Previewer::preview_text(&file, &coordinates) + return Previewer::preview_text(&file, &coordinates, stale.clone()) } - let preview = Previewer::preview_external(&file, &coordinates); + let preview = Previewer::preview_external(&file, + &coordinates, + stale.clone()); if preview.is_ok() { return preview; } else { let mut blank = Box::new(TextView::new_blank()); @@ -237,39 +234,40 @@ impl Previewer { Err(HError::PreviewFailed { file: file.name.clone() }) } - fn preview_dir(file: &File, coordinates: &Coordinates) + fn preview_dir(file: &File, coordinates: &Coordinates, stale: Arc<Mutex<bool>>) -> Result<WidgetO, HError> { - let files = Files::new_from_path(&file.path)?; + let files = Files::new_from_path_cancellable(&file.path, + stale.clone())?; let len = files.len(); - if len == 0 || !is_current(&file) { return Previewer::preview_failed(&file) } + if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) } let mut file_list = ListView::new(files); file_list.set_coordinates(&coordinates); file_list.refresh(); - if !is_current(&file) { return Previewer::preview_failed(&file) } + if is_stale(&stale)? { return Previewer::preview_failed(&file) } file_list.animate_slide_up(); Ok(Box::new(file_list) as Box<dyn Widget + Send>) } - fn preview_text(file: &File, coordinates: &Coordinates) + fn preview_text(file: &File, coordinates: &Coordinates, stale: Arc<Mutex<bool>>) -> HResult<WidgetO> { let lines = coordinates.ysize() as usize; let mut textview = TextView::new_from_file_limit_lines(&file, lines); - if !is_current(&file) { return Previewer::preview_failed(&file) } + if is_stale(&stale)? { return Previewer::preview_failed(&file) } textview.set_coordinates(&coordinates); textview.refresh(); - if !is_current(&file) { return Previewer::preview_failed(&file) } + if is_stale(&stale)? { return Previewer::preview_failed(&file) } textview.animate_slide_up(); Ok(Box::new(textview)) } - fn preview_external(file: &File, coordinates: &Coordinates) + fn preview_external(file: &File, coordinates: &Coordinates, stale: Arc<Mutex<bool>>) -> Result<Box<dyn Widget + Send>, HError> { let process = std::process::Command::new("scope.sh") @@ -289,12 +287,11 @@ impl Previewer { *pid_ = Some(pid); } - if !is_current(&file) { return Previewer::preview_failed(&file) } + if is_stale(&stale)? { return Previewer::preview_failed(&file) } let output = process.wait_with_output()?; - if !is_current(&file) { return Previewer::preview_failed(&file) } - + if is_stale(&stale)? { return Previewer::preview_failed(&file) } { let mut pid_ = SUBPROC.lock()?; *pid_ = None; @@ -303,7 +300,7 @@ impl Previewer { let status = output.status.code() .ok_or(HError::PreviewFailed{file: file.name.clone()})?; - if status == 0 || status == 5 && is_current(&file) { + if status == 0 || status == 5 && !is_stale(&stale)? { //is_current(&file) { let output = std::str::from_utf8(&output.stdout) .unwrap() .to_string(); diff --git a/src/tabview.rs b/src/tabview.rs index 39286a0..65cfaef 100644 --- a/src/tabview.rs +++ b/src/tabview.rs @@ -1,6 +1,6 @@ use termion::event::Key; -use crate::coordinates::{Coordinates, Position, Size}; +use crate::coordinates::{Coordinates}; use crate::widget::Widget; pub trait Tabbable<T: Widget> { diff --git a/src/textview.rs b/src/textview.rs index 304d664..a7a28c6 100644 --- a/src/textview.rs +++ b/src/textview.rs @@ -1,8 +1,6 @@ -use ::rayon::prelude::*; - use std::io::BufRead; -use crate::coordinates::{Coordinates, Position, Size}; +use crate::coordinates::{Coordinates}; use crate::files::File; use crate::term::sized_string; use crate::widget::Widget; diff --git a/src/widget.rs b/src/widget.rs index dec2fa7..ccf8f72 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -6,6 +6,9 @@ use std::io::{BufWriter, Write}; pub trait Widget { + fn get_widget(&self) -> Box<dyn Widget> { + Box::new(crate::textview::TextView::new_blank()) + } fn get_coordinates(&self) -> &Coordinates; fn set_coordinates(&mut self, coordinates: &Coordinates); fn render_header(&self) -> String; @@ -120,7 +123,7 @@ pub trait Widget { let ysize = coords.ysize(); let clear = self.get_clearlist(); let pause = std::time::Duration::from_millis(5); - let mut bufout = std::io::BufWriter::new(std::io::stdout()); + let mut bufout = BufWriter::new(std::io::stdout()); for i in (0..10).rev() { let coords = Coordinates { size: Size((xsize,ysize-i)), @@ -132,8 +135,7 @@ pub trait Widget { let buffer = self.get_drawlist(); write!(bufout, "{}{}", clear, buffer).unwrap(); - bufout.flush(); - + bufout.flush().ok(); std::thread::sleep(pause); } |