summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-03-29 02:53:21 +0100
committerrabite <rabite@posteo.de>2019-03-29 02:53:21 +0100
commitb52e63fd8d74ed7587db6d37aca585f6185c77a3 (patch)
tree2e4a24610b1ab090dcb133ca542f39595e831436
parent8d8d9631b5fce48e2e65410aa4d0df64948f5ad8 (diff)
global file cache/event dispatch
-rw-r--r--src/fail.rs2
-rw-r--r--src/file_browser.rs277
-rw-r--r--src/files.rs56
-rw-r--r--src/fscache.rs294
-rw-r--r--src/main.rs6
-rw-r--r--src/preview.rs351
-rw-r--r--src/tabview.rs8
-rw-r--r--src/widget.rs24
8 files changed, 529 insertions, 489 deletions
diff --git a/src/fail.rs b/src/fail.rs
index c85028c..ea5a4a7 100644
--- a/src/fail.rs
+++ b/src/fail.rs
@@ -88,7 +88,7 @@ pub enum HError {
}
impl HError {
- pub fn log(log: String) -> HResult<()> {
+ pub fn log<T>(log: String) -> HResult<T> {
Err(HError::Log(log))
}
pub fn quit() -> HResult<()> {
diff --git a/src/file_browser.rs b/src/file_browser.rs
index 3cf05e7..469b2d8 100644
--- a/src/file_browser.rs
+++ b/src/file_browser.rs
@@ -1,19 +1,15 @@
use termion::event::Key;
-use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
use std::io::Write;
use std::sync::{Arc, Mutex};
-use std::sync::mpsc::{channel, Receiver, Sender};
-use std::time::Duration;
use std::path::PathBuf;
-use std::collections::HashMap;
use std::ffi::OsString;
use crate::files::{File, Files, PathBufExt};
+use crate::fscache::FsCache;
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};
@@ -68,35 +64,37 @@ pub struct FileBrowser {
pub columns: HBox<FileBrowserWidgets>,
pub cwd: File,
pub prev_cwd: Option<File>,
- selections: HashMap<File, File>,
- cached_files: HashMap<File, Files>,
core: WidgetCore,
- watcher: INotifyWatcher,
- watches: Vec<PathBuf>,
- dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
proc_view: Arc<Mutex<ProcView>>,
bookmarks: Arc<Mutex<BMPopup>>,
- log_view: Arc<Mutex<LogView>>
+ log_view: Arc<Mutex<LogView>>,
+ fs_cache: FsCache,
}
impl Tabbable for TabView<FileBrowser> {
fn new_tab(&mut self) -> HResult<()> {
- let mut tab = FileBrowser::new_cored(&self.active_tab_().core)?;
+ let cur_tab = self.active_tab_();
- let proc_view = self.active_tab_().proc_view.clone();
- let bookmarks = self.active_tab_().bookmarks.clone();
- let log_view = self.active_tab_().log_view.clone();
+ let settings = cur_tab.fs_cache.tab_settings.read()?.clone();
+ let cache = cur_tab.fs_cache.new_client(settings).ok();
+
+ let mut tab = FileBrowser::new(&self.active_tab_().core, cache)?;
+
+ let proc_view = cur_tab.proc_view.clone();
+ let bookmarks = cur_tab.bookmarks.clone();
+ let log_view = cur_tab.log_view.clone();
tab.proc_view = proc_view;
tab.bookmarks = bookmarks;
tab.log_view = log_view;
self.push_widget(tab)?;
- self.active += 1;
+ self.active = self.widgets.len() - 1;
Ok(())
}
fn close_tab(&mut self) -> HResult<()> {
- self.close_tab_()
+ self.close_tab_().log();
+ Ok(())
}
fn next_tab(&mut self) -> HResult<()> {
@@ -148,29 +146,36 @@ impl Tabbable for TabView<FileBrowser> {
_ => { self.active_tab_mut().on_key(key) }
}
}
-}
-
-
+ fn on_refresh(&mut self) -> HResult<()> {
+ let fs_changes = self.active_tab_()
+ .fs_cache
+ .fs_changes
+ .write()?
+ .drain(..)
+ .collect::<Vec<_>>();
-
-fn watch_dir(rx: Receiver<DebouncedEvent>,
- dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
- sender: Sender<Events>) {
- std::thread::spawn(move || {
- for event in rx.iter() {
- dir_events.lock().unwrap().push(event);
- sender.send(Events::WidgetReady).unwrap();
+ for tab in &mut self.widgets {
+ for (dir, old_file, new_file) in fs_changes.iter() {
+ tab.replace_file(&dir,
+ old_file.as_ref(),
+ new_file.as_ref()).log()
+ }
}
- });
+ Ok(())
+ }
}
+
+
impl FileBrowser {
- pub fn new_cored(core: &WidgetCore) -> HResult<FileBrowser> {
+ pub fn new(core: &WidgetCore, cache: Option<FsCache>) -> HResult<FileBrowser> {
+ let fs_cache = cache.unwrap_or_else(|| FsCache::new(core.get_sender()));
+
let cwd = std::env::current_dir().unwrap();
let mut core_m = core.clone();
let mut core_l = core.clone();
@@ -191,18 +196,40 @@ impl FileBrowser {
}).last()?;
let left_path = main_path.parent().map(|p| p.to_path_buf());
+ let cache = fs_cache.clone();
let main_widget = AsyncWidget::new(&core, Box::new(move |_| {
- let mut list = ListView::new(&core_m,
- Files::new_from_path(&main_path)?);
+ let main_dir = File::new(&main_path.file_name()?
+ .to_string_lossy()
+ .to_string(),
+ main_path.clone(),
+ None);
+ let files = cache.get_files_sync(&main_dir)?;
+ let selection = cache.get_selection(&main_dir).ok();
+ let mut list = ListView::new(&core_m.clone(),
+ files);
+ if let Some(file) = selection {
+ list.select_file(&file);
+ }
list.animate_slide_up().log();
list.content.meta_all();
Ok(list)
}));
+ let cache = fs_cache.clone();
if let Some(left_path) = left_path {
let left_widget = AsyncWidget::new(&core, Box::new(move |_| {
+ let left_dir = File::new(&left_path.file_name()?
+ .to_string_lossy()
+ .to_string(),
+ left_path.clone(),
+ None);
+ let files = cache.get_files_sync(&left_dir)?;
+ let selection = cache.get_selection(&left_dir).ok();
let mut list = ListView::new(&core_l,
- Files::new_from_path(&left_path)?);
+ files);
+ if let Some(file) = selection {
+ list.select_file(&file);
+ }
list.animate_slide_up().log();
Ok(list)
}));
@@ -210,7 +237,7 @@ impl FileBrowser {
columns.push_widget(left_widget);
}
- let previewer = Previewer::new(&core_p);
+ let previewer = Previewer::new(&core_p, fs_cache.clone());
columns.push_widget(FileBrowserWidgets::FileList(main_widget));
columns.push_widget(FileBrowserWidgets::Previewer(previewer));
@@ -219,11 +246,6 @@ impl FileBrowser {
let cwd = File::new_from_path(&cwd, None).unwrap();
- let dir_events = Arc::new(Mutex::new(vec![]));
-
- let (tx_watch, rx_watch) = channel();
- let watcher = INotifyWatcher::new(tx_watch, Duration::from_secs(2)).unwrap();
- watch_dir(rx_watch, dir_events.clone(), core.get_sender());
let proc_view = ProcView::new(&core);
let bookmarks = BMPopup::new(&core);
@@ -234,15 +256,12 @@ impl FileBrowser {
Ok(FileBrowser { columns: columns,
cwd: cwd,
prev_cwd: None,
- selections: HashMap::new(),
- cached_files: HashMap::new(),
core: core.clone(),
- watcher: watcher,
- watches: vec![],
- dir_events: dir_events,
proc_view: Arc::new(Mutex::new(proc_view)),
bookmarks: Arc::new(Mutex::new(bookmarks)),
- log_view: Arc::new(Mutex::new(log_view)) })
+ log_view: Arc::new(Mutex::new(log_view)),
+ fs_cache: fs_cache,
+ })
}
pub fn enter_dir(&mut self) -> HResult<()> {
@@ -306,33 +325,25 @@ impl FileBrowser {
}
pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> {
-
+ self.cache_files().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();
+ let cache = self.fs_cache.clone();
self.prev_cwd = Some(self.cwd.clone());
self.cwd = dir.clone();
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();
-
- let files = cached_files.or_else(|| {
- Files::new_from_path_cancellable(&path, stale.clone()).ok()
- })?;
+ let (selected_file, files) = cache.get_files(&dir, stale)?;
+ let files = files.wait()?;
let mut list = ListView::new(&core, files);
list.content.meta_set_fresh().log();
- if let Some(file) = &selected_file {
- list.select_file(file);
+ if let Some(file) = selected_file {
+ list.select_file(&file);
}
Ok(list)
})).log();
@@ -341,7 +352,7 @@ impl FileBrowser {
self.left_widget_goto(&grand_parent).log();
} else {
self.left_async_widget_mut()?.clear().log();
- self.screen()?.flush();
+ Ok(self.screen()?.flush()?).log();
self.left_async_widget_mut()?.set_stale().log();
}
@@ -349,17 +360,15 @@ impl FileBrowser {
}
pub fn left_widget_goto(&mut self, dir: &File) -> HResult<()> {
- let cached_files = self.get_cached_files(&dir).ok();
+ let cache = self.fs_cache.clone();
let dir = dir.clone();
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();
+ let cached_files = cache.get_files(&dir, stale)?;
+ let (_, files) = cached_files;
- let files = cached_files.or_else(|| {
- Files::new_from_path_cancellable(&path, stale).ok()
- })?;
+ let files = files.wait()?;
let list = ListView::new(&core, files);
Ok(list)
@@ -430,10 +439,8 @@ impl FileBrowser {
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).log();
+ preview.set_file(&file).log();
Ok(())
}
@@ -441,47 +448,45 @@ impl FileBrowser {
if !self.left_async_widget_mut()?.ready() { return Ok(()) }
if self.cwd.parent().is_none() { return Ok(()) }
- let parent = self.cwd()?.parent_as_file();
+ let selection = self.cwd()?.clone();
- let left_selection = self.get_selection(&parent?)?.clone();
- self.left_widget_mut()?.select_file(&left_selection);
+ self.left_widget_mut()?.select_file(&selection);
Ok(())
}
- pub fn get_selection(&self, dir: &File) -> HResult<&File> {
- Ok(self.selections.get(dir)?)
- }
-
- pub fn get_files(&mut self) -> HResult<Files> {
+ pub fn get_files(&self) -> HResult<Files> {
Ok(self.main_widget()?.content.clone())
}
- pub fn get_left_files(&mut self) -> HResult<Files> {
+ pub fn get_left_files(&self) -> HResult<Files> {
Ok(self.left_widget()?.content.clone())
}
- pub fn cache_files(&mut self, files: Files) -> HResult<()> {
- let dir = files.directory.clone();
- self.cached_files.insert(dir, files);
- Ok(())
- }
-
- pub fn get_cached_files(&mut self, dir: &File) -> HResult<Files> {
- Ok(self.cached_files.get(dir)?.clone())
- }
-
- pub fn save_selection(&mut self) -> HResult<()> {
- let cwd = self.cwd()?.clone();
- if let Ok(main_selection) = self.selected_file() {
- self.selections.insert(cwd.clone(), main_selection);
+ pub fn cache_files(&self) -> HResult<()> {
+ if !self.fs_cache.is_cached(&self.cwd)? {
+ let files = self.get_files()?;
+ let selected_file = self.selected_file().ok();
+ self.fs_cache.put_files(files, selected_file).log();
+ } else {
+ let files = &self.main_widget()?.content;
+ let selected_file = self.selected_file().ok();
+ self.fs_cache.save_settings(&files, selected_file).log();
}
- if let Ok(left_dir) = self.cwd()?.parent_as_file() {
- self.selections.insert(left_dir, cwd);
+
+ if !self.fs_cache.is_cached(&self.left_widget()?.content.directory)? {
+ let left_selection = self.left_widget()?.clone_selected_file();
+ let left_files = self.get_left_files()?;
+ self.fs_cache.put_files(left_files, Some(left_selection)).log();
+ } else {
+ let files = &self.left_widget()?.content;
+ let selected_file = self.left_widget()?.clone_selected_file();
+ self.fs_cache.save_settings(&files, Some(selected_file)).log();
}
Ok(())
}
+
pub fn cwd(&self) -> HResult<&File> {
Ok(&self.cwd)
}
@@ -492,77 +497,25 @@ impl FileBrowser {
Ok(())
}
- pub fn left_dir(&self) -> HResult<File> {
+ pub fn left_dir(&self) -> HResult<&File> {
let widget = self.left_widget()?;
- let dir = widget.content.directory.clone();
+ let dir = &widget.content.directory;
Ok(dir)
}
- fn update_watches(&mut self) -> HResult<()> {
- if !self.left_async_widget_mut()?.ready() ||
- !self.main_async_widget_mut()?.ready() {
- return Ok(())
- }
- let watched_dirs = self.watches.clone();
- let cwd = self.cwd()?.clone();
- let left_dir = self.left_dir()?;
- let preview_dir = self.selected_file().ok().map(|f| f.path);
-
- for watched_dir in watched_dirs.iter() {
- if watched_dir != &cwd.path && watched_dir != &left_dir.path &&
- Some(watched_dir.clone()) != preview_dir {
- self.watcher.unwatch(&watched_dir).ok();
- self.watches.remove_item(&watched_dir);
- }
- }
- if !watched_dirs.contains(&cwd.path) {
- self.watcher.watch(&cwd.path, RecursiveMode::NonRecursive)?;
- self.watches.push(cwd.path);
- }
- if !watched_dirs.contains(&left_dir.path) {
- self.watcher.watch(&left_dir.path, RecursiveMode::NonRecursive)?;
- self.watches.push(left_dir.path);
+ fn replace_file(&mut self,
+ dir: &File,
+ old: Option<&File>,
+ new: Option<&File>) -> HResult<()> {
+ if &self.cwd == dir {
+ self.main_widget_mut()?.content.replace_file(old, new.cloned()).log();
}
- if let Some(preview_dir) = preview_dir {
- if !watched_dirs.contains(&preview_dir) && preview_dir.is_dir() {
- match self.watcher.watch(&preview_dir, RecursiveMode::NonRecursive) {
- Ok(_) => self.watches.push(preview_dir),
- Err(notify::Error::Io(ioerr)) => {
- if ioerr.kind() != std::io::ErrorKind::PermissionDenied {
- Err(ioerr)?
- }
- }
- err @ _ => err?
- }
- }
+ if &self.left_dir()? == &dir {
+ self.left_widget_mut()?.content.replace_file(old, new.cloned()).log();
}
Ok(())
}
- 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_mut()?;
- let main_result = main_widget.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 { .. }) => {
- match left_result {
- Err(HError::WrongDirectoryError { .. }) => {
- let preview = self.preview_widget_mut()?;
- preview.reload();
- }, _ => {}
- }
- }, _ => {}
- }
- }
- dir_events.lock()?.clear();
- Ok(())
- }
-
pub fn selected_file(&self) -> HResult<File> {
let widget = self.main_widget()?;
let file = widget.selected_file().clone();
@@ -631,13 +584,6 @@ impl FileBrowser {
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_mut(&mut self) -> HResult<&mut Previewer> {
match self.columns.widgets.get_mut(2)? {
FileBrowserWidgets::Previewer(previewer) => Ok(previewer),
@@ -843,16 +789,13 @@ impl Widget for FileBrowser {
}
}
fn refresh(&mut self) -> HResult<()> {
- //self.proc_view.lock()?.set_coordinates(self.get_coordinates()?);
self.set_title().log();
- self.handle_dir_events().log();
self.columns.refresh().log();
self.set_left_selection().log();
- self.save_selection().log();
self.set_cwd().log();
- self.update_watches().log();
if !self.columns.zoom_active { self.update_preview().log(); }
self.columns.refresh().log();
+ self.cache_files().log();
Ok(())
}
diff --git a/src/files.rs b/src/files.rs
index 8b3265c..7af7551 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -18,7 +18,6 @@ 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, ErrorLog};
@@ -234,7 +233,7 @@ impl Files {
.files
.sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)),
SortBy::Size => {
- self.meta_all_sync();
+ self.meta_all_sync().log();
self.files.sort_by(|a, b| {
if a.meta().unwrap().size() == b.meta().unwrap().size() {
return alphanumeric_sort::compare_str(&b.name, &a.name);
@@ -243,7 +242,7 @@ impl Files {
});
}
SortBy::MTime => {
- self.meta_all_sync();
+ self.meta_all_sync().log();
self.files.sort_by(|a, b| {
if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() {
return alphanumeric_sort::compare_str(&a.name, &b.name);
@@ -292,7 +291,17 @@ impl Files {
self.show_hidden = !self.show_hidden
}
- pub fn handle_event(&mut self, event: &DebouncedEvent) -> HResult<()> {
+ pub fn replace_file(&mut self,
+ old: Option<&File>,
+ new: Option<File>) -> HResult<()> {
+ old.map(|old| self.files.remove_item(old));
+ new.map(|new| self.files.push(new));
+ self.sort();
+ Ok(())
+ }
+
+ pub fn handle_event(&mut self,
+ event: &DebouncedEvent) -> HResult<()> {
match event {
DebouncedEvent::Create(path) => {
self.path_in_here(&path)?;
@@ -329,12 +338,12 @@ impl Files {
}
pub fn path_in_here(&self, path: &Path) -> HResult<bool> {
- let dir = self.directory.path();
+ let dir = &self.directory.path;
let path = if path.is_dir() { path } else { path.parent().unwrap() };
if dir == path {
Ok(true)
} else {
- HError::wrong_directory(path.into(), dir)?
+ HError::wrong_directory(path.into(), dir.to_path_buf())?
}
}
@@ -345,15 +354,10 @@ impl Files {
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()?;
+ file.meta_sync().log();
}
}
- self.dirty_meta.set_dirty();
+ self.set_dirty();
Ok(())
}
@@ -476,7 +480,6 @@ pub struct File {
pub meta_processed: bool,
pub selected: bool,
pub tag: Option<bool>
- // flags: Option<String>,
}
impl File {
@@ -552,6 +555,14 @@ impl File {
Ok(file)
}
+ pub fn meta_sync(&mut self) -> HResult<()> {
+ let stale = self.meta.get_stale();
+ let meta = std::fs::metadata(&self.path)?;
+ self.meta = Async::new_with_value(meta);
+ self.meta.put_stale(stale);
+ self.process_meta()
+ }
+
pub fn make_async_meta(path: &PathBuf,
dirty_meta: Option<AsyncDirtyBit>,
stale_preview: Option<Stale>) -> Async<Metadata> {
@@ -632,6 +643,12 @@ impl File {
err @ Err(_) => { err?; }
}
+ self.process_meta()?;
+
+ Ok(())
+ }
+
+ pub fn process_meta(&mut self) -> HResult<()> {
if let Ok(meta) = self.meta.get() {
let color = self.get_color(&meta);
let target = if meta.file_type().is_symlink() {
@@ -641,10 +658,7 @@ impl File {
self.color = color;
self.target = target;
self.meta_processed = true;
-
- return Ok(())
}
-
Ok(())
}
@@ -653,11 +667,12 @@ impl File {
self.meta = File::make_async_meta(&self.path,
self.dirty_meta.clone(),
None);
- self.meta.run();
+ self.meta.run().log();
+
if self.dirsize.is_some() {
self.dirsize
= Some(File::make_async_dirsize(&self.path, self.dirty_meta.clone(), None));
- self.dirsize.as_mut()?.run();
+ self.dirsize.as_mut()?.run().log();
}
Ok(())
}
@@ -872,7 +887,6 @@ impl File {
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.meta().unwrap().mtime(), 0);
Some(time.format("%F %R").to_string())
@@ -930,7 +944,6 @@ impl PathBufExt for PathBuf {
if let Some(name) = self.file_name() {
let mut name = name.as_bytes().to_vec();
let mut quote = "\"".as_bytes().to_vec();
- //let mut quote_after = "\"".as_bytes().to_vec();
let mut quoted = vec![];
quoted.append(&mut quote.clone());
quoted.append(&mut name);
@@ -988,7 +1001,6 @@ impl OsStrTools for OsStr {
split_pos
}).iter()
.map(|(start, end)| {
- //let orig_string = orig_string.clone();
OsString::from_vec(orig_string[*start..*end]
.to_vec()).replace(&OsString::from_vec(pat.clone()),
&OsString::from(""))
diff --git a/src/fscache.rs b/src/fscache.rs
new file mode 100644
index 0000000..c650dff
--- /dev/null
+++ b/src/fscache.rs
@@ -0,0 +1,294 @@
+use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
+
+use std::sync::{Arc, RwLock};
+use std::sync::mpsc::{channel, Sender, Receiver};
+use std::collections::{HashMap, HashSet};
+use std::time::Duration;
+use std::path::PathBuf;
+
+use crate::preview::{Async, Stale};
+use crate::files::{Files, File, SortBy};
+use crate::dirty::*;
+use crate::widget::Events;
+use crate::fail::{HResult, HError, ErrorLog};
+
+
+#[derive(Debug, Clone)]
+pub struct DirSettings {
+ sort: SortBy,
+ dirs_first: bool,
+ reverse: bool,
+ show_hidden: bool,
+ filter: Option<String>,
+}
+
+impl DirSettings {
+ fn new() -> DirSettings {
+ DirSettings {
+ sort: SortBy::Name,
+ dirs_first: true,
+ reverse: false,
+ show_hidden: true,
+ filter: None
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct TabSettings {
+ selection: Option<File>,
+ multi_selections: Vec<File>,
+ dir_settings: DirSettings,
+}
+
+impl TabSettings {
+ fn new() -> TabSettings {
+ TabSettings {
+ selection: None,
+ multi_selections: vec![],
+ dir_settings: DirSettings::new()
+ }
+ }
+}
+
+
+impl std::fmt::Debug for FsCache {
+ fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(formatter,
+ "{:?}\n{:?}\n{:?}",
+ self.tab_settings,
+ self.watched_dirs,
+ self.files)
+ }
+}
+
+unsafe impl Sync for FsCache {}
+
+
+#[derive(Clone)]
+pub struct FsCache {
+ files: Arc<RwLock<HashMap<File, Files>>>,
+ pub tab_settings: Arc<RwLock<HashMap<File, TabSettings>>>,
+ watched_dirs: Arc<RwLock<HashSet<File>>>,
+ watcher: Arc<RwLock<INotifyWatcher>>,
+ pub fs_changes: Arc<RwLock<Vec<(File, Option<File>, Option<File>)>>>,
+ sender: Sender<Events>,
+}
+
+impl FsCache {
+ pub fn new(sender: Sender<Events>) -> FsCache {
+ let (tx_fs_event, rx_fs_event) = channel();
+ let watcher = INotifyWatcher::new(tx_fs_event,
+ Duration::from_secs(2)).unwrap();
+
+
+ let fs_cache = FsCache {
+ files: Arc::new(RwLock::new(HashMap::new())),
+ tab_settings: Arc::new(RwLock::new(HashMap::new())),
+ watched_dirs: Arc::new(RwLock::new(HashSet::new())),
+ watcher: Arc::new(RwLock::new(watcher)),
+ fs_changes: Arc::new(RwLock::new(vec![])),
+ sender: sender.clone(),
+ };
+
+ watch_fs(rx_fs_event,
+ fs_cache.files.clone(),
+ fs_cache.fs_changes.clone(),
+ sender.clone());
+
+ fs_cache
+ }
+
+ pub fn new_client(&self, settings: HashMap<File, TabSettings>) -> HResult<FsCache> {
+ let mut cache = self.clone();
+ cache.tab_settings = Arc::new(RwLock::new(settings));
+ Ok(cache)
+ }
+}
+
+pub type CachedFiles = (Option<File>, Async<Files>);
+
+impl FsCache {
+ pub fn get_files(&self, dir: &File, stale: Stale) -> HResult<CachedFiles> {
+ if self.files.read()?.contains_key(dir) {
+ self.get_cached_files(dir)
+ } else {
+ self.add_watch(&dir).log();
+ let dir = dir.clone();
+ let cache = self.files.clone();
+ let files = Async::new(Box::new(move |_| {
+ let files = Files::new_from_path_cancellable(&dir.path, stale)?;
+ Ok(files)
+ }));
+ Ok((None, files))
+ }
+ }
+
+ pub fn get_files_sync(&self, dir: &File) -> HResult<Files> {
+ let mut files = self.get_files(&dir, Stale::new())?.1;
+ files.wait()
+ }
+
+ pub fn get_selection(&self, dir: &File) -> HResult<File> {
+ Ok(self.tab_settings.read()?.get(&dir).as_ref()?.selection.as_ref()?.clone())
+ }
+
+ pub fn save_settings(&self, files: &Files, selection: Option<File>) -> HResult<()> {
+ let dir = files.directory.clone();
+ let tab_settings = FsCache::extract_tab_settings(&files, selection);
+ self.tab_settings.write()?.insert(dir, tab_settings);
+ Ok(())
+ }
+
+ pub fn put_files(&self, files: Files, selection: Option<File>) -> HResult<()> {
+ let dir = files.directory.clone();
+
+ let tab_settings = FsCache::extract_tab_settings(&files, selection);
+
+ self.tab_settings.write()?.insert(dir.clone(), tab_settings);
+
+ let mut file_cache = self.files.write()?;
+
+ if !file_cache.contains_key(&files.directory) {
+ file_cache.insert(dir, files);
+ }
+
+ Ok(())
+ }
+
+ pub fn is_cached(&self, dir: &File) -> HResult<bool> {
+ Ok(self.files.read()?.contains_key(dir))
+ }
+
+ fn add_watch(&self, dir: &File) -> HResult<()> {
+ if !self.watched_dirs.read()?.contains(&dir) {
+ self.watcher.write()?.watch(&dir.path, RecursiveMode::NonRecursive)?
+ }
+ Ok(())
+ }
+
+ fn remove_watch(&self, dir: &File) -> HResult<()> {
+ if self.watched_dirs.read()?.contains(&dir) {
+ self.watched_dirs.write()?.remove(dir);
+ self.watcher.write()?.unwatch(&dir.path)?
+ }
+ Ok(())
+ }
+
+ fn get_cached_files(&self, dir: &File) -> HResult<CachedFiles> {
+ let tab_settings = match self.tab_settings.read()?.get(&dir) {
+ Some(tab_settings) => tab_settings.clone(),
+ None => TabSettings::new()
+ };
+ let selection = tab_settings.selection.clone();
+ let file_cache = self.files.clone();
+ let dir = dir.clone();
+
+ let files = Async::new(Box::new(move |_| {
+ let mut files = file_cache.read()?.get(&dir)?.clone();
+ let tab_settings = &tab_settings;
+
+ files.sort = tab_settings.dir_settings.sort;
+ files.dirs_first = tab_settings.dir_settings.dirs_first;
+ files.reverse = tab_settings.dir_settings.reverse;
+ files.show_hidden = tab_settings.dir_settings.show_hidden;
+ files.filter = tab_settings.dir_settings.filter.clone();
+
+ if tab_settings.multi_selections.len() > 0 {
+ for file in &mut files.files {
+ for selected_files in &tab_settings.multi_selections {
+ if file.path == selected_files.path {
+ file.selected = true;
+ }
+ }
+ }
+ }
+
+ files.sort();
+ Ok(files)
+ }));
+
+ Ok((selection, files))
+ }
+
+ fn extract_tab_settings(files: &Files, selection: Option<File>) -> TabSettings {
+ TabSettings {
+ selection: selection,
+ multi_selections: files.get_selected().into_iter().cloned().collect(),
+ dir_settings: DirSettings {
+ sort: files.sort,
+ dirs_first: files.dirs_first,
+ reverse: files.reverse,
+ show_hidden: files.show_hidden,
+ filter: files.filter.clone(),
+ }
+ }
+ }
+}
+
+
+fn watch_fs(rx_fs_events: Receiver<DebouncedEvent>,
+ fs_cache: Arc<RwLock<HashMap<File, Files>>>,
+ fs_changes: Arc<RwLock<Vec<(File, Option<File>, Option<File>)>>>,
+ sender: Sender<Events>) {
+ std::thread::spawn(move || -> HResult<()> {
+ for event in rx_fs_events.iter() {
+ apply_event(&fs_cache, &fs_changes, event).log();
+
+ Ok(sender.send(Events::WidgetReady)?).log();
+ }
+ Ok(())
+ });
+}
+
+fn apply_event(fs_cache: &Arc<RwLock<HashMap<File, Files>>>,
+ fs_changes: &Arc<RwLock<Vec<(File, Option<File>, Option<File>)>>>,
+ event: DebouncedEvent)
+ -> HResult<()> {
+ let path = &event.get_source_path()?;
+
+ for dir in fs_cache.write()?.values_mut() {
+ if dir.path_in_here(&path).unwrap_or(false) {
+ let old_file = dir.find_file_with_path(&path).cloned();
+ let dirty_meta = old_file
+ .as_ref()
+ .map(|f| f.dirty_meta.clone())
+ .unwrap_or(None);
+ let mut new_file = match event {
+ DebouncedEvent::Remove(_) => None,
+ _ => Some(File::new_from_path(&path, dirty_meta)?)
+ };
+
+ new_file.as_mut().map(|file| file.meta_sync());
+ dir.replace_file(old_file.as_ref(), new_file.clone()).log();
+
+ fs_changes.write()?.push((dir.directory.clone(),
+ old_file,
+ new_file));
+ }
+ }
+ Ok(())
+}
+
+trait PathFromEvent {
+ fn get_source_path(&self) -> HResult<&PathBuf>;
+}
+
+impl PathFromEvent for DebouncedEvent {
+ fn get_source_path(&self) -> HResult<&