global file cache/event dispatch

This commit is contained in:
rabite 2019-03-29 02:53:21 +01:00
parent 8d8d9631b5
commit b52e63fd8d
8 changed files with 529 additions and 489 deletions

View File

@ -88,7 +88,7 @@ pub enum HError {
} }
impl HError { impl HError {
pub fn log(log: String) -> HResult<()> { pub fn log<T>(log: String) -> HResult<T> {
Err(HError::Log(log)) Err(HError::Log(log))
} }
pub fn quit() -> HResult<()> { pub fn quit() -> HResult<()> {

View File

@ -1,19 +1,15 @@
use termion::event::Key; use termion::event::Key;
use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
use std::io::Write; use std::io::Write;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::time::Duration;
use std::path::PathBuf; use std::path::PathBuf;
use std::collections::HashMap;
use std::ffi::OsString; use std::ffi::OsString;
use crate::files::{File, Files, PathBufExt}; use crate::files::{File, Files, PathBufExt};
use crate::fscache::FsCache;
use crate::listview::ListView; use crate::listview::ListView;
use crate::hbox::HBox; use crate::hbox::HBox;
use crate::widget::Widget; use crate::widget::Widget;
use crate::dirty::Dirtyable;
use crate::tabview::{TabView, Tabbable}; use crate::tabview::{TabView, Tabbable};
use crate::preview::{Previewer, AsyncWidget}; use crate::preview::{Previewer, AsyncWidget};
use crate::fail::{HResult, HError, ErrorLog}; use crate::fail::{HResult, HError, ErrorLog};
@ -68,35 +64,37 @@ pub struct FileBrowser {
pub columns: HBox<FileBrowserWidgets>, pub columns: HBox<FileBrowserWidgets>,
pub cwd: File, pub cwd: File,
pub prev_cwd: Option<File>, pub prev_cwd: Option<File>,
selections: HashMap<File, File>,
cached_files: HashMap<File, Files>,
core: WidgetCore, core: WidgetCore,
watcher: INotifyWatcher,
watches: Vec<PathBuf>,
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>,
proc_view: Arc<Mutex<ProcView>>, proc_view: Arc<Mutex<ProcView>>,
bookmarks: Arc<Mutex<BMPopup>>, bookmarks: Arc<Mutex<BMPopup>>,
log_view: Arc<Mutex<LogView>> log_view: Arc<Mutex<LogView>>,
fs_cache: FsCache,
} }
impl Tabbable for TabView<FileBrowser> { impl Tabbable for TabView<FileBrowser> {
fn new_tab(&mut self) -> HResult<()> { 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 settings = cur_tab.fs_cache.tab_settings.read()?.clone();
let bookmarks = self.active_tab_().bookmarks.clone(); let cache = cur_tab.fs_cache.new_client(settings).ok();
let log_view = self.active_tab_().log_view.clone();
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.proc_view = proc_view;
tab.bookmarks = bookmarks; tab.bookmarks = bookmarks;
tab.log_view = log_view; tab.log_view = log_view;
self.push_widget(tab)?; self.push_widget(tab)?;
self.active += 1; self.active = self.widgets.len() - 1;
Ok(()) Ok(())
} }
fn close_tab(&mut self) -> HResult<()> { fn close_tab(&mut self) -> HResult<()> {
self.close_tab_() self.close_tab_().log();
Ok(())
} }
fn next_tab(&mut self) -> HResult<()> { fn next_tab(&mut self) -> HResult<()> {
@ -148,29 +146,36 @@ impl Tabbable for TabView<FileBrowser> {
_ => { self.active_tab_mut().on_key(key) } _ => { 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<_>>();
for tab in &mut self.widgets {
for (dir, old_file, new_file) in fs_changes.iter() {
tab.replace_file(&dir,
fn watch_dir(rx: Receiver<DebouncedEvent>, old_file.as_ref(),
dir_events: Arc<Mutex<Vec<DebouncedEvent>>>, new_file.as_ref()).log()
sender: Sender<Events>) { }
std::thread::spawn(move || {
for event in rx.iter() {
dir_events.lock().unwrap().push(event);
sender.send(Events::WidgetReady).unwrap();
} }
}); Ok(())
}
} }
impl FileBrowser { 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 cwd = std::env::current_dir().unwrap();
let mut core_m = core.clone(); let mut core_m = core.clone();
let mut core_l = core.clone(); let mut core_l = core.clone();
@ -191,18 +196,40 @@ impl FileBrowser {
}).last()?; }).last()?;
let left_path = main_path.parent().map(|p| p.to_path_buf()); 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 main_widget = AsyncWidget::new(&core, Box::new(move |_| {
let mut list = ListView::new(&core_m, let main_dir = File::new(&main_path.file_name()?
Files::new_from_path(&main_path)?); .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.animate_slide_up().log();
list.content.meta_all(); list.content.meta_all();
Ok(list) Ok(list)
})); }));
let cache = fs_cache.clone();
if let Some(left_path) = left_path { if let Some(left_path) = left_path {
let left_widget = AsyncWidget::new(&core, Box::new(move |_| { 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, 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(); list.animate_slide_up().log();
Ok(list) Ok(list)
})); }));
@ -210,7 +237,7 @@ impl FileBrowser {
columns.push_widget(left_widget); 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::FileList(main_widget));
columns.push_widget(FileBrowserWidgets::Previewer(previewer)); columns.push_widget(FileBrowserWidgets::Previewer(previewer));
@ -219,11 +246,6 @@ impl FileBrowser {
let cwd = File::new_from_path(&cwd, None).unwrap(); 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 proc_view = ProcView::new(&core);
let bookmarks = BMPopup::new(&core); let bookmarks = BMPopup::new(&core);
@ -234,15 +256,12 @@ impl FileBrowser {
Ok(FileBrowser { columns: columns, Ok(FileBrowser { columns: columns,
cwd: cwd, cwd: cwd,
prev_cwd: None, prev_cwd: None,
selections: HashMap::new(),
cached_files: HashMap::new(),
core: core.clone(), core: core.clone(),
watcher: watcher,
watches: vec![],
dir_events: dir_events,
proc_view: Arc::new(Mutex::new(proc_view)), proc_view: Arc::new(Mutex::new(proc_view)),
bookmarks: Arc::new(Mutex::new(bookmarks)), 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<()> { pub fn enter_dir(&mut self) -> HResult<()> {
@ -306,33 +325,25 @@ impl FileBrowser {
} }
pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> { pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> {
self.cache_files().log();
let dir = dir.clone(); let dir = dir.clone();
let selected_file = self.get_selection(&dir).ok().cloned(); let cache = self.fs_cache.clone();
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()); self.prev_cwd = Some(self.cwd.clone());
self.cwd = dir.clone(); self.cwd = dir.clone();
let main_async_widget = self.main_async_widget_mut()?; let main_async_widget = self.main_async_widget_mut()?;
main_async_widget.change_to(Box::new(move |stale, core| { main_async_widget.change_to(Box::new(move |stale, core| {
let path = dir.path(); let (selected_file, files) = cache.get_files(&dir, stale)?;
let cached_files = cached_files.clone(); let files = files.wait()?;
let files = cached_files.or_else(|| {
Files::new_from_path_cancellable(&path, stale.clone()).ok()
})?;
let mut list = ListView::new(&core, files); let mut list = ListView::new(&core, files);
list.content.meta_set_fresh().log(); list.content.meta_set_fresh().log();
if let Some(file) = &selected_file { if let Some(file) = selected_file {
list.select_file(file); list.select_file(&file);
} }
Ok(list) Ok(list)
})).log(); })).log();
@ -341,7 +352,7 @@ impl FileBrowser {
self.left_widget_goto(&grand_parent).log(); self.left_widget_goto(&grand_parent).log();
} else { } else {
self.left_async_widget_mut()?.clear().log(); self.left_async_widget_mut()?.clear().log();
self.screen()?.flush(); Ok(self.screen()?.flush()?).log();
self.left_async_widget_mut()?.set_stale().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<()> { 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 dir = dir.clone();
let left_async_widget = self.left_async_widget_mut()?; let left_async_widget = self.left_async_widget_mut()?;
left_async_widget.change_to(Box::new(move |stale, core| { left_async_widget.change_to(Box::new(move |stale, core| {
let path = dir.path(); let cached_files = cache.get_files(&dir, stale)?;
let cached_files = cached_files.clone(); let (_, files) = cached_files;
let files = cached_files.or_else(|| { let files = files.wait()?;
Files::new_from_path_cancellable(&path, stale).ok()
})?;
let list = ListView::new(&core, files); let list = ListView::new(&core, files);
Ok(list) Ok(list)
@ -430,10 +439,8 @@ impl FileBrowser {
return Ok(()); return Ok(());
} }
let file = self.selected_file()?.clone(); 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()?; let preview = self.preview_widget_mut()?;
preview.set_file(&file, selection, cached_files).log(); preview.set_file(&file).log();
Ok(()) Ok(())
} }
@ -441,47 +448,45 @@ impl FileBrowser {
if !self.left_async_widget_mut()?.ready() { return Ok(()) } if !self.left_async_widget_mut()?.ready() { return Ok(()) }
if self.cwd.parent().is_none() { 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(&selection);
self.left_widget_mut()?.select_file(&left_selection);
Ok(()) Ok(())
} }
pub fn get_selection(&self, dir: &File) -> HResult<&File> { pub fn get_files(&self) -> HResult<Files> {
Ok(self.selections.get(dir)?)
}
pub fn get_files(&mut self) -> HResult<Files> {
Ok(self.main_widget()?.content.clone()) 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()) Ok(self.left_widget()?.content.clone())
} }
pub fn cache_files(&mut self, files: Files) -> HResult<()> { pub fn cache_files(&self) -> HResult<()> {
let dir = files.directory.clone(); if !self.fs_cache.is_cached(&self.cwd)? {
self.cached_files.insert(dir, files); let files = self.get_files()?;
Ok(()) let selected_file = self.selected_file().ok();
} self.fs_cache.put_files(files, selected_file).log();
} else {
pub fn get_cached_files(&mut self, dir: &File) -> HResult<Files> { let files = &self.main_widget()?.content;
Ok(self.cached_files.get(dir)?.clone()) let selected_file = self.selected_file().ok();
} self.fs_cache.save_settings(&files, selected_file).log();
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);
} }
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(()) Ok(())
} }
pub fn cwd(&self) -> HResult<&File> { pub fn cwd(&self) -> HResult<&File> {
Ok(&self.cwd) Ok(&self.cwd)
} }
@ -492,74 +497,22 @@ impl FileBrowser {
Ok(()) Ok(())
} }
pub fn left_dir(&self) -> HResult<File> { pub fn left_dir(&self) -> HResult<&File> {
let widget = self.left_widget()?; let widget = self.left_widget()?;
let dir = widget.content.directory.clone(); let dir = &widget.content.directory;
Ok(dir) Ok(dir)
} }
fn update_watches(&mut self) -> HResult<()> { fn replace_file(&mut self,
if !self.left_async_widget_mut()?.ready() || dir: &File,
!self.main_async_widget_mut()?.ready() { old: Option<&File>,
return Ok(()) new: Option<&File>) -> HResult<()> {
if &self.cwd == dir {
self.main_widget_mut()?.content.replace_file(old, new.cloned()).log();
} }
let watched_dirs = self.watches.clone(); if &self.left_dir()? == &dir {
let cwd = self.cwd()?.clone(); self.left_widget_mut()?.content.replace_file(old, new.cloned()).log();
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);
}
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?
}
}
}
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(()) Ok(())
} }
@ -631,13 +584,6 @@ impl FileBrowser {
widget 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> { pub fn preview_widget_mut(&mut self) -> HResult<&mut Previewer> {
match self.columns.widgets.get_mut(2)? { match self.columns.widgets.get_mut(2)? {
FileBrowserWidgets::Previewer(previewer) => Ok(previewer), FileBrowserWidgets::Previewer(previewer) => Ok(previewer),
@ -843,16 +789,13 @@ impl Widget for FileBrowser {
} }
} }
fn refresh(&mut self) -> HResult<()> { fn refresh(&mut self) -> HResult<()> {
//self.proc_view.lock()?.set_coordinates(self.get_coordinates()?);
self.set_title().log(); self.set_title().log();
self.handle_dir_events().log();
self.columns.refresh().log(); self.columns.refresh().log();
self.set_left_selection().log(); self.set_left_selection().log();
self.save_selection().log();
self.set_cwd().log(); self.set_cwd().log();
self.update_watches().log();
if !self.columns.zoom_active { self.update_preview().log(); } if !self.columns.zoom_active { self.update_preview().log(); }
self.columns.refresh().log(); self.columns.refresh().log();
self.cache_files().log();
Ok(()) Ok(())
} }

View File

@ -18,7 +18,6 @@ use users::{get_current_username,
use chrono::TimeZone; use chrono::TimeZone;
use failure::Error; use failure::Error;
use notify::DebouncedEvent; use notify::DebouncedEvent;
use rayon::prelude::*;
use rayon::{ThreadPool, ThreadPoolBuilder}; use rayon::{ThreadPool, ThreadPoolBuilder};
use crate::fail::{HResult, HError, ErrorLog}; use crate::fail::{HResult, HError, ErrorLog};
@ -234,7 +233,7 @@ impl Files {
.files .files
.sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)), .sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)),
SortBy::Size => { SortBy::Size => {
self.meta_all_sync(); self.meta_all_sync().log();
self.files.sort_by(|a, b| { self.files.sort_by(|a, b| {
if a.meta().unwrap().size() == b.meta().unwrap().size() { if a.meta().unwrap().size() == b.meta().unwrap().size() {
return alphanumeric_sort::compare_str(&b.name, &a.name); return alphanumeric_sort::compare_str(&b.name, &a.name);
@ -243,7 +242,7 @@ impl Files {
}); });
} }
SortBy::MTime => { SortBy::MTime => {
self.meta_all_sync(); self.meta_all_sync().log();
self.files.sort_by(|a, b| { self.files.sort_by(|a, b| {
if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() { if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() {
return alphanumeric_sort::compare_str(&a.name, &b.name); return alphanumeric_sort::compare_str(&a.name, &b.name);
@ -292,7 +291,17 @@ impl Files {
self.show_hidden = !self.show_hidden 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 { match event {
DebouncedEvent::Create(path) => { DebouncedEvent::Create(path) => {
self.path_in_here(&path)?; self.path_in_here(&path)?;
@ -329,12 +338,12 @@ impl Files {
} }
pub fn path_in_here(&self, path: &Path) -> HResult<bool> { 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() }; let path = if path.is_dir() { path } else { path.parent().unwrap() };
if dir == path { if dir == path {
Ok(true) Ok(true)
} else { } 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<()> { pub fn meta_all_sync(&mut self) -> HResult<()> {
for file in self.files.iter_mut() { for file in self.files.iter_mut() {
if !file.meta_processed { if !file.meta_processed {
let path = file.path.clone(); file.meta_sync().log();
file.meta = Async::new(Box::new(move|_| {
let meta = std::fs::metadata(&path)?;
Ok(meta)
}));
file.meta.wait()?;
} }
} }
self.dirty_meta.set_dirty(); self.set_dirty();
Ok(()) Ok(())
} }
@ -476,7 +480,6 @@ pub struct File {
pub meta_processed: bool, pub meta_processed: bool,
pub selected: bool, pub selected: bool,
pub tag: Option<bool> pub tag: Option<bool>
// flags: Option<String>,
} }
impl File { impl File {
@ -552,6 +555,14 @@ impl File {
Ok(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, pub fn make_async_meta(path: &PathBuf,
dirty_meta: Option<AsyncDirtyBit>, dirty_meta: Option<AsyncDirtyBit>,
stale_preview: Option<Stale>) -> Async<Metadata> { stale_preview: Option<Stale>) -> Async<Metadata> {
@ -632,6 +643,12 @@ impl File {
err @ Err(_) => { err?; } err @ Err(_) => { err?; }
} }
self.process_meta()?;
Ok(())
}
pub fn process_meta(&mut self) -> HResult<()> {
if let Ok(meta) = self.meta.get() { if let Ok(meta) = self.meta.get() {
let color = self.get_color(&meta); let color = self.get_color(&meta);
let target = if meta.file_type().is_symlink() { let target = if meta.file_type().is_symlink() {
@ -641,10 +658,7 @@ impl File {
self.color = color; self.color = color;
self.target = target; self.target = target;
self.meta_processed = true; self.meta_processed = true;
return Ok(())
} }
Ok(()) Ok(())
} }
@ -653,11 +667,12 @@ impl File {
self.meta = File::make_async_meta(&self.path, self.meta = File::make_async_meta(&self.path,
self.dirty_meta.clone(), self.dirty_meta.clone(),
None); None);
self.meta.run(); self.meta.run().log();
if self.dirsize.is_some() { if self.dirsize.is_some() {
self.dirsize self.dirsize
= Some(File::make_async_dirsize(&self.path, self.dirty_meta.clone(), None)); = Some(File::make_async_dirsize(&self.path, self.dirty_meta.clone(), None));
self.dirsize.as_mut()?.run(); self.dirsize.as_mut()?.run().log();
} }
Ok(()) Ok(())
} }
@ -872,7 +887,6 @@ impl File {
pub fn pretty_mtime(&self) -> Option<String> { pub fn pretty_mtime(&self) -> Option<String> {
if self.meta().is_err() { return None } if self.meta().is_err() { return None }
//let time = chrono::DateTime::from_timestamp(self.mtime, 0);
let time: chrono::DateTime<chrono::Local> let time: chrono::DateTime<chrono::Local>
= chrono::Local.timestamp(self.meta().unwrap().mtime(), 0); = chrono::Local.timestamp(self.meta().unwrap().mtime(), 0);
Some(time.format("%F %R").to_string()) Some(time.format("%F %R").to_string())
@ -930,7 +944,6 @@ impl PathBufExt for PathBuf {
if let Some(name) = self.file_name() { if let Some(name) = self.file_name() {
let mut name = name.as_bytes().to_vec(); let mut name = name.as_bytes().to_vec();
let mut quote = "\"".as_bytes().to_vec(); let mut quote = "\"".as_bytes().to_vec();
//let mut quote_after = "\"".as_bytes().to_vec();
let mut quoted = vec![]; let mut quoted = vec![];
quoted.append(&mut quote.clone()); quoted.append(&mut quote.clone());
quoted.append(&mut name); quoted.append(&mut name);
@ -988,7 +1001,6 @@ impl OsStrTools for OsStr {
split_pos split_pos
}).iter() }).iter()
.map(|(start, end)| { .map(|(start, end)| {
//let orig_string = orig_string.clone();
OsString::from_vec(orig_string[*start..*end] OsString::from_vec(orig_string[*start..*end]
.to_vec()).replace(&OsString::from_vec(pat.clone()), .to_vec()).replace(&OsString::from_vec(pat.clone()),
&OsString::from("")) &OsString::from(""))

294
src/fscache.rs Normal file
View File

@ -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<&PathBuf> {
match self {
DebouncedEvent::Create(path) |
DebouncedEvent::Write(path) |
DebouncedEvent::Chmod(path) |
DebouncedEvent::Remove(path) |
DebouncedEvent::NoticeWrite(path) |
DebouncedEvent::NoticeRemove(path) => Ok(path),
DebouncedEvent::Rename(old_path, _) => Ok(old_path),
DebouncedEvent::Error(err, path)
=> Err(HError::INotifyError(format!("{}, {:?}", err, path))),
DebouncedEvent::Rescan
=> Err(HError::INotifyError("Need to rescan".to_string()))
}
}
}

View File

@ -1,6 +1,7 @@
#![feature(vec_remove_item)] #![feature(vec_remove_item)]
#![feature(trivial_bounds)] #![feature(trivial_bounds)]
#![feature(try_trait)] #![feature(try_trait)]
#![feature(fnbox)]
extern crate termion; extern crate termion;
extern crate unicode_width; extern crate unicode_width;
@ -46,6 +47,9 @@ mod bookmarks;
mod paths; mod paths;
mod foldview; mod foldview;
mod dirty; mod dirty;
mod fscache;
@ -84,7 +88,7 @@ fn main() -> HResult<()> {
fn run(mut core: WidgetCore) -> HResult<()> { fn run(mut core: WidgetCore) -> HResult<()> {
core.screen.clear()?; core.screen.clear()?;
let filebrowser = FileBrowser::new_cored(&core)?; let filebrowser = FileBrowser::new(&core, None)?;
let mut tabview = TabView::new(&core); let mut tabview = TabView::new(&core);
tabview.push_widget(filebrowser)?; tabview.push_widget(filebrowser)?;

View File

@ -1,8 +1,9 @@
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use std::boxed::FnBox;
use rayon::ThreadPool; use rayon::ThreadPool;
use crate::files::{File, Files, Kind}; use crate::files::{File, Kind};
use crate::listview::ListView; use crate::listview::ListView;
use crate::textview::TextView; use crate::textview::TextView;
use crate::widget::{Widget, WidgetCore}; use crate::widget::{Widget, WidgetCore};
@ -10,10 +11,11 @@ use crate::coordinates::Coordinates;
use crate::fail::{HResult, HError, ErrorLog}; use crate::fail::{HResult, HError, ErrorLog};
pub type AsyncValueFn<T> = Box<Fn(Stale) -> HResult<T> + Send>; pub type AsyncValueFn<T> = Box<dyn FnBox(Stale) -> HResult<T> + Send + Sync>;
pub type AsyncValue<T> = Arc<Mutex<Option<HResult<T>>>>; pub type AsyncValue<T> = Arc<Mutex<Option<HResult<T>>>>;
pub type AsyncReadyFn = Box<Fn() -> HResult<()> + Send>; pub type AsyncReadyFn = Box<dyn FnBox() -> HResult<()> + Send + Sync>;
pub type AsyncWidgetFn<W> = Box<Fn(Stale, WidgetCore) -> HResult<W> + Send>; pub type AsyncWidgetFn<W> = Box<dyn FnBox(Stale, WidgetCore)
-> HResult<W> + Send + Sync>;
type WidgetO = Box<dyn Widget + Send>; type WidgetO = Box<dyn Widget + Send>;
@ -81,6 +83,8 @@ pub struct Async<T: Send> {
stale: Stale, stale: Stale,
} }
impl<T: Send + 'static> Async<T> { impl<T: Send + 'static> Async<T> {
pub fn new(closure: AsyncValueFn<T>) pub fn new(closure: AsyncValueFn<T>)
-> Async<T> { -> Async<T> {
@ -120,6 +124,17 @@ impl<T: Send + 'static> Async<T> {
} }
} }
pub fn run_async(async_fn: Arc<Mutex<Option<AsyncValueFn<T>>>>,
async_value: AsyncValue<T>,
on_ready_fn: Arc<Mutex<Option<AsyncReadyFn>>>,
stale: Stale) -> HResult<()> {
let value_fn = async_fn.lock()?.take()?;
let value = value_fn.call_box((stale.clone(),));
async_value.lock()?.replace(value);
on_ready_fn.lock()?.take()?.call_box(()).log();
Ok(())
}
pub fn run(&mut self) -> HResult<()> { pub fn run(&mut self) -> HResult<()> {
if self.started { if self.started {
HError::async_started()? HError::async_started()?
@ -131,45 +146,11 @@ impl<T: Send + 'static> Async<T> {
let on_ready_fn = self.on_ready.clone(); let on_ready_fn = self.on_ready.clone();
self.started = true; self.started = true;
std::thread::spawn(move|| -> HResult<()> { std::thread::spawn(move || {
let value = closure.lock().map(|closure| Async::run_async(closure,
closure.as_ref().map(|closure| async_value,
closure(stale))); on_ready_fn,
stale).log();
if let Ok(value) = value {
if let Some(value) = value {
match value {
Ok(mut value) => {
async_value
.lock()
.map(|mut async_value|
async_value.replace(Ok(value))).ok();
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().log();
}
on_ready_fn.take();
}
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();
}
Ok(())
}); });
Ok(()) Ok(())
} }
@ -186,58 +167,23 @@ impl<T: Send + 'static> Async<T> {
self.started = true; self.started = true;
pool.spawn(move || { pool.spawn(move || {
let value = closure.lock().map(|closure| Async::run_async(closure,
closure.as_ref().map(|closure| async_value,
closure(stale))); on_ready_fn,
stale).log();
if let Ok(value) = value {
if let Some(value) = value {
match value {
Ok(mut value) => {
async_value
.lock()
.map(|mut async_value|
async_value.replace(Ok(value))).ok();
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().log();
}
on_ready_fn.take();
}
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();
}
}); });
Ok(()) Ok(())
} }
pub fn wait(&mut self) -> HResult<()> { pub fn wait(self) -> HResult<T> {
let closure = self.async_closure.lock()?.take()?; Async::run_async(self.async_closure,
let mut value = closure(self.stale.clone()); self.async_value.clone(),
let on_ready_fn = self.on_ready.lock()?.take(); self.on_ready,
self.stale).log();
let value = self.async_value.lock()?.take()?;
if let Ok(ref mut value) = value { value
on_ready_fn.map(|on_ready_fn| on_ready_fn());
}
self.value = value;
Ok(())
} }
pub fn set_stale(&mut self) -> HResult<()> { pub fn set_stale(&mut self) -> HResult<()> {
@ -258,6 +204,10 @@ impl<T: Send + 'static> Async<T> {
self.stale.clone() self.stale.clone()
} }
pub fn put_stale(&mut self, stale: Stale) {
self.stale = stale;
}
pub fn is_started(&self) -> bool { pub fn is_started(&self) -> bool {
self.started self.started
} }
@ -326,10 +276,11 @@ pub struct AsyncWidget<W: Widget + Send + 'static> {
impl<W: Widget + Send + 'static> AsyncWidget<W> { impl<W: Widget + Send + 'static> AsyncWidget<W> {
pub fn new(core: &WidgetCore, closure: AsyncValueFn<W>) -> AsyncWidget<W> { pub fn new(core: &WidgetCore, closure: AsyncValueFn<W>) -> AsyncWidget<W> {
let sender = core.get_sender(); let sender = Mutex::new(core.get_sender());
let mut widget = Async::new(Box::new(move |stale| closure(stale))); let mut widget = Async::new(Box::new(move |stale|
closure.call_box((stale,))));
widget.on_ready(Box::new(move || { widget.on_ready(Box::new(move || {
sender.send(crate::widget::Events::WidgetReady)?; sender.lock()?.send(crate::widget::Events::WidgetReady)?;
Ok(()) Ok(())
})); }));
widget.run().log(); widget.run().log();
@ -342,15 +293,15 @@ impl<W: Widget + Send + 'static> AsyncWidget<W> {
pub fn change_to(&mut self, closure: AsyncWidgetFn<W>) -> HResult<()> { pub fn change_to(&mut self, closure: AsyncWidgetFn<W>) -> HResult<()> {
self.set_stale().log(); self.set_stale().log();
let sender = self.get_core()?.get_sender(); let sender = Mutex::new(self.get_core()?.get_sender());
let core = self.get_core()?.clone(); let core = self.get_core()?.clone();
let mut widget = Async::new(Box::new(move |stale| { let mut widget = Async::new(Box::new(move |stale| {
closure(stale, core.clone()) closure.call_box((stale, core.clone(),))
})); }));
widget.on_ready(Box::new(move || { widget.on_ready(Box::new(move || {
sender.send(crate::widget::Events::WidgetReady)?; sender.lock()?.send(crate::widget::Events::WidgetReady)?;
Ok(()) Ok(())
})); }));
@ -385,16 +336,7 @@ impl<W: Widget + Send + 'static> AsyncWidget<W> {
} }
} }
// impl<T: Widget + Send> WillBeWidget<T> {
// fn is_widget(&self) -> bool {
// self.willbe.check().is_ok()
// }
// fn take_widget(self) {
// if self.is_widget() {
// let widget = self.willbe.take();
// }
// }
//}
impl<T: Widget + Send + 'static> Widget for AsyncWidget<T> { impl<T: Widget + Send + 'static> Widget for AsyncWidget<T> {
fn get_core(&self) -> HResult<&WidgetCore> { fn get_core(&self) -> HResult<&WidgetCore> {
@ -458,17 +400,18 @@ impl PartialEq for Previewer {
} }
} }
use crate::fscache::FsCache;
pub struct Previewer { pub struct Previewer {
widget: AsyncWidget<Box<dyn Widget + Send>>, widget: AsyncWidget<Box<dyn Widget + Send>>,
core: WidgetCore, core: WidgetCore,
file: Option<File>, file: Option<File>,
selection: Option<File>, pub cache: FsCache,
cached_files: Option<Files>,
} }
impl Previewer { impl Previewer {
pub fn new(core: &WidgetCore) -> Previewer { pub fn new(core: &WidgetCore, cache: FsCache) -> Previewer {
let core_ = core.clone(); let core_ = core.clone();
let widget = AsyncWidget::new(&core, Box::new(move |_| { let widget = AsyncWidget::new(&core, Box::new(move |_| {
Ok(Box::new(TextView::new_blank(&core_)) as Box<dyn Widget + Send>) Ok(Box::new(TextView::new_blank(&core_)) as Box<dyn Widget + Send>)
@ -476,8 +419,7 @@ impl Previewer {
Previewer { widget: widget, Previewer { widget: widget,
core: core.clone(), core: core.clone(),
file: None, file: None,
selection: None, cache: cache }
cached_files: None }
} }
fn become_preview(&mut self, fn become_preview(&mut self,
@ -493,42 +435,34 @@ impl Previewer {
} }
pub fn set_file(&mut self, pub fn set_file(&mut self,
file: &File, file: &File) -> HResult<()> {
selection: Option<File>,
cached_files: Option<Files>) -> HResult<()> {
if Some(file) == self.file.as_ref() && !self.widget.is_stale()? { return Ok(()) } if Some(file) == self.file.as_ref() && !self.widget.is_stale()? { return Ok(()) }
self.file = Some(file.clone()); self.file = Some(file.clone());
self.selection = selection.clone();
self.cached_files = cached_files.clone();
let coordinates = self.get_coordinates().unwrap().clone(); let coordinates = self.get_coordinates().unwrap().clone();
let file = file.clone(); let file = file.clone();
let core = self.core.clone(); let core = self.core.clone();
let cache = self.cache.clone();
self.widget.set_stale().ok(); self.widget.set_stale().ok();
self.become_preview(Ok(AsyncWidget::new(&self.core, self.become_preview(Ok(AsyncWidget::new(&self.core,
Box::new(move |stale| { Box::new(move |stale: Stale| {
kill_proc().unwrap(); kill_proc().unwrap();
let file = file.clone();
let selection = selection.clone();
let cached_files = cached_files.clone();
if file.kind == Kind::Directory { if file.kind == Kind::Directory {
let preview = Previewer::preview_dir(&file, let preview = Previewer::preview_dir(&file,
selection, cache,
cached_files,
&core, &core,
stale.clone()); stale);
return preview; return preview;
} }
if file.get_mime() == Some("text".to_string()) { if file.get_mime() == Some("text".to_string()) {
return Previewer::preview_text(&file, &core, stale.clone()) return Previewer::preview_text(&file, &core, stale)
} }
let preview = Previewer::preview_external(&file, &core, stale.clone()); let preview = Previewer::preview_external(&file, &core, stale);
if preview.is_ok() { return preview; } if preview.is_ok() { return preview; }
else { else {
let mut blank = Box::new(TextView::new_blank(&core)); let mut blank = Box::new(TextView::new_blank(&core));
@ -543,8 +477,7 @@ impl Previewer {
pub fn reload(&mut self) { pub fn reload(&mut self) {
if let Some(file) = self.file.clone() { if let Some(file) = self.file.clone() {
self.file = None; self.file = None;
let cache = self.cached_files.take(); self.set_file(&file).log();
self.set_file(&file, self.selection.clone(), cache).log();
} }
} }
@ -553,15 +486,14 @@ impl Previewer {
} }
fn preview_dir(file: &File, fn preview_dir(file: &File,
selection: Option<File>, cache: FsCache,
cached_files: Option<Files>,
core: &WidgetCore, core: &WidgetCore,
stale: Stale) stale: Stale)
-> Result<WidgetO, HError> { -> Result<WidgetO, HError> {
let files = cached_files.or_else(|| { let (selection, cached_files) = cache.get_files(&file, stale.clone())?;
Files::new_from_path_cancellable(&file.path,
stale.clone()).ok() let files = cached_files.wait()?;
})?;
let len = files.len(); let len = files.len();
if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) } if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) }
@ -675,157 +607,6 @@ impl Widget for Previewer {
// #[derive(PartialEq)]
// pub struct AsyncPreviewer {
// pub file: Option<File>,
// pub buffer: String,
// pub coordinates: Coordinates,
// pub async_plug: AsyncPlug2<Box<dyn Widget + Send + 'static>>
// }
// impl AsyncPreviewer {
// pub fn new() -> AsyncPreviewer {
// let closure = Box::new(|| {
// Box::new(crate::textview::TextView {
// lines: vec![],
// buffer: "".to_string(),
// coordinates: Coordinates::new()
// }) as Box<dyn Widget + Send + 'static>
// });
// AsyncPreviewer {
// file: None,
// buffer: String::new(),
// coordinates: Coordinates::new(),
// async_plug: AsyncPlug2::new_from_closure(closure),
// }
// }
// pub fn set_file(&mut self, file: &File) {
// let coordinates = self.coordinates.clone();
// let file = file.clone();
// let redraw = crate::term::reset() + &self.get_redraw_empty_list(0);
// //let pids = PIDS.clone();
// //kill_procs();
// self.async_plug.replace_widget(Box::new(move || {
// kill_procs();
// let mut bufout = std::io::BufWriter::new(std::io::stdout());
// match &file.kind {
// Kind::Directory => match Files::new_from_path(&file.path) {
// Ok(files) => {
// //if !is_current(&file) { return }
// let len = files.len();
// //if len == 0 { return };
// let mut file_list = ListView::new(files);
// file_list.set_coordinates(&coordinates);
// file_list.refresh();
// //if !is_current(&file) { return }
// file_list.animate_slide_up();
// return Box::new(file_list)
// }
// Err(err) => {
// write!(bufout, "{}", redraw).unwrap();
// let textview = crate::textview::TextView {
// lines: vec![],
// buffer: "".to_string(),
// coordinates: Coordinates::new(),
// };
// return Box::new(textview)
// },
// }
// _ => {
// if file.get_mime() == Some("text".to_string()) {
// let lines = coordinates.ysize() as usize;
// let mut textview
// = TextView::new_from_file_limit_lines(&file,
// lines);
// //if !is_current(&file) { return }
// textview.set_coordinates(&coordinates);
// textview.refresh();
// //if !is_current(&file) { return }
// textview.animate_slide_up();
// return Box::new(textview)
// } else {
// let process =
// std::process::Command::new("scope.sh")
// .arg(&file.name)
// .arg("10".to_string())
// .arg("10".to_string())
// .arg("".to_string())
// .arg("false".to_string())
// .stdin(std::process::Stdio::null())
// .stdout(std::process::Stdio::piped())
// .stderr(std::process::Stdio::null())
// .spawn().unwrap();
// let pid = process.id();
// PIDS.lock().unwrap().push(pid);
// //if !is_current(&file) { return }
// let output = process.wait_with_output();
// match output {
// Ok(output) => {
// let status = output.status.code();
// match status {
// Some(status) => {
// if status == 0 || status == 5 && is_current(&file) {
// let output = std::str::from_utf8(&output.stdout)
// .unwrap()
// .to_string();
// let mut textview = TextView {
// lines: output.lines().map(|s| s.to_string()).collect(),
// buffer: String::new(),
// coordinates: Coordinates::new() };
// textview.set_coordinates(&coordinates);
// textview.refresh();
// textview.animate_slide_up();
// return Box::new(textview)
// }
// }, None => {}
// }
// }, Err(_) => {}
// }
// write!(bufout, "{}", redraw).unwrap();
// //std::io::stdout().flush().unwrap();
// let textview = crate::textview::TextView {
// lines: vec![],
// buffer: "".to_string(),
// coordinates: Coordinates::new(),
// };
// return Box::new(textview)
// }
// }
// }}))
// }
// }
impl<T> Widget for Box<T> where T: Widget + ?Sized { impl<T> Widget for Box<T> where T: Widget + ?Sized {
fn get_core(&self) -> HResult<&WidgetCore> { fn get_core(&self) -> HResult<&WidgetCore> {
Ok((**self).get_core()?) Ok((**self).get_core()?)

View File

@ -1,9 +1,8 @@
use termion::event::Key; use termion::event::Key;
use crate::widget::{Widget, WidgetCore}; use crate::widget::{Widget, WidgetCore, Events};
use crate::fail::{HResult, ErrorLog}; use crate::fail::{HResult, ErrorLog};
use crate::coordinates::Coordinates; use crate::coordinates::Coordinates;
use crate::dirty::Dirtyable;
pub trait Tabbable { pub trait Tabbable {
fn new_tab(&mut self) -> HResult<()>; fn new_tab(&mut self) -> HResult<()>;
@ -26,6 +25,7 @@ pub trait Tabbable {
_ => self.on_key_sub(key) _ => self.on_key_sub(key)
} }
} }
fn on_refresh(&mut self) -> HResult<()> { Ok(()) }
} }
@ -137,7 +137,8 @@ impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
} }
}).collect::<String>(); }).collect::<String>();
let nums_pos = xsize - nums_length as u16;
let nums_pos = xsize.saturating_sub(nums_length as u16);
Ok(format!("{}{}{}{}", Ok(format!("{}{}{}{}",
header, header,
@ -152,6 +153,7 @@ impl<T> Widget for TabView<T> where T: Widget, TabView<T>: Tabbable {
} }
fn refresh(&mut self) -> HResult<()> { fn refresh(&mut self) -> HResult<()> {
Tabbable::on_refresh(self).log();
self.active_tab_mut().refresh() self.active_tab_mut().refresh()
} }

View File

@ -1,6 +1,6 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, Receiver, channel}; use std::sync::mpsc::{Sender, Receiver, channel};
use std::io::Write; use std::io::{Write, stdin};
use termion::event::{Event, Key, MouseEvent}; use termion::event::{Event, Key, MouseEvent};
use termion::input::TermRead; use termion::input::TermRead;
@ -11,17 +11,16 @@ use crate::minibuffer::MiniBuffer;
use crate::term; use crate::term;
use crate::term::{Screen, ScreenExt}; use crate::term::{Screen, ScreenExt};
use crate::dirty::{Dirtyable, DirtyBit}; use crate::dirty::{Dirtyable, DirtyBit};
use crate::preview::Stale;
use crate::signal_notify::{notify, Signal}; use crate::signal_notify::{notify, Signal};
use std::io::stdin;
#[derive(Debug)] #[derive(Debug)]
pub enum Events { pub enum Events {
InputEvent(Event), InputEvent(Event),
WidgetReady, WidgetReady,
TerminalResized, TerminalResized,
ExclusiveEvent(Option<Sender<Events>>), ExclusiveEvent(Option<Mutex<Option<Sender<Events>>>>),
InputEnabled(bool), InputEnabled(bool),
RequestInput, RequestInput,
Status(String) Status(String)
@ -52,7 +51,7 @@ pub struct WidgetCore {
pub screen: Screen, pub screen: Screen,
pub coordinates: Coordinates, pub coordinates: Coordinates,
pub minibuffer: Arc<Mutex<Option<MiniBuffer>>>, pub minibuffer: Arc<Mutex<Option<MiniBuffer>>>,
pub event_sender: Sender<Events>, pub event_sender: Arc<Mutex<Sender<Events>>>,
event_receiver: Arc<Mutex<Option<Receiver<Events>>>>, event_receiver: Arc<Mutex<Option<Receiver<Events>>>>,
pub status_bar_content: Arc<Mutex<Option<String>>>, pub status_bar_content: Arc<Mutex<Option<String>>>,
term_size: (usize, usize), term_size: (usize, usize),
@ -74,7 +73,7 @@ impl WidgetCore {
screen: screen, screen: screen,
coordinates: coords, coordinates: coords,
minibuffer: Arc::new(Mutex::new(None)), minibuffer: Arc::new(Mutex::new(None)),
event_sender: sender, event_sender: Arc::new(Mutex::new(sender)),
event_receiver: Arc::new(Mutex::new(Some(receiver))), event_receiver: Arc::new(Mutex::new(Some(receiver))),
status_bar_content: status_bar_content, status_bar_content: status_bar_content,
term_size: (xsize, ysize), term_size: (xsize, ysize),
@ -86,7 +85,7 @@ impl WidgetCore {
} }
pub fn get_sender(&self) -> Sender<Events> { pub fn get_sender(&self) -> Sender<Events> {
self.event_sender.clone() self.event_sender.lock().unwrap().clone()
} }
} }
@ -241,7 +240,9 @@ pub trait Widget {
fn run_widget(&mut self) -> HResult<()> { fn run_widget(&mut self) -> HResult<()> {
let (tx_event, rx_event) = channel(); let (tx_event, rx_event) = channel();
self.get_core()?.get_sender().send(Events::ExclusiveEvent(Some(tx_event)))?; self.get_core()?
.get_sender()
.send(Events::ExclusiveEvent(Some(Mutex::new(Some(tx_event)))))?;
self.get_core()?.get_sender().send(Events::RequestInput)?; self.get_core()?.get_sender().send(Events::RequestInput)?;
self.clear()?; self.clear()?;
@ -378,7 +379,7 @@ pub trait Widget {
fn show_status(&self, status: &str) -> HResult<()> { fn show_status(&self, status: &str) -> HResult<()> {
let xsize = self.get_core()?.coordinates.xsize_u(); let xsize = self.get_core()?.coordinates.xsize_u();
let sized_status = term::sized_string_u(status, xsize); let sized_status = term::sized_string_u(status, xsize);
HError::log(status.to_string()).log(); HError::log::<()>(status.to_string()).log();
{ {
let mut status_content = self.get_core()?.status_bar_content.lock()?; let mut status_content = self.get_core()?.status_bar_content.lock()?;
*status_content = Some(sized_status); *status_content = Some(sized_status);
@ -437,7 +438,10 @@ fn dispatch_events(tx_internal: Sender<Events>,
for event in rx_event.iter() { for event in rx_event.iter() {
match &event { match &event {
Events::ExclusiveEvent(tx_event) => { Events::ExclusiveEvent(tx_event) => {
tx_exclusive_event = tx_event.clone(); tx_exclusive_event = match tx_event {
Some(locked_sender) => locked_sender.lock().unwrap().take(),
None => None
}
} }
Events::InputEnabled(state) => { Events::InputEnabled(state) => {
input_enabled = *state; input_enabled = *state;