diff --git a/src/file_browser.rs b/src/file_browser.rs index 3a55f14..3cf05e7 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -13,6 +13,7 @@ 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, AsyncWidget}; use crate::fail::{HResult, HError, ErrorLog}; @@ -248,6 +249,18 @@ impl FileBrowser { let file = self.selected_file()?; if file.is_dir() { + match file.is_readable() { + Ok(true) => {}, + Ok(false) => { + let status = + format!("{}Stop right there, cowboy! Check your permisions!", + term::color_red()); + self.show_status(&status).log(); + return Ok(()); + } + err @ Err(_) => err.log() + } + self.main_widget_goto(&file).log(); } else { self.core.get_sender().send(Events::InputEnabled(false))?; @@ -293,22 +306,13 @@ impl FileBrowser { } pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> { - match dir.is_readable() { - Ok(true) => {}, - Ok(false) => { - let status = - format!("{}Stop right there, cowboy! Check your permisions!", - term::color_red()); - self.show_status(&status).log(); - return Ok(()); - } - err @ Err(_) => err.log() - } + let dir = dir.clone(); let selected_file = self.get_selection(&dir).ok().cloned(); self.get_files().and_then(|files| self.cache_files(files)).log(); + self.get_left_files().and_then(|files| self.cache_files(files)).log(); let cached_files = self.get_cached_files(&dir).ok(); self.prev_cwd = Some(self.cwd.clone()); @@ -320,11 +324,12 @@ impl FileBrowser { let cached_files = cached_files.clone(); let files = cached_files.or_else(|| { - Files::new_from_path_cancellable(&path, stale).ok() + Files::new_from_path_cancellable(&path, stale.clone()).ok() })?; let mut list = ListView::new(&core, files); - list.content.meta_all(); + + list.content.meta_set_fresh().log(); if let Some(file) = &selected_file { list.select_file(file); @@ -335,6 +340,8 @@ impl FileBrowser { if let Ok(grand_parent) = self.cwd()?.parent_as_file() { self.left_widget_goto(&grand_parent).log(); } else { + self.left_async_widget_mut()?.clear().log(); + self.screen()?.flush(); self.left_async_widget_mut()?.set_stale().log(); } @@ -342,7 +349,6 @@ impl FileBrowser { } pub fn left_widget_goto(&mut self, dir: &File) -> HResult<()> { - self.get_left_files().and_then(|files| self.cache_files(files)).log(); let cached_files = self.get_cached_files(&dir).ok(); let dir = dir.clone(); diff --git a/src/files.rs b/src/files.rs index 424b648..90646ba 100644 --- a/src/files.rs +++ b/src/files.rs @@ -3,7 +3,7 @@ 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::{Arc, Mutex, RwLock}; use std::sync::mpsc::Sender; use std::hash::{Hash, Hasher}; use std::os::unix::ffi::{OsStringExt, OsStrExt}; @@ -23,13 +23,13 @@ use rayon::{ThreadPool, ThreadPoolBuilder}; use crate::fail::{HResult, HError, ErrorLog}; use crate::dirty::{AsyncDirtyBit, DirtyBit, Dirtyable}; -use crate::preview::Async; +use crate::preview::{Async, Stale}; use crate::widget::Events; lazy_static! { static ref COLORS: LsColors = LsColors::from_env().unwrap(); - static ref TAGS: Mutex<(bool, Vec)> = Mutex::new((false, vec![])); + static ref TAGS: RwLock<(bool, Vec)> = RwLock::new((false, vec![])); } fn make_pool(sender: Option>) -> ThreadPool { @@ -54,7 +54,7 @@ pub fn load_tags() -> HResult<()> { let tag_path = crate::paths::tagfile_path()?; let tags = std::fs::read_to_string(tag_path)?; let mut tags = tags.lines().map(|f| PathBuf::from(f)).collect::>(); - let mut tag_lock = TAGS.lock()?; + let mut tag_lock = TAGS.write()?; tag_lock.0 = true; tag_lock.1.append(&mut tags); Ok(()) @@ -64,12 +64,12 @@ pub fn load_tags() -> HResult<()> { pub fn check_tag(path: &PathBuf) -> HResult { tags_loaded()?; - let tagged = TAGS.lock()?.1.contains(path); + let tagged = TAGS.read()?.1.contains(path); Ok(tagged) } pub fn tags_loaded() -> HResult<()> { - let loaded = TAGS.lock()?.0; + let loaded = TAGS.read()?.0; if loaded { Ok(()) } else { HError::tags_not_loaded() } } @@ -155,7 +155,7 @@ impl Files { } pub fn new_from_path_cancellable(path: &Path, - stale: Arc>) + stale: Stale) -> Result { let direntries: Result, _> = std::fs::read_dir(&path)?.collect(); let dirty = DirtyBit::new(); @@ -170,9 +170,10 @@ impl Files { let name = file.file_name(); let name = name.to_string_lossy(); let path = file.path(); - Some(File::new(&name, - path, - Some(dirty_meta.clone()))) + Some(File::new_with_stale(&name, + path, + Some(dirty_meta.clone()), + stale.clone())) } }) .fuse() @@ -392,6 +393,11 @@ impl Files { self.meta_upto = Some(meta_files); } + pub fn meta_set_fresh(&self) -> HResult<()> { + self.files.get(0)?.meta.set_fresh()?; + Ok(()) + } + pub fn set_filter(&mut self, filter: Option) { self.filter = filter; @@ -480,9 +486,38 @@ impl File { path: PathBuf, dirty_meta: Option) -> File { let tag = check_tag(&path).ok(); - let meta = File::make_async_meta(&path, dirty_meta.clone()); + let meta = File::make_async_meta(&path, dirty_meta.clone(), None); let dirsize = if path.is_dir() { - Some(File::make_async_dirsize(&path, dirty_meta.clone())) + Some(File::make_async_dirsize(&path, dirty_meta.clone(), None)) + } else { None }; + + File { + name: name.to_string(), + kind: if path.is_dir() { Kind::Directory } else { Kind::File }, + path: path, + dirsize: dirsize, + target: None, + meta: meta, + meta_processed: false, + dirty_meta: dirty_meta, + color: None, + selected: false, + tag: tag, + } + } + + pub fn new_with_stale(name: &str, + path: PathBuf, + dirty_meta: Option, + stale: Stale) -> File { + let tag = check_tag(&path).ok(); + let meta = File::make_async_meta(&path, + dirty_meta.clone(), + Some(stale.clone())); + let dirsize = if path.is_dir() { + Some(File::make_async_dirsize(&path, + dirty_meta.clone(), + Some(stale))) } else { None }; File { @@ -519,13 +554,19 @@ impl File { } pub fn make_async_meta(path: &PathBuf, - dirty_meta: Option) -> Async { + dirty_meta: Option, + stale_preview: 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()) - })); + let meta_closure = Box::new(move |stale: Stale| { + if stale.is_stale()? { HError::stale()? } + Ok(std::fs::symlink_metadata(&path)?) + }); + + let mut meta = match stale_preview { + Some(stale) => Async::new_with_stale(meta_closure, stale), + None => Async::new(meta_closure) + }; if let Some(dirty_meta) = dirty_meta { meta.on_ready(Box::new(move |_| { let mut dirty_meta = dirty_meta.clone(); @@ -538,13 +579,20 @@ impl File { } pub fn make_async_dirsize(path: &PathBuf, - dirty_meta: Option) -> Async { + dirty_meta: Option, + stale_preview: Option) -> Async { let path = path.clone(); - let mut dirsize = Async::new(Box::new(move |stale| { - if *stale.lock()? { HError::stale()? } + let dirsize_closure = Box::new(move |stale: Stale| { + if stale.is_stale()? { HError::stale()? } Ok(std::fs::read_dir(&path)?.count()) - })); + }); + + let mut dirsize = match stale_preview { + Some(stale) => Async::new_with_stale(dirsize_closure, stale), + None => Async::new(dirsize_closure) + }; + if let Some(dirty_meta) = dirty_meta { dirsize.on_ready(Box::new(move |_| { let mut dirty_meta = dirty_meta.clone(); @@ -604,10 +652,12 @@ impl File { pub fn reload_meta(&mut self) -> HResult<()> { self.dirty_meta.as_mut()?.set_dirty(); self.meta_processed = false; - self.meta = File::make_async_meta(&self.path, self.dirty_meta.clone()); + self.meta = File::make_async_meta(&self.path, + self.dirty_meta.clone(), + None); if self.dirsize.is_some() { self.dirsize - = Some(File::make_async_dirsize(&self.path, self.dirty_meta.clone())); + = Some(File::make_async_dirsize(&self.path, self.dirty_meta.clone(), None)); } Ok(()) } @@ -720,8 +770,8 @@ impl File { self.tag = Some(new_state); match new_state { - true => TAGS.lock()?.1.push(self.path.clone()), - false => { TAGS.lock()?.1.remove_item(&self.path); }, + true => TAGS.write()?.1.push(self.path.clone()), + false => { TAGS.write()?.1.remove_item(&self.path); }, } self.save_tags()?; Ok(()) @@ -730,7 +780,7 @@ impl File { pub fn save_tags(&self) -> HResult<()> { std::thread::spawn(|| -> HResult<()> { let tagfile_path = crate::paths::tagfile_path()?; - let tags = TAGS.lock()?.clone(); + let tags = TAGS.read()?.clone(); let tags_str = tags.1.iter().map(|p| { let path = p.to_string_lossy().to_string(); format!("{}\n", path) diff --git a/src/preview.rs b/src/preview.rs index da2a90a..d70b011 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use rayon::ThreadPool; @@ -10,8 +10,6 @@ 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>; @@ -33,8 +31,30 @@ fn kill_proc() -> HResult<()> { Ok(()) } -pub fn is_stale(stale: &Arc>) -> HResult { - let stale = *(stale.lock().unwrap()); +#[derive(Clone, Debug)] +pub struct Stale(Arc>); + +impl Stale { + pub fn new() -> Stale { + Stale(Arc::new(RwLock::new(false))) + } + pub fn is_stale(&self) -> HResult { + Ok(*self.0.read()?) + } + pub fn set_stale(&self) -> HResult<()> { + *self.0.write()? = true; + Ok(()) + } + pub fn set_fresh(&self) -> HResult<()> { + *self.0.write()? = false; + Ok(()) + } +} + + + +pub fn is_stale(stale: &Stale) -> HResult { + let stale = stale.is_stale()?; Ok(stale) } @@ -68,7 +88,20 @@ impl Async { async_value: Arc::new(Mutex::new(None)), async_closure: Arc::new(Mutex::new(Some(closure))), on_ready: Arc::new(Mutex::new(None)), - stale: Arc::new(Mutex::new(false)) }; + stale: Stale::new() }; + + async_value + } + + pub fn new_with_stale(closure: AsyncValueFn, + stale: Stale) + -> Async { + let async_value = Async { + value: HError::async_not_ready(), + async_value: Arc::new(Mutex::new(None)), + async_closure: Arc::new(Mutex::new(Some(closure))), + on_ready: Arc::new(Mutex::new(None)), + stale: stale }; async_value } @@ -79,53 +112,104 @@ impl Async { async_value: Arc::new(Mutex::new(None)), async_closure: Arc::new(Mutex::new(None)), on_ready: Arc::new(Mutex::new(None)), - stale: Arc::new(Mutex::new(false)) + stale: Stale::new() } } pub fn run(&mut self) -> HResult<()> { - let closure = self.async_closure.lock()?.take()?; + let closure = self.async_closure.clone(); let async_value = self.async_value.clone(); let stale = self.stale.clone(); - let on_ready_fn = self.on_ready.lock()?.take(); + let on_ready_fn = self.on_ready.clone(); std::thread::spawn(move|| -> HResult<()> { - let mut value = closure(stale); + let value = closure.lock().map(|closure| + closure.as_ref().map(|closure| + closure(stale))); - if let Ok(ref mut value) = value { - if let Some(on_ready_fn) = on_ready_fn { - on_ready_fn(value); + if let Ok(value) = value { + if let Some(value) = value { + match value { + Ok(mut value) => { + if let Ok(mut on_ready_fn) = on_ready_fn.lock() { + if let Some(on_ready_fn) = on_ready_fn.as_ref() { + on_ready_fn(&mut value).log(); + } + on_ready_fn.take(); + } + async_value + .lock() + .map(|mut async_value| + async_value.replace(Ok(value))).ok(); + closure.lock().map(|mut closure| + closure.take()).ok(); + }, + Err(err) => { + async_value + .lock() + .map(|mut async_value| + async_value.replace(Err(err))).ok(); + } + } } + } else { + async_value + .lock() + .map(|mut async_value| + async_value.replace(Err(HError::MutexError))).ok(); } - - async_value.lock()?.replace(value); Ok(()) }); Ok(()) } pub fn run_pooled(&mut self, pool: &ThreadPool) -> HResult<()> { - let closure = self.async_closure.lock()?.take()?; + let closure = self.async_closure.clone(); let async_value = self.async_value.clone(); let stale = self.stale.clone(); - let on_ready_fn = self.on_ready.lock()?.take(); + let on_ready_fn = self.on_ready.clone(); pool.spawn(move || { - let mut value = closure(stale); + let value = closure.lock().map(|closure| + closure.as_ref().map(|closure| + closure(stale))); - if let Ok(ref mut value) = value { - if let Some(on_ready_fn) = on_ready_fn { - on_ready_fn(value); + if let Ok(value) = value { + if let Some(value) = value { + match value { + Ok(mut value) => { + if let Ok(mut on_ready_fn) = on_ready_fn.lock() { + if let Some(on_ready_fn) = on_ready_fn.as_ref() { + on_ready_fn(&mut value).log(); + } + on_ready_fn.take(); + } + async_value + .lock() + .map(|mut async_value| + async_value.replace(Ok(value))).ok(); + closure.lock().map(|mut closure| + closure.take()).ok(); + }, + Err(err) => { + async_value + .lock() + .map(|mut async_value| + async_value.replace(Err(err))).ok(); + } + } } + } else { + async_value + .lock() + .map(|mut async_value| + async_value.replace(Err(HError::MutexError))).ok(); } - - async_value - .lock() - .map(|mut async_value| async_value.replace(value)); }); Ok(()) } + pub fn wait(&mut self) -> HResult<()> { let closure = self.async_closure.lock()?.take()?; let mut value = closure(self.stale.clone()); @@ -141,12 +225,21 @@ impl Async { } pub fn set_stale(&mut self) -> HResult<()> { - *self.stale.lock()? = true; + self.stale.set_stale()?; + Ok(()) + } + + pub fn set_fresh(&self) -> HResult<()> { + self.stale.set_fresh()?; Ok(()) } pub fn is_stale(&self) -> HResult { - is_stale(&self.stale) + self.stale.is_stale() + } + + pub fn get_stale(&self) -> Stale { + self.stale.clone() } pub fn take_async(&mut self) -> HResult<()> { @@ -251,6 +344,10 @@ impl AsyncWidget { self.widget.is_stale() } + pub fn get_stale(&self) -> Stale { + self.widget.get_stale() + } + pub fn widget(&self) -> HResult<&W> { self.widget.get() } @@ -292,7 +389,7 @@ impl Widget for AsyncWidget { } fn refresh(&mut self) -> HResult<()> { - self.widget.take_async().log(); + self.widget.take_async().ok(); let coords = self.get_coordinates()?.clone(); if let Ok(widget) = self.widget_mut() { @@ -342,7 +439,7 @@ pub struct Previewer { core: WidgetCore, file: Option, selection: Option, - cached_files: Option + cached_files: Option, } @@ -435,7 +532,7 @@ impl Previewer { selection: Option, cached_files: Option, core: &WidgetCore, - stale: Arc>) + stale: Stale) -> Result { let files = cached_files.or_else(|| { Files::new_from_path_cancellable(&file.path, @@ -456,7 +553,7 @@ impl Previewer { Ok(Box::new(file_list) as Box) } - fn preview_text(file: &File, core: &WidgetCore, stale: Arc>) + fn preview_text(file: &File, core: &WidgetCore, stale: Stale) -> HResult { let lines = core.coordinates.ysize() as usize; let mut textview @@ -476,7 +573,7 @@ impl Previewer { fn preview_external(file: &File, core: &WidgetCore, - stale: Arc>) + stale: Stale) -> Result, HError> { let process = std::process::Command::new("scope.sh") diff --git a/src/widget.rs b/src/widget.rs index 2093d2d..9c0fe43 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -11,6 +11,7 @@ use crate::minibuffer::MiniBuffer; use crate::term; use crate::term::{Screen, ScreenExt}; use crate::dirty::{Dirtyable, DirtyBit}; +use crate::preview::Stale; use crate::signal_notify::{notify, Signal}; use std::io::stdin;