From fd366a26dcddfc50d54ec0481a8f6e4848356b32 Mon Sep 17 00:00:00 2001 From: rabite Date: Fri, 22 Mar 2019 22:49:34 +0100 Subject: async stuff much improved --- src/fail.rs | 20 ++++- src/file_browser.rs | 125 +++++++++++++++------------- src/main.rs | 1 + src/preview.rs | 236 ++++++++++++++++++++++++++++------------------------ src/proclist.rs | 42 ++++------ 5 files changed, 232 insertions(+), 192 deletions(-) diff --git a/src/fail.rs b/src/fail.rs index 6cf4afe..039002a 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -37,6 +37,12 @@ pub enum HError { NoneError(Backtrace), #[fail(display = "Not ready yet!")] WillBeNotReady(Backtrace), + #[fail(display = "Not ready yet!")] + AsyncNotReadyError(Backtrace), + #[fail(display = "Value has already been taken!")] + AsyncAlreadyTakenError(Backtrace), + #[fail(display = "Async Error: {}", _0)] + AsyncError(String), #[fail(display = "No widget found")] NoWidgetError(Backtrace), #[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)] @@ -72,7 +78,7 @@ pub enum HError { #[fail(display = "Terminal has been resized!")] TerminalResizedError, #[fail(display = "{}", _0)] - Log(String) + Log(String), } impl HError { @@ -126,6 +132,18 @@ impl HError { pub fn stale() -> HResult { Err(HError::StaleError(Backtrace::new())) } + + pub fn async_not_ready() -> HResult { + Err(HError::AsyncNotReadyError(Backtrace::new())) + } + + pub fn async_taken() -> HResult { + Err(HError::AsyncAlreadyTakenError(Backtrace::new())) + } + + pub fn async_error(error: &HError) -> HResult { + Err(HError::AsyncError(format!("{}", error))) + } } diff --git a/src/file_browser.rs b/src/file_browser.rs index 558e5a6..37d44bb 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -7,15 +7,14 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; use std::path::PathBuf; use std::collections::HashMap; -use std::ffi::{OsString, OsStr}; +use std::ffi::OsString; -use crate::files::{File, Files, PathBufExt, OsStrTools}; +use crate::files::{File, Files, PathBufExt}; use crate::listview::ListView; use crate::hbox::HBox; use crate::widget::Widget; -use crate::dirty::Dirtyable; use crate::tabview::{TabView, Tabbable}; -use crate::preview::{Previewer, WillBeWidget}; +use crate::preview::{Previewer, AsyncWidget}; use crate::fail::{HResult, HError, ErrorLog}; use crate::widget::{Events, WidgetCore}; use crate::proclist::ProcView; @@ -27,7 +26,7 @@ use crate::coordinates::Coordinates; #[derive(PartialEq)] pub enum FileBrowserWidgets { - FileList(WillBeWidget>), + FileList(AsyncWidget>), Previewer(Previewer), } @@ -191,7 +190,7 @@ impl FileBrowser { }).last()?; let left_path = main_path.parent().map(|p| p.to_path_buf()); - let main_widget = WillBeWidget::new(&core, Box::new(move |_| { + let main_widget = AsyncWidget::new(&core, Box::new(move |_| { let mut list = ListView::new(&core_m, Files::new_from_path(&main_path)?); list.animate_slide_up().log(); @@ -199,7 +198,7 @@ impl FileBrowser { })); if let Some(left_path) = left_path { - let left_widget = WillBeWidget::new(&core, Box::new(move |_| { + let left_widget = AsyncWidget::new(&core, Box::new(move |_| { let mut list = ListView::new(&core_l, Files::new_from_path(&left_path)?); list.animate_slide_up().log(); @@ -314,8 +313,8 @@ impl FileBrowser { self.prev_cwd = Some(self.cwd.clone()); self.cwd = dir.clone(); - let main_widget = self.main_widget_mut()?; - main_widget.change_to(Box::new(move |stale, core| { + let main_async_widget = self.main_async_widget_mut()?; + main_async_widget.change_to(Box::new(move |stale, core| { let path = dir.path(); let cached_files = cached_files.clone(); @@ -334,7 +333,7 @@ impl FileBrowser { if let Ok(grand_parent) = self.cwd()?.parent_as_file() { self.left_widget_goto(&grand_parent).log(); } else { - self.left_widget_mut()?.set_stale().log(); + self.left_async_widget_mut()?.set_stale().log(); } Ok(()) @@ -345,8 +344,8 @@ impl FileBrowser { let cached_files = self.get_cached_files(&dir).ok(); let dir = dir.clone(); - let left_widget = self.left_widget_mut()?; - left_widget.change_to(Box::new(move |stale, core| { + let left_async_widget = self.left_async_widget_mut()?; + left_async_widget.change_to(Box::new(move |stale, core| { let path = dir.path(); let cached_files = cached_files.clone(); @@ -415,34 +414,29 @@ impl FileBrowser { } pub fn update_preview(&mut self) -> HResult<()> { - if !self.main_widget()?.ready() { return Ok(()) } + if !self.main_async_widget_mut()?.ready() { return Ok(()) } if self.main_widget()? - .widget()? - .lock()? - .as_ref() - .unwrap() .content .len() == 0 { - self.preview_widget_mut()?.set_stale(); + self.preview_widget_mut()?.set_stale().log(); return Ok(()); } let file = self.selected_file()?.clone(); let selection = self.get_selection(&file).ok().cloned(); let cached_files = self.get_cached_files(&file).ok(); let preview = self.preview_widget_mut()?; - preview.set_file(&file, selection, cached_files); + preview.set_file(&file, selection, cached_files).log(); Ok(()) } pub fn set_left_selection(&mut self) -> HResult<()> { - if !self.left_widget()?.ready() { return Ok(()) } + if !self.left_async_widget_mut()?.ready() { return Ok(()) } if self.cwd.parent().is_none() { return Ok(()) } let parent = self.cwd()?.parent_as_file(); - if let Ok(left_selection) = self.get_selection(&parent?) { - self.left_widget()?.widget()?.lock()?.as_mut()?.select_file(&left_selection); - } + let left_selection = self.get_selection(&parent?)?.clone(); + self.left_widget_mut()?.select_file(&left_selection); Ok(()) } @@ -452,11 +446,11 @@ impl FileBrowser { } pub fn get_files(&mut self) -> HResult { - Ok(self.main_widget()?.widget()?.lock()?.as_ref()?.content.clone()) + Ok(self.main_widget()?.content.clone()) } pub fn get_left_files(&mut self) -> HResult { - Ok(self.left_widget()?.widget()?.lock()?.as_ref()?.content.clone()) + Ok(self.left_widget()?.content.clone()) } pub fn cache_files(&mut self, files: Files) -> HResult<()> { @@ -491,13 +485,14 @@ impl FileBrowser { } pub fn left_dir(&self) -> HResult { - let widget = self.left_widget()?.widget()?; - let dir = widget.lock()?.as_ref()?.content.directory.clone(); + let widget = self.left_widget()?; + let dir = widget.content.directory.clone(); Ok(dir) } fn update_watches(&mut self) -> HResult<()> { - if !self.left_widget()?.ready() || !self.main_widget()?.ready() { + if !self.left_async_widget_mut()?.ready() || + !self.main_async_widget_mut()?.ready() { return Ok(()) } let watched_dirs = self.watches.clone(); @@ -539,13 +534,11 @@ impl FileBrowser { fn handle_dir_events(&mut self) -> HResult<()> { let dir_events = self.dir_events.clone(); for event in dir_events.lock()?.iter() { - let main_widget = self.main_widget()?.widget()?; - let mut main_widget = main_widget.lock()?; - let main_result = main_widget.as_mut()?.content.handle_event(event); + let main_widget = self.main_widget_mut()?; + let main_result = main_widget.content.handle_event(event); - let left_widget = self.left_widget()?.widget()?; - let mut left_files = left_widget.lock()?; - let left_result = left_files.as_mut()?.content.handle_event(event); + let left_widget = self.left_widget_mut()?; + let left_result = left_widget.content.handle_event(event); match main_result { Err(HError::WrongDirectoryError { .. }) => { @@ -563,61 +556,79 @@ impl FileBrowser { } pub fn selected_file(&self) -> HResult { - let widget = self.main_widget()?.widget()?; - let file = widget.lock()?.as_ref()?.selected_file().clone(); + let widget = self.main_widget()?; + let file = widget.selected_file().clone(); Ok(file) } pub fn selected_files(&self) -> HResult> { - let widget = self.main_widget()?.widget()?; - let files = widget.lock()?.as_ref()?.content.get_selected().into_iter().map(|f| { + let widget = self.main_widget()?; + let files = widget.content.get_selected().into_iter().map(|f| { f.clone() }).collect(); Ok(files) } - pub fn main_widget(&self) -> HResult<&WillBeWidget>> { + pub fn main_async_widget_mut(&mut self) -> HResult<&mut AsyncWidget>> { + let widget = self.columns.active_widget_mut()?; + + let widget = match widget { + FileBrowserWidgets::FileList(filelist) => filelist, + _ => { HError::wrong_widget("previewer", "filelist")? } + }; + Ok(widget) + } + + pub fn main_widget(&self) -> HResult<&ListView> { let widget = self.columns.active_widget()?; let widget = match widget { - FileBrowserWidgets::FileList(filelist) => Ok(filelist), + FileBrowserWidgets::FileList(filelist) => filelist.widget(), _ => { HError::wrong_widget("previewer", "filelist")? } }; widget } - pub fn main_widget_mut(&mut self) -> HResult<&mut WillBeWidget>> { + pub fn main_widget_mut(&mut self) -> HResult<&mut ListView> { let widget = self.columns.active_widget_mut()?; let widget = match widget { - FileBrowserWidgets::FileList(filelist) => Ok(filelist), + FileBrowserWidgets::FileList(filelist) => filelist.widget_mut(), _ => { HError::wrong_widget("previewer", "filelist")? } }; widget } - pub fn left_widget(&self) -> HResult<&WillBeWidget>> { + pub fn left_async_widget_mut(&mut self) -> HResult<&mut AsyncWidget>> { + let widget = match self.columns.widgets.get_mut(0)? { + FileBrowserWidgets::FileList(filelist) => filelist, + _ => { return HError::wrong_widget("previewer", "filelist"); } + }; + Ok(widget) + } + + pub fn left_widget(&self) -> HResult<&ListView> { let widget = match self.columns.widgets.get(0)? { - FileBrowserWidgets::FileList(filelist) => Ok(filelist), + FileBrowserWidgets::FileList(filelist) => filelist.widget(), _ => { return HError::wrong_widget("previewer", "filelist"); } }; widget } - pub fn left_widget_mut(&mut self) -> HResult<&mut WillBeWidget>> { + pub fn left_widget_mut(&mut self) -> HResult<&mut ListView> { let widget = match self.columns.widgets.get_mut(0)? { - FileBrowserWidgets::FileList(filelist) => Ok(filelist), + FileBrowserWidgets::FileList(filelist) => filelist.widget_mut(), _ => { return HError::wrong_widget("previewer", "filelist"); } }; widget } - pub fn preview_widget(&self) -> HResult<&Previewer> { - match self.columns.widgets.get(2)? { - FileBrowserWidgets::Previewer(previewer) => Ok(previewer), - _ => { return HError::wrong_widget("filelist", "previewer"); } - } - } + // pub fn preview_widget(&self) -> HResult<&Previewer> { + // match self.columns.widgets.get(2)? { + // FileBrowserWidgets::Previewer(previewer) => Ok(previewer), + // _ => { return HError::wrong_widget("filelist", "previewer"); } + // } + // } pub fn preview_widget_mut(&mut self) -> HResult<&mut Previewer> { match self.columns.widgets.get_mut(2)? { @@ -660,14 +671,14 @@ impl FileBrowser { let mcore = self.main_widget()?.get_core()?.clone(); let lcore = self.left_widget()?.get_core()?.clone();; - let middle = WillBeWidget::new(&self.core, Box::new(move |_| { + let middle = AsyncWidget::new(&self.core, Box::new(move |_| { let files = Files::new_from_path(&dir.clone())?; let listview = ListView::new(&mcore, files); Ok(listview) })); let middle = FileBrowserWidgets::FileList(middle); - let left = WillBeWidget::new(&self.core, Box::new(move |_| { + let left = AsyncWidget::new(&self.core, Box::new(move |_| { let files = Files::new_from_path(&left_dir.parent()?)?; let listview = ListView::new(&lcore, files); Ok(listview) @@ -751,9 +762,9 @@ impl FileBrowser { "--> ".to_string() + &target.short_string() } else { "".to_string() }; - let main_widget = self.main_widget()?.widget()?; - let selection = main_widget.lock()?.as_ref().unwrap().get_selection(); - let file_count = main_widget.lock()?.as_ref().unwrap().content.len(); + let main_widget = self.main_widget()?; + let selection = main_widget.get_selection(); + let file_count = main_widget.content.len(); let file_count = format!("{}", file_count); let digits = file_count.len(); let file_count = format!("{:digits$}/{:digits$}", diff --git a/src/main.rs b/src/main.rs index 0042ff0..8376fc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,6 +58,7 @@ use term::ScreenExt; use fail::{HResult, HError}; use file_browser::FileBrowser; use tabview::TabView; +use preview::Async; fn main() -> HResult<()> { diff --git a/src/preview.rs b/src/preview.rs index 4c51ceb..d02fc50 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -1,7 +1,5 @@ use std::sync::{Arc, Mutex}; -use failure::Backtrace; - use crate::files::{File, Files, Kind}; use crate::listview::ListView; use crate::textview::TextView; @@ -10,9 +8,14 @@ use crate::coordinates::Coordinates; use crate::fail::{HResult, HError, ErrorLog}; +pub type Stale = Arc>; + +pub type AsyncValueFn = Box HResult + Send>; +pub type AsyncValue = Arc>>>; +pub type AsyncReadyFn = Box HResult<()> + Send>; +pub type AsyncWidgetFn = Box HResult + Send>; + -type HClosure = Box>) -> Result + Send>; -type HCClosure = Box>, WidgetCore) -> Result + Send>; type WidgetO = Box; lazy_static! { @@ -33,49 +36,49 @@ pub fn is_stale(stale: &Arc>) -> HResult { Ok(stale) } -enum State { - Is, - Becoming, - Fail(HError) -} -struct WillBe { - pub state: Arc>, - pub thing: Arc>>, - on_ready: Arc>>) -> HResult<()> + Send>>>>, - stale: Arc> +pub struct Async { + pub value: HResult, + async_value: AsyncValue, + async_closure: Option>, + on_ready: Arc>>>, + stale: Stale } -impl WillBe where { - pub fn new_become(closure: HClosure) - -> WillBe { - let mut willbe = WillBe { state: Arc::new(Mutex::new(State::Becoming)), - thing: Arc::new(Mutex::new(None)), - on_ready: Arc::new(Mutex::new(None)), - stale: Arc::new(Mutex::new(false)) }; - willbe.run(closure); - willbe +impl Async { + pub fn new(closure: AsyncValueFn) + -> Async { + let async_value = Async { + value: HError::async_not_ready(), + async_value: Arc::new(Mutex::new(None)), + async_closure: Some(closure), + on_ready: Arc::new(Mutex::new(None)), + stale: Arc::new(Mutex::new(false)) }; + + async_value } - fn run(&mut self, closure: HClosure) { - let state = self.state.clone(); + fn run(&mut self) -> HResult<()> { + let closure = self.async_closure.take()?; + let async_value = self.async_value.clone(); let stale = self.stale.clone(); - let thing = self.thing.clone(); let on_ready_fn = self.on_ready.clone(); - std::thread::spawn(move|| { - let got_thing = closure(stale); - match got_thing { - Ok(got_thing) => { - *thing.lock().unwrap() = Some(got_thing); - *state.lock().unwrap() = State::Is; - match *on_ready_fn.lock().unwrap() { - Some(ref on_ready) => { on_ready(thing.clone()).ok(); }, + + std::thread::spawn(move|| -> HResult<()> { + let value = closure(stale); + match value { + Ok(mut value) => { + match *on_ready_fn.lock()? { + Some(ref on_ready) => { on_ready(&mut value).log(); }, None => {} } + async_value.lock()?.replace(Ok(value)); }, - Err(err) => *state.lock().unwrap() = State::Fail(err) + Err(err) => *async_value.lock()? = Some(Err(err)) } + Ok(()) }); + Ok(()) } pub fn set_stale(&mut self) -> HResult<()> { @@ -87,23 +90,46 @@ impl WillBe where { is_stale(&self.stale) } - pub fn take(&mut self) -> HResult { - self.check()?; - Ok(self.thing.lock()?.take()?) + pub fn take_async(&mut self) -> HResult<()> { + if self.value.is_ok() { return Ok(()) } + + let mut async_value = self.async_value.lock()?; + match async_value.as_ref() { + Some(Ok(_)) => { + let value = async_value.take()?; + self.value = value; + } + Some(Err(HError::AsyncAlreadyTakenError(..))) => HError::async_taken()?, + Some(Err(_)) => { + let value = async_value.take()?; + self.value = value; + } + None => HError::async_not_ready()?, + } + Ok(()) + } + + pub fn get(&self) -> HResult<&T> { + match self.value { + Ok(ref value) => Ok(value), + Err(ref err) => HError::async_error(err) + } } - pub fn check(&self) -> HResult<()> { - match *self.state.lock()? { - State::Is => Ok(()), - _ => Err(HError::WillBeNotReady(Backtrace::new())) + pub fn get_mut(&mut self) -> HResult<&mut T> { + self.take_async().ok(); + + match self.value { + Ok(ref mut value) => Ok(value), + Err(ref err) => HError::async_error(err) } } pub fn on_ready(&mut self, - fun: Box>>) -> HResult<()> + Send>) + fun: AsyncReadyFn) -> HResult<()> { - if self.check().is_ok() { - fun(self.thing.clone())?; + if self.value.is_ok() { + fun(self.value.as_mut().unwrap())?; } else { *self.on_ready.lock()? = Some(fun); } @@ -111,8 +137,8 @@ impl WillBe where { } } -impl PartialEq for WillBeWidget { - fn eq(&self, other: &WillBeWidget) -> bool { +impl PartialEq for AsyncWidget { + fn eq(&self, other: &AsyncWidget) -> bool { if self.get_coordinates().unwrap() == other.get_coordinates().unwrap() { true @@ -122,65 +148,66 @@ impl PartialEq for WillBeWidget { } } -pub struct WillBeWidget { - willbe: WillBe, + +pub struct AsyncWidget { + widget: Async, core: WidgetCore } -impl WillBeWidget { - pub fn new(core: &WidgetCore, closure: HClosure) -> WillBeWidget { +impl AsyncWidget { + pub fn new(core: &WidgetCore, closure: AsyncValueFn) -> AsyncWidget { let sender = core.get_sender(); - let mut willbe = WillBe::new_become(Box::new(move |stale| closure(stale))); - willbe.on_ready(Box::new(move |_| { + let mut widget = Async::new(Box::new(move |stale| closure(stale))); + widget.on_ready(Box::new(move |_| { sender.send(crate::widget::Events::WidgetReady)?; - Ok(()) })).ok(); + Ok(()) + })).log(); + widget.run().log(); - WillBeWidget { - willbe: willbe, + AsyncWidget { + widget: widget, core: core.clone() } } - pub fn change_to(&mut self, closure: HCClosure) -> HResult<()> { + pub fn change_to(&mut self, closure: AsyncWidgetFn) -> HResult<()> { self.set_stale().log(); let sender = self.get_core()?.get_sender(); let core = self.get_core()?.clone(); - let mut willbe = WillBe::new_become(Box::new(move |stale| { - let core = core.clone(); - closure(stale, core) + let mut widget = Async::new(Box::new(move |stale| { + closure(stale, core.clone()) })); - willbe.on_ready(Box::new(move |_| { + + widget.on_ready(Box::new(move |_| { sender.send(crate::widget::Events::WidgetReady)?; Ok(()) }))?; - self.willbe = willbe; + widget.run().log(); + + self.widget = widget; Ok(()) } pub fn set_stale(&mut self) -> HResult<()> { - self.willbe.set_stale() + self.widget.set_stale() } pub fn is_stale(&self) -> HResult { - self.willbe.is_stale() + self.widget.is_stale() } - pub fn widget(&self) -> HResult>>> { - self.willbe.check()?; - Ok(self.willbe.thing.clone()) + pub fn widget(&self) -> HResult<&W> { + self.widget.get() } - pub fn ready(&self) -> bool { - match self.willbe.check() { - Ok(_) => true, - _ => false - } + pub fn widget_mut(&mut self) -> HResult<&mut W> { + self.widget.get_mut() } - pub fn take(&mut self) -> HResult { - self.willbe.take() + pub fn ready(&self) -> bool { + self.widget().is_ok() } } @@ -195,7 +222,7 @@ impl WillBeWidget { // } //} -impl Widget for WillBeWidget { +impl Widget for AsyncWidget { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } @@ -205,22 +232,19 @@ impl Widget for WillBeWidget { fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { self.core.coordinates = coordinates.clone(); - if let Ok(widget) = self.widget() { - let mut widget = widget.lock()?; - let widget = widget.as_mut()?; + if let Ok(widget) = self.widget_mut() { widget.set_coordinates(&coordinates)?; } Ok(()) } fn refresh(&mut self) -> HResult<()> { - let coords = self.get_coordinates()?; - if let Ok(widget) = self.widget() { - let mut widget = widget.lock()?; - let widget = widget.as_mut()?; + self.widget.take_async().log(); - if widget.get_coordinates()? != coords { - widget.set_coordinates(self.get_coordinates()?)?; + let coords = self.get_coordinates()?.clone(); + if let Ok(widget) = self.widget_mut() { + if widget.get_coordinates()? != &coords { + widget.set_coordinates(&coords)?; widget.refresh()?; } else { widget.refresh()?; @@ -229,7 +253,7 @@ impl Widget for WillBeWidget { Ok(()) } fn get_drawlist(&self) -> HResult { - if self.willbe.check().is_err() { + if self.widget().is_err() { let clear = self.get_clearlist()?; let (xpos, ypos) = self.get_coordinates()?.u16position(); let pos = crate::term::goto_xy(xpos, ypos); @@ -240,17 +264,11 @@ impl Widget for WillBeWidget { return self.get_clearlist() } - let widget = self.widget()?; - let widget = widget.lock()?; - let widget = widget.as_ref()?; - widget.get_drawlist() + self.widget()?.get_drawlist() } fn on_key(&mut self, key: termion::event::Key) -> HResult<()> { - if self.willbe.check().is_err() { return Ok(()) } - let widget = self.widget()?; - let mut widget = widget.lock()?; - let widget = widget.as_mut()?; - widget.on_key(key) + if self.widget().is_err() { return Ok(()) } + self.widget_mut()?.on_key(key) } } @@ -267,7 +285,7 @@ impl PartialEq for Previewer { } pub struct Previewer { - widget: WillBeWidget>, + widget: AsyncWidget>, core: WidgetCore, file: Option, selection: Option, @@ -278,11 +296,10 @@ pub struct Previewer { impl Previewer { pub fn new(core: &WidgetCore) -> Previewer { let core_ = core.clone(); - let willbe = WillBeWidget::new(&core, Box::new(move |_| { - Ok(Box::new(crate::textview::TextView::new_blank(&core_)) - as Box) + let widget = AsyncWidget::new(&core, Box::new(move |_| { + Ok(Box::new(TextView::new_blank(&core_)) as Box) })); - Previewer { widget: willbe, + Previewer { widget: widget, core: core.clone(), file: None, selection: None, @@ -290,10 +307,11 @@ impl Previewer { } fn become_preview(&mut self, - widget: HResult>) { - let coordinates = self.get_coordinates().unwrap().clone(); - self.widget = widget.unwrap(); - self.widget.set_coordinates(&coordinates).ok(); + widget: HResult>) -> HResult<()> { + let coordinates = self.get_coordinates()?.clone(); + self.widget = widget?; + self.widget.set_coordinates(&coordinates)?; + Ok(()) } pub fn set_stale(&mut self) -> HResult<()> { @@ -315,7 +333,8 @@ impl Previewer { self.widget.set_stale().ok(); - self.become_preview(Ok(WillBeWidget::new(&self.core, Box::new(move |stale| { + self.become_preview(Ok(AsyncWidget::new(&self.core, + Box::new(move |stale| { kill_proc().unwrap(); let file = file.clone(); @@ -344,15 +363,14 @@ impl Previewer { blank.animate_slide_up().log(); return Ok(blank) } - })))); - Ok(()) + })))) } pub fn reload(&mut self) { if let Some(file) = self.file.clone() { self.file = None; let cache = self.cached_files.take(); - self.set_file(&file, self.selection.clone(), cache); + self.set_file(&file, self.selection.clone(), cache).log(); } } @@ -403,7 +421,9 @@ impl Previewer { Ok(Box::new(textview)) } - fn preview_external(file: &File, core: &WidgetCore, stale: Arc>) + fn preview_external(file: &File, + core: &WidgetCore, + stale: Arc>) -> Result, HError> { let process = std::process::Command::new("scope.sh") diff --git a/src/proclist.rs b/src/proclist.rs index a4efabf..98645c8 100644 --- a/src/proclist.rs +++ b/src/proclist.rs @@ -15,7 +15,6 @@ use crate::widget::{Widget, Events, WidgetCore}; use crate::coordinates::Coordinates; use crate::dirty::Dirtyable; use crate::hbox::HBox; -use crate::preview::WillBeWidget; use crate::fail::{HResult, HError, ErrorLog}; use crate::term; use crate::files::{File, OsStrTools}; @@ -299,7 +298,7 @@ impl ListView> { #[derive(PartialEq)] enum ProcViewWidgets { List(ListView>), - TextView(WillBeWidget), + TextView(TextView), } impl Widget for ProcViewWidgets { @@ -348,7 +347,7 @@ impl HBox { _ => unreachable!() } } - fn get_textview(&mut self) -> &mut WillBeWidget { + fn get_textview(&mut self) -> &mut TextView { match &mut self.widgets[1] { ProcViewWidgets::TextView(textview) => textview, _ => unreachable!() @@ -360,8 +359,7 @@ impl ProcView { pub fn new(core: &WidgetCore) -> ProcView { let tcore = core.clone(); let listview = ListView::new(&core, vec![]); - let textview = Box::new(move |_| Ok(TextView::new_blank(&tcore))); - let textview = WillBeWidget::new(&core, textview); + let textview = TextView::new_blank(&tcore); let mut hbox = HBox::new(&core); hbox.push_widget(ProcViewWidgets::List(listview)); hbox.push_widget(ProcViewWidgets::TextView(textview)); @@ -382,7 +380,7 @@ impl ProcView { self.hbox.get_listview_mut() } - fn get_textview(&mut self) -> &mut WillBeWidget { + fn get_textview(&mut self) -> &mut TextView { self.hbox.get_textview() } @@ -399,13 +397,8 @@ impl ProcView { pub fn remove_proc(&mut self) -> HResult<()> { if self.get_listview_mut().content.len() == 0 { return Ok(()) } self.get_listview_mut().remove_proc()?; - self.get_textview().change_to(Box::new(move |_, core| { - let mut textview = TextView::new_blank(&core); - textview.refresh().log(); - textview.animate_slide_up().log(); - Ok(textview) - })).log(); - Ok(()) + self.get_textview().clear(); + self.get_textview().set_text("") } fn show_output(&mut self) -> HResult<()> { @@ -414,48 +407,45 @@ impl ProcView { } let output = self.get_listview_mut().selected_proc()?.output.lock()?.clone(); - self.get_textview().change_to(Box::new(move |_, core| { - let mut textview = TextView::new_blank(&core); - textview.set_text(&output).log(); - textview.animate_slide_up().log(); - Ok(textview) - })).log(); + self.get_textview().set_text(&output).log(); + self.get_textview().animate_slide_up().log(); + self.viewing = Some(self.get_listview_mut().get_selection()); Ok(()) } pub fn toggle_follow(&mut self) -> HResult<()> { - self.get_textview().widget()?.lock()?.as_mut()?.toggle_follow(); + self.get_textview().toggle_follow(); Ok(()) } pub fn scroll_up(&mut self) -> HResult<()> { - self.get_textview().widget()?.lock()?.as_mut()?.scroll_up(); + self.get_textview().scroll_up(); Ok(()) } pub fn scroll_down(&mut self) -> HResult<()> { - self.get_textview().widget()?.lock()?.as_mut()?.scroll_down(); + self.get_textview().scroll_down(); Ok(()) } pub fn page_up(&mut self) -> HResult<()> { - self.get_textview().widget()?.lock()?.as_mut()?.page_up(); + self.get_textview().page_up(); Ok(()) } pub fn page_down(&mut self) -> HResult<()> { - self.get_textview().widget()?.lock()?.as_mut()?.page_down(); + self.get_textview().page_down(); Ok(()) } pub fn scroll_top(&mut self) -> HResult<()> { - self.get_textview().widget()?.lock()?.as_mut()?.scroll_top(); + self.get_textview().scroll_top(); Ok(()) } pub fn scroll_bottom(&mut self) -> HResult<()> { - self.get_textview().widget()?.lock()?.as_mut()?.scroll_bottom(); + self.get_textview().scroll_bottom(); Ok(()) } } -- cgit v1.2.3 From 3888f49aebb911ac6b2d83f9f78f2feb092069a8 Mon Sep 17 00:00:00 2001 From: rabite Date: Mon, 25 Mar 2019 02:53:59 +0100 Subject: load metadata and file-count asynchronously --- src/dirty.rs | 63 +++++++-- src/fail.rs | 86 +++++++------ src/file_browser.rs | 13 +- src/files.rs | 359 +++++++++++++++++++++++++++++++++++++++------------- src/listview.rs | 36 +++--- src/preview.rs | 104 +++++++++++---- src/widget.rs | 12 +- 7 files changed, 480 insertions(+), 193 deletions(-) diff --git a/src/dirty.rs b/src/dirty.rs index fe6e4d1..14c6245 100644 --- a/src/dirty.rs +++ b/src/dirty.rs @@ -1,22 +1,38 @@ +use std::sync::{Arc, RwLock}; +use std::hash::{Hash, Hasher}; + #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct DirtyBit(bool); -pub trait Dirtyable { - fn get_bit(&self) -> &DirtyBit; - fn get_bit_mut(&mut self) -> &mut DirtyBit; +#[derive(Debug)] +pub struct AsyncDirtyBit(pub Arc>); - fn is_dirty(&self) -> bool { - self.get_bit().0 +impl PartialEq for AsyncDirtyBit { + fn eq(&self, other: &AsyncDirtyBit) -> bool { + *self.0.read().unwrap() == *other.0.read().unwrap() } +} - fn set_dirty(&mut self) { - self.get_bit_mut().0 = true; +impl Eq for AsyncDirtyBit {} + +impl Hash for AsyncDirtyBit { + fn hash(&self, state: &mut H) { + self.0.read().unwrap().hash(state) } - fn set_clean(&mut self) { - self.get_bit_mut().0 = false; +} + +impl Clone for AsyncDirtyBit { + fn clone(&self) -> Self { + AsyncDirtyBit(self.0.clone()) } } +pub trait Dirtyable { + fn is_dirty(&self) -> bool; + fn set_dirty(&mut self); + fn set_clean(&mut self); +} + impl DirtyBit { pub fn new() -> DirtyBit { @@ -24,12 +40,33 @@ impl DirtyBit { } } +impl AsyncDirtyBit { + pub fn new() -> AsyncDirtyBit { + AsyncDirtyBit(Arc::new(RwLock::new(DirtyBit::new()))) + } +} + impl Dirtyable for DirtyBit { - fn get_bit(&self) -> &DirtyBit { - self + fn is_dirty(&self) -> bool { + self.0 + } + fn set_dirty(&mut self) { + self.0 = true; } - fn get_bit_mut(&mut self) -> &mut DirtyBit { - self + fn set_clean(&mut self) { + self.0 = false; + } +} + +impl Dirtyable for AsyncDirtyBit { + fn is_dirty(&self) -> bool { + self.0.read().unwrap().is_dirty() + } + fn set_dirty(&mut self) { + self.0.write().unwrap().set_dirty(); + } + fn set_clean(&mut self) { + self.0.write().unwrap().set_clean(); } } diff --git a/src/fail.rs b/src/fail.rs index 039002a..55cd5aa 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -11,42 +11,44 @@ use crate::foldview::LogEntry; pub type HResult = Result; -#[derive(Fail, Debug)] +#[derive(Fail, Debug, Clone)] pub enum HError { - #[fail(display = "IO error: {} ", error)] - IoError{#[cause] error: std::io::Error, backtrace: Backtrace}, + #[fail(display = "IO error: {} ", _0)] + IoError(String), #[fail(display = "Mutex failed")] - MutexError(Backtrace), + MutexError, #[fail(display = "Can't lock!")] - TryLockError(Backtrace), + TryLockError, #[fail(display = "Channel failed: {}", error)] ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError}, #[fail(display = "Channel failed: {}", error)] ChannelRecvError{#[cause] error: std::sync::mpsc::RecvError}, #[fail(display = "Channel failed")] - ChannelSendError(Backtrace), + ChannelSendError, #[fail(display = "Previewer failed on file: {}", file)] - PreviewFailed{file: String, backtrace: Backtrace}, + PreviewFailed{file: String}, #[fail(display = "StalePreviewer for file: {}", file)] StalePreviewError{file: String}, #[fail(display = "Accessed stale value")] - StaleError(Backtrace), - #[fail(display = "Failed: {}", error)] - Error{#[cause] error: failure::Error , backtrace: Backtrace}, + StaleError, + #[fail(display = "Failed: {}", _0)] + Error(String), #[fail(display = "Was None!")] - NoneError(Backtrace), + NoneError, #[fail(display = "Not ready yet!")] - WillBeNotReady(Backtrace), + WillBeNotReady, #[fail(display = "Not ready yet!")] - AsyncNotReadyError(Backtrace), + AsyncNotReadyError, + #[fail(display = "Async is stale!")] + AsyncStaleError, #[fail(display = "Value has already been taken!")] - AsyncAlreadyTakenError(Backtrace), + AsyncAlreadyTakenError, #[fail(display = "Async Error: {}", _0)] AsyncError(String), #[fail(display = "No widget found")] - NoWidgetError(Backtrace), + NoWidgetError, #[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)] - WrongDirectoryError{ path: PathBuf, dir: PathBuf , backtrace: Backtrace}, + WrongDirectoryError{ path: PathBuf, dir: PathBuf}, #[fail(display = "Widget finnished")] PopupFinnished, #[fail(display = "No completions found")] @@ -54,7 +56,7 @@ pub enum HError { #[fail(display = "No more history")] NoHistoryError, #[fail(display = "No core for widget")] - NoWidgetCoreError(Backtrace), + NoWidgetCoreError, #[fail(display = "No header for widget")] NoHeaderError, #[fail(display = "You wanted this!")] @@ -62,11 +64,11 @@ pub enum HError { #[fail(display = "HBox ratio mismatch: {} widgets, ratio is {:?}", wnum, ratio)] HBoxWrongRatioError{ wnum: usize, ratio: Vec }, #[fail(display = "Got wrong widget: {}! Wanted: {}", got, wanted)] - WrongWidgetError{got: String, wanted: String, backtrace: Backtrace}, + WrongWidgetError{got: String, wanted: String}, #[fail(display = "Strip Prefix Error: {}", error)] - StripPrefixError{#[cause] error: std::path::StripPrefixError, backtrace: Backtrace}, - #[fail(display = "INofify failed: {}", error)] - INotifyError{#[cause] error: notify::Error, backtrace: Backtrace}, + StripPrefixError{#[cause] error: std::path::StripPrefixError}, + #[fail(display = "INofify failed: {}", _0)] + INotifyError(String), #[fail(display = "Tags not loaded yet")] TagsNotLoadedYetError, #[fail(display = "Input cancelled!")] @@ -79,6 +81,8 @@ pub enum HError { TerminalResizedError, #[fail(display = "{}", _0)] Log(String), + #[fail(display = "Metadata already processed")] + MetadataProcessedError } impl HError { @@ -92,12 +96,12 @@ impl HError { Err(HError::HBoxWrongRatioError{ wnum: wnum, ratio: ratio }) } pub fn no_widget() -> HResult { - Err(HError::NoWidgetError(Backtrace::new())) + Err(HError::NoWidgetError) } pub fn wrong_widget(got: &str, wanted: &str) -> HResult { Err(HError::WrongWidgetError{ got: got.to_string(), - wanted: wanted.to_string(), - backtrace: Backtrace::new()}) + wanted: wanted.to_string() }) + } pub fn popup_finnished() -> HResult { Err(HError::PopupFinnished) @@ -116,13 +120,13 @@ impl HError { } pub fn wrong_directory(path: PathBuf, dir: PathBuf) -> HResult { Err(HError::WrongDirectoryError{ path: path, - dir: dir, - backtrace: Backtrace::new() }) + dir: dir }) + } pub fn preview_failed(file: &crate::files::File) -> HResult { let name = file.name.clone(); - Err(HError::PreviewFailed{ file: name, - backtrace: Backtrace::new() }) + Err(HError::PreviewFailed{ file: name }) + } pub fn terminal_resized() -> HResult { @@ -130,20 +134,24 @@ impl HError { } pub fn stale() -> HResult { - Err(HError::StaleError(Backtrace::new())) + Err(HError::StaleError) } pub fn async_not_ready() -> HResult { - Err(HError::AsyncNotReadyError(Backtrace::new())) + Err(HError::AsyncNotReadyError) } pub fn async_taken() -> HResult { - Err(HError::AsyncAlreadyTakenError(Backtrace::new())) + Err(HError::AsyncAlreadyTakenError) } pub fn async_error(error: &HError) -> HResult { Err(HError::AsyncError(format!("{}", error))) } + + pub fn metadata_processed() -> HResult { + Err(HError::MetadataProcessedError) + } } @@ -195,7 +203,7 @@ impl ErrorLog for HResult { impl From for HError { fn from(error: std::io::Error) -> Self { // dbg!(&error); - let err = HError::IoError { error: error, backtrace: Backtrace::new() }; + let err = HError::IoError(format!("{}", error)); put_log(&err).ok(); err } @@ -204,7 +212,7 @@ impl From for HError { impl From for HError { fn from(error: failure::Error) -> Self { // dbg!(&error); - let err = HError::Error { error: error, backtrace: Backtrace::new() }; + let err = HError::Error(format!("{}", error)); put_log(&err).ok(); err } @@ -231,7 +239,7 @@ impl From for HError { impl From> for HError { fn from(error: std::sync::mpsc::SendError) -> Self { dbg!(&error); - let err = HError::ChannelSendError(Backtrace::new()); + let err = HError::ChannelSendError; put_log(&err).ok(); err } @@ -240,7 +248,7 @@ impl From> for HError { impl From> for HError { fn from(_: std::sync::PoisonError) -> Self { // dbg!("Poisoned Mutex"); - let err = HError::MutexError(Backtrace::new()); + let err = HError::MutexError; put_log(&err).ok(); err } @@ -249,7 +257,7 @@ impl From> for HError { impl From> for HError { fn from(error: std::sync::TryLockError) -> Self { // dbg!(&error); - let err = HError::TryLockError(Backtrace::new()); + let err = HError::TryLockError; put_log(&err).ok(); err } @@ -258,7 +266,7 @@ impl From> for HError { impl From for HError { fn from(error: std::option::NoneError) -> Self { //dbg!(&error); - let err = HError::NoneError(Backtrace::new()); + let err = HError::NoneError; //put_log(&err).ok(); err } @@ -267,7 +275,7 @@ impl From for HError { impl From for HError { fn from(error: std::path::StripPrefixError) -> Self { // dbg!(&error); - let err = HError::StripPrefixError{error: error, backtrace: Backtrace::new() }; + let err = HError::StripPrefixError{error: error }; put_log(&err).ok(); err } @@ -276,7 +284,7 @@ impl From for HError { impl From for HError { fn from(error: notify::Error) -> Self { // dbg!(&error); - let err = HError::INotifyError{error: error, backtrace: Backtrace::new() }; + let err = HError::INotifyError(format!("{}", error)); put_log(&err).ok(); err } diff --git a/src/file_browser.rs b/src/file_browser.rs index 37d44bb..3a55f14 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -194,6 +194,7 @@ impl FileBrowser { let mut list = ListView::new(&core_m, Files::new_from_path(&main_path)?); list.animate_slide_up().log(); + list.content.meta_all(); Ok(list) })); @@ -216,7 +217,7 @@ impl FileBrowser { columns.refresh().log(); - let cwd = File::new_from_path(&cwd).unwrap(); + let cwd = File::new_from_path(&cwd, None).unwrap(); let dir_events = Arc::new(Mutex::new(vec![])); let (tx_watch, rx_watch) = channel(); @@ -323,6 +324,7 @@ impl FileBrowser { })?; let mut list = ListView::new(&core, files); + list.content.meta_all(); if let Some(file) = &selected_file { list.select_file(file); @@ -395,7 +397,7 @@ impl FileBrowser { pub fn goto_bookmark(&mut self) -> HResult<()> { let path = self.get_boomark()?; - let path = File::new_from_path(&PathBuf::from(path))?; + let path = File::new_from_path(&PathBuf::from(path), None)?; self.main_widget_goto(&path)?; Ok(()) } @@ -664,7 +666,7 @@ impl FileBrowser { match dir { Ok(dir) => { self.columns.widgets.clear(); - let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?; + let cwd = File::new_from_path(&std::path::PathBuf::from(&dir), None)?; self.cwd = cwd; let dir = std::path::PathBuf::from(&dir); let left_dir = std::path::PathBuf::from(&dir); @@ -752,13 +754,14 @@ impl FileBrowser { pub fn get_footer(&self) -> HResult { let xsize = self.get_coordinates()?.xsize(); let ypos = self.get_coordinates()?.position().y(); - let file = self.selected_file()?; + let pos = self.main_widget()?.get_selection(); + let file = self.main_widget()?.content.files.get(pos)?; 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().unwrap_or("NOMTIME".into()); - let target = if let Some(target) = file.target { + let target = if let Some(target) = &file.target { "--> ".to_string() + &target.short_string() } else { "".to_string() }; diff --git a/src/files.rs b/src/files.rs index 1a038ac..40d5ac0 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,8 +1,10 @@ use std::cmp::{Ord, Ordering}; use std::ops::Index; +use std::fs::Metadata; use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; +use std::sync::mpsc::Sender; use std::hash::{Hash, Hasher}; use std::os::unix::ffi::{OsStringExt, OsStrExt}; use std::ffi::{OsStr, OsString}; @@ -16,11 +18,13 @@ use users::{get_current_username, use chrono::TimeZone; use failure::Error; use notify::DebouncedEvent; +use rayon::prelude::*; +use rayon::{ThreadPool, ThreadPoolBuilder}; -use crate::fail::{HResult, HError}; -use crate::dirty::{DirtyBit, Dirtyable}; - - +use crate::fail::{HResult, HError, ErrorLog}; +use crate::dirty::{AsyncDirtyBit, DirtyBit, Dirtyable}; +use crate::preview::Async; +use crate::widget::Events; lazy_static! { @@ -28,6 +32,23 @@ lazy_static! { static ref TAGS: Mutex<(bool, Vec)> = Mutex::new((false, vec![])); } +fn make_pool(sender: Option>) -> ThreadPool { + let sender = Arc::new(Mutex::new(sender)); + ThreadPoolBuilder::new() + .num_threads(8) + .exit_handler(move |thread_num| { + if thread_num == 0 { + if let Ok(lock) = sender.lock() { + if let Some(sender) = lock.as_ref() { + sender.send(Events::WidgetReady).ok(); + } + } + } + }) + .build() + .expect("Failed to create thread pool") +} + pub fn load_tags() -> HResult<()> { std::thread::spawn(|| -> HResult<()> { let tag_path = crate::paths::tagfile_path()?; @@ -53,16 +74,19 @@ pub fn tags_loaded() -> HResult<()> { else { HError::tags_not_loaded() } } + #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct Files { pub directory: File, pub files: Vec, + pub meta_upto: Option, pub sort: SortBy, pub dirs_first: bool, pub reverse: bool, pub show_hidden: bool, pub filter: Option, - pub dirty: DirtyBit + pub dirty: DirtyBit, + pub dirty_meta: AsyncDirtyBit, } impl Index for Files { @@ -74,12 +98,16 @@ impl Index for Files { impl Dirtyable for Files { - fn get_bit(&self) -> &DirtyBit { - &self.dirty + fn is_dirty(&self) -> bool { + self.dirty.is_dirty() + } + + fn set_dirty(&mut self) { + self.dirty.set_dirty(); } - fn get_bit_mut(&mut self) -> &mut DirtyBit { - &mut self.dirty + fn set_clean(&mut self) { + self.dirty.set_clean(); } } @@ -87,6 +115,8 @@ impl Dirtyable for Files { impl Files { pub fn new_from_path(path: &Path) -> Result { let direntries: Result, _> = std::fs::read_dir(&path)?.collect(); + let dirty = DirtyBit::new(); + let dirty_meta = AsyncDirtyBit::new(); let files: Vec<_> = direntries? .iter() @@ -94,23 +124,29 @@ impl Files { let name = file.file_name(); let name = name.to_string_lossy(); let path = file.path(); - File::new(&name, path) + File::new(&name, + path, + Some(dirty_meta.clone())) }) .collect(); let mut files = Files { - directory: File::new_from_path(&path)?, + directory: File::new_from_path(&path, None)?, files: files, + meta_upto: None, sort: SortBy::Name, dirs_first: true, reverse: false, show_hidden: true, filter: None, - dirty: DirtyBit::new() + dirty: dirty, + dirty_meta: dirty_meta, }; files.sort(); + + if files.files.len() == 0 { files.files = vec![File::new_placeholder(&path)?]; } @@ -118,8 +154,12 @@ impl Files { Ok(files) } - pub fn new_from_path_cancellable(path: &Path, stale: Arc>) -> Result { + pub fn new_from_path_cancellable(path: &Path, + stale: Arc>) + -> Result { let direntries: Result, _> = std::fs::read_dir(&path)?.collect(); + let dirty = DirtyBit::new(); + let dirty_meta = AsyncDirtyBit::new(); let files: Vec<_> = direntries? .iter() @@ -130,7 +170,9 @@ impl Files { let name = file.file_name(); let name = name.to_string_lossy(); let path = file.path(); - Some(File::new(&name, path)) + Some(File::new(&name, + path, + Some(dirty_meta.clone()))) } }) .fuse() @@ -144,14 +186,16 @@ impl Files { } let mut files = Files { - directory: File::new_from_path(&path)?, + directory: File::new_from_path(&path, None)?, files: files, + meta_upto: None, sort: SortBy::Name, dirs_first: true, reverse: false, show_hidden: true, filter: None, - dirty: DirtyBit::new() + dirty: dirty, + dirty_meta: dirty_meta, }; files.sort(); @@ -163,13 +207,33 @@ impl Files { Ok(files) } + pub fn get_files(&self) -> Vec<&File> { + self.files + .iter() + .filter(|f| !(self.filter.is_some() && + !f.name.contains(self.filter.as_ref().unwrap()))) + .filter(|f| !(!self.show_hidden && f.name.starts_with("."))) + .collect() + } + + pub fn get_files_mut(&mut self) -> Vec<&mut File> { + let filter = self.filter.clone(); + let show_hidden = self.show_hidden; + self.files + .iter_mut() + .filter(|f| !(filter.is_some() && + !f.name.contains(filter.as_ref().unwrap()))) + .filter(|f| !(!show_hidden && f.name.starts_with("."))) + .collect() + } + pub fn sort(&mut self) { match self.sort { SortBy::Name => self .files .sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)), SortBy::Size => { - self.meta_all(); + self.meta_all_sync(); self.files.sort_by(|a, b| { if a.meta().unwrap().size() == b.meta().unwrap().size() { return alphanumeric_sort::compare_str(&b.name, &a.name); @@ -178,7 +242,7 @@ impl Files { }); } SortBy::MTime => { - self.meta_all(); + self.meta_all_sync(); self.files.sort_by(|a, b| { if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() { return alphanumeric_sort::compare_str(&a.name, &b.name); @@ -227,25 +291,14 @@ impl Files { self.show_hidden = !self.show_hidden } - pub fn reload_files(&mut self) { - let dir = self.directory.clone(); - let files = Files::new_from_path(&dir.path()).unwrap(); - let files = files - .files - .into_iter() - .skip_while(|f| f.name.starts_with(".") && !self.show_hidden ) - .collect(); - - self.files = files; - self.set_dirty(); - } - pub fn handle_event(&mut self, event: &DebouncedEvent) -> HResult<()> { match event { DebouncedEvent::Create(path) => { self.path_in_here(&path)?; - let file = File::new_from_path(&path)?; + let file = File::new_from_path(&path, + Some(self.dirty_meta.clone()))?; self.files.push(file); + self.sort(); }, DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => { self.path_in_here(&path)?; @@ -262,6 +315,7 @@ impl Files { let mut file = self.find_file_with_path(&old_path)?; file.name = new_path.file_name()?.to_string_lossy().to_string(); file.path = new_path.into(); + file.reload_meta()?; }, DebouncedEvent::Error(err, path) => { dbg!(err); @@ -287,17 +341,58 @@ impl Files { self.files.iter_mut().find(|file| file.path == path) } + pub fn meta_all_sync(&mut self) -> HResult<()> { + for file in self.files.iter_mut() { + if !file.meta_processed { + let path = file.path.clone(); + file.meta = Async::new(Box::new(move|_| { + let meta = std::fs::metadata(&path)?; + Ok(meta) + })); + file.meta.wait()?; + } + } + self.dirty_meta.set_dirty(); + Ok(()) + } + pub fn meta_all(&mut self) { - let len = self.files.len(); - self.meta_upto(len); + let len = self.len(); + self.meta_upto(len, None); } - pub fn meta_upto(&mut self, to: usize) { - for file in self.files.iter_mut().take(to) { - file.get_meta().ok(); + pub fn meta_upto(&mut self, to: usize, sender: Option>) { + let meta_files = if self.meta_upto > Some(to) { + self.meta_upto.unwrap() + } else { + if to > self.len() { + self.len() + } else { + to + } + }; + + if self.meta_upto >= Some(meta_files) && !self.dirty_meta.is_dirty() { return } + + self.set_dirty(); + self.dirty_meta.set_clean(); + + let meta_pool = make_pool(sender.clone()); + let dirsize_pool = make_pool(sender); + + for file in self.files.iter_mut().take(meta_files) { + if !file.meta_processed { + file.take_meta(&meta_pool).ok(); + } + if file.is_dir() { + file.take_dirsize(&dirsize_pool).ok(); + } } + + self.meta_upto = Some(meta_files); } + pub fn set_filter(&mut self, filter: Option) { self.filter = filter; self.set_dirty(); @@ -308,15 +403,7 @@ impl Files { } pub fn len(&self) -> usize { - match &self.filter { - None => self.files.len(), - Some(filter) => { - self.files - .iter() - .filter(|f| f.name.contains(filter)) - .count() - } - } + self.get_files().len() } pub fn get_selected(&self) -> Vec<&File> { @@ -370,14 +457,51 @@ impl Hash for File { impl Eq for File {} -#[derive(Debug, Clone)] +impl Clone for File { + fn clone(&self) -> Self { + let meta = self.meta.value.clone(); + let meta = match meta { + Ok(meta) => Async::new_with_value(meta.clone()), + Err(_) => File::make_async_meta(&self.path, self.dirty_meta.clone()) + }; + + let dirsize = if let Some(ref dirsize) = self.dirsize { + let dirsize = dirsize.value.clone(); + let dirsize = match dirsize { + Ok(dirsize) => Async::new_with_value(dirsize), + Err(_) => File::make_async_dirsize(&self.path, + self.dirty_meta.clone()) + }; + Some(dirsize) + } else { None }; + + File { + name: self.name.clone(), + path: self.path.clone(), + kind: self.kind.clone(), + dirsize: dirsize, + target: self.target.clone(), + color: self.color.clone(), + meta: meta, + dirty_meta: self.dirty_meta.clone(), + meta_processed: self.meta_processed.clone(), + selected: self.selected.clone(), + tag: self.tag.clone() + } + } +} + +#[derive(Debug)] pub struct File { pub name: String, pub path: PathBuf, pub kind: Kind, + pub dirsize: Option>, pub target: Option, pub color: Option, - pub meta: Option, + pub meta: Async, + pub dirty_meta: Option, + pub meta_processed: bool, pub selected: bool, pub tag: Option // flags: Option, @@ -387,63 +511,138 @@ impl File { pub fn new( name: &str, path: PathBuf, - ) -> File { + dirty_meta: Option) -> File { let tag = check_tag(&path).ok(); + let meta = File::make_async_meta(&path, dirty_meta.clone()); + let dirsize = if path.is_dir() { + Some(File::make_async_dirsize(&path, dirty_meta.clone())) + } else { None }; File { name: name.to_string(), kind: if path.is_dir() { Kind::Directory } else { Kind::File }, path: path, + dirsize: dirsize, target: None, - meta: None, + meta: meta, + meta_processed: false, + dirty_meta: dirty_meta, color: None, selected: false, tag: tag, } } - pub fn new_from_path(path: &Path) -> HResult { + pub fn new_from_path(path: &Path, + dirty_meta: Option) -> HResult { let pathbuf = path.to_path_buf(); let name = path .file_name() .map(|name| name.to_string_lossy().to_string()) .unwrap_or("/".to_string()); - Ok(File::new(&name, pathbuf)) + Ok(File::new(&name, pathbuf, dirty_meta)) } pub fn new_placeholder(path: &Path) -> Result { - let mut file = File::new_from_path(path)?; + let mut file = File::new_from_path(path, None)?; file.name = "".to_string(); file.kind = Kind::Placeholder; Ok(file) } - pub fn meta(&self) -> HResult { - match &self.meta { - Some(meta) => Ok(meta.clone()), - None => { Ok(std::fs::symlink_metadata(&self.path)?) } + pub fn make_async_meta(path: &PathBuf, + dirty_meta: Option) -> Async { + let path = path.clone(); + + let mut meta = Async::new(Box::new(move |stale| { + if *stale.lock()? { HError::stale()? } + Ok(std::fs::symlink_metadata(&path).unwrap()) + })); + if let Some(dirty_meta) = dirty_meta { + meta.on_ready(Box::new(move |_| { + let mut dirty_meta = dirty_meta.clone(); + dirty_meta.set_dirty(); + + Ok(()) + })); } + meta } - pub fn get_meta(&mut self) -> HResult<()> { - if let Some(_) = self.meta { return Ok(()) } + pub fn make_async_dirsize(path: &PathBuf, + dirty_meta: Option) -> Async { + let path = path.clone(); - let meta = std::fs::symlink_metadata(&self.path)?; - let color = self.get_color(&meta); - let target = if meta.file_type().is_symlink() { - self.path.read_link().ok() - } else { None }; + let mut dirsize = Async::new(Box::new(move |stale| { + if *stale.lock()? { HError::stale()? } + Ok(std::fs::read_dir(&path)?.count()) + })); + if let Some(dirty_meta) = dirty_meta { + dirsize.on_ready(Box::new(move |_| { + let mut dirty_meta = dirty_meta.clone(); + dirty_meta.set_dirty(); + + Ok(()) + })); + } + dirsize + } + + pub fn meta(&self) -> HResult<&Metadata> { + self.meta.get() + } + + fn take_dirsize(&mut self, pool: &ThreadPool) -> HResult<()> { + let dirsize = self.dirsize.as_mut()?; + if let Ok(_) = dirsize.value { return Ok(()) } + + match dirsize.take_async() { + Ok(_) => {}, + Err(HError::AsyncNotReadyError) => { dirsize.run_pooled(&*pool).ok(); }, + Err(HError::AsyncAlreadyTakenError) => {}, + Err(HError::NoneError) => {}, + err @ Err(_) => { err?; } + } + Ok(()) + } + + pub fn take_meta(&mut self, pool: &ThreadPool) -> HResult<()> { + if self.meta_processed { return Ok(()) } + + match self.meta.take_async() { + Ok(_) => {}, + Err(HError::AsyncNotReadyError) => { self.meta.run_pooled(&*pool).ok(); }, + Err(HError::AsyncAlreadyTakenError) => {}, + Err(HError::NoneError) => {}, + err @ Err(_) => { err?; } + } + + if let Ok(meta) = self.meta.get() { + let color = self.get_color(&meta); + let target = if meta.file_type().is_symlink() { + self.path.read_link().ok() + } else { None }; + + self.color = color; + self.target = target; + self.meta_processed = true; + +